Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,94 +2,26 @@ import streamlit as st
|
|
2 |
import pandas as pd
|
3 |
from datetime import datetime
|
4 |
import os
|
5 |
-
import threading
|
6 |
-
import time
|
7 |
-
from PIL import Image
|
8 |
-
from io import BytesIO
|
9 |
import base64
|
10 |
-
import cv2
|
11 |
-
import numpy as np
|
12 |
-
import queue
|
13 |
|
14 |
# ππ₯ Initialize session state like a galactic DJ spinning tracks!
|
15 |
if 'file_history' not in st.session_state:
|
16 |
st.session_state['file_history'] = []
|
17 |
-
if 'auto_capture_running' not in st.session_state:
|
18 |
-
st.session_state['auto_capture_running'] = False
|
19 |
-
|
20 |
-
# πΈπ· Camera thread class for OpenCV capture
|
21 |
-
class CamThread(threading.Thread):
|
22 |
-
def __init__(self, preview_name, cam_id, frame_queue):
|
23 |
-
threading.Thread.__init__(self)
|
24 |
-
self.preview_name = preview_name
|
25 |
-
self.cam_id = cam_id
|
26 |
-
self.frame_queue = frame_queue
|
27 |
-
self.running = True
|
28 |
-
|
29 |
-
def run(self):
|
30 |
-
print(f"Starting {self.preview_name}")
|
31 |
-
self.cam_preview()
|
32 |
-
|
33 |
-
def cam_preview(self):
|
34 |
-
cam = cv2.VideoCapture(self.cam_id)
|
35 |
-
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # Set reasonable resolution
|
36 |
-
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
37 |
-
cam.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) # Use MJPG for better USB compatibility
|
38 |
-
if not cam.isOpened():
|
39 |
-
st.error(f"π¨ Failed to open {self.preview_name}. Check connection or index.")
|
40 |
-
return
|
41 |
-
|
42 |
-
while self.running:
|
43 |
-
ret, frame = cam.read()
|
44 |
-
if ret:
|
45 |
-
# Convert BGR to RGB for display
|
46 |
-
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
47 |
-
try:
|
48 |
-
self.frame_queue.put_nowait(frame_rgb)
|
49 |
-
except queue.Full:
|
50 |
-
pass # Skip if queue is full
|
51 |
-
time.sleep(0.03) # ~30 FPS
|
52 |
-
|
53 |
-
cam.release()
|
54 |
-
|
55 |
-
def stop(self):
|
56 |
-
self.running = False
|
57 |
|
58 |
# ππΎ Save to history like a time-traveling scribe! | π
β¨ save_to_history("πΌοΈ Image", "pic.jpg", img_data) - Stamps a pic in the history books like a boss!
|
59 |
def save_to_history(file_type, file_path, img_data):
|
60 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
61 |
-
|
62 |
-
|
63 |
-
cv2.imwrite(file_path, img_bgr)
|
64 |
st.session_state['file_history'].append({
|
65 |
"Timestamp": timestamp,
|
66 |
"Type": file_type,
|
67 |
"Path": file_path
|
68 |
})
|
69 |
|
70 |
-
# πΈβ° Auto-capture every 10 secs like a sneaky shutterbug!
|
71 |
-
def auto_capture(frame_queues):
|
72 |
-
while st.session_state['auto_capture_running']:
|
73 |
-
for i, q in enumerate(frame_queues):
|
74 |
-
try:
|
75 |
-
frame = q.get_nowait()
|
76 |
-
filename = f"cam{i}_auto_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
77 |
-
save_to_history("πΌοΈ Image", filename, frame)
|
78 |
-
except queue.Empty:
|
79 |
-
pass # Skip if no frame available
|
80 |
-
time.sleep(10)
|
81 |
-
|
82 |
# ποΈ Sidebar config like a spaceship control panel!
|
83 |
with st.sidebar:
|
84 |
st.header("ποΈπΈ Snap Shack")
|
85 |
-
frame_queues = [queue.Queue(maxsize=1), queue.Queue(maxsize=1)] # Define frame_queues here
|
86 |
-
if st.button("β° Start Auto-Snap"):
|
87 |
-
st.session_state['auto_capture_running'] = True
|
88 |
-
threading.Thread(target=auto_capture, args=(frame_queues,), daemon=True).start()
|
89 |
-
if st.button("βΉοΈ Stop Auto-Snap"):
|
90 |
-
st.session_state['auto_capture_running'] = False
|
91 |
-
|
92 |
-
# π Sidebar file outline with emoji flair!
|
93 |
st.subheader("π Snap Stash")
|
94 |
if st.session_state['file_history']:
|
95 |
images = [f for f in st.session_state['file_history'] if f['Type'] == "πΌοΈ Image"]
|
@@ -101,33 +33,94 @@ with st.sidebar:
|
|
101 |
st.write("π³οΈ Empty Stash!")
|
102 |
|
103 |
# ππ¨ Main UI kicks off like a cosmic art show!
|
104 |
-
st.title("πΈ
|
105 |
-
|
106 |
-
# πΈπ· Start camera threads
|
107 |
-
threads = [
|
108 |
-
CamThread("Camera 0", 0, frame_queues[0]),
|
109 |
-
CamThread("Camera 1", 1, frame_queues[1])
|
110 |
-
]
|
111 |
-
for thread in threads:
|
112 |
-
thread.start()
|
113 |
|
114 |
-
# πΈπ·
|
115 |
st.header("πΈπ₯ Snap Zone")
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
|
132 |
# π Upload zone like a media drop party!
|
133 |
st.header("π₯π Drop Zone")
|
@@ -135,10 +128,8 @@ uploaded_files = st.file_uploader("πΈ Toss Pics", accept_multiple_files=True,
|
|
135 |
if uploaded_files:
|
136 |
for uploaded_file in uploaded_files:
|
137 |
file_path = f"uploaded_{uploaded_file.name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
138 |
-
|
139 |
-
|
140 |
-
save_to_history("πΌοΈ Image", file_path, img_array)
|
141 |
-
st.image(img, caption=uploaded_file.name, use_container_width=True)
|
142 |
|
143 |
# πΌοΈ Gallery like a media circus!
|
144 |
st.header("πͺ Snap Show")
|
@@ -165,14 +156,4 @@ if st.session_state['file_history']:
|
|
165 |
df = pd.DataFrame(st.session_state['file_history'])
|
166 |
st.dataframe(df)
|
167 |
else:
|
168 |
-
st.write("π³οΈ Nothing snapped yet!")
|
169 |
-
|
170 |
-
# Cleanup on app exit
|
171 |
-
def cleanup():
|
172 |
-
for thread in threads:
|
173 |
-
thread.stop()
|
174 |
-
for thread in threads:
|
175 |
-
thread.join()
|
176 |
-
|
177 |
-
import atexit
|
178 |
-
atexit.register(cleanup)
|
|
|
2 |
import pandas as pd
|
3 |
from datetime import datetime
|
4 |
import os
|
|
|
|
|
|
|
|
|
5 |
import base64
|
|
|
|
|
|
|
6 |
|
7 |
# ππ₯ Initialize session state like a galactic DJ spinning tracks!
|
8 |
if 'file_history' not in st.session_state:
|
9 |
st.session_state['file_history'] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
# ππΎ Save to history like a time-traveling scribe! | π
β¨ save_to_history("πΌοΈ Image", "pic.jpg", img_data) - Stamps a pic in the history books like a boss!
|
12 |
def save_to_history(file_type, file_path, img_data):
|
13 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
14 |
+
with open(file_path, "wb") as f:
|
15 |
+
f.write(base64.b64decode(img_data.split(',')[1]))
|
|
|
16 |
st.session_state['file_history'].append({
|
17 |
"Timestamp": timestamp,
|
18 |
"Type": file_type,
|
19 |
"Path": file_path
|
20 |
})
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
# ποΈ Sidebar config like a spaceship control panel!
|
23 |
with st.sidebar:
|
24 |
st.header("ποΈπΈ Snap Shack")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
st.subheader("π Snap Stash")
|
26 |
if st.session_state['file_history']:
|
27 |
images = [f for f in st.session_state['file_history'] if f['Type'] == "πΌοΈ Image"]
|
|
|
33 |
st.write("π³οΈ Empty Stash!")
|
34 |
|
35 |
# ππ¨ Main UI kicks off like a cosmic art show!
|
36 |
+
st.title("πΈ Multi-Cam Snap Craze")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
+
# πΈπ· JS camera zone with multiple streams!
|
39 |
st.header("πΈπ₯ Snap Zone")
|
40 |
+
multi_cam_html = """
|
41 |
+
<div id="videoContainer"></div>
|
42 |
+
<script>
|
43 |
+
let streams = [];
|
44 |
+
const container = document.getElementById('videoContainer');
|
45 |
+
let autoCaptureIntervals = [];
|
46 |
+
|
47 |
+
// πΉπ Enumerate cameras like a tech detective!
|
48 |
+
async function enumerateAndStartCameras() {
|
49 |
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
50 |
+
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
51 |
+
videoDevices.forEach((device, index) => {
|
52 |
+
const div = document.createElement('div');
|
53 |
+
div.innerHTML = `
|
54 |
+
<p>${device.label || 'Camera ' + index}</p>
|
55 |
+
<video id="video${index}" autoplay playsinline style="width:100%;"></video>
|
56 |
+
<canvas id="canvas${index}" style="display:none;"></canvas>
|
57 |
+
<button onclick="takeSnapshot(${index})">πΈ Snap Cam ${index}</button>
|
58 |
+
`;
|
59 |
+
container.appendChild(div);
|
60 |
+
startStream(device.deviceId, index);
|
61 |
+
});
|
62 |
+
}
|
63 |
+
|
64 |
+
// πΈπ₯ Start streaming like a live broadcast pro!
|
65 |
+
async function startStream(deviceId, index) {
|
66 |
+
const constraints = { video: { deviceId: { exact: deviceId } } };
|
67 |
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
68 |
+
const video = document.getElementById(`video${index}`);
|
69 |
+
video.srcObject = stream;
|
70 |
+
streams.push(stream);
|
71 |
+
// Auto-capture every 10 seconds
|
72 |
+
autoCaptureIntervals[index] = setInterval(() => takeSnapshot(index), 10000);
|
73 |
+
}
|
74 |
+
|
75 |
+
// πΈβοΈ Snap a pic like a stealthy paparazzi!
|
76 |
+
function takeSnapshot(index) {
|
77 |
+
const video = document.getElementById(`video${index}`);
|
78 |
+
const canvas = document.getElementById(`canvas${index}`);
|
79 |
+
canvas.width = video.videoWidth;
|
80 |
+
canvas.height = video.videoHeight;
|
81 |
+
canvas.getContext('2d').drawImage(video, 0, 0);
|
82 |
+
const dataUrl = canvas.toDataURL('image/jpeg');
|
83 |
+
window.parent.postMessage({ type: 'snapshot', data: dataUrl, camIndex: index }, '*');
|
84 |
+
}
|
85 |
+
|
86 |
+
// βΉοΈπ΄ Stop all streams like a broadcast kill switch!
|
87 |
+
function stopStreams() {
|
88 |
+
streams.forEach(stream => stream.getTracks().forEach(track => track.stop()));
|
89 |
+
streams = [];
|
90 |
+
autoCaptureIntervals.forEach(interval => clearInterval(interval));
|
91 |
+
autoCaptureIntervals = [];
|
92 |
+
}
|
93 |
+
|
94 |
+
// π¬ Kick off the camera party!
|
95 |
+
enumerateAndStartCameras();
|
96 |
+
window.onunload = stopStreams;
|
97 |
+
</script>
|
98 |
+
"""
|
99 |
+
st.markdown(multi_cam_html, unsafe_allow_html=True)
|
100 |
+
|
101 |
+
# πΈπ₯ Handle snapshots from JS
|
102 |
+
def handle_snapshot():
|
103 |
+
if "snapshot" in st.session_state:
|
104 |
+
snapshot_data = st.session_state["snapshot"]
|
105 |
+
cam_index = st.session_state.get("camIndex", 0)
|
106 |
+
filename = f"cam{cam_index}_snap_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
107 |
+
save_to_history("πΌοΈ Image", filename, snapshot_data)
|
108 |
+
st.image(Image.open(BytesIO(base64.b64decode(snapshot_data.split(',')[1]))), caption=f"Cam {cam_index} Snap", use_container_width=True)
|
109 |
+
del st.session_state["snapshot"]
|
110 |
+
|
111 |
+
st.components.v1.html(
|
112 |
+
"""
|
113 |
+
<script>
|
114 |
+
window.addEventListener('message', function(event) {
|
115 |
+
if (event.data.type === 'snapshot') {
|
116 |
+
window.streamlitAPI.setComponentValue({snapshot: event.data.data, camIndex: event.data.camIndex});
|
117 |
+
}
|
118 |
+
});
|
119 |
+
</script>
|
120 |
+
""",
|
121 |
+
height=0
|
122 |
+
)
|
123 |
+
handle_snapshot()
|
124 |
|
125 |
# π Upload zone like a media drop party!
|
126 |
st.header("π₯π Drop Zone")
|
|
|
128 |
if uploaded_files:
|
129 |
for uploaded_file in uploaded_files:
|
130 |
file_path = f"uploaded_{uploaded_file.name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
131 |
+
save_to_history("πΌοΈ Image", file_path, base64.b64encode(uploaded_file.getvalue()).decode())
|
132 |
+
st.image(Image.open(uploaded_file), caption=uploaded_file.name, use_container_width=True)
|
|
|
|
|
133 |
|
134 |
# πΌοΈ Gallery like a media circus!
|
135 |
st.header("πͺ Snap Show")
|
|
|
156 |
df = pd.DataFrame(st.session_state['file_history'])
|
157 |
st.dataframe(df)
|
158 |
else:
|
159 |
+
st.write("π³οΈ Nothing snapped yet!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|