AI Lock Selector - мобильное приложение для Android/iOS, которое позволяет:
- Сфотографировать врезной дверной замок
- Автоматически определить тип замка по фото
- Измерить ключевые размеры с точностью ±0.5-1.0mm
- Подобрать аналоги из базы данных
- Platform: Android (Flutter)
- Camera: Minimum 8MP with autofocus
- Display: 720p+ recommended
- Target user: Мастера по установке замков, продавцы замков
- Врезные замки (mortise locks) - основной тип
- Euro profile cylinders (евроцилиндр)
- Apecs (Россия)
- Avers (Россия)
| Measurement | Russian | Description | Range (mm) | Tolerance |
|---|---|---|---|---|
| Backset | Бэксет | Center of cylinder to center of handle square | 20-68 | ±2.0mm |
| Center Distance | Межосевое | Cylinder center to handle center (or mounting holes) | 50-92 | ±3.0mm |
| Measurement | Russian | Description | Range (mm) |
|---|---|---|---|
| Plate Width | Ширина планки | Thickness of lock plate | 3-8 |
| Plate Height | Высота планки | Height of visible lock plate | 150-280 |
| Square Size | Размер квадрата | Handle square hole (usually 8x8) | 8-10 |
| Mounting Hole | Крепёжное отверстие | Diameter of mounting holes | 6-12 |
The cylinder hole serves as the scale reference:
- Euro cylinder total size: 33mm height x 17mm width
- Top circle: diameter 17mm
- Slot width: 10mm (the vertical "bridge" between circles)
- Bottom semicircle: diameter 10mm
Formula: scale_factor = slot_width_pixels / 10.0
Note: The 10mm slot is the most reliable dimension for calibration.
- Framework: Flutter 3.41.2
- State Management: Riverpod 2.x
- Navigation: GoRouter
- Camera: camera package
- Architecture: Clean Architecture (features/providers/core)
- Framework: FastAPI
- Database: SQLite (aiosqlite) → PostgreSQL (future)
- ORM: SQLAlchemy 2.0 (async)
- Validation: Pydantic v2
- Port: 8001
- Detection: YOLOv8 (OBB - Oriented Bounding Box)
- Image Processing: OpenCV 4.x
- Python: 3.x
- Scripts:
train_yolo.py- Trainingpre_label.py- Auto-labeling
locks (
id INTEGER PRIMARY KEY,
vendor_code VARCHAR(50),
name VARCHAR(255),
brand VARCHAR(100),
type VARCHAR(10), -- ' врезной'
backset FLOAT NOT NULL, -- Бэксет (мм)
center_distance FLOAT, -- Межосевое (мм)
plate_width FLOAT, -- Ширина планки (мм)
plate_height FLOAT, -- Высота планки (мм)
square_hole_size FLOAT, -- Размер квадрата (мм)
lock_cylinder_hole VARCHAR(20) -- напр "33x17"
)measurements (
id INTEGER PRIMARY KEY,
lock_id INTEGER,
backset_measured FLOAT,
center_distance_measured FLOAT,
plate_width_measured FLOAT,
plate_height_measured FLOAT,
confidence FLOAT,
image_path VARCHAR(500)
)| Endpoint | Method | Description |
|---|---|---|
| /api/v1/locks | GET | List all locks |
| /api/v1/locks/{id} | GET | Get lock by ID |
| /api/v1/match | POST | Find matching locks by measurements |
| /api/v1/measure | POST | Process image and measure |
| /health | GET | Health check |
POST /api/v1/match
{
"backset": 45.0,
"center_distance": 72.0,
"tolerance_backset": 2.0,
"tolerance_center": 3.0
}Input Image (with AR mask)
↓
┌─────────────┐
│ Preprocess │ → grayscale, blur, edge detection
└─────────────┘
↓
┌─────────────┐
│ YOLO Detect │ → find lock_plate, cylinder_hole,
│ │ mounting_hole, handle_square
└─────────────┘
↓
┌─────────────┐
│ Calculate │ → use DIN hole (10mm) for scale
│ Scale │ → pixels_per_mm = hole_width / 10
└─────────────┘
↓
┌─────────────┐
│ Measure │ → backset, center_distance,
│ Dimensions │ plate dimensions
└─────────────┘
↓
┌─────────────┐
│ Match DB │ → find closest locks
└─────────────┘
↓
Output: JSON with measurements + matched locks
| Class ID | Name | Description | Detection Priority |
|---|---|---|---|
| 0 | lock_plate | Вся планка замка | HIGH |
| 1 | cylinder_hole | Отверстие цилиндра (10x17mm) | CRITICAL |
| 2 | mounting_hole | Крепёжные отверстия (2 шт) | HIGH |
| 3 | handle_square | Квадрат ручки (8x8mm) | HIGH |
| 4 | din_marker | DIN маркер (если отдельный) | LOW |
NOTE: class 1 (cylinder_hole) is CRITICAL - it's the scale reference!
| File | Purpose |
|---|---|
cv_pipeline/train_yolo.py |
Train YOLO model |
cv_pipeline/pre_label.py |
Auto-generate labels from CV |
cv_pipeline/src/geometry_calculator.py |
Calculate measurements |
cv_pipeline/src/contour_analyzer.py |
Find contours |
cv_pipeline/dataset/lock_dataset.yaml |
Dataset config |
backend/app/main.py |
FastAPI app |
backend/app/api/v1/locks.py |
Lock endpoints |
backend/app/api/v1/match.py |
Matching logic |
mobile_app/lib/features/ar_camera/ |
AR camera screen |
mobile_app/lib/features/catalog/ |
Lock catalog |
| File | Purpose |
|---|---|
PROJECT_STATE.md |
Current project state |
ROBOFLOW_INSTRUCTION.md |
Labeling guide for Roboflow |
AI_EXPERTS/ |
Expert analysis results |
STARTUP.md |
How to start development |
cd backend
uvicorn app.main:app --port 8001 --reloadcd mobile_app
flutter runcd cv_pipeline
python train_yolo.py --epochs 100| Component | Status | Notes |
|---|---|---|
| Backend | ✅ Working | 147 locks in DB |
| Mobile App | ✅ Working | AR camera + catalog |
| CV Pipeline | Needs trained YOLO | |
| YOLO Dataset | ❌ In Progress | Collecting photos |
| Tests | ✅ 38 tests | Backend unit tests |
- GitHub: https://github.com/cjpanches/ai_lock_selector
- Branch: develop
- License: MIT
# geometry_calculator.py
DIN_CYLINDER_WIDTH = 10.0 # mm
DIN_CYLINDER_HEIGHT = 17.0 # mm
BACKSET_TOLERANCE = 2.0 # mm
CENTER_DISTANCE_TOLERANCE = 3.0 # mm
# Standard lock measurements
TYPICAL_SQUARE_SIZES = [8, 9, 10] # mm
TYPICAL_BACKSETS = [25, 30, 35, 40, 45, 50, 55, 60, 65, 68] # mm
TYPICAL_CENTER_DISTANCES = [50, 55, 60, 65, 70, 72, 75, 80, 85, 90, 92] # mmThis document is for AI/ML engineers to understand:
- What the lock looks like
- What measurements matter
- How calibration works (DIN hole = scale reference)
- What YOLO should detect
Key insight: The cylinder_hole (class 1) is both an object to detect AND the calibration reference. Label it accurately!