CosmickVisions commited on
Commit
11bf68e
·
verified ·
1 Parent(s): 7fe0f8a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +221 -125
app.py CHANGED
@@ -162,11 +162,11 @@ st.markdown("""
162
  </style>
163
  """, unsafe_allow_html=True)
164
 
165
- def analyze_image(image, analysis_types):
166
- """Analyze image with selected analysis types"""
167
  # Convert uploaded image to bytes
168
  if image is None:
169
- return None, {}, {}, ""
170
 
171
  img_byte_arr = io.BytesIO()
172
  image.save(img_byte_arr, format='PNG')
@@ -179,22 +179,42 @@ def analyze_image(image, analysis_types):
179
  labels_data = {}
180
  objects_data = {}
181
  text_content = ""
 
 
182
 
183
  img_with_boxes = image.copy()
184
  draw = ImageDraw.Draw(img_with_boxes)
185
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  if "Labels" in analysis_types:
187
  labels = client.label_detection(image=vision_image)
 
188
  labels_data = {label.description: round(label.score * 100)
189
- for label in labels.label_annotations}
 
190
 
191
  if "Objects" in analysis_types:
192
  objects = client.object_localization(image=vision_image)
 
 
 
 
193
  objects_data = {obj.name: round(obj.score * 100)
194
- for obj in objects.localized_object_annotations}
195
 
196
  # Draw object boundaries
197
- for obj in objects.localized_object_annotations:
198
  box = [(vertex.x * image.width, vertex.y * image.height)
199
  for vertex in obj.bounding_poly.normalized_vertices]
200
  draw.polygon(box, outline='red', width=2)
@@ -207,6 +227,23 @@ def analyze_image(image, analysis_types):
207
  if text.text_annotations:
208
  text_content = text.text_annotations[0].description
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  # Draw text boundaries
211
  for text_annot in text.text_annotations[1:]: # Skip the first one (full text)
212
  box = [(vertex.x, vertex.y) for vertex in text_annot.bounding_poly.vertices]
@@ -214,7 +251,11 @@ def analyze_image(image, analysis_types):
214
 
215
  if "Face Detection" in analysis_types:
216
  faces = client.face_detection(image=vision_image)
217
- for face in faces.face_annotations:
 
 
 
 
218
  vertices = face.bounding_poly.vertices
219
  box = [(vertex.x, vertex.y) for vertex in vertices]
220
  draw.polygon(box, outline='green', width=2)
@@ -225,15 +266,18 @@ def analyze_image(image, analysis_types):
225
  py = landmark.position.y
226
  draw.ellipse((px-2, py-2, px+2, py+2), fill='yellow')
227
 
228
- return img_with_boxes, labels_data, objects_data, text_content
 
229
 
230
- def display_results(annotated_img, labels, objects, text):
231
- """Display analysis results in a clean format"""
232
  # Store results in session state for chatbot context
233
  st.session_state.analysis_results = {
234
  "labels": labels,
235
  "objects": objects,
236
  "text": text,
 
 
237
  "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
238
  }
239
 
@@ -268,12 +312,37 @@ def display_results(annotated_img, labels, objects, text):
268
  # Text tab
269
  if text:
270
  st.markdown("##### 📝 Text Detected")
 
 
271
  st.markdown('<div class="result-container">', unsafe_allow_html=True)
272
  st.markdown(f'<div class="text-item">{text}</div>', unsafe_allow_html=True)
273
  st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
  # Add Download Summary Image button
276
- summary_img = create_summary_image(annotated_img, labels, objects, text)
277
  buf = io.BytesIO()
278
  summary_img.save(buf, format="JPEG", quality=90)
279
  byte_im = buf.getvalue()
@@ -286,7 +355,7 @@ def display_results(annotated_img, labels, objects, text):
286
  help="Download a complete image showing the analyzed image and all detected features"
287
  )
288
 
289
- def create_summary_image(annotated_img, labels, objects, text):
290
  """Create a downloadable summary image with analysis results"""
291
  # Create a new image with space for results
292
  img_width, img_height = annotated_img.size
