Skip to content

Repository files navigation

GorillaIdentifier: Android App

Android Kotlin TFLite

Individual facial recognition for mountain gorillas (Gorilla beringei beringei). Fully offline, runs on any Android phone.

Field companion to the GorillaIdentifier ML Pipeline, which produced the models and gallery bundled in this app.

User guide: GorillaIdentifier_User_Guide.docx, a step-by-step guide to the app for rangers.


Screenshots

Screenshots go here. Suggested set: home screen, a scan result on a known individual, an unknown-individual result, and the gallery list.

github_assets/
    home_screen.png
    result_screen.png
    gallery_screen.png

Drop the images in github_assets/ with those names and they will render in this section. sample_assets/ contains a few real field photos and a short screen recording that can be used to produce them.


Features

The app is fully offline: every step, from face detection to identity matching, runs on-device via TensorFlow Lite, with no network connection required.

A photo is analyzed with the YOLO face detector first, which can find one or several gorilla faces in the same image, then each face is matched against the gallery using the MegaDescriptor backbone. Matching uses max-over-exemplars scoring: every known individual is represented by up to 25 exemplar embeddings rather than a single average, and a photo is matched against the closest of those exemplars. This captures natural variation in pose, lighting, and age far better than a single centroid would.

Rangers can add new individuals directly in the field by taking 5 to 20 or more photos; the app computes and stores the embedding prototype on-device, after checking that the result is neither too generic nor liable to be confused with an existing individual. Existing individuals can also be reinforced with additional field photos over time, gated by a similarity check so that blurry or mismatched shots do not corrupt the profile.

The gallery, a single gallery.json file, can be exported in full to back it up or hand it to a colleague, or exported as a lightweight per-individual patch for sharing just the latest additions. Every change to the gallery is backed up automatically, with up to 20 rolling restore points and a one-tap undo for the most recent change. Both TFLite models and the gallery can be replaced from the Settings screen without reinstalling the app, either individually or as a single update bundle. The app also keeps a local history of every scan performed.


Inference Pipeline

flowchart LR
    A[Camera / Photo] --> B["YOLO TFLite\n(6 MB)\nFace detection"]
    B --> C["Crop with 10% margin\n+ optional luminance\nequalization"]
    C --> D["MegaDescriptor-T-224 TFLite\n(108 MB)\nSwin Transformer\n-> 768-dim embedding"]
    D --> E["L2-normalize"]
    E --> F{"Max cosine similarity\nvs up to 25 exemplars\nper individual"}
    F -->|"score ≥ 0.4689"| G["Known individual\n+ confidence"]
    F -->|"score < 0.4689"| H["Unknown"]
    style A fill:#1e1e2e,stroke:#585b70,color:#cdd6f4
    style B fill:#1e1e2e,stroke:#585b70,color:#cdd6f4
    style C fill:#1e1e2e,stroke:#585b70,color:#cdd6f4
    style D fill:#1e1e2e,stroke:#585b70,color:#cdd6f4
    style E fill:#1e1e2e,stroke:#585b70,color:#cdd6f4
    style F fill:#1e1e2e,stroke:#fab387,color:#cdd6f4
    style G fill:#1e1e2e,stroke:#a6e3a1,color:#a6e3a1
    style H fill:#1e1e2e,stroke:#f38ba8,color:#f38ba8
Loading

The 0.4689 threshold and the 0.4351 separability gap between individuals both come from the calibration run on the V1 gorilla gallery, described in the pipeline's technical documentation.

Preprocessing (megadescriptor normalization)

pixel_normalized = (pixel / 255.0 - 0.5) / 0.5

Applied channel-wise (RGB) before the backbone. TfliteInterpreterFactory picks the fastest available accelerator for both models automatically: NNAPI first, then a GPU delegate, then CPU with 4 threads and XNNPACK if neither is available. This fallback chain runs on every device the app supports, down to Android 8.0.

