-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcamera_streamer.py
More file actions
234 lines (186 loc) · 6.65 KB
/
Copy pathcamera_streamer.py
File metadata and controls
234 lines (186 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
"""
Camera Streamer Module
Streams camera feed from Python OpenCV to web frontend via HTTP.
Author: Gesture Recognition Team Member
For: BigRedHacks MVP
"""
import cv2
import threading
import time
import base64
import json
import logging
from typing import Optional
import requests
from io import BytesIO
from PIL import Image
logger = logging.getLogger(__name__)
class CameraStreamer:
"""
Streams camera feed to web frontend via HTTP endpoint.
"""
def __init__(self, camera_index: int = 1, server_url: str = "http://localhost:3001"):
"""
Initialize camera streamer.
Args:
camera_index: Camera device index
server_url: URL of the web server to send frames to
"""
self.camera_index = camera_index
self.server_url = server_url.rstrip("/")
self.cap = None
self.streaming = False
self.frame_data = None
self.last_gesture = None
self.frame_lock = threading.Lock()
logger.info(f"CameraStreamer initialized for camera {camera_index}")
def start_camera(self) -> bool:
"""
Start the camera capture.
Returns:
True if camera started successfully, False otherwise
"""
try:
self.cap = cv2.VideoCapture(self.camera_index)
if not self.cap.isOpened():
logger.error(f"Failed to open camera {self.camera_index}")
return False
# Set camera properties
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
self.cap.set(cv2.CAP_PROP_FPS, 30)
logger.info(f"Camera {self.camera_index} started successfully")
return True
except Exception as e:
logger.error(f"Error starting camera: {e}")
return False
def start_streaming(self):
"""Start streaming camera feed to web server."""
if not self.cap or not self.cap.isOpened():
if not self.start_camera():
logger.error("Failed to start camera for streaming")
return
self.streaming = True
logger.info("Starting camera stream to web server")
# Start streaming thread
threading.Thread(target=self._stream_loop, daemon=True).start()
def stop_streaming(self):
"""Stop streaming camera feed."""
self.streaming = False
logger.info("Stopping camera stream")
def stop_camera(self):
"""Stop camera and release resources."""
self.stop_streaming()
if self.cap:
self.cap.release()
self.cap = None
logger.info("Camera stopped")
def update_frame(self, frame, detected_gesture: Optional[str] = None):
"""
Update the current frame data.
Args:
frame: OpenCV frame (BGR format)
detected_gesture: Recently detected gesture name
"""
if frame is None:
return
# Convert BGR to RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Convert to PIL Image
pil_image = Image.fromarray(rgb_frame)
# Resize for web display
pil_image = pil_image.resize((640, 480), Image.Resampling.LANCZOS)
# Convert to base64
buffer = BytesIO()
pil_image.save(buffer, format='JPEG', quality=85)
img_str = base64.b64encode(buffer.getvalue()).decode()
with self.frame_lock:
self.frame_data = {
'image': img_str,
'timestamp': time.time(),
'gesture': detected_gesture
}
if detected_gesture:
self.last_gesture = detected_gesture
def _stream_loop(self):
"""Main streaming loop that sends frames to web server."""
while self.streaming:
try:
with self.frame_lock:
if self.frame_data:
# Send frame to web server
self._send_frame_to_server(self.frame_data)
# Stream at ~10 FPS to reduce bandwidth
time.sleep(0.1)
except Exception as e:
logger.error(f"Error in streaming loop: {e}")
time.sleep(1)
def _send_frame_to_server(self, frame_data):
"""
Send frame data to web server.
Args:
frame_data: Dictionary containing image and metadata
"""
try:
response = requests.post(
f"{self.server_url}/api/camera-frame",
json=frame_data,
timeout=1.0
)
if response.status_code != 200:
logger.warning(f"Server returned status {response.status_code}")
except requests.exceptions.RequestException as e:
# Don't log every connection error to avoid spam
pass
def get_last_gesture(self) -> Optional[dict]:
"""
Get information about the last detected gesture.
Returns:
Dictionary with gesture info or None
"""
if self.last_gesture:
return {
'gesture': self.last_gesture,
'timestamp': time.time()
}
return None
# Global camera streamer instance
camera_streamer = None
def initialize_camera_streamer(camera_index: int = 1, server_url: str = "http://localhost:3001"):
"""
Initialize the global camera streamer.
Args:
camera_index: Camera device index
server_url: URL of the web server
"""
global camera_streamer
camera_streamer = CameraStreamer(camera_index, server_url)
return camera_streamer
def get_camera_streamer() -> Optional[CameraStreamer]:
"""
Get the global camera streamer instance.
Returns:
CameraStreamer instance or None if not initialized
"""
return camera_streamer
if __name__ == "__main__":
# Test the camera streamer
print("Testing Camera Streamer...")
streamer = CameraStreamer(camera_index=1)
if streamer.start_camera():
print("✅ Camera started successfully")
streamer.start_streaming()
print("Streaming to web server...")
print("Press Ctrl+C to stop")
try:
while True:
ret, frame = streamer.cap.read()
if ret:
streamer.update_frame(frame)
time.sleep(0.1)
except KeyboardInterrupt:
print("\nStopping...")
finally:
streamer.stop_camera()
else:
print("❌ Failed to start camera")