Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,28 +2,56 @@ import streamlit as st
|
|
2 |
import pandas as pd
|
3 |
from datetime import datetime
|
4 |
import os
|
5 |
-
import
|
6 |
-
|
7 |
from PIL import Image
|
|
|
|
|
|
|
|
|
8 |
|
9 |
# ππ₯ Initialize session state like a galactic DJ spinning tracks!
|
10 |
if 'file_history' not in st.session_state:
|
11 |
st.session_state['file_history'] = []
|
|
|
|
|
12 |
|
13 |
-
# ππΎ Save to history like a time-traveling scribe!
|
14 |
def save_to_history(file_type, file_path, img_data):
|
15 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
16 |
-
|
17 |
-
|
|
|
18 |
st.session_state['file_history'].append({
|
19 |
"Timestamp": timestamp,
|
20 |
"Type": file_type,
|
21 |
"Path": file_path
|
22 |
})
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
# ποΈ Sidebar config like a spaceship control panel!
|
25 |
with st.sidebar:
|
26 |
st.header("ποΈπΈ Snap Shack")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
st.subheader("π Snap Stash")
|
28 |
if st.session_state['file_history']:
|
29 |
images = [f for f in st.session_state['file_history'] if f['Type'] == "πΌοΈ Image"]
|
@@ -35,116 +63,26 @@ with st.sidebar:
|
|
35 |
st.write("π³οΈ Empty Stash!")
|
36 |
|
37 |
# ππ¨ Main UI kicks off like a cosmic art show!
|
38 |
-
st.title("πΈ
|
39 |
-
|
40 |
-
# πΈπ· JS live camera zone!
|
41 |
-
st.header("πΈπ₯ Live Snap Zone")
|
42 |
-
live_camera_html = """
|
43 |
-
<div>
|
44 |
-
<select id="cameraSelect"></select>
|
45 |
-
<button id="startBtn" onclick="toggleStream()">π· Start Live</button>
|
46 |
-
<button onclick="takeSnapshot()">πΈ Snap Now</button>
|
47 |
-
<video id="video" autoplay playsinline style="width:100%;"></video>
|
48 |
-
<canvas id="canvas" style="display:none;"></canvas>
|
49 |
-
</div>
|
50 |
-
<script>
|
51 |
-
let stream = null;
|
52 |
-
const video = document.getElementById('video');
|
53 |
-
const canvas = document.getElementById('canvas');
|
54 |
-
let autoCaptureInterval = null;
|
55 |
-
let isStreaming = false;
|
56 |
-
|
57 |
-
// πΉπ Enumerate cameras like a tech detective!
|
58 |
-
async function enumerateCameras() {
|
59 |
-
const devices = await navigator.mediaDevices.enumerateDevices();
|
60 |
-
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
61 |
-
const select = document.getElementById('cameraSelect');
|
62 |
-
select.innerHTML = '';
|
63 |
-
videoDevices.forEach((device, index) => {
|
64 |
-
const option = document.createElement('option');
|
65 |
-
option.value = device.deviceId;
|
66 |
-
option.text = device.label || `Camera ${index}`;
|
67 |
-
select.appendChild(option);
|
68 |
-
});
|
69 |
-
if (videoDevices.length === 0) {
|
70 |
-
select.innerHTML = '<option>No cameras found</option>';
|
71 |
-
}
|
72 |
-
}
|
73 |
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
isStreaming = true;
|
93 |
-
autoCaptureInterval = setInterval(takeSnapshot, 10000); // Auto-save every 10s
|
94 |
-
} catch (e) {
|
95 |
-
alert('Failed to start camera: ' + e.message);
|
96 |
-
}
|
97 |
-
}
|
98 |
-
}
|
99 |
-
|
100 |
-
// πΈβοΈ Snap a pic like a stealthy paparazzi!
|
101 |
-
function takeSnapshot() {
|
102 |
-
if (!stream) return;
|
103 |
-
canvas.width = video.videoWidth;
|
104 |
-
canvas.height = video.videoHeight;
|
105 |
-
canvas.getContext('2d').drawImage(video, 0, 0);
|
106 |
-
const dataUrl = canvas.toDataURL('image/jpeg');
|
107 |
-
window.parent.postMessage({ type: 'snapshot', data: dataUrl }, '*');
|
108 |
-
}
|
109 |
-
|
110 |
-
// βΉοΈπ΄ Stop the stream like a broadcast kill switch!
|
111 |
-
function stopStream() {
|
112 |
-
if (stream) {
|
113 |
-
stream.getTracks().forEach(track => track.stop());
|
114 |
-
stream = null;
|
115 |
-
video.srcObject = null;
|
116 |
-
clearInterval(autoCaptureInterval);
|
117 |
-
}
|
118 |
-
}
|
119 |
-
|
120 |
-
// π¬ Kick off the camera party!
|
121 |
-
enumerateCameras();
|
122 |
-
</script>
|
123 |
-
"""
|
124 |
-
st.markdown(live_camera_html, unsafe_allow_html=True)
|
125 |
-
|
126 |
-
# πΈπ₯ Handle snapshots from JS
|
127 |
-
def handle_snapshot():
|
128 |
-
if "snapshot" in st.session_state:
|
129 |
-
snapshot_data = st.session_state["snapshot"]
|
130 |
-
filename = f"snap_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
131 |
-
save_to_history("πΌοΈ Image", filename, snapshot_data)
|
132 |
-
st.image(Image.open(BytesIO(base64.b64decode(snapshot_data.split(',')[1]))), caption="Latest Snap", use_container_width=True)
|
133 |
-
del st.session_state["snapshot"]
|
134 |
-
|
135 |
-
st.components.v1.html(
|
136 |
-
"""
|
137 |
-
<script>
|
138 |
-
window.addEventListener('message', function(event) {
|
139 |
-
if (event.data.type === 'snapshot') {
|
140 |
-
window.streamlitAPI.setComponentValue({snapshot: event.data.data});
|
141 |
-
}
|
142 |
-
});
|
143 |
-
</script>
|
144 |
-
""",
|
145 |
-
height=0
|
146 |
-
)
|
147 |
-
handle_snapshot()
|
148 |
|
149 |
# π Upload zone like a media drop party!
|
150 |
st.header("π₯π Drop Zone")
|
@@ -152,8 +90,10 @@ uploaded_files = st.file_uploader("πΈ Toss Pics", accept_multiple_files=True,
|
|
152 |
if uploaded_files:
|
153 |
for uploaded_file in uploaded_files:
|
154 |
file_path = f"uploaded_{uploaded_file.name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
155 |
-
|
156 |
-
|
|
|
|
|
157 |
|
158 |
# πΌοΈ Gallery like a media circus!
|
159 |
st.header("πͺ Snap Show")
|
@@ -180,4 +120,12 @@ if st.session_state['file_history']:
|
|
180 |
df = pd.DataFrame(st.session_state['file_history'])
|
181 |
st.dataframe(df)
|
182 |
else:
|
183 |
-
st.write("π³οΈ Nothing snapped yet!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
from imutils.video import VideoStream
|
11 |
+
import cv2
|
12 |
|
13 |
# ππ₯ Initialize session state like a galactic DJ spinning tracks!
|
14 |
if 'file_history' not in st.session_state:
|
15 |
st.session_state['file_history'] = []
|
16 |
+
if 'auto_capture_running' not in st.session_state:
|
17 |
+
st.session_state['auto_capture_running'] = False
|
18 |
|
19 |
+
# ππΎ Save to history like a time-traveling scribe!
|
20 |
def save_to_history(file_type, file_path, img_data):
|
21 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
22 |
+
# Convert RGB to BGR for OpenCV saving
|
23 |
+
img_bgr = cv2.cvtColor(img_data, cv2.COLOR_RGB2BGR)
|
24 |
+
cv2.imwrite(file_path, img_bgr)
|
25 |
st.session_state['file_history'].append({
|
26 |
"Timestamp": timestamp,
|
27 |
"Type": file_type,
|
28 |
"Path": file_path
|
29 |
})
|
30 |
|
31 |
+
# πΈβ° Auto-capture every 10 secs like a sneaky shutterbug!
|
32 |
+
def auto_capture(streams):
|
33 |
+
while st.session_state['auto_capture_running']:
|
34 |
+
for i, stream in enumerate(streams):
|
35 |
+
frame = stream.read()
|
36 |
+
if frame is not None:
|
37 |
+
filename = f"cam{i}_auto_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
38 |
+
save_to_history("πΌοΈ Image", filename, frame)
|
39 |
+
time.sleep(10)
|
40 |
+
|
41 |
# ποΈ Sidebar config like a spaceship control panel!
|
42 |
with st.sidebar:
|
43 |
st.header("ποΈπΈ Snap Shack")
|
44 |
+
streams = [
|
45 |
+
VideoStream(src=0).start(), # Camera 0
|
46 |
+
VideoStream(src=1).start() # Camera 1
|
47 |
+
]
|
48 |
+
if st.button("β° Start Auto-Snap"):
|
49 |
+
st.session_state['auto_capture_running'] = True
|
50 |
+
threading.Thread(target=auto_capture, args=(streams,), daemon=True).start()
|
51 |
+
if st.button("βΉοΈ Stop Auto-Snap"):
|
52 |
+
st.session_state['auto_capture_running'] = False
|
53 |
+
|
54 |
+
# π Sidebar file outline with emoji flair!
|
55 |
st.subheader("π Snap Stash")
|
56 |
if st.session_state['file_history']:
|
57 |
images = [f for f in st.session_state['file_history'] if f['Type'] == "πΌοΈ Image"]
|
|
|
63 |
st.write("π³οΈ Empty Stash!")
|
64 |
|
65 |
# ππ¨ Main UI kicks off like a cosmic art show!
|
66 |
+
st.title("πΈ Imutils Snap Craze")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
+
# πΈπ· Camera snap zone!
|
69 |
+
st.header("πΈπ₯ Snap Zone")
|
70 |
+
cols = st.columns(2)
|
71 |
+
placeholders = [cols[0].empty(), cols[1].empty()]
|
72 |
+
for i, (placeholder, stream) in enumerate(zip(placeholders, streams)):
|
73 |
+
frame = stream.read()
|
74 |
+
if frame is not None:
|
75 |
+
placeholder.image(frame, caption=f"Live Cam {i}", use_container_width=True)
|
76 |
+
else:
|
77 |
+
placeholder.error(f"π¨ No feed from Cam {i}. Check connection or index.")
|
78 |
+
|
79 |
+
if st.button(f"πΈ Snap Cam {i}", key=f"snap{i}"):
|
80 |
+
frame = stream.read()
|
81 |
+
if frame is not None:
|
82 |
+
filename = f"cam{i}_snap_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
83 |
+
save_to_history("πΌοΈ Image", filename, frame)
|
84 |
+
else:
|
85 |
+
st.error(f"π¨ Failed to snap Cam {i}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
# π Upload zone like a media drop party!
|
88 |
st.header("π₯π Drop Zone")
|
|
|
90 |
if uploaded_files:
|
91 |
for uploaded_file in uploaded_files:
|
92 |
file_path = f"uploaded_{uploaded_file.name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
93 |
+
img = Image.open(uploaded_file)
|
94 |
+
img_array = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) # Convert for consistency
|
95 |
+
save_to_history("πΌοΈ Image", file_path, img_array)
|
96 |
+
st.image(img, caption=uploaded_file.name, use_container_width=True)
|
97 |
|
98 |
# πΌοΈ Gallery like a media circus!
|
99 |
st.header("πͺ Snap Show")
|
|
|
120 |
df = pd.DataFrame(st.session_state['file_history'])
|
121 |
st.dataframe(df)
|
122 |
else:
|
123 |
+
st.write("π³οΈ Nothing snapped yet!")
|
124 |
+
|
125 |
+
# Cleanup on app exit
|
126 |
+
def cleanup():
|
127 |
+
for stream in streams:
|
128 |
+
stream.stop()
|
129 |
+
|
130 |
+
import atexit
|
131 |
+
atexit.register(cleanup)
|