Skip to content

Commit 223abc9

Browse files
gmathy2104claude
andcommitted
feat: add wide-angle camera detection and intelligent sensor mode selection (v2.7.0)
This release adds automatic detection of wide-angle cameras (Camera Module 3 Wide) and intelligent sensor mode selection to preserve the full 120° field of view. ### Core Features **Wide-Angle Camera Detection** - Auto-detect Camera Module 3 Wide (120° FOV) via model name (imx708_wide_noir) - Distinguish wide cameras (120° FOV) from standard cameras (66° FOV) - Graceful fallback to standard camera if detection fails **Intelligent Sensor Mode Selection** - Wide cameras: Always use full sensor (Mode 0/1) to preserve 120° FOV - Mode 2 crops the sensor and would lose the wide-angle advantage - 720p-1080p: Use Mode 1 (2304x1296 @ 56fps) - Full sensor with binning - 4K: Use Mode 0 (4608x2592 @ 14fps) - Full sensor, no binning - Standard cameras: Can use Mode 2 (cropped) for higher framerates - Optimized for framerate vs resolution tradeoffs **Critical Fix** - Wide-angle cameras no longer lose field of view at lower resolutions - Previously: Mode 2 (1536x864 cropped) was used for 720p → Lost wide FOV - Now: Mode 1 (2304x1296 full sensor) preserves full 120° FOV at 720p/1080p ### API Changes **Enhanced GET /v1/camera/status** - Added is_wide_camera: Boolean flag for camera type - Added sensor_mode_width: Sensor mode width being used - Added sensor_mode_height: Sensor mode height being used **Enhanced GET /v1/camera/capabilities** - Added is_wide_camera: Camera type detection result - Added field_of_view_degrees: FOV in degrees (120° or 66°) - Added sensor_modes: Complete IMX708 sensor mode specifications - Added recommended_resolutions: Resolution presets optimized for camera type - Wide cameras: Prioritize full FOV preservation - Standard cameras: Prioritize framerate optimization ### Technical Details **camera_service/camera_controller.py** - New method: _detect_wide_camera() -> bool - New method: _get_recommended_resolutions() -> list - Updated: _get_optimal_sensor_mode() - Camera type-aware selection - New instance variable: self._is_wide_camera **camera_service/api.py** - Updated CameraStatusResponse model with v2.7 fields - Updated CameraCapabilitiesResponse model with v2.7 fields - Field mappings in get_camera_status endpoint - Field mappings in get_camera_capabilities endpoint **Documentation** - Updated CHANGELOG.md with comprehensive v2.7.0 release notes - Updated README.md with wide-angle camera features section - Updated API endpoint examples with v2.7 fields - Added client integration examples for dynamic preset generation ### Breaking Changes None - Fully backwards compatible with v2.6.x ### Tested On - Raspberry Pi 5 with Camera Module 3 Wide NoIR (imx708_wide_noir) - Verified 720p uses Mode 1 (2304x1296) preserving 120° FOV - Verified API returns is_wide_camera=true and sensor mode info 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent dbde127 commit 223abc9

File tree

4 files changed

+312
-33
lines changed

4 files changed

+312
-33
lines changed

