A Python service for identifying Space Invader street art flashes from images using CLIP embeddings and FAISS similarity search.
This service:
- Preprocesses user photos to detect and extract the mosaic region (grid detection)
- Uses CLIP (ViT-L/14) to generate 768-dimensional embeddings from images
- Stores reference flash embeddings in a FAISS index for fast similarity search
- Provides a FastAPI endpoint to identify flashes from query images
- Python 3.10+
- M1/M2 Mac (uses Metal), or NVIDIA GPU, or CPU
cd invaders.embeddings
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txtThis downloads all ~3,900 reference flash images and builds the FAISS index:
python -m src.scripts.build_indexThis takes ~5-10 minutes on M1 Mac and creates:
data/flash_index.index- FAISS index filedata/flash_index.meta.json- Flash metadata
uvicorn src.api.main:app --host 0.0.0.0 --port 8000The API will be available at http://localhost:8000
Identify a flash from an uploaded image.
curl -X POST "http://localhost:8000/identify" -F "file=@path/to/image.jpg" -F "top_k=5"Response:
{
"matches": [
{
"flash_id": 1234,
"flash_name": "PA_567",
"similarity": 0.92,
"confidence": 0.92,
"location": {"lat": 48.8566, "lng": 2.3522},
"image_url": "https://..."
}
],
"processing_time_ms": 150.5
}Check service health.
curl http://localhost:8000/healthUser-submitted photos undergo grid detection to extract the mosaic region:
- Edge Detection: Canny edge detection finds structural lines
- Autocorrelation: Detects repeating grid patterns characteristic of mosaics
- Region Scoring: Scores candidate regions by grid strength
- Margin Exclusion: Excludes top 20% (sky/graffiti) and bottom 10% (ground)
- Crop Extraction: Extracts the best mosaic region for embedding
This preprocessing significantly improves identification accuracy for real-world photos where the mosaic may be partially visible among other elements.
The service is configured for Railway deployment:
# railway.json configures the build and start commands
railway upEnvironment Variables:
PORT- Set automatically by RailwayHF_HOME- HuggingFace model cache directory
Notes:
- The FAISS index and metadata are bundled in the Docker image
- First request may be slow (~10-30s) as the CLIP model loads
- Subsequent requests are fast (~150-200ms)
# Build the image
docker build -t invaders-embeddings .
# Run locally
docker run -p 8000:8000 invaders-embeddingsOn M1 MacBook Pro:
- Index build: ~5-10 minutes (one-time)
- Single image identification: ~150-200ms
- FAISS search: <1ms
On Railway (shared CPU):
- Cold start: ~10-30 seconds (model loading)
- Warm request: ~200-400ms
invaders.embeddings/
├── src/
│ ├── encoder/
│ │ ├── clip.py # CLIP model wrapper
│ │ ├── grid_detect.py # Mosaic region detection
│ │ └── preprocess.py # Image preprocessing
│ ├── index/
│ │ └── faiss_manager.py # FAISS index management
│ ├── api/
│ │ ├── main.py # FastAPI application
│ │ └── models.py # Pydantic models
│ └── scripts/
│ └── build_index.py # Build reference index
├── data/
│ ├── flash_index.index # FAISS index (generated)
│ └── flash_index.meta.json # Metadata (generated)
├── Dockerfile # Container build
├── railway.json # Railway deployment config
├── requirements.txt
└── README.md
This service is called by invaders.consumer after IPFS upload:
User Flash -> Consumer -> IPFS Upload -> Embeddings API -> Database
Identifications with >= 80% similarity are stored in the flash_identifications table for review.