NNAPI note: the Swin Transformer backbone uses 6-dimensional tensors in its FullyConnected operations, which exceed NNAPI's 4-dimensional tensor limit on most devices. The app then falls back to GPU or CPU + XNNPACK automatically. This is expected behavior, not an error.


Gallery Format

The bundled gallery.json (about 30 MB) contains the identity prototypes for the 66 individuals identified by the V1 pipeline run.

Structure

{
  "version": "1.0",
  "species": "Gorilla beringei beringei",
  "model": "MegaDescriptor-T-224 + SubCenterArcFace V1",
  "embedding_dim": 768,
  "normalization": "megadescriptor",
  "unknown_threshold": 0.4689,
  "separability_gap": 0.4351,
  "n_individuals": 66,
  "individuals": {
    "ADF Anangana": {
      "class_index": 0,
      "category": "ADF",
      "embeddings": [/* 14 exemplars, each a float[768] array, L2-normalized */],
      "mean_intra": 0.8128
    }
  }
}

category follows the same age-sex convention as the pipeline: SB (silverback), ADF (adult female), AD (adult), SAM/SAF (subadult male/female), JUV (juvenile), BB (baby/blackback). It is informational only and does not affect matching.

Backward Compatibility

Individuals added from within the app are stored with a single-element embeddings list, one anchor exemplar. Max-over-exemplars over one element is equivalent to a plain dot product, so app-created entries work transparently alongside the pipeline-trained gallery.


Adding a New Individual

Open the home screen and tap Add Individual. Enter a name, then take or import at least 5 photos, ten to twenty are recommended for a more robust profile. The app runs the same face detector used for scanning, so the individual does not need to be pre-cropped in the source photos. Review the detected face crops and remove any that are the wrong angle, blurry, or of the wrong individual, then confirm.

Two checks run automatically before the new prototype is written to the gallery. The merged embedding must be similar enough to its own exemplars to be usable at all, which prevents saving an overly generic profile that would never match anything. It must also stay below the matching threshold against every other individual already in the gallery, which prevents two individuals from being confused with each other later.

Adding Field Photos to an Existing Individual

From the gallery list, tap the camera icon on any individual to add more field photos. These are kept in a separate field prototype and never overwrite the original training exemplars. A single submitted photo is checked strictly against the individual's anchor embedding, since one bad photo, a screenshot or a heavily compressed image for instance, would otherwise corrupt the profile outright. A batch of three or more photos is checked instead against the anchor using the average of the whole batch, which is a more lenient check since averaging several photos of the same individual cancels out per-photo noise. Field contributions are capped at 50 photos per individual; further submissions are accepted for review but silently discarded from the profile once the cap is reached.


Gallery Sharing & Versioning

Full Gallery Export

The gallery screen's export option shares the complete gallery.json. A recipient imports it from Settings to replace their own gallery outright.

Per-Individual Patch

After adding one or more individuals, tap Share with Colleagues to export just those additions as a small JSON patch, tagged with the ranger's name and a timestamp. Recipients import it from Settings; it merges into their existing gallery without touching any individual not included in the patch.

Automatic Backups & Restore

Every write to the gallery, whether from adding an individual, adding field photos, or importing a patch, creates an automatic backup first, with the 20 most recent kept on the device. The gallery screen offers a one-tap undo for the last change, and Settings shows the full backup history with a summary of what changed since each one, which individuals were added or removed and how many field photos were merged, so a specific point can be restored deliberately rather than blindly.


Model Hot-Swap

TFLite models and the gallery can be replaced from Settings -> Import, either as individual files or as a single .zip update bundle. The app identifies file roles by name rather than by fixed filenames: any .tflite file whose name contains backbone, megadesc, resnet, classifier, or embedder is treated as the recognition backbone, and any file containing yolo or detector is treated as the face detector. A gallery.json or legacy embeddings.json in the same bundle updates the gallery. Bundle import validates every file (TFLite magic bytes, JSON structure, and embedding-dimension compatibility between the gallery and the active backbone) before replacing anything, and backs up the current gallery first if it is being changed.


Models