CHANGELOG.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,107 @@ All notable changes to Pi Camera Service will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.7.0] - 2025-11-23
9+
10+
### Added
11+
12+
#### Wide-Angle Camera Detection and Intelligent Sensor Mode Selection
13+
- **NEW FEATURE**: Automatic wide-angle camera detection (Camera Module 3 Wide - 120° FOV)
14+
- **Problem Solved**: Wide-angle cameras were using cropped sensor modes (Mode 2), losing the wide 120° field of view advantage
15+
- **Solution**: Auto-detect wide vs standard cameras and intelligently select sensor mode to preserve FOV
16+
- **Impact**: Wide cameras now preserve full 120° FOV at all resolutions up to 1080p
17+
18+
#### Camera Type-Aware Sensor Mode Selection
19+
The system now adapts sensor mode selection based on camera type:
20+
21+
**For Wide-Angle Cameras (120° FOV)**:
22+
- **Always use full sensor** (Mode 0 or Mode 1) to preserve wide field of view
23+
- Mode 2 (cropped) would lose the wide-angle advantage
24+
- Recommended resolutions:
25+
- 720p-1080p: Mode 1 (2304x1296 @ 56fps) - Full sensor with binning
26+
- 4K: Mode 0 (4608x2592 @ 14fps) - Full sensor, no binning
27+
- All preserve full 120° FOV
28+
29+
**For Standard Cameras (66° FOV)**:
30+
- Can use Mode 2 (cropped) for higher framerates at lower resolutions
31+
- Mode 2 acceptable since cropping doesn't lose wide-angle advantage
32+
- Optimized for framerate vs resolution tradeoffs
33+
34+
#### New API Fields - Camera Type Information
35+
36+
**Enhanced `GET /v1/camera/status` Response**:
37+
- `is_wide_camera`: Boolean flag indicating wide-angle camera (120° FOV)
38+
- `sensor_mode_width`: Sensor mode width being used (e.g., 2304 for Mode 1)
39+
- `sensor_mode_height`: Sensor mode height being used (e.g., 1296 for Mode 1)
40+
41+
**Enhanced `GET /v1/camera/capabilities` Response**:
42+
- `is_wide_camera`: Camera type detection result
43+
- `field_of_view_degrees`: Field of view in degrees (120° for wide, 66° for standard)
44+
- `sensor_modes`: Complete specifications for all 3 IMX708 sensor modes
45+
- Mode 0: 4608x2592 @ 14.35fps (full sensor, no binning)
46+
- Mode 1: 2304x1296 @ 56.03fps (full sensor with 2x2 binning)
47+
- Mode 2: 1536x864 @ 120.13fps (cropped sensor with 2x2 binning)
48+
- `recommended_resolutions`: Array of recommended resolutions for this camera type
49+
- Includes resolution, label, max FPS, sensor mode, and FOV preservation info
50+
- Different recommendations for wide vs standard cameras
51+
52+
#### Wide-Angle Camera Detection Logic
53+
- Detects Camera Module 3 Wide via model name: `imx708_wide_noir`, `imx708_wide`
54+
- Logs camera type at startup for visibility
55+
- Graceful fallback to standard camera if detection fails
56+
57+
### Changed
58+
- Updated `CameraController._get_optimal_sensor_mode()` to consider camera type
59+
- Wide cameras now default to Mode 1 for all resolutions ≤ 1080p (preserves 120° FOV)
60+
- Standard cameras continue to use Mode 2 for ≤ 720p (optimizes framerate)
61+
- Enhanced logging to show FOV preservation rationale in sensor mode selection
62+
63+
### Fixed
64+
- **CRITICAL FIX**: Wide-angle cameras no longer lose field of view at lower resolutions
65+
- Root cause: Mode 2 crops the sensor, eliminating the wide-angle advantage
66+
- Impact: Users with Camera Module 3 Wide now get full 120° FOV at 720p and 1080p
67+
- Verified: `sensor_mode_width/height` shows 2304x1296 (Mode 1) instead of 1536x864 (Mode 2)
68+
69+
### Technical Details
70+
- New method: `CameraController._detect_wide_camera() -> bool`
71+
- New method: `CameraController._get_recommended_resolutions() -> list`
72+
- Updated method: `_get_optimal_sensor_mode()` now accepts camera type parameter
73+
- New instance variable: `self._is_wide_camera`
74+
- API models updated: `CameraStatusResponse`, `CameraCapabilitiesResponse`
75+
- Zero breaking changes - fully backwards compatible
76+
77+
### Use Cases
78+
- **Surveillance with wide FOV**: Preserve 120° coverage at all resolutions
79+
- **Dynamic preset selection**: Client can query recommended resolutions for camera type
80+
- **Intelligent client UIs**: Display appropriate resolution options based on camera capabilities
81+
- **FOV-aware applications**: Adjust counting lines, detection zones based on actual FOV
82+
83+
### Client Integration Example
84+
85+
```javascript
86+
// Query camera capabilities
87+
const capabilities = await fetch('/v1/camera/capabilities').then(r => r.json());
88+
89+
if (capabilities.is_wide_camera) {
90+
console.log(`Wide-angle camera detected: ${capabilities.field_of_view_degrees}° FOV`);
91+
92+
// Use recommended resolutions that preserve full FOV
93+
const presets = capabilities.recommended_resolutions.map(res => ({
94+
label: res.label,
95+
width: res.width,
96+
height: res.height,
97+
maxFps: res.max_fps,
98+
fov: res.fov
99+
}));
100+
}
101+
```
102+
103+
### Upgrade Notes
104+
- Existing deployments with Camera Module 3 Wide will automatically benefit from FOV preservation
105+
- No configuration changes required - detection is automatic
106+
- Check `/v1/camera/capabilities` to verify camera type detection
107+
- Recommended to update client UIs to use `recommended_resolutions` endpoint for dynamic presets
108+
8109
## [2.6.1] - 2025-11-22
9110

10111
### Added

README.md

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
Production-ready **FastAPI** microservice for controlling Raspberry Pi Camera (libcamera/Picamera2) with **H.264 streaming** to **MediaMTX** via RTSP.
44

5-
**Version 2.6.1** - Intelligent bitrate auto-selection prevents encoding errors!
5+
**Version 2.7.0** - Wide-angle camera support preserves 120° field of view!
66

