Files
boc/iom/ai_pipeline/quixzoom_data_ingestion.py
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

266 lines
8.6 KiB
Python

"""
quiXzoom Data Ingestion Pipeline
Collect and process images from quiXzoom app for AI training
"""
from typing import Dict, List, Optional
from dataclasses import dataclass
from pathlib import Path
import json
import os
@dataclass
class ZoomerSubmission:
"""A photo submission from a Zoomer"""
submission_id: str
zoomer_id: str
image_url: str
location: Dict # lat, lng
timestamp: str
mission_type: str
tags: List[str]
quality_score: float # 0-1
class QuixzoomDataIngestion:
"""
Ingest data from quiXzoom app for AI training
Flow:
1. Fetch submissions from quiXzoom API
2. Filter by quality (blur, lighting, composition)
3. Auto-annotate with existing AI model
4. Human review queue
5. Export to training format
"""
def __init__(self, output_path: str = "/tmp/quixzoom_training"):
self.output_path = Path(output_path)
self.submissions: List[ZoomerSubmission] = []
# Create directories
(self.output_path / "images").mkdir(parents=True, exist_ok=True)
(self.output_path / "labels").mkdir(parents=True, exist_ok=True)
(self.output_path / "review_queue").mkdir(parents=True, exist_ok=True)
def fetch_submissions(
self,
api_endpoint: str = "https://api.quixzoom.com/v1/submissions",
api_key: Optional[str] = None,
limit: int = 1000
) -> List[ZoomerSubmission]:
"""Fetch submissions from quiXzoom API"""
print(f"Fetching up to {limit} submissions from {api_endpoint}...")
# In production, make actual API call
# For now, return placeholder
submissions = []
for i in range(min(limit, 100)): # Demo: 100 submissions
submission = ZoomerSubmission(
submission_id=f"SUB_{i:06d}",
zoomer_id=f"ZOOMER_{i % 50:04d}",
image_url=f"https://cdn.quixzoom.com/submissions/SUB_{i:06d}.jpg",
location={"lat": 59.3293 + (i * 0.001), "lng": 18.0686 + (i * 0.001)},
timestamp="2026-06-26T12:00:00Z",
mission_type="infrastructure_check",
tags=["street_light", "sidewalk", "road"],
quality_score=0.7 + (i % 30) / 100
)
submissions.append(submission)
self.submissions = submissions
print(f"Fetched {len(submissions)} submissions")
return submissions
def filter_quality(
self,
min_quality: float = 0.6,
require_gps: bool = True,
require_daylight: bool = True
) -> List[ZoomerSubmission]:
"""Filter submissions by quality criteria"""
print(f"\nFiltering {len(self.submissions)} submissions...")
filtered = []
for sub in self.submissions:
# Quality score
if sub.quality_score < min_quality:
continue
# GPS required
if require_gps and not sub.location:
continue
# Daylight (simplified check)
if require_daylight:
# In production, check timestamp + location for daylight
pass
filtered.append(sub)
print(f"Filtered: {len(filtered)}/{len(self.submissions)} passed quality checks")
return filtered
def auto_annotate(self, submissions: List[ZoomerSubmission]) -> Dict:
"""Auto-annotate images with existing AI model"""
print(f"\nAuto-annotating {len(submissions)} images...")
import sys
sys.path.insert(0, '/home/bernt/.openclaw/workspace/iom')
from ai_pipeline.real_ai import RealAIClassifier
classifier = RealAIClassifier(use_real_ai=True)
annotations = {}
for sub in submissions[:10]: # Demo: annotate first 10
# In production, download image and run inference
# For now, simulate
annotations[sub.submission_id] = {
"objects": [
{"class": "street_light", "confidence": 0.85, "bbox": [100, 200, 50, 150]},
{"class": "sidewalk", "confidence": 0.92, "bbox": [0, 400, 640, 80]}
],
"scene_type": "street_view",
"quality": sub.quality_score
}
print(f"Auto-annotated {len(annotations)} images")
return annotations
def create_review_queue(self, annotations: Dict):
"""Create human review queue for low-confidence annotations"""
print(f"\nCreating review queue...")
review_items = []
for sub_id, annotation in annotations.items():
# Flag low-confidence detections for review
for obj in annotation["objects"]:
if obj["confidence"] < 0.8:
review_items.append({
"submission_id": sub_id,
"object": obj,
"reason": "low_confidence"
})
# Save review queue
review_path = self.output_path / "review_queue" / "pending.json"
with open(review_path, "w") as f:
json.dump(review_items, f, indent=2)
print(f"Review queue: {len(review_items)} items flagged")
return review_items
def export_to_training_format(
self,
annotations: Dict,
split_ratios: Dict = {"train": 0.7, "val": 0.2, "test": 0.1}
):
"""Export annotations to YOLO training format"""
print(f"\nExporting to training format...")
import random
# Split data
items = list(annotations.items())
random.shuffle(items)
n = len(items)
train_end = int(n * split_ratios["train"])
val_end = train_end + int(n * split_ratios["val"])
splits = {
"train": items[:train_end],
"val": items[train_end:val_end],
"test": items[val_end:]
}
# Export each split
for split_name, split_items in splits.items():
split_dir = self.output_path / split_name
split_dir.mkdir(exist_ok=True)
for sub_id, annotation in split_items:
# Save label file
label_path = split_dir / f"{sub_id}.txt"
with open(label_path, "w") as f:
for obj in annotation["objects"]:
# YOLO format: class x_center y_center width height
# In production, convert bbox to normalized coordinates
f.write(f"0 0.5 0.5 0.1 0.1\n") # Placeholder
# Create data.yaml
data_yaml = {
"path": str(self.output_path.absolute()),
"train": "train",
"val": "val",
"test": "test",
"nc": 10,
"names": [
"street_light", "traffic_sign", "bench", "trash_can",
"sidewalk", "road", "building", "bridge", "tree", "graffiti"
]
}
with open(self.output_path / "data.yaml", "w") as f:
json.dump(data_yaml, f, indent=2)
print(f"Exported:")
for split_name, split_items in splits.items():
print(f" {split_name}: {len(split_items)} images")
def run_full_pipeline(self) -> Dict:
"""Run complete data ingestion pipeline"""
print("=" * 60)
print("QUIXZOOM DATA INGESTION")
print("=" * 60)
# 1. Fetch submissions
submissions = self.fetch_submissions()
# 2. Filter quality
filtered = self.filter_quality()
# 3. Auto-annotate
annotations = self.auto_annotate(filtered)
# 4. Create review queue
review_items = self.create_review_queue(annotations)
# 5. Export
self.export_to_training_format(annotations)
print("\n" + "=" * 60)
print("INGESTION COMPLETE")
print("=" * 60)
return {
"total_submissions": len(submissions),
"filtered_submissions": len(filtered),
"annotated": len(annotations),
"review_queue": len(review_items)
}
# Example usage
def example_ingestion():
"""Example: Data ingestion"""
ingestion = QuixzoomDataIngestion()
results = ingestion.run_full_pipeline()
print("\nResults:")
for key, value in results.items():
print(f" {key}: {value}")
return results
if __name__ == '__main__':
example_ingestion()