File Size Description In this repository
yolo_v2_detector.tflite 6 MB Gorilla face detector Bundled
gallery.json 30 MB 66 individuals, up to 25 exemplars each, 768 dimensions Bundled
megadesc_T_arcface_backbone.tflite 108 MB MegaDescriptor-T-224 (Swin Transformer Tiny), fine-tuned with Sub-center ArcFace, produces 768-dim embeddings Downloaded separately

All three come directly from a V1 training run of the GorillaIdentifier pipeline; see that repository to retrain on a different population or export updated models.

Backbone model (manual download required)

megadesc_T_arcface_backbone.tflite exceeds GitHub's 100 MB file size limit, so it is hosted on HuggingFace instead of being committed to this repository:

https://huggingface.co/tit0000/GorillaIdentifier

Download it and place it at app/src/main/assets/megadesc_T_arcface_backbone.tflite before building. The other two files, yolo_v2_detector.tflite and gallery.json, are already bundled in app/src/main/assets/ and need no separate download.


Technical Architecture

Layer Technology
Language Kotlin
Architecture MVVM + Clean Architecture
Dependency Injection Hilt
Local Database Room (scan history)
Machine Learning TensorFlow Lite
Camera CameraX
Navigation Jetpack Navigation Component
Concurrency Kotlin Coroutines & Flow (Dispatchers.IO for all I/O and inference)
Min SDK 26 (Android 8.0 Oreo)
Target SDK 35

Build from Source

Prerequisites

  • Android Studio (Ladybug or later recommended)
  • Git

Steps

git clone https://github.com/tit-exe/GorillaIdentifier_AndroidApp.git
cd GorillaIdentifier_AndroidApp

gallery.json and yolo_v2_detector.tflite are already included under app/src/main/assets/. Before the first build, download megadesc_T_arcface_backbone.tflite from HuggingFace and place it in the same folder, see the Models section above. Then open the project in Android Studio, let Gradle sync, and Build and Run.

To try a different backbone or gallery without rebuilding, use Settings -> Import once the app is installed, rather than replacing the bundled assets and recompiling.


Project Structure

GorillaIdentifier_AndroidApp/
├── app/src/main/
│   ├── assets/
│   │   ├── gallery.json                        66-individual V1 gallery
│   │   ├── megadesc_T_arcface_backbone.tflite   Recognition backbone (108 MB), not tracked by git, see Models
│   │   └── yolo_v2_detector.tflite              Face detector (6 MB)
│   ├── java/com/iphc/gorillaidentifier/
│   │   ├── data/
│   │   │   ├── local/                           Room database and AppPreferences
│   │   │   └── repository/
│   │   │       ├── GalleryManager.kt            Gallery read/write, export, import, quality gates
│   │   │       └── ModelManager.kt              TFLite lifecycle, hot-swap, bundle install
│   │   ├── domain/                              Use cases and repository interfaces
│   │   ├── ml/
│   │   │   ├── EmbeddingUtils.kt                L2-normalize, dot product, batch averaging
│   │   │   ├── TfliteInterpreterFactory.kt      NNAPI -> GPU -> CPU+XNNPACK fallback chain
│   │   │   ├── classifier/                      Backbone wrapper and image preprocessing
│   │   │   └── detector/                        YOLO output decoding and NMS
│   │   └── ui/
│   │       ├── add_individual/                  Photo capture, crop review, prototype save
│   │       ├── box_editor/                      Manual bounding-box correction
│   │       ├── gallery/                         Gallery list, export, undo, field photo add
│   │       ├── home/                             Camera and photo picker entry point
│   │       ├── history/                         Scan history list
│   │       ├── result/                          Per-face result cards
│   │       └── settings/                        Threshold display, model import, backup history
│   └── res/                                     Layouts, navigation graph, strings, themes
├── sample_assets/                               A few real field photos for local testing
├── github_assets/                               README images (app icon, screenshots)
└── build.gradle.kts, settings.gradle.kts, gradle/    Standard Gradle project files

Related

  • GorillaIdentifier: the ML pipeline that trains the recognition model and builds the gallery consumed by this app.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages