feat: Add Camera Settings dialog for UVC controls and Qt parameters

- Implemented CameraSettingsDialog to manage UVC and Qt camera controls.
- Integrated UVC parameter sliders and auto controls for brightness, contrast, saturation, hue, sharpness, gamma, white balance, backlight compensation, and exposure.
- Added functionality to change white balance and exposure settings via Qt controls.
- Updated MainWindow to open CameraSettingsDialog and manage UVC controller lifecycle.
- Enhanced AppMenuBar to include a Camera Settings option.
- Created tests for UVC controller abstraction layer and parameter info.
- Documented camera specifications and supported features in new markdown files.
This commit is contained in:
2026-05-13 19:19:39 +02:00
parent d62416db4e
commit cdeac53555
12 changed files with 1756 additions and 118 deletions

480
notes/03-mvp-summary.md Normal file
View File

@@ -0,0 +1,480 @@
# Goal
Zbudować aplikację do podglądu kamery w czasie rzeczywistym (PySide6) z telemetrią, overlayami i systemem logowania do pliku — docelowo na Mac Mini z kamerą ELP.
---
# Constraints & Preferences
- Python 3.12.10, venv w `.venv-win`
- Dev: Windows 11
- Target: Mac Mini Intel i7, macOS Ventura, kamera ELP USB
- `PySide6 6.11.0`, `psutil`, `pytest`, `ruff`
- `QVideoWidget` porzucony — na Windows zasłania wszystkie child/sibling widgets przez natywny HWND D3D
- Logi:
- `logs/` w katalogu projektu
- nowy plik z timestamp per sesja
- max 20 plików
- CSV telemetrii:
- co 5 sekund
- `flush` po każdym wierszu
- CPU:
- pokazywać oba:
- sys %
- per-core % (jak Task Manager)
- Zmiana formatu kamery przez `stop+start`
- nie `setCameraFormat` na żywej kamerze
---
# Progress
## Done
### Faza 07 MVP
- scaffolding
- `CameraService`
- `FrameDispatcher`
- `TelemetryCollector`
- `CameraView`
- `TelemetryOverlay`
- `IOverlayLayer`
- `AppMenuBar`
- `MainWindow`
### Rendering
- Usunięto `QVideoWidget`
- `CameraView(QWidget)` z `paintEvent`
- render + overlay w jednym przejściu
### Telemetria
`TelemetrySnapshot` — pola:
- `fps`
- `target_fps`
- `frame_time_ms`
- `dropped_frames`
- `cpu_percent_sys`
- `cpu_percent_core`
- `memory_mb`
### Kamera
- `CameraService.format_changed(float)` sygnał
- `stop+start` przy:
- `set_resolution`
- `set_fps`
### Logowanie
- `_log_actual_format()`
- loguje rzeczywisty format po starcie kamery
- warning jeśli FPS się nie zgadza
### CameraFormat
`CameraFormat` dataclass z:
```python
pixel_format: str
```
Przykłady:
- `MJPG`
- `YUY2`
- `NV12`
w `camera_enumerator.py`
### Logging
`logging_setup.py`
- `FileHandler`
- `DEBUG`
- zawsze aktywny
- `StreamHandler`
- `WARNING`
- przełączalny
- nagłówek sesji:
- wersja
- platforma
- Python
- PySide6
- CPU
- RAM
- pruning starych logów
### CSV
`csv_logger.py`
- `CsvTelemetryLogger`
- throttling:
- 5 s
- `flush` po każdym wierszu
### Menu
`menu_bar.py`
- dostosowany do `CameraFormat`
- `set_log_file_path()`
- `set_console_level()`
### Config
`config.py`
- `LOG_DIR`
- `MAX_LOG_FILES`
- `TELEMETRY_CSV_INTERVAL_S`
### Main
`main.py`
- wywołuje `setup_logging()`
- przekazuje `log_path` do `MainWindow`
### Main Window
`main_window.py`
- przyjmuje:
```python
log_path: Path | None
```
- tworzy:
```python
CsvTelemetryLogger(log_path.with_suffix(".csv"))
```
- podpina do:
- `telemetry.metrics_updated`
- wywołuje:
- `menu.set_log_file_path()`
- `csv_logger.close()` w `closeEvent`
### Repo
`.gitignore`
- dodano `logs/`
### Testy i jakość
- 20 testów jednostkowych
- wszystkie zielone
- `ruff`
- czysty
- wszystkie błędy naprawione
### Artefakty sesji
Para plików per sesja:
```text
logs/duck-preview_<timestamp>.log
logs/duck-preview_<timestamp>.csv
```
---
## In Progress
- (none)
---
## Blocked
- (none)
---
# Key Decisions
## Rendering
### `QVideoWidget` → własny `CameraView(QWidget)`
Jedyny skuteczny sposób obejścia problemu z natywnym HWND na Windows.
## Overlay Architecture
### `IOverlayLayer` ABC
Pluggable overlaye bez modyfikacji `CameraView`.
Gotowe pod:
- YOLO
- OCR
- inne overlaye
## Pipeline
### `FrameDispatcher`
Pub/sub z `drop_if_busy`:
- render może gubić klatki
- telemetria nigdy
## CPU Metrics
- CPU per-core (`psutil`)
- dzielone przez `cpu_count`
- wynik zgodny z Task Manager
## Memory Metrics
- `memory_info().wset` (Windows)
- zamiast `rss`
- odpowiada `Private Working Set`
## Logging Strategy
Dwa osobne pliki per sesja:
- `.log` — diagnostyka
- `.csv` — metryki szeregów czasowych
## CameraFormat
`CameraFormat` dataclass zamiast krotki:
- niesie `pixel_format`
- potrzebny w logach
---
# Next Steps
1. Przetestować na Windows:
- czy logi powstają w `logs/`
- czy CSV zapisuje dane co ~5 s
2. Przenieść na Mac Mini i przetestować z kamerą ELP:
- sprawdzić `pixel_format` w logu
- sprawdzić wykrycie backendu AVFoundation
3. Zweryfikować overlay telemetrii na żywym obrazie
4. (Opcjonalnie) dodać kolejne `IOverlayLayer`
- YOLO
- OCR
---
# Critical Context
## Camera Enumerator
`CameraEnumerator.list_cameras()` zwraca:
```python
list[CameraInfo]
```
gdzie:
```python
formats: list[CameraFormat]
```
`menu_bar.py` i `camera_service.py` iterują po:
- `fmt.width`
- `fmt.height`
- `fmt.max_fps`
---
## CSV Logger
Ścieżka CSV:
```python
log_path.with_suffix(".csv")
```
Powstaje para:
- `.log`
- `.csv`
z tym samym timestamp.
---
## Console Logging
```python
logging_setup.set_console_level(debug: bool)
```
wywoływane z:
```python
menu_bar._on_log_toggled
```
---
## Pixel Format
```python
pixel_format_name()
```
eksportowane z:
```python
camera_enumerator.py
```
używane również w:
```python
camera_service.py
```
---
## Log Header
Nagłówek logu zawiera:
- wersję aplikacji
- platformę
- Python
- PySide6
- liczbę CPU
- RAM
---
## Uruchamianie
```bash
.venv-win\Scripts\python.exe -m app.main
```
---
## Ruff Fixes
Naprawiono:
- `E501`
- długa linia w `camera_enumerator.py`
- `F401`
- `I001`
- w `camera_service.py`
- w `main.py`
---
# Relevant Files
## Entry Point
- `app/main.py`
- wywołuje `setup_logging()`
- przekazuje `log_path`
## Config
- `app/config.py`
- `LOG_DIR`
- `MAX_LOG_FILES`
- `TELEMETRY_CSV_INTERVAL_S`
## Logging
- `app/logging_setup.py`
- konfiguracja logowania
- nagłówek sesji
- pruning
## Telemetry
- `app/telemetry/csv_logger.py`
- zapis CSV z throttlingiem
- `app/telemetry/telemetry_collector.py`
- `TelemetrySnapshot`
- `TelemetryCollector`
## Camera
- `app/camera/camera_enumerator.py`
- `CameraFormat`
- `CameraInfo`
- `pixel_format_name()`
- `app/camera/camera_service.py`
- `CameraService`
- `format_changed`
- `_log_actual_format()`
## UI
- `app/ui/main_window.py`
- kompletny
- podpięty `CsvTelemetryLogger`
- `app/ui/menu_bar.py`
- `set_log_file_path()`
- `set_console_level()`
- `app/ui/camera_view.py`
- `CameraView`
- rejestr `IOverlayLayer`
## Overlay
- `app/overlay/overlay_layer.py`
- `IOverlayLayer` ABC
- `app/overlay/telemetry_overlay.py`
- `TelemetryOverlay(IOverlayLayer)`
## Pipeline
- `app/pipeline/frame_dispatcher.py`
- pub/sub
- `drop_if_busy`
## Tests
- `tests/test_telemetry_collector.py`
- 12 testów
- mockuje `cpu_count`
- `tests/test_frame_dispatcher.py`
- 8 testów
## Repo / Docs
- `.gitignore`
- zawiera `logs/`
- `notes/01-mvp-preview.md`
- PRD
- `notes/01-mvp-plan.md`
- plan implementacji
- `notes/02-mvp-app.md`
- stan aplikacji
- historia prób i decyzji architektonicznych

182
notes/camera_elp.md Normal file
View File

@@ -0,0 +1,182 @@
# Camera Specification
## Model
**ELP-USB4KHDR01-MFV(5-50)**
(10X zoom manual lens)
### Optional
**ELP-USB4KHDR01-MFV(2.8-12)**
(2.8-12mm manual zoom lens)
---
## Sensor
- SONY IMX317 (1/2.5”)
## Max Resolution
- 3840(H) × 2160(V)
## Sensitivity
- 1000mV/Lux-sec
## Image Area
- 6100μm × 4524μm
## Picture Format
- MJPEG
- YUY2 (YUYV)
---
# Resolution & FPS
## MJPEG Modes
| Resolution | FPS |
|---|---|
| 3840×2160 | 30 fps |
| 2592×1944 | 30 fps |
| 2048×1536 | 30 fps |
| 1600×1200 | 30 fps |
| 1920×1080 | 30 fps |
| 1280×1024 | 30 fps |
| 1280×960 | 30 fps |
| 1280×720 | 30 fps |
| 1024×768 | 30 fps |
| 800×600 | 30 fps |
| 640×480 | 30 fps |
| 320×240 | 30 fps |
## YUY2 Modes
| Resolution | FPS |
|---|---|
| 3840×2160 | 1 fps |
| 2592×1944 | 1 fps |
| 2048×1536 | 3 fps |
| 1600×1200 | 3 fps |
| 1920×1080 | 3 fps |
| 1280×1024 | 3 fps |
| 1280×960 | 5 fps |
| 1280×720 | 5 fps |
| 1024×768 | 5 fps |
| 800×600 | 20 fps |
| 640×480 | 30 fps |
| 320×240 | 30 fps |
---
## Optical / Image Parameters
| Parameter | Value |
|---|---|
| Center Definition | 1000LW/PH (Center) |
| S/N Ratio | 26dB |
| Sensitivity | 0.65V/lux-sec@550nm |
| Low Illumination | 0.2lux |
| Shutter | Electronic rolling shutter / Frame exposure |
---
## Interface
- USB2.0 High Speed
---
## Supported Features
| Feature | Support |
|---|---|
| AEC | Yes |
| AEB | Yes |
| AGC | Yes |
---
## Adjustable Parameters
- Brightness
- Contrast
- Saturation
- Hue
- Sharpness
- Gamma
- White Balance
- Backlight Contrast
- Exposure
---
## Lens
- 2.8-12mm / 5-50mm varifocal manual lens optional
---
## Audio
- Built-in microphone
- Supports audio recording
---
## Power
| Parameter | Value |
|---|---|
| Power Supply | USB BUS POWER 4P-2.0mm socket |
| Voltage | DC5V |
| Current | 200mA |
---
## Physical Dimensions
- 45mm × 45mm × 50mm
---
## Temperature
| Parameter | Value |
|---|---|
| Storage Temperature | -20°C to 70°C |
| Working Temperature | 0°C to 60°C |
---
## USB Cable
- 3M standard
- Optional: 1M / 2M / 5M
---
# Supported Operating Systems
## Windows
- Windows XP
- Windows Vista
- Windows 7
- Windows 8
- Windows 10
- Windows 11
## Linux
- Linux with UVC support
- Kernel above linux-2.6.26
## macOS / Android
- macOS X 10.4.8 or later
- Android 4.0 or above with UVC