Update app.py
Browse files
app.py
CHANGED
@@ -193,28 +193,27 @@ def zip_files(files, zip_name):
|
|
193 |
class CameraProcessor(VideoProcessorBase):
|
194 |
def __init__(self):
|
195 |
self.snapshot = None
|
196 |
-
self.recording = False
|
197 |
self.frames = []
|
198 |
-
self.
|
199 |
|
200 |
def recv(self, frame):
|
201 |
from PIL import Image
|
202 |
img = frame.to_image()
|
203 |
self.snapshot = img
|
204 |
-
if self.recording
|
205 |
self.frames.append(frame.to_ndarray(format="bgr24"))
|
206 |
return av.VideoFrame.from_image(img)
|
207 |
|
208 |
-
def
|
209 |
from PIL import Image
|
210 |
return self.snapshot
|
211 |
|
212 |
-
def
|
213 |
self.recording = True
|
214 |
self.frames = []
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
self.recording = False
|
219 |
return self.frames
|
220 |
|
@@ -290,11 +289,11 @@ with tab2:
|
|
290 |
video_processor_factory=CameraProcessor,
|
291 |
frontend_rtc_configuration={"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]}
|
292 |
)
|
293 |
-
if
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
snapshot = processors[key].video_processor.
|
298 |
if snapshot:
|
299 |
filename = generate_filename(i)
|
300 |
snapshot.save(filename)
|
@@ -304,56 +303,56 @@ with tab2:
|
|
304 |
st.session_state['captured_images'] = []
|
305 |
st.session_state['captured_images'].append(filename)
|
306 |
update_gallery()
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
record_key = f"record_{i}"
|
311 |
-
if record_key not in st.session_state:
|
312 |
-
st.session_state[record_key] = False
|
313 |
-
if st.button(f"{'Stop' if st.session_state[record_key] else 'Record'} 🎥 Cam {i}", key=f"rec_{i}"):
|
314 |
-
if not st.session_state[record_key]:
|
315 |
-
logger.info(f"Starting recording from Camera {i}")
|
316 |
-
try:
|
317 |
-
processors[key].video_processor.start_recording()
|
318 |
-
st.session_state[record_key] = True
|
319 |
-
except Exception as e:
|
320 |
-
st.error(f"Start recording failed: {str(e)}")
|
321 |
-
logger.error(f"Error starting recording: {str(e)}")
|
322 |
else:
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
|
|
|
|
|
|
|
|
|
|
337 |
container.mux(packet)
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
|
358 |
with tab3:
|
359 |
st.header("Fine-Tune Titans 🔧 (Tune Fast!)")
|
|
|
193 |
class CameraProcessor(VideoProcessorBase):
|
194 |
def __init__(self):
|
195 |
self.snapshot = None
|
|
|
196 |
self.frames = []
|
197 |
+
self.recording = False
|
198 |
|
199 |
def recv(self, frame):
|
200 |
from PIL import Image
|
201 |
img = frame.to_image()
|
202 |
self.snapshot = img
|
203 |
+
if self.recording:
|
204 |
self.frames.append(frame.to_ndarray(format="bgr24"))
|
205 |
return av.VideoFrame.from_image(img)
|
206 |
|
207 |
+
def capture_frame(self):
|
208 |
from PIL import Image
|
209 |
return self.snapshot
|
210 |
|
211 |
+
def capture_video(self, duration=10):
|
212 |
self.recording = True
|
213 |
self.frames = []
|
214 |
+
start_time = time.time()
|
215 |
+
while time.time() - start_time < duration and self.recording:
|
216 |
+
time.sleep(0.033) # ~30 FPS
|
217 |
self.recording = False
|
218 |
return self.frames
|
219 |
|
|
|
289 |
video_processor_factory=CameraProcessor,
|
290 |
frontend_rtc_configuration={"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]}
|
291 |
)
|
292 |
+
if st.button(f"Capture Frame 📸 Cam {i}", key=f"snap_{i}"):
|
293 |
+
logger.info(f"Capturing frame from Camera {i}")
|
294 |
+
try:
|
295 |
+
if processors[key].video_processor:
|
296 |
+
snapshot = processors[key].video_processor.capture_frame()
|
297 |
if snapshot:
|
298 |
filename = generate_filename(i)
|
299 |
snapshot.save(filename)
|
|
|
303 |
st.session_state['captured_images'] = []
|
304 |
st.session_state['captured_images'].append(filename)
|
305 |
update_gallery()
|
306 |
+
else:
|
307 |
+
st.error("No frame captured!")
|
308 |
+
logger.error("No frame available for snapshot")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
else:
|
310 |
+
st.error("Camera processor not initialized!")
|
311 |
+
logger.error("Processor not ready for Camera {i}")
|
312 |
+
except Exception as e:
|
313 |
+
st.error(f"Frame capture failed: {str(e)}")
|
314 |
+
logger.error(f"Error capturing frame: {str(e)}")
|
315 |
+
if st.button(f"Capture Video 🎥 Cam {i}", key=f"rec_{i}"):
|
316 |
+
logger.info(f"Capturing 10s video from Camera {i}")
|
317 |
+
try:
|
318 |
+
if processors[key].video_processor:
|
319 |
+
frames = processors[key].video_processor.capture_video()
|
320 |
+
if frames:
|
321 |
+
mp4_filename = generate_filename(i, "mp4")
|
322 |
+
with av.open(mp4_filename, "w") as container:
|
323 |
+
stream = container.add_stream("h264", rate=30)
|
324 |
+
stream.width = frames[0].shape[1]
|
325 |
+
stream.height = frames[0].shape[0]
|
326 |
+
for frame in frames:
|
327 |
+
av_frame = av.VideoFrame.from_ndarray(frame, format="bgr24")
|
328 |
+
for packet in stream.encode(av_frame):
|
329 |
container.mux(packet)
|
330 |
+
for packet in stream.encode():
|
331 |
+
container.mux(packet)
|
332 |
+
st.video(mp4_filename)
|
333 |
+
logger.info(f"Saved video: {mp4_filename}")
|
334 |
+
# Slice into 10 frames
|
335 |
+
sliced_images = []
|
336 |
+
step = max(1, len(frames) // 10)
|
337 |
+
for j in range(0, len(frames), step):
|
338 |
+
if len(sliced_images) < 10:
|
339 |
+
img = Image.fromarray(frames[j][:, :, ::-1]) # BGR to RGB
|
340 |
+
img_filename = generate_filename(f"{i}_{len(sliced_images)}")
|
341 |
+
img.save(img_filename)
|
342 |
+
sliced_images.append(img_filename)
|
343 |
+
st.image(img, caption=img_filename, use_container_width=True)
|
344 |
+
st.session_state['captured_images'] = st.session_state.get('captured_images', []) + sliced_images
|
345 |
+
logger.info(f"Sliced video into {len(sliced_images)} images")
|
346 |
+
update_gallery()
|
347 |
+
else:
|
348 |
+
st.error("No frames recorded!")
|
349 |
+
logger.error("No frames captured during video recording")
|
350 |
+
else:
|
351 |
+
st.error("Camera processor not initialized!")
|
352 |
+
logger.error("Processor not ready for Camera {i}")
|
353 |
+
except Exception as e:
|
354 |
+
st.error(f"Video capture failed: {str(e)}")
|
355 |
+
logger.error(f"Error capturing video: {str(e)}")
|
356 |
|
357 |
with tab3:
|
358 |
st.header("Fine-Tune Titans 🔧 (Tune Fast!)")
|