@@ -690,7 +759,7 @@ def list_bigquery_resources():
690
  return resources
691
 
692
  def process_video_file(video_file, analysis_types):
693
- """Process an uploaded video file with enhanced Vision AI detection and slow down output for better visibility"""
694
  # Create a temporary file to save the uploaded video
695
  with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_file:
696
  temp_file.write(video_file.read())
@@ -743,14 +812,22 @@ def process_video_file(video_file, analysis_types):
743
  progress_bar = st.progress(0)
744
  status_text = st.empty()
745
 
746
- # Statistics tracking for summary
747
  detection_stats = {
748
  "objects": {},
749
  "faces": 0,
750
  "text_blocks": 0,
751
- "labels": {}
 
 
 
 
752
  }
753
 
 
 
 
 
754
  try:
755
  frame_count = 0
756
  while frame_count < max_frames: # Limit to 10 seconds
@@ -769,7 +846,26 @@ def process_video_file(video_file, analysis_types):
769
  cv2.putText(frame, f"Time: {frame_count/fps:.2f}s",
770
  (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
771
 
772
- # Process frames with Vision API (more frequently for detail)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  if frame_count % process_every_n_frames == 0:
774
  try:
775
  # Convert OpenCV frame to PIL Image for Vision API
@@ -786,12 +882,28 @@ def process_video_file(video_file, analysis_types):
786
  objects = client.object_localization(image=vision_image)
787
  # Draw boxes around detected objects with enhanced info
788
  for obj in objects.localized_object_annotations:
789
- # Track statistics
790
- if obj.name in detection_stats["objects"]:
791
- detection_stats["objects"][obj.name] += 1
 
792
  else:
793
- detection_stats["objects"][obj.name] = 1
794
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
  # Calculate box coordinates
796
  box = [(vertex.x * frame.shape[1], vertex.y * frame.shape[0])
797
  for vertex in obj.bounding_poly.normalized_vertices]
@@ -932,7 +1044,15 @@ def process_video_file(video_file, analysis_types):
932
  os.unlink(temp_video_path)
933
  os.unlink(output_path)
934
 
935
- # Return both the video and the detection statistics
 
 
 
 
 
 
 
 
936
  results = {"detection_stats": detection_stats}
937
 
938
  # Store results in session state for chatbot context
@@ -1457,12 +1577,28 @@ def main():
1457
  if st.checkbox("Face Detection"):
1458
  analysis_types.append("Face Detection")
1459
 
 
 
 
 
1460
  st.markdown("---")
1461
 
 
 
 
 
 
1462
  # Image quality settings
1463
  st.write("Image settings:")
1464
  quality = st.slider("Image Quality", min_value=0, max_value=100, value=100)
1465
 
 
 
 
 
 
 
 
1466
  st.markdown("---")
1467
  st.info("This application analyzes images using Google Cloud Vision AI. Upload an image to get started.")
1468
 
@@ -1492,10 +1628,10 @@ def main():
1492
  else:
1493
  with st.spinner("Analyzing image..."):
1494
  # Call analyze function
1495
- annotated_img, labels, objects, text = analyze_image(image, analysis_types)
1496
 
1497
  # Display results
1498
- display_results(annotated_img, labels, objects, text)
1499
 
1500
  # Add download button for the annotated image
1501
  buf = io.BytesIO()
@@ -1515,118 +1651,78 @@ def main():
1515
 
1516
  uploaded_files = st.file_uploader("Choose images...", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
1517
 
1518
- if uploaded_files:
1519
- # Limit to 5 images
1520
  if len(uploaded_files) > 5:
1521
- st.warning("Maximum 5 images allowed. Only the first 5 will be processed.")
1522
  uploaded_files = uploaded_files[:5]
1523
 
1524
- # Display thumbnails of uploaded images
1525
- st.markdown('<div class="subheader">Uploaded Images</div>', unsafe_allow_html=True)
1526
- cols = st.columns(len(uploaded_files))
1527
- for i, uploaded_file in enumerate(uploaded_files):
1528
- with cols[i]:
1529
- image = Image.open(uploaded_file)
1530
- st.image(image, caption=f"Image {i+1}", use_container_width=True)
1531
-
1532
- # Add analyze button for batch processing
1533
- if st.button("Analyze All Images"):
1534
- if not analysis_types:
1535
- st.warning("Please select at least one analysis type.")
1536
- else:
1537
- # Initialize containers for batch summary
1538
- all_labels = {}
1539
- all_objects = {}
1540
 
1541
- # Process each image
1542
- for i, uploaded_file in enumerate(uploaded_files):
1543
- with st.spinner(f"Analyzing image {i+1} of {len(uploaded_files)}..."):
1544
- # Convert uploaded file to image
1545
- image = Image.open(uploaded_file)
1546
-
1547
- # Apply quality adjustment if needed
1548
- if quality < 100:
1549
- img_byte_arr = io.BytesIO()
1550
- image.save(img_byte_arr, format='JPEG', quality=quality)
1551
- image = Image.open(img_byte_arr)
1552
-
1553
- # Analyze image
1554
- annotated_img, labels, objects, text = analyze_image(image, analysis_types)
 
 
 
 
 
 
 
 
1555
 
1556
- # Update batch summaries
1557
- for label, confidence in labels.items():
1558
- if label in all_labels:
1559
- all_labels[label] = max(all_labels[label], confidence)
1560
- else:
1561
- all_labels[label] = confidence
1562
 
1563
- for obj, confidence in objects.items():
1564
- if obj in all_objects:
1565
- all_objects[obj] = max(all_objects[obj], confidence)
1566
- else:
1567
- all_objects[obj] = confidence
1568
 
1569
- # Create expander for each image result
1570
- with st.expander(f"Results for Image {i+1}", expanded=i==0):
1571
- # Display results for this image
1572
- display_results(annotated_img, labels, objects, text)
1573
-
1574
- # Add download button for each annotated image
1575
- buf = io.BytesIO()
1576
- annotated_img.save(buf, format="PNG")
1577
- byte_im = buf.getvalue()
1578
-
1579
- st.download_button(
1580
- label=f"Download Annotated Image {i+1}",
1581
- data=byte_im,
1582
- file_name=f"annotated_image_{i+1}.png",
1583
- mime="image/png"
1584
- )
1585
-
1586
- # Display batch summary
1587
- st.markdown('<div class="subheader">Batch Analysis Summary</div>', unsafe_allow_html=True)
1588
-
1589
- col1, col2 = st.columns(2)
1590
-
1591
- with col1:
1592
- if all_labels:
1593
- st.markdown("#### Common Labels Across Images")
1594
- # Sort by confidence
1595
- sorted_labels = dict(sorted(all_labels.items(), key=lambda x: x[1], reverse=True))
1596
- for label, confidence in sorted_labels.items():
1597
- st.markdown(f'<div class="label-item">{label}: {confidence}%</div>', unsafe_allow_html=True)
1598
 
1599
- with col2:
1600
- if all_objects:
1601
- st.markdown("#### Common Objects Across Images")
1602
- # Sort by confidence
1603
- sorted_objects = dict(sorted(all_objects.items(), key=lambda x: x[1], reverse=True))
1604
- for obj, confidence in sorted_objects.items():
1605
- st.markdown(f'<div class="object-item">{obj}: {confidence}%</div>', unsafe_allow_html=True)
1606
-
1607
- # Create visualization for batch summary if there are labels or objects
1608
- if all_labels or all_objects:
1609
- st.markdown("#### Visual Summary")
1610
 
1611
- # Create label chart
1612
- if all_labels:
1613
- fig_labels = px.bar(
1614
- x=list(all_labels.keys()),
1615
- y=list(all_labels.values()),
1616
- labels={'x': 'Label', 'y': 'Confidence (%)'},
1617
- title='Top Labels Across All Images'
1618
- )
1619
- st.plotly_chart(fig_labels)
1620
 
1621
- # Create object chart
1622
- if all_objects:
1623
- fig_objects = px.bar(
1624
- x=list(all_objects.keys()),
1625
- y=list(all_objects.values()),
1626
- labels={'x': 'Object', 'y': 'Confidence (%)'},
1627
- title='Top Objects Across All Images'
1628
- )
1629
- st.plotly_chart(fig_objects)
1630
 
1631
  elif selected == "Video Analysis":
1632
  st.markdown('<div class="subheader">Video Analysis</div>', unsafe_allow_html=True)
 
162
  </style>
163
  """, unsafe_allow_html=True)
164
 
165
+ def analyze_image(image, analysis_types, confidence_threshold=0.5):
166
+ """Analyze image with selected analysis types and confidence filtering"""
167
  # Convert uploaded image to bytes
168
  if image is None:
169
+ return None, {}, {}, "", {}
170
 
171
  img_byte_arr = io.BytesIO()
172
  image.save(img_byte_arr, format='PNG')
 
179
  labels_data = {}
180
  objects_data = {}
181
  text_content = ""
182
+ colors_data = {} # New: store dominant colors
183
+ text_language = "" # New: store detected language
184
 
185
  img_with_boxes = image.copy()
186
  draw = ImageDraw.Draw(img_with_boxes)
187
 
188
+ # Extract color information regardless of analysis types
189
+ if "Visual Attributes" in analysis_types:
190
+ image_properties = client.image_properties(image=vision_image).image_properties_annotation
191
+ # Get top 5 dominant colors with scores
192
+ colors_data = {
193
+ f"Color #{i+1}": {
194
+ "rgb": (int(color.color.red), int(color.color.green), int(color.color.blue)),
195
+ "score": round(color.score * 100, 2),
196
+ "pixel_fraction": round(color.pixel_fraction * 100, 2)
197
+ } for i, color in enumerate(image_properties.dominant_colors.colors[:5])
198
+ }
199
+
200
  if "Labels" in analysis_types:
201
  labels = client.label_detection(image=vision_image)
202
+ # Apply confidence threshold
203
  labels_data = {label.description: round(label.score * 100)
204
+ for label in labels.label_annotations
205
+ if label.score >= confidence_threshold}
206
 
207
  if "Objects" in analysis_types:
208
  objects = client.object_localization(image=vision_image)
209
+ # Apply confidence threshold
210
+ filtered_objects = [obj for obj in objects.localized_object_annotations
211
+ if obj.score >= confidence_threshold]
212
+
213
  objects_data = {obj.name: round(obj.score * 100)
214
+ for obj in filtered_objects}
215
 
216
  # Draw object boundaries
217
+ for obj in filtered_objects:
218
  box = [(vertex.x * image.width, vertex.y * image.height)
219
  for vertex in obj.bounding_poly.normalized_vertices]
220
  draw.polygon(box, outline='red', width=2)
 
227
  if text.text_annotations:
228
  text_content = text.text_annotations[0].description
229
 
230
+ # New: Detect language if text is found
231
+ if text_content:
232
+ try:
233
+ # Get language of text
234
+ document = vision.types.Document(
235
+ content=content,
236
+ type_=vision.types.Document.Type.GENERAL_DOCUMENT
237
+ )
238
+ response = client.document_text_detection(image=vision_image)
239
+ if response.text_annotations:
240
+ # Get the language code from the first page
241
+ if response.pages and response.pages[0].property.detected_languages:
242
+ lang = response.pages[0].property.detected_languages[0]
243
+ text_language = f"{lang.language_code} ({round(lang.confidence * 100)}%)"
244
+ except Exception as e:
245
+ text_language = "Detection failed"
246
+
247
  # Draw text boundaries
248
  for text_annot in text.text_annotations[1:]: # Skip the first one (full text)
249
  box = [(vertex.x, vertex.y) for vertex in text_annot.bounding_poly.vertices]
 
251
 
252
  if "Face Detection" in analysis_types:
253
  faces = client.face_detection(image=vision_image)
254
+ # Apply confidence threshold - filter by detection confidence
255
+ filtered_faces = [face for face in faces.face_annotations
256
+ if face.detection_confidence >= confidence_threshold]
257
+
258
+ for face in filtered_faces:
259
  vertices = face.bounding_poly.vertices
260
  box = [(vertex.x, vertex.y) for vertex in vertices]
261
  draw.polygon(box, outline='green', width=2)
 
266
  py = landmark.position.y
267
  draw.ellipse((px-2, py-2, px+2, py+2), fill='yellow')
268
 
269
+ # Return extended results
270
+ return img_with_boxes, labels_data, objects_data, text_content, colors_data, text_language
271
 
272
+ def display_results(annotated_img, labels, objects, text, colors=None, text_language=None):
273
+ """Display analysis results in a clean format with enhanced features"""
274
  # Store results in session state for chatbot context
275
  st.session_state.analysis_results = {
276
  "labels": labels,
277
  "objects": objects,
278
  "text": text,
279
+ "colors": colors if colors else {},
280
+ "text_language": text_language if text_language else "",
281
  "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
282
  }
283
 
 
312
  # Text tab
313
  if text:
314
  st.markdown("##### 📝 Text Detected")
315
+ if text_language:
316
+ st.markdown(f"**Detected Language:** {text_language}")
317
  st.markdown('<div class="result-container">', unsafe_allow_html=True)
318
  st.markdown(f'<div class="text-item">{text}</div>', unsafe_allow_html=True)
319
  st.markdown('</div>', unsafe_allow_html=True)
320
+
321
+ # Color analysis tab (new)
322
+ if colors:
323
+ st.markdown("##### 🎨 Dominant Colors")
324
+ st.markdown('<div class="result-container">', unsafe_allow_html=True)
325
+
326
+ # Create color swatches
327
+ for color_name, color_data in colors.items():
328
+ rgb = color_data["rgb"]
329
+ hex_color = f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}"
330
+
331
+ # Display color swatch with info
332
+ st.markdown(f"""
333
+ <div style="display:flex; align-items:center; margin-bottom:10px;">
334
+ <div style="background-color:{hex_color}; width:50px; height:30px; margin-right:15px; border:1px solid #ddd;"></div>
335
+ <div>
336
+ <strong>{color_name}</strong>: {color_data["score"]}% coverage<br>
337
+ RGB: {rgb}
338
+ </div>
339
+ </div>
340
+ """, unsafe_allow_html=True)
341
+
342
+ st.markdown('</div>', unsafe_allow_html=True)
343
 
344
  # Add Download Summary Image button
345
+ summary_img = create_summary_image(annotated_img, labels, objects, text, colors)
346
  buf = io.BytesIO()
347
  summary_img.save(buf, format="JPEG", quality=90)
348
  byte_im = buf.getvalue()
 
355
  help="Download a complete image showing the analyzed image and all detected features"
356
  )
357
 
358
+ def create_summary_image(annotated_img, labels, objects, text, colors=None):
359
  """Create a downloadable summary image with analysis results"""
360
  # Create a new image with space for results
361
  img_width, img_height = annotated_img.size
 
759
  return resources
760
 
761
  def process_video_file(video_file, analysis_types):
762
+ """Process an uploaded video file with enhanced Vision AI detection and analytics"""
763
  # Create a temporary file to save the uploaded video
764
  with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_file:
765
  temp_file.write(video_file.read())
 
812
  progress_bar = st.progress(0)
813
  status_text = st.empty()
814
 
815
+ # Enhanced statistics tracking
816
  detection_stats = {
817
  "objects": {},
818
  "faces": 0,
819
  "text_blocks": 0,
820
+ "labels": {},
821
+ # New advanced tracking
822
+ "object_tracking": {}, # Track object appearances by frame
823
+ "activity_metrics": [], # Track frame-to-frame differences
824
+ "scene_changes": [] # Track major scene transitions
825
  }
826
 
827
+ # For scene change detection
828
+ previous_frame_gray = None
829
+ scene_change_threshold = 40.0 # Threshold for scene change detection
830
+
831
  try:
832
  frame_count = 0
833
  while frame_count < max_frames: # Limit to 10 seconds
 
846
  cv2.putText(frame, f"Time: {frame_count/fps:.2f}s",
847
  (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
848
 
849
+ # Activity detection and scene change detection
850
+ current_frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
851
+ current_frame_gray = cv2.GaussianBlur(current_frame_gray, (21, 21), 0)
852
+
853
+ if previous_frame_gray is not None:
854
+ # Calculate frame difference for activity detection
855
+ frame_diff = cv2.absdiff(current_frame_gray, previous_frame_gray)
856
+ activity_level = np.mean(frame_diff)
857
+ detection_stats["activity_metrics"].append((frame_count/fps, activity_level))
858
+
859
+ # Scene change detection
860
+ if activity_level > scene_change_threshold:
861
+ detection_stats["scene_changes"].append(frame_count/fps)
862
+ # Mark scene change on frame
863
+ cv2.putText(frame, "SCENE CHANGE",
864
+ (width // 2 - 100, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2)
865
+
866
+ previous_frame_gray = current_frame_gray
867
+
868
+ # Process frames with Vision API
869
  if frame_count % process_every_n_frames == 0:
870
  try:
871
  # Convert OpenCV frame to PIL Image for Vision API
 
882
  objects = client.object_localization(image=vision_image)
883
  # Draw boxes around detected objects with enhanced info
884
  for obj in objects.localized_object_annotations:
885
+ obj_name = obj.name
886
+ # Update basic stats
887
+ if obj_name in detection_stats["objects"]:
888
+ detection_stats["objects"][obj_name] += 1
889
  else:
890
+ detection_stats["objects"][obj_name] = 1
891
 
892
+ # Enhanced object tracking
893
+ timestamp = frame_count/fps
894
+ if obj_name not in detection_stats["object_tracking"]:
895
+ detection_stats["object_tracking"][obj_name] = {
896
+ "first_seen": timestamp,
897
+ "last_seen": timestamp,
898
+ "frames_present": 1,
899
+ "timestamps": [timestamp]
900
+ }
901
+ else:
902
+ tracking = detection_stats["object_tracking"][obj_name]
903
+ tracking["frames_present"] += 1
904
+ tracking["last_seen"] = timestamp
905
+ tracking["timestamps"].append(timestamp)
906
+
907
  # Calculate box coordinates
908
  box = [(vertex.x * frame.shape[1], vertex.y * frame.shape[0])
909
  for vertex in obj.bounding_poly.normalized_vertices]
 
1044
  os.unlink(temp_video_path)
1045
  os.unlink(output_path)
1046
 
1047
+ # Calculate additional statistics
1048
+ for obj_name, tracking in detection_stats["object_tracking"].items():
1049
+ # Calculate total screen time
1050
+ tracking["screen_time"] = round(tracking["frames_present"] * (1/fps) * process_every_n_frames, 2)
1051
+ # Calculate average confidence if available
1052
+ if "confidences" in tracking and tracking["confidences"]:
1053
+ tracking["avg_confidence"] = sum(tracking["confidences"]) / len(tracking["confidences"])
1054
+
1055
+ # Return enhanced results
1056
  results = {"detection_stats": detection_stats}
1057
 
1058
  # Store results in session state for chatbot context
 
1577
  if st.checkbox("Face Detection"):
1578
  analysis_types.append("Face Detection")
1579
 
1580
+ # New enhanced analysis options
1581
+ if st.checkbox("Visual Attributes (Colors)", value=False):
1582
+ analysis_types.append("Visual Attributes")
1583
+
1584
  st.markdown("---")
1585
 
1586
+ # Confidence threshold control
1587
+ confidence_threshold = st.slider("Detection Confidence Threshold",
1588
+ min_value=0.0, max_value=1.0, value=0.5,
1589
+ help="Filter results based on confidence level")
1590
+
1591
  # Image quality settings
1592
  st.write("Image settings:")
1593
  quality = st.slider("Image Quality", min_value=0, max_value=100, value=100)
1594
 
1595
+ # Add a slider in the sidebar
1596
+ confidence_threshold = st.sidebar.slider("Detection Confidence Threshold", 0.0, 1.0, 0.5)
1597
+
1598
+ # Then filter results based on this threshold
1599
+ filtered_objects = [obj for obj in response.localized_object_annotations
1600
+ if obj.score >= confidence_threshold]
1601
+
1602
  st.markdown("---")
1603
  st.info("This application analyzes images using Google Cloud Vision AI. Upload an image to get started.")
1604
 
 
1628
  else:
1629
  with st.spinner("Analyzing image..."):
1630
  # Call analyze function
1631
+ annotated_img, labels, objects, text, colors, text_language = analyze_image(image, analysis_types)
1632
 
1633
  # Display results
1634
+ display_results(annotated_img, labels, objects, text, colors, text_language)
1635
 
1636
  # Add download button for the annotated image
1637
  buf = io.BytesIO()
 
1651
 
1652
  uploaded_files = st.file_uploader("Choose images...", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
1653
 
1654
+ if uploaded_files and len(uploaded_files) > 0:
 
1655
  if len(uploaded_files) > 5:
1656
+ st.warning("You've uploaded more than 5 images. Only the first 5 will be processed.")
1657
  uploaded_files = uploaded_files[:5]
1658
 
1659
+ if st.button("Process Batch"):
1660
+ st.write(f"Processing {len(uploaded_files)} images...")
1661
+
1662
+ # Process each image with a unique key for each download button
1663
+ for i, uploaded_file in enumerate(uploaded_files):
1664
+ st.markdown(f"### Image {i+1}: {uploaded_file.name}")
 
 
 
 
 
 
 
 
 
 
1665
 
1666
+ # Open and process the image
1667
+ try:
1668
+ image = Image.open(uploaded_file)
1669
+ annotated_img, labels, objects, text, colors, text_language = analyze_image(
1670
+ image, analysis_types, confidence_threshold
1671
+ )
1672
+
1673
+ # Create a unique identifier for this image
1674
+ image_id = f"{i}_{uploaded_file.name.replace(' ', '_')}"
1675
+
1676
+ # Display results with unique download button keys
1677
+ col1, col2 = st.columns([3, 2])
1678
+
1679
+ with col1:
1680
+ st.image(annotated_img, use_container_width=True)
1681
+
1682
+ with col2:
1683
+ # Display analysis results
1684
+ if labels:
1685
+ st.markdown("##### Labels Detected")
1686
+ for label, confidence in labels.items():
1687
+ st.write(f"{label}: {confidence}%")
1688
 
1689
+ if objects:
1690
+ st.markdown("##### Objects Detected")
1691
+ for obj, confidence in objects.items():
1692
+ st.write(f"{obj}: {confidence}%")
 
 
1693
 
1694
+ if text:
1695
+ st.markdown("##### Text Detected")
1696
+ if text_language:
1697
+ st.markdown(f"**Language:** {text_language}")
1698
+ st.text(text)
1699
 
1700
+ if colors:
1701
+ st.markdown("##### Dominant Colors")
1702
+ for color_name, color_data in colors.items():
1703
+ rgb = color_data["rgb"]
1704
+ hex_color = f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}"
1705
+ st.markdown(f"<div style='background-color:{hex_color};width:50px;height:20px;display:inline-block;'></div> {color_name}: {color_data['score']}%", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1706
 
1707
+ # Create summary image for download
1708
+ summary_img = create_summary_image(annotated_img, labels, objects, text, colors)
1709
+ buf = io.BytesIO()
1710
+ summary_img.save(buf, format="JPEG", quality=90)
1711
+ byte_im = buf.getvalue()
 
 
 
 
 
 
1712
 
1713
+ # Use unique key for each download button
1714
+ st.download_button(
1715
+ label=f"📥 Download Results for {uploaded_file.name}",
1716
+ data=byte_im,
1717
+ file_name=f"analysis_{image_id}.jpg",
1718
+ mime="image/jpeg",
1719
+ key=f"download_batch_{image_id}" # Unique key for each image
1720
+ )
 
1721
 
1722
+ st.markdown("---") # Add separator between images
1723
+
1724
+ except Exception as e:
1725
+ st.error(f"Error processing {uploaded_file.name}: {str(e)}")
 
 
 
 
 
1726
 
1727
  elif selected == "Video Analysis":
1728
  st.markdown('<div class="subheader">Video Analysis</div>', unsafe_allow_html=True)