--- title: PawMap emoji: 🐾 colorFrom: green colorTo: green sdk: docker app_file: app.py pinned: true license: mit short_description: Collaborative stray animal mapping with AI tags: - stray-animals - animal-welfare - maps - computer-vision - small-models - gradio - build-small-hackathon - backyard-ai - leaflet - cosine-similarity models: - nvidia/nemotron-3-nano-omni-30b-a3b-reasoning - sentence-transformers/all-MiniLM-L6-v2 - meta-llama/Llama-3.2-11B-Vision-Instruct --- # PawMap 🐾 β€” Collaborative stray animal mapping with AI Take a photo of the stray animal you just spotted. The AI identifies species, breed and color β€” and checks if it's already been registered before. Over time, the map tells each animal's story: where it shows up, who helped, whether it's doing okay. Built for the [**Build Small Hackathon 2026**](https://huggingface.co/build-small-hackathon) Β· **Backyard AI** track 🏑 **Try it:** [live Space](https://huggingface.co/spaces/build-small-hackathon/viralata-mapper) Β· **Traces:** [dataset on the Hub](https://huggingface.co/datasets/build-small-hackathon/viralata-mapper-storage) --- ## The Problem BrasΓ­lia, DF β€” the capital of Brazil β€” has thousands of stray dogs and cats scattered across its residential wings, parking lots and green areas. Independent rescuers know each animal by nickname, by street, by habit. But that knowledge lives in WhatsApp groups, in individual memory, nowhere persistent. The friend who inspired this app has been feeding a colony of cats near the Asa Norte commercial strip for years. She knows exactly which ones have been neutered, which ones have a history of injury, which one vanished last month. But when someone new wants to help β€” or when she needs backing to request food donations β€” there's nothing to show. No map, no timeline, no proof that a specific animal has been living on that block for two years. **PawMap is that record.** Anyone takes a photo of a stray animal with their phone. The AI identifies it, GPS pins the location, and the app checks whether that animal has been seen before β€” linking all sightings to its profile. The map is public. The animal's trail becomes visible. Anyone who wants to help knows exactly where to go. --- ## How it works There are two AI-powered flows: **registering a sighting** and **recording help**. ### Sighting flow ```mermaid flowchart TD A["πŸ“· Photo from phone"] --> B["πŸ€– Vision AI\nLlama-3.2-11B or Nemotron Omni\n(serverless API)"] B -->|"is it an animal?"| C{Animal detected?} C -->|"No"| ERR["❌ Error message\n'No dog or cat identified'"] C -->|"Yes"| D["πŸ“ Structured JSON\nspecies Β· breed Β· color Β· condition Β· marks"] D --> E["πŸ”’ Semantic embedding\nall-MiniLM-L6-v2 Β· 384 dim\n(local, 22M params)"] E --> F{Cosine similarity\nβ‰₯ 0.80?} F -->|"Yes β€” known animal"| G["βž• New sighting\nadded to existing animal"] F -->|"No β€” new animal"| H["πŸ†• New animal\nregistered in the database"] G --> I["πŸ’Ύ SQLite"] H --> I I --> J["πŸ—ΊοΈ Leaflet map\nprofiles Β· trail Β· gallery"] ``` Before confirming, the user sees the top 3 most similar animals already in the database β€” they can edit what the AI detected, give the animal a name, and note its condition. Only then is the sighting saved. ### Help flow When someone wants to help an animal, they can submit a **help proof** β€” a photo showing they fed it, treated a wound, or got it to a vet. The AI does two things with that photo: ```mermaid flowchart TD A["πŸ“· Help proof photo\n(optional)"] --> B["πŸ€– Vision AI\nanalyzes the new photo"] B --> C["πŸ”’ New embedding\ngenerated from description"] C --> D{Cosine similarity\nagainst registered animal?} D -->|"Match β‰₯ 0.80\nsame animal ID"| E["βœ… AI-verified\nmatch score shown"] D -->|"No match"| F["⚠️ Unverified\nstill recorded"] B --> G{Condition improved?} G -->|"e.g. injured β†’ healthy"| H["πŸ“ˆ Condition update\nlogged on profile"] G -->|"same or worse"| I["No change"] E --> J["πŸ’Ύ Help event saved\nto sightings table"] F --> J H --> J I --> J ``` The profile page shows the full help history with photos, help type (fed / treated / rescued / other), AI verification status and any detected condition improvement. The map uses color to signal urgency: **🟒 green** = dog Β· **🟠 orange** = cat Β· **πŸ”΄ red** = not seen in +30 days. --- ## What's inside | Component | Model / Library | Where it runs | |---|---|---| | Visual identification | **Nemotron Omni 30B** via NVIDIA NIM | remote API | | Vision fallback | **Llama-3.2-11B-Vision-Instruct** via HF Serverless | remote API | | Semantic embedding | **all-MiniLM-L6-v2** (384 dim) | CPU, local | | Animal matching | Cosine similarity (threshold 0.80) | CPU, local | | Database | SQLite | local / persistent | | Frontend | SPA via `gradio.Server` + Leaflet.js + Lucide Icons | browser | **Total parameters running locally: ~22M.** The vision model runs via serverless API β€” nothing heavy on the Space machine. The only local model is MiniLM for embeddings. --- ## Screens | Screen | Description | |---|---| | πŸ—ΊοΈ **Map** | Color-coded pins by species/urgency, floating card with "View profile" | | πŸ“· **Register** | Photo upload + automatic GPS + AI analysis | | πŸ€– **Analysis** | Auto-identification with editable fields + top 3 similar animals | | βœ… **Confirm** | Sighting summary before saving | | πŸ‘οΈ **Spotted** | List of all registered animals | | 🐾 **Profile** | Full animal card with gallery, map trail and help history | --- ## Badges This submission earns the following hackathon bonus quests: - 🎨 **Off-Brand** β€” Fully custom interface via `gradio.Server`, no default Gradio components visible. SPA frontend with Leaflet.js for the map, Lucide Icons, and custom design. - πŸ“‘ **Sharing is Caring** β€” Full traces of every sighting (photo β†’ AI analysis β†’ embedding β†’ matching result) are automatically published as a dataset on the Hub via `/admin/push-traces`. --- ## Running locally ```bash git clone https://huggingface.co/spaces/build-small-hackathon/viralata-mapper cd viralata-mapper pip install -r requirements.txt # with HuggingFace Serverless HF_TOKEN=hf_... python app.py # or with NVIDIA NIM (takes precedence over HF_TOKEN) NVIDIA_API_KEY=nvapi_... python app.py # no key β€” runs in fallback mode without AI identification python app.py # open http://localhost:7860 ``` On first run the app seeds the database with demo animals around BrasΓ­lia so the map isn't empty. --- ## Space configuration ### Secrets | Secret | Description | |---|---| | `HF_TOKEN` | HuggingFace token for Llama-3.2-11B-Vision via Serverless Inference | | `NVIDIA_API_KEY` | Alternative: Nemotron Omni via NVIDIA NIM (takes precedence over HF_TOKEN) | | `MATCH_THRESHOLD` | Optional. Similarity threshold. Default: `0.80` | | `HF_DATASET_ID` | Optional. Hub dataset for trace publishing (e.g. `org/dataset-name`) | > Without a Vision AI key the app runs in fallback mode β€” registration and matching still work, but automatic breed/color/condition identification is disabled. ### Persistent Storage Set up a **Persistent Storage Bucket** in the Space and mount it at `/data/` so photos and the database survive restarts. Without it, all data is wiped on each deploy. --- ## Repository structure ``` . β”œβ”€β”€ app.py # Entrypoint β€” FastAPI routes + Gradio Server β”œβ”€β”€ requirements.txt β”œβ”€β”€ Dockerfile β”œβ”€β”€ index.html # SPA frontend β”œβ”€β”€ static/ β”‚ β”œβ”€β”€ app.js # Client logic (map, camera, registration flow) β”‚ β”œβ”€β”€ style.css β”‚ └── lottie/ # Loading animations β”œβ”€β”€ core/ β”‚ β”œβ”€β”€ ai.py # Vision AI (HF or NVIDIA NIM) + embeddings β”‚ β”œβ”€β”€ database.py # SQLite β€” animals and sightings β”‚ β”œβ”€β”€ matcher.py # Cosine similarity for animal matching β”‚ β”œβ”€β”€ seed.py # Demo data for the initial map (BrasΓ­lia, DF) β”‚ └── tracer.py # Trace logging + push to HF Hub β”œβ”€β”€ db/ β”‚ └── schema.sql # Database schema └── data/ # Runtime β€” SQLite database + photos + traces β”œβ”€β”€ viralata.db β”œβ”€β”€ photos/ └── traces.jsonl ``` --- ## Limitations - **Vision AI via API** β€” breed identification requires `HF_TOKEN` or `NVIDIA_API_KEY`. Without a key, registrations use generic data (breed "SRD / Domestic Shorthair"). - **GPS on mobile** β€” accuracy depends on the device. Indoors or with weak signal, coordinates may be imprecise. - **Matching is not foolproof** β€” the 0.80 threshold is conservative to avoid false positives, but two very similar animals (e.g. two caramel SRDs) may get grouped together. The user reviews the candidates before confirming and can correct. - **Portuguese / English only** β€” the interface is in Portuguese; the AI generates descriptions in English for embedding consistency. --- ## Credits - **[Meta](https://ai.meta.com/llama/)** β€” Llama 3.2 11B Vision Instruct (Llama 3.2 Community License). - **[NVIDIA](https://build.nvidia.com/)** β€” Nemotron Omni 30B via NVIDIA NIM. - **[Hugging Face](https://huggingface.co/)** β€” `sentence-transformers/all-MiniLM-L6-v2`, Serverless Inference, Space hosting. - **[Leaflet.js](https://leafletjs.com/)** β€” interactive map. - **[Gradio](https://gradio.app/)** β€” `gradio.Server` for the custom frontend. - **[Lucide Icons](https://lucide.dev/)** β€” UI icons. --- ## License MIT for the application code. Models follow their own licenses β€” see the Meta (Llama 3.2) and NVIDIA (Nemotron Omni) model cards for full terms. --- *Built for BrasΓ­lia, DF β€” capital of Brazil β€” and any city that wants to map its stray animals.* 🐾