7-
[![Version](https://img.shields.io/badge/version-2.6.1-blue.svg)](https://github.com/gmathy2104/pi-camera-service/releases)
7+
[![Version](https://img.shields.io/badge/version-2.7.0-blue.svg)](https://github.com/gmathy2104/pi-camera-service/releases)
88
[![Python](https://img.shields.io/badge/python-3.9+-green.svg)](https://www.python.org/)
99
[![FastAPI](https://img.shields.io/badge/FastAPI-0.121+-teal.svg)](https://fastapi.tiangolo.com/)
1010
[![License](https://img.shields.io/badge/license-MIT-orange.svg)](LICENSE)
1111
[![Tests](https://github.com/gmathy2104/pi-camera-service/workflows/Tests/badge.svg)](https://github.com/gmathy2104/pi-camera-service/actions)
1212

13-
> 🔥 **New in v2.6.1**: **Intelligent Bitrate Auto-Selection** - Automatic bitrate calculation prevents corrupted macroblock errors. Bitrate now adapts to resolution×framerate (12 Mbps @ 720p/60fps, 25 Mbps @ 1080p/60fps). Visible in status endpoint!
13+
> 🔥 **New in v2.7.0**: **Wide-Angle Camera Detection** - Automatic detection of Camera Module 3 Wide (120° FOV). Intelligent sensor mode selection preserves full wide-angle field of view at all resolutions. New API fields expose camera type, sensor modes, and recommended resolutions!
14+
>
15+
> 🔥 **v2.6.1**: **Intelligent Bitrate Auto-Selection** - Automatic bitrate calculation prevents corrupted macroblock errors. Bitrate now adapts to resolution×framerate (12 Mbps @ 720p/60fps, 25 Mbps @ 1080p/60fps). Visible in status endpoint!
1416
>
1517
> **v2.6 features**: **Intelligent Sensor Mode Auto-Selection** - Camera Module 3 (IMX708) automatically selects optimal native sensor mode. 720p achieves 60fps (was 14fps) - 4.2x faster!
1618
>
@@ -216,6 +218,43 @@ This service runs **on the Raspberry Pi**, controls the camera (e.g., Raspberry
216218
- `max_framerate_for_current_resolution`: Maximum fps for active resolution
217219
- `framerate_limits_by_resolution`: Complete table of max fps for each resolution
218220

221+
### Advanced Features (v2.7) 🆕
222+
223+
#### 📐 Wide-Angle Camera Detection & FOV Preservation
224+
- **Automatic camera type detection**: Detects Camera Module 3 Wide (120° FOV) vs standard cameras (66° FOV)
225+
- Detection via camera model name (e.g., `imx708_wide_noir`)
226+
- Graceful fallback to standard camera if detection fails
227+
- **Intelligent sensor mode selection**: Preserves wide-angle field of view
228+
- **Wide cameras**: Always use full sensor (Mode 0 or Mode 1) to maintain 120° FOV
229+
- **Standard cameras**: Can use cropped modes (Mode 2) for higher framerates
230+
- **Critical fix**: Wide cameras no longer lose FOV at lower resolutions
231+
- **New API fields in `GET /v1/camera/status`**:
232+
- `is_wide_camera`: Boolean flag for camera type (true = 120° FOV)
233+
- `sensor_mode_width`: Current sensor mode width being used
234+
- `sensor_mode_height`: Current sensor mode height being used
235+
- **New API fields in `GET /v1/camera/capabilities`**:
236+
- `is_wide_camera`: Camera type detection result
237+
- `field_of_view_degrees`: Field of view (120° or 66°)
238+
- `sensor_modes`: Complete IMX708 sensor mode specifications
239+
- `recommended_resolutions`: Resolution presets optimized for camera type
240+
- Wide cameras: Prioritize full FOV preservation
241+
- Standard cameras: Prioritize framerate optimization
242+
- **Perfect for**: Surveillance with wide coverage, dynamic client UIs, FOV-aware applications
243+
244+
**Example - Query camera type and get recommendations**:
245+
```javascript
246+
const capabilities = await fetch('/v1/camera/capabilities').then(r => r.json());
247+
248+
if (capabilities.is_wide_camera) {
249+
console.log(`Wide-angle camera: ${capabilities.field_of_view_degrees}° FOV`);
250+
251+
// Use recommended resolutions that preserve full 120° FOV
252+
capabilities.recommended_resolutions.forEach(res => {
253+
console.log(`${res.label}: ${res.width}x${res.height} @ ${res.max_fps}fps (${res.fov})`);
254+
});
255+
}
256+
```
257+
219258
### Advanced Features (v2.5) 🆕
220259

221260
#### 🖥️ System Monitoring & Health Metrics
@@ -472,7 +511,7 @@ sudo journalctl -u pi-camera-service -f
472511
}
473512
```
474513

475-
### Camera Status (Enhanced in v2.0)
514+
### Camera Status (Enhanced in v2.0, v2.2, v2.7)
476515

477516
**GET** `/v1/camera/status`
478517
```json
@@ -504,11 +543,16 @@ sudo journalctl -u pi-camera-service -f
504543
"min": 100,
505544
"max": 1000000
506545
}
507-
}
546+
},
547+
548+
// New v2.7 fields (wide-angle camera support)
549+
"is_wide_camera": true,
550+
"sensor_mode_width": 2304,
551+
"sensor_mode_height": 1296
508552
}
509553
```
510554

511-
### Camera Capabilities (New in v2.2, Enhanced in v2.3)
555+
### Camera Capabilities (New in v2.2, Enhanced in v2.3, v2.7)
512556

513557
**GET** `/v1/camera/capabilities`
514558

@@ -550,6 +594,21 @@ Discover camera hardware capabilities and supported features.
550594
{"width": 1920, "height": 1080, "label": "1080p", "max_fps": 50.0},
551595
{"width": 1280, "height": 720, "label": "720p", "max_fps": 120.0},
552596
{"width": 640, "height": 480, "label": "VGA", "max_fps": 120.0}
597+
],
598+
599+
// New v2.7 fields (wide-angle camera support)
600+
"is_wide_camera": true,
601+
"field_of_view_degrees": 120,
602+
"sensor_modes": {
603+
"mode_0": {"width": 4608, "height": 2592, "max_fps": 14.35, "description": "Full sensor, no binning"},
604+
"mode_1": {"width": 2304, "height": 1296, "max_fps": 56.03, "description": "Full sensor with 2x2 binning"},
605+
"mode_2": {"width": 1536, "height": 864, "max_fps": 120.13, "description": "Cropped sensor with 2x2 binning"}
606+
},
607+
"recommended_resolutions": [
608+
{"width": 2304, "height": 1296, "label": "Native Mode 1", "max_fps": 56, "sensor_mode": "mode_1", "fov": "Full 120°"},
609+
{"width": 1920, "height": 1080, "label": "1080p (Full FOV)", "max_fps": 56, "sensor_mode": "mode_1", "fov": "Full 120°"},
610+
{"width": 1280, "height": 720, "label": "720p (Full FOV)", "max_fps": 56, "sensor_mode": "mode_1", "fov": "Full 120°"},
611+
{"width": 4608, "height": 2592, "label": "4K (Full sensor)", "max_fps": 14, "sensor_mode": "mode_0", "fov": "Full 120°"}
553612
]
554613
}
555614
```

camera_service/api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ class CameraStatusResponse(BaseModel):
257257
bitrate_bps: int | None = Field(None, description="Current bitrate in bits per second")
258258
bitrate_mbps: float | None = Field(None, description="Current bitrate in megabits per second")
259259

260+
# Camera type and sensor mode (v2.7.0)
261+
is_wide_camera: bool | None = Field(None, description="True if wide-angle camera (120° FOV)")
262+
sensor_mode_width: int | None = Field(None, description="Sensor mode width being used")
263+
sensor_mode_height: int | None = Field(None, description="Sensor mode height being used")
264+
260265
# Current limits (v2.2)
261266
current_limits: dict | None = Field(None, description="Currently applied exposure/frame duration limits")
262267

@@ -278,6 +283,11 @@ class CameraCapabilitiesResponse(BaseModel):
278283
current_framerate: float = Field(..., description="Current configured framerate")
279284
framerate_limits_by_resolution: list[dict] = Field(..., description="Maximum framerate for each resolution")
280285
max_framerate_for_current_resolution: float = Field(..., description="Maximum framerate for current resolution")
286+
# Wide-angle camera info (v2.7.0)
287+
is_wide_camera: bool | None = Field(None, description="True if wide-angle camera (120° FOV)")
288+
field_of_view_degrees: int | None = Field(None, description="Field of view in degrees (120° or 66°)")
289+
sensor_modes: dict | None = Field(None, description="Available sensor modes with specifications")
290+
recommended_resolutions: list[dict] | None = Field(None, description="Recommended resolutions for this camera type")
281291

282292

283293
class AutoExposureResponse(StatusResponse):
@@ -588,6 +598,11 @@ def get_camera_status(
588598
bitrate_bps=status_data.get("bitrate_bps"),
589599
bitrate_mbps=status_data.get("bitrate_mbps"),
590600

601+
# Camera type and sensor mode (v2.7.0)
602+
is_wide_camera=status_data.get("is_wide_camera"),
603+
sensor_mode_width=status_data.get("sensor_mode_width"),
604+
sensor_mode_height=status_data.get("sensor_mode_height"),
605+
591606
# Current limits (v2.2)
592607
current_limits=status_data.get("current_limits"),
593608
)

0 commit comments

Comments
 (0)