PawVision

Real-time cat vs dog detector for Android. Point the camera or upload a photo and it tells you which one it sees, running a custom-trained YOLO11 model fully on-device.

KotlinAndroidTensorFlow LiteCameraXYOLO11

PawVision started as a classifier trained on the classic Kaggle Dogs vs Cats dataset and turned into a real Android app, the kind of project that shows what it actually takes to ship a model as something people can install and use, not just a notebook. It's also my first Android app, the project that got me into mobile development in the first place.

The first version was a MobileNetV3Large transfer-learning classifier converted to an int8 TFLite model, doing single-shot classification on a gallery photo. I later retrained it as a YOLO11 detector so the app could draw a live bounding box and run continuously off the camera feed, not just on uploaded images.

The clip above is PawVision correctly spotting a real stray cat outside, live, at 92% confidence.

Technical Breakdown

PawVision is really two systems stitched together: an offline training pipeline that runs once, in a Kaggle notebook, to produce a model file, and an on-device inference pipeline that runs continuously on the phone every time you point the camera at something. The four pieces below walk through both halves, plus the two ways I checked that the training actually worked before shipping it.

Training pipeline

Training pipeline

This is the offline half of the system: everything here happens once, in the Kaggle notebook, before the app ever runs. The purple box (training) is where the actual learning happens: YOLO11 is fed labeled cat/dog images and adjusts its weights to minimize detection loss over multiple epochs. Once training converges, the model is exported and quantized down to float16 TFLite, trading a bit of precision for a much smaller, faster file that can run live on a phone's CPU/GPU instead of needing a server.

Inference pipeline

Inference pipeline

This is the online half: it runs about every 300ms on your phone, live, once the trained model above is bundled into the app. CameraX streams a frame from the live camera preview (or a picked gallery photo), which gets resized to 640×640 and normalized to match what the model saw during training. The float16 TFLite model then runs entirely on-device, no server round trip, and its raw output tensor gets decoded into bounding boxes and Cat/Dog confidence scores, which are drawn straight onto a Canvas overlay on top of the live feed.

Training and validation loss

Training and validation loss

Training loss and validation loss (top and bottom rows) fall together and stay close the whole way through 50 epochs. If validation loss had started climbing back up while training loss kept dropping, that's the classic sign of overfitting, the model memorizing training images instead of learning the general pattern. Precision, recall, and mAP, the model's actual detection accuracy scores, all climb past 98% over the same run.

Detections on unseen validation images

Detections on unseen validation images

A batch of real validation photos the model never saw during training, with its own predicted boxes and confidence scores drawn on top by the model itself. Almost every box lands in the 0.8–1.0 confidence range, including on trickier cases like partially visible pets and odd angles, which is what actually generalizing to new photos looks like, not just a number in a metrics table.