Project Info
Solving the "Static Image Failure" with Temporal RAG
Inspiration
The idea for ADEX was born from a recurring dead roadblock I encountered while contributing to Truthin, a crowdsourced platform for packaged food ratings. Users frequently submitted unusable images—blurry, out-of-focus, or obscured by glare. In many cases, reflections from shiny plastic or folds in packaging made ingredient lists unreadable, even to the human eye. This problem extends far beyond Truthin. Industry-leading apps such as Yuka, Think Dirty, EWG Healthy Living, Open Food Facts, MyFitnessPal, Lifesum, and Calorie Mama still rely on static image capture. When users have shaky hands, poor lighting, or low-end devices, OCR fails—forcing repeated retakes or tedious manual entry. The core limitations of single-image approaches: The breakthrough came during a Serverpod 3.0 demo by Vik, showcasing its Industrial RAG (Retrieval-Augmented Generation) capabilities. I realized that by applying RAG to video frames instead of single images, we could triangulate accurate data from imperfect visual input over time. What It Does ADEX replaces unreliable one-shot photography with temporal video-based data extraction. Instead of asking users to take a perfect photo, the system captures a short video sweep of a product. By analyzing dozens of frames, ADEX reconstructs missing or obscured information—recovering text hidden by glare, motion blur, or physical distortion. The result is fast, accurate extraction of ingredients, nutrition facts, allergens, and any other product data with minimal user effort. The key insight: a single image is a gamble; a video is a guarantee. Text hidden by glare in frame 12 is visible in frame 37. A fold that obscures ingredients at one angle is flat at another. Motion blur in one frame is sharp in the next. ADEX doesn't need any single frame to be perfect — it reconstructs complete data from the collective information across all frames. Video Text Extraction Flow User-Configurable Parameters The extraction pipeline is fully configurable from the app's settings: How We Built It Backend — Serverpod 3 (Dart) Built on Serverpod 3.2, leveraging its native vector embedding support via pgvector and typed endpoint generation. The server handles video processing, AI orchestration, S3 storage, and JWT authentication — all in Dart. Video Processing — FFmpeg Integrated FFmpeg invoked at the OS level using Dart's Process API to extract frames at 2 FPS. Each video produces dozens of frames that form the input to the RAG pipeline. Multimodal Temporal RAG The core innovation. Each frame is converted to a 1408-dimensional vector embedding using Vertex AI multimodalembedding@001. Frame type descriptions are converted to text embeddings in the same vector space. PostgreSQL with pgvector performs cosine similarity search to find the frames that best match each category — this is the retrieval step of RAG. Gemini 2.0 Flash then performs the generation step, extracting structured text from the retrieved frames. Cloud Storage — AWS S3 Videos and extracted frames are stored in AWS S3 (eu-north-1). The pipeline saves video bytes locally for processing and uploads to S3 asynchronously in the background to avoid round-trip latency. Frontend — Flutter A cross-platform Flutter app with platform-adaptive UI: Mobile: Full-screen camera with record button, settings sheet for prompts and performance tuning, processing progress view, and tabbed results display Desktop/Web: File picker for video upload, responsive two-pane layout, model browsing, and JSON data viewer Authentication — JWT with Email Verification Serverpod Auth IDP provides JWT-based authentication with email identity provider, email verification codes, and password reset flow via Gmail SMTP. Challenges We Ran Into Optical Distortions: Packaging is often curved, glossy, or crinkled. Ensuring the AI recognized distorted text across multiple angles as the same semantic entity required careful embedding tuning. The multimodal embedding model handles this by encoding visual semantics rather than raw pixels. Optical Distortions: Packaging is often curved, glossy, or crinkled. Ensuring the AI recognized distorted text across multiple angles as the same semantic entity required careful embedding tuning. The multimodal embedding model handles this by encoding visual semantics rather than raw pixels. Rate Limiting at Scale: Processing a single video generates dozens of frames, each requiring an embedding API call. We implemented configurable batch concurrency (default 5 parallel calls), delays between batches (200ms), and exponential backoff with jitter — capped at 60 seconds — to handle 429 and RESOURCE_EXHAUSTED errors gracefully. Rate Limiting at Scale: Processing a single video generates dozens of frames, each requiring an embedding API call. We implemented configurable batch concurrency (default 5 parallel calls), delays between batches (200ms), and exponential backoff with jitter — capped at 60 seconds — to handle 429 and RESOURCE_EXHAUSTED errors gracefully. Single-Call Text Extraction: Initially we called Gemini per frame type, but this hit rate limits fast and lost cross-frame context. We refactored to send ALL extracted frames to Gemini in a single multimodal API call, which is both faster and more accurate since Gemini can cross-reference information across images. Single-Call Text Extraction: Initially we called Gemini per frame type, but this hit rate limits fast and lost cross-frame context. We refactored to send ALL extracted frames to Gemini in a single multimodal API call, which is both faster and more accurate since Gemini can cross-reference information across images. FFmpeg & Dart Ecosystem Gaps: FFmpeg lacks a robust native Dart package for frame extraction. As a workaround, we invoked FFmpeg directly at the OS level using Dart's Process API, enabling reliable frame slicing while preserving performance. FFmpeg & Dart Ecosystem Gaps: FFmpeg lacks a robust native Dart package for frame extraction. As a workaround, we invoked FFmpeg directly at the OS level using Dart's Process API, enabling reliable frame slicing while preserving performance. Upload Latency: High-resolution video uploads are expensive. We eliminated the S3 round-trip by saving video bytes locally for immediate processing and uploading to S3 asynchronously in the background using Dart's unawaited(). Upload Latency: High-resolution video uploads are expensive. We eliminated the S3 round-trip by saving video bytes locally for immediate processing and uploading to S3 asynchronously in the background using Dart's unawaited(). Accomplishments That We're Proud Of Successfully replaced fragile static OCR with a video-first temporal extraction pipeline. Achieved high accuracy even on low-end devices and poor lighting conditions. Built a production-grade multimodal RAG system in Dart — vector embeddings, cosine similarity search, and AI-powered text extraction. Single API call text extraction across all frame types, avoiding rate limits and improving accuracy. Reduced user friction by shifting accuracy responsibility from the user to the system — no more retaking photos. Cross-platform support from a single Flutter codebase — mobile camera capture and desktop file upload. What We Learned The biggest insight was the power of temporal redundancy. A single image is a gamble; a video is a guarantee. We learned how to use software intelligence to overcome hardware constraints — proving that you don't need a $1,200 phone camera when you can reason over imperfect frames from a $100 device. The project also deepened our expertise in: Real-time AI pipelines and multimodal embeddings Vector databases (pgvector) and similarity search Rate limit management with exponential backoff at scale Asynchronous cloud storage patterns Scalable RAG systems within the Serverpod ecosystem What's Next for ADEX Phase 1: Edge-Side Intelligence Integrate YOLO-based on-device models to perform real-time frame quality assessment, discarding junk frames locally before upload — reducing bandwidth and processing cost. Phase 1: Edge-Side Intelligence Integrate YOLO-based on-device models to perform real-time frame quality assessment, discarding junk frames locally before upload — reducing bandwidth and processing cost. Phase 2: Multi-Surface Reconstruction Extend temporal RAG to handle 3D object scans, allowing full-package capture (front, back, and sides) in a single video sweep with spatial awareness. Phase 2: Multi-Surface Reconstruction Extend temporal RAG to handle 3D object scans, allowing full-package capture (front, back, and sides) in a single video sweep with spatial awareness. Phase 3: Human-in-the-Loop Feedback Introduce community verification for low-confidence results, creating a feedback loop that continuously improves embedding quality and extraction accuracy. Phase 3: Human-in-the-Loop Feedback Introduce community verification for low-confidence results, creating a feedback loop that continuously improves embedding quality and extraction accuracy. Phase 4: Developer API Package ADEX as a plug-and-play API, enabling third-party apps like Yuka, Truthin, or Open Food Facts to replace static OCR with temporal video extraction. Phase 4: Developer API Package ADEX as a plug-and-play API, enabling third-party apps like Yuka, Truthin, or Open Food Facts to replace static OCR with temporal video extraction.
ADEX — Adaptive Data Extraction System
Video-first data extraction that succeeds where single-image OCR fails.
ADEX replaces the fragile "take one perfect photo" approach with temporal video analysis. Users record a short video sweep of a product, and ADEX extracts accurate text and structured data — even from blurry, glare-obscured, or folded surfaces.
The Problem
Consumer apps that analyze packaged products — food scanners, ingredient checkers, health ratings — all rely on static image capture. This approach breaks constantly in real-world conditions:
| Issue | Why It Happens |
|---|---|
| Light reflection / glare | Shiny plastic packaging reflects camera flash or ambient light, whiting out text |
| Folds and creases | Flexible packaging wrinkles, hiding portions of ingredient lists |
| Curved surfaces | Bottles and cans distort text at the edges, making OCR unreliable |
| Motion blur | Shaky hands or low-end cameras produce unusable captures |
| Unstructured layouts | Every product has a unique label layout — no single template works |
Apps like Yuka, Truthin, Think Dirty, EWG Healthy Living, and Open Food Facts all suffer from this. Users are forced to retake photos multiple times or resort to tedious manual entry when OCR fails.
How ADEX Solves It
Instead of gambling on a single frame, ADEX captures a short video sweep and analyzes dozens of frames across time. This temporal redundancy means:
- Text hidden by glare in frame 12 is visible in frame 37
- A fold that obscures ingredients at one angle is flat at another
- Motion blur in one frame is sharp in the next
The system doesn't need any single frame to be perfect — it reconstructs complete, accurate data from the collective information across all frames.
How It Works
┌─────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ User records│ │ Server extracts │ │ AI classifies & │
│ video sweep │────>│ frames (FFmpeg) │────>│ generates │
│ of product │ │ at 2 FPS │ │ embeddings │
└─────────────┘ └──────────────────┘ └─────────┬─────────┘
│
┌─────────────┐ ┌──────────────────┐ ┌──────────▼─────────┐
│ Structured │ │ Gemini extracts │ │ RAG identifies │
│ JSON data │<────│ text from best │<────│ best frames per │
│ returned │ │ frames │ │ category │
└─────────────┘ └──────────────────┘ └────────────────────┘
Step-by-step flow
-
Capture — On mobile, the user opens the camera and records a short video sweep around the product. On desktop, the user uploads a video file.
-
Frame extraction — The server uses FFmpeg to extract frames at 2 frames per second, capturing the start and middle of each second.
-
Embedding generation — Each frame is sent to Google Vertex AI (
multimodalembedding@001) to generate 1408-dimensional vector embeddings, stored in PostgreSQL with pgvector. -
Frame classification — Using timeline heuristics and RAG, the system identifies frame types:
- Product front (brand, name, claims)
- Nutrition facts panel
- Ingredients list
- Product back (additional info)
- Barcode
-
Text extraction — Google Gemini analyzes the best frames for each category and extracts structured text, handling distortions that would break traditional OCR.
-
Result delivery — The app displays extracted frames organized by type, structured JSON data (nutrition facts, ingredients, allergens), and the original video with timeline markers.
Comparison with Existing Solutions
| Feature | Yuka / Truthin / Think Dirty | Open Food Facts | ADEX |
|---|---|---|---|
| Input method | Single photo | Single photo + barcode | Video sweep |
| Handles glare | No | No | Yes — temporal redundancy |
| Handles folds/creases | No | No | Yes — multi-angle coverage |
| Unstructured layouts | Limited templates | Community-curated | Yes — AI-driven extraction |
| Low-end device support | Poor (needs sharp photo) | Poor | Good — compensates across frames |
| User effort on failure | Retake photo repeatedly | Manual entry | Minimal — just re-sweep |
| Data extraction | Barcode lookup + basic OCR | Barcode database | AI-powered multimodal analysis |
| Works without barcode | Rarely | No | Yes — vision-based |
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | Flutter (iOS, Android, Web, macOS, Windows, Linux) |
| Backend | Serverpod 3.2 (Dart) |
| Database | PostgreSQL with pgvector extension |
| Cache | Redis |
| File storage | AWS S3 |
| AI embeddings | Google Vertex AI (multimodal, 1408D) |
| Text extraction | Google Gemini 2.5 Flash |
| Video processing | FFmpeg (OS-level via Dart Process API) |
| Authentication | JWT with Serverpod Auth IDP |
Project Structure
adex/
├── adex_server/ # Dart backend — endpoints, video processing, AI integration
├── adex_client/ # Auto-generated typed client library
├── adex_flutter/ # Cross-platform Flutter app
├── SETUP.md # How to set up and run the project
└── README.md # This file
Getting Started
See SETUP.md for full instructions on setting up and running the project locally.
Key Features
- Video-based capture — Record a sweep instead of taking a single photo
- Temporal RAG — Retrieval-Augmented Generation across video frames
- Multi-surface reconstruction — Handles front, back, sides in one sweep
- Configurable extraction — Custom prompts for what data to extract
- Processing history — Browse and revisit past extractions
- Cross-platform — Mobile camera capture and desktop file upload
- Structured output — JSON data with nutrition facts, ingredients, allergens
- Rate-limit resilient — Exponential backoff with configurable concurrency
Roadmap
- Edge-side intelligence — On-device frame quality assessment (YOLO-based) to discard junk frames before upload
- 3D object scanning — Full-package capture (front, back, sides) via multi-surface reconstruction
- Community verification — Human-in-the-loop feedback for low-confidence results
- Developer API — Plug-and-play API for third-party apps to replace static OCR with temporal video extraction
License
Proprietary. All rights reserved.
Analysis
View
Metric
No commits on this project resolved to a GitHub account.
Technology
- CIn code
- C++In code
- CSSIn code
- DartIn code
- HTMLIn code
- KotlinIn code
- SQLIn code
- SwiftIn code
- Google GeminiClaimed
8 of 9 appear in the indexed code. 1 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
- Claude CodeConfig
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
653 KB
Source files
116
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
kanakapalli/adex
247 files · 9.1 MB · @ 9b96a00
Structure
Interface
26 files · 11%Screens, components and styles rendered to the user.
API & routing
2 files · 1%Request entry points: routes, handlers and controllers.
Application logic
95 files · 38%Domain rules, services and shared utilities.
+7 moreData & schema
15 files · 6%Schema definitions, migrations and data access.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Dart70%
- SQL14%
- Markdown6%
- YAML3%
- C++3%
- C1%
- Other (5)3%
Share of indexed source by file size. Binary and vendored files are excluded.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.