CosmickVisions commited on
Commit
34bca14
·
verified ·
1 Parent(s): 6085bfc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +615 -6
app.py CHANGED
@@ -8,6 +8,17 @@ from streamlit_option_menu import option_menu
8
  import json
9
  from google.oauth2 import service_account
10
  import google.auth
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  # Set page config
13
  st.set_page_config(
@@ -156,15 +167,241 @@ def display_results(annotated_img, labels, objects, text):
156
  st.markdown(f'<div class="text-item">{text}</div>', unsafe_allow_html=True)
157
  st.markdown('</div>', unsafe_allow_html=True)
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  def main():
160
  # Header
161
- st.markdown('<div class="main-header">Google Cloud Vision AI Analyzer</div>', unsafe_allow_html=True)
162
 
163
  # Navigation
164
  selected = option_menu(
165
  menu_title=None,
166
- options=["Image Analysis", "About"],
167
- icons=["image", "info-circle"],
168
  menu_icon="cast",
169
  default_index=0,
170
  orientation="horizontal",
@@ -241,21 +478,393 @@ def main():
241
  mime="image/png"
242
  )
243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  elif selected == "About":
245
  st.markdown("## About This App")
246
  st.write("""
247
- This application uses Google Cloud Vision AI to analyze images. It can:
248
 
249
  - **Detect labels** in images
250
  - **Identify objects** and their locations
251
  - **Extract text** from images
252
  - **Detect faces** and facial landmarks
 
253
 
254
  To use this app, you need to:
255
  1. Set up Google Cloud Vision API credentials
256
- 2. Upload an image
257
  3. Select the types of analysis you want to perform
258
- 4. Click "Analyze Image"
259
 
260
  The app is built with Streamlit and Google Cloud Vision API.
261
  """)
 
8
  import json
9
  from google.oauth2 import service_account
10
  import google.auth
11
+ import av
12
+ from streamlit_webrtc import webrtc_streamer, VideoProcessorBase, RTCConfiguration
13
+ import cv2
14
+ from typing import List, Union
15
+ from google.cloud import documentai_v1 as documentai
16
+ import pandas as pd
17
+ from google.cloud import bigquery
18
+ from google.cloud.exceptions import NotFound
19
+ import tempfile
20
+ import time
21
+ import matplotlib.pyplot as plt
22
 
23
  # Set page config
24
  st.set_page_config(
 
167
  st.markdown(f'<div class="text-item">{text}</div>', unsafe_allow_html=True)
168
  st.markdown('</div>', unsafe_allow_html=True)
169
 
170
+ class VideoProcessor(VideoProcessorBase):
171
+ """Process video frames for real-time analysis"""
172
+
173
+ def __init__(self, analysis_types: List[str]):
174
+ self.analysis_types = analysis_types
175
+ self.frame_counter = 0
176
+ self.process_every_n_frames = 10 # Process every 10th frame to reduce API calls
177
+
178
+ def transform(self, frame: av.VideoFrame) -> av.VideoFrame:
179
+ self.frame_counter += 1
180
+
181
+ # Process only every nth frame to reduce API usage
182
+ if self.frame_counter % self.process_every_n_frames != 0:
183
+ return frame
184
+
185
+ img = frame.to_ndarray(format="bgr24")
186
+
187
+ # Convert numpy array to PIL Image for Vision API
188
+ pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
189
+
190
+ # Process with Vision API
191
+ try:
192
+ # Create vision image
193
+ img_byte_arr = io.BytesIO()
194
+ pil_img.save(img_byte_arr, format='PNG')
195
+ content = img_byte_arr.getvalue()
196
+ vision_image = vision.Image(content=content)
197
+
198
+ # Process with selected analysis types
199
+ if "Objects" in self.analysis_types:
200
+ objects = client.object_localization(image=vision_image)
201
+ # Draw boxes around detected objects
202
+ for obj in objects.localized_object_annotations:
203
+ box = [(vertex.x * img.shape[1], vertex.y * img.shape[0])
204
+ for vertex in obj.bounding_poly.normalized_vertices]
205
+ box = np.array(box, np.int32).reshape((-1, 1, 2))
206
+ cv2.polylines(img, [box], True, (0, 255, 0), 2)
207
+ # Add label
208
+ cv2.putText(img, f"{obj.name}: {int(obj.score * 100)}%",
209
+ (int(box[0][0][0]), int(box[0][0][1]) - 10),
210
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
211
+
212
+ if "Face Detection" in self.analysis_types:
213
+ faces = client.face_detection(image=vision_image)
214
+ for face in faces.face_annotations:
215
+ vertices = face.bounding_poly.vertices
216
+ points = [(vertex.x, vertex.y) for vertex in vertices]
217
+ # Draw face box
218
+ pts = np.array(points, np.int32).reshape((-1, 1, 2))
219
+ cv2.polylines(img, [pts], True, (0, 0, 255), 2)
220
+
221
+ # Draw landmarks
222
+ for landmark in face.landmarks:
223
+ px = int(landmark.position.x)
224
+ py = int(landmark.position.y)
225
+ cv2.circle(img, (px, py), 2, (255, 255, 0), -1)
226
+
227
+ if "Text" in self.analysis_types:
228
+ text = client.text_detection(image=vision_image)
229
+ for text_annot in text.text_annotations[1:]: # Skip the first one (full text)
230
+ box = [(vertex.x, vertex.y) for vertex in text_annot.bounding_poly.vertices]
231
+ pts = np.array(box, np.int32).reshape((-1, 1, 2))
232
+ cv2.polylines(img, [pts], True, (255, 0, 0), 1)
233
+
234
+ except Exception as e:
235
+ # Log error but don't crash the stream
236
+ print(f"Error analyzing frame: {str(e)}")
237
+
238
+ return av.VideoFrame.from_ndarray(img, format="bgr24")
239
+
240
+ def analyze_document(file_content, processor_id, location="us"):
241
+ """Analyze document using Document AI"""
242
+ # Create Document AI client
243
+ client = documentai.DocumentProcessorServiceClient(credentials=credentials)
244
+
245
+ # The full resource name of the processor
246
+ name = f"projects/{credentials.project_id}/locations/{location}/processors/{processor_id}"
247
+
248
+ # Create document object
249
+ document = documentai.Document(
250
+ content=file_content,
251
+ mime_type="application/pdf" # Adjust based on input type
252
+ )
253
+
254
+ # Configure the process request
255
+ request = documentai.ProcessRequest(
256
+ name=name,
257
+ document=document
258
+ )
259
+
260
+ # Process the document
261
+ result = client.process_document(request=request)
262
+ document = result.document
263
+
264
+ # Extract text, entities, etc.
265
+ text = document.text
266
+ entities = {}
267
+
268
+ # Extract entities and their values
269
+ for entity in document.entities:
270
+ entities[entity.type_] = entity.mention_text
271
+
272
+ # Extract table data if available
273
+ tables = []
274
+ for page in document.pages:
275
+ for table in page.tables:
276
+ table_data = []
277
+ # Get header row
278
+ headers = []
279
+ for cell in table.header_rows[0].cells:
280
+ headers.append(text[cell.layout.text_anchor.text_segments[0].start_index:
281
+ cell.layout.text_anchor.text_segments[0].end_index])
282
+
283
+ # Get data rows
284
+ for row in table.body_rows:
285
+ row_data = []
286
+ for cell in row.cells:
287
+ if len(cell.layout.text_anchor.text_segments) > 0:
288
+ cell_text = text[cell.layout.text_anchor.text_segments[0].start_index:
289
+ cell.layout.text_anchor.text_segments[0].end_index]
290
+ row_data.append(cell_text)
291
+ else:
292
+ row_data.append("")
293
+ table_data.append(row_data)
294
+
295
+ tables.append({"headers": headers, "data": table_data})
296
+
297
+ return text, entities, tables
298
+
299
+ def create_bigquery_table(dataset_id, table_id, schema=None):
300
+ """Create a BigQuery table if it doesn't exist"""
301
+ # Create client
302
+ bq_client = bigquery.Client(credentials=credentials, project=credentials.project_id)
303
+
304
+ # Create dataset if it doesn't exist
305
+ dataset_ref = bq_client.dataset(dataset_id)
306
+ try:
307
+ bq_client.get_dataset(dataset_ref)
308
+ except NotFound:
309
+ dataset = bigquery.Dataset(dataset_ref)
310
+ dataset.location = "US"
311
+ bq_client.create_dataset(dataset)
312
+ st.info(f"Dataset '{dataset_id}' created.")
313
+
314
+ # Create table reference
315
+ table_ref = dataset_ref.table(table_id)
316
+
317
+ # Check if table exists
318
+ try:
319
+ bq_client.get_table(table_ref)
320
+ st.info(f"Table '{table_id}' already exists.")
321
+ return table_ref
322
+ except NotFound:
323
+ # Create the table with schema if provided
324
+ if schema:
325
+ table = bigquery.Table(table_ref, schema=schema)
326
+ else:
327
+ table = bigquery.Table(table_ref)
328
+
329
+ bq_client.create_table(table)
330
+ st.info(f"Table '{table_id}' created.")
331
+ return table_ref
332
+
333
+ def upload_csv_to_bigquery(file, dataset_id, table_id, append=False):
334
+ """Upload a CSV file to BigQuery"""
335
+ # Create client
336
+ bq_client = bigquery.Client(credentials=credentials, project=credentials.project_id)
337
+
338
+ # Create a temporary file
339
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.csv') as temp_file:
340
+ temp_file.write(file.getvalue())
341
+ temp_file_path = temp_file.name
342
+
343
+ # Configure the load job
344
+ job_config = bigquery.LoadJobConfig(
345
+ source_format=bigquery.SourceFormat.CSV,
346
+ skip_leading_rows=1, # Skip header row
347
+ autodetect=True, # Auto-detect schema
348
+ )
349
+
350
+ if append:
351
+ job_config.write_disposition = bigquery.WriteDisposition.WRITE_APPEND
352
+ else:
353
+ job_config.write_disposition = bigquery.WriteDisposition.WRITE_TRUNCATE
354
+
355
+ # Create table reference
356
+ dataset_ref = bq_client.dataset(dataset_id)
357
+ table_ref = dataset_ref.table(table_id)
358
+
359
+ # Load the file
360
+ with open(temp_file_path, "rb") as source_file:
361
+ job = bq_client.load_table_from_file(
362
+ source_file, table_ref, job_config=job_config
363
+ )
364
+
365
+ # Wait for the job to complete
366
+ job.result()
367
+
368
+ # Clean up the temp file
369
+ os.unlink(temp_file_path)
370
+
371
+ # Get the table
372
+ table = bq_client.get_table(table_ref)
373
+
374
+ return {
375
+ "num_rows": table.num_rows,
376
+ "size_bytes": table.num_bytes,
377
+ "schema": [field.name for field in table.schema]
378
+ }
379
+
380
+ def run_bigquery(query):
381
+ """Run a BigQuery query and return results"""
382
+ # Create client
383
+ bq_client = bigquery.Client(credentials=credentials, project=credentials.project_id)
384
+
385
+ # Run the query
386
+ query_job = bq_client.query(query)
387
+
388
+ # Wait for the query to finish
389
+ results = query_job.result()
390
+
391
+ # Convert to dataframe
392
+ df = results.to_dataframe()
393
+
394
+ return df
395
+
396
  def main():
397
  # Header
398
+ st.markdown('<div class="main-header">Google Cloud AI Analyzer</div>', unsafe_allow_html=True)
399
 
400
  # Navigation
401
  selected = option_menu(
402
  menu_title=None,
403
+ options=["Image Analysis", "Video Analysis", "Document Analysis", "Data Analysis", "About"],
404
+ icons=["image", "camera-video", "file-text", "bar-chart", "info-circle"],
405
  menu_icon="cast",
406
  default_index=0,
407
  orientation="horizontal",
 
478
  mime="image/png"
479
  )
480
 
481
+ elif selected == "Video Analysis":
482
+ st.markdown('<div class="subheader">Real-Time Video Analysis</div>', unsafe_allow_html=True)
483
+
484
+ # Analysis settings
485
+ st.sidebar.markdown("### Video Analysis Settings")
486
+ analysis_types = []
487
+ if st.sidebar.checkbox("Object Detection", value=True):
488
+ analysis_types.append("Objects")
489
+ if st.sidebar.checkbox("Face Detection"):
490
+ analysis_types.append("Face Detection")
491
+ if st.sidebar.checkbox("Text Recognition"):
492
+ analysis_types.append("Text")
493
+
494
+ st.sidebar.markdown("---")
495
+ st.sidebar.warning("⚠️ Real-time analysis may use a significant amount of API calls. Use responsibly.")
496
+
497
+ # Configure WebRTC
498
+ rtc_configuration = RTCConfiguration(
499
+ {"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]}
500
+ )
501
+
502
+ # Display instructions
503
+ st.markdown("""
504
+ #### 📹 Real-Time Camera Analysis
505
+
506
+ This feature analyzes your camera feed in real-time using Google Cloud Vision AI.
507
+
508
+ **Instructions:**
509
+ 1. Select the analysis types in the sidebar
510
+ 2. Click the "START" button below to begin
511
+ 3. Allow camera access when prompted
512
+ 4. The app will analyze every few frames to reduce API usage
513
+ """)
514
+
515
+ # Start WebRTC streamer
516
+ if analysis_types:
517
+ webrtc_ctx = webrtc_streamer(
518
+ key="vision-analyzer",
519
+ video_processor_factory=lambda: VideoProcessor(analysis_types),
520
+ rtc_configuration=rtc_configuration,
521
+ media_stream_constraints={
522
+ "video": True,
523
+ "audio": False
524
+ },
525
+ )
526
+ else:
527
+ st.warning("Please select at least one analysis type from the sidebar.")
528
+
529
+ elif selected == "Document Analysis":
530
+ st.markdown('<div class="subheader">Document Processing & Analysis</div>', unsafe_allow_html=True)
531
+
532
+ # Sidebar controls for document analysis
533
+ with st.sidebar:
534
+ st.markdown("### Document Analysis Settings")
535
+
536
+ # Select document processor type
537
+ processor_type = st.selectbox(
538
+ "Select Document Type",
539
+ ["General Document", "Form Parser", "Invoice Parser", "Receipt Parser", "ID Document"]
540
+ )
541
+
542
+ # Mapping of processor types to processor IDs (you would need to create these in GCP)
543
+ processor_mapping = {
544
+ "General Document": "your-general-processor-id",
545
+ "Form Parser": "your-form-processor-id",
546
+ "Invoice Parser": "your-invoice-processor-id",
547
+ "Receipt Parser": "your-receipt-processor-id",
548
+ "ID Document": "your-id-processor-id"
549
+ }
550
+
551
+ st.markdown("---")
552
+ st.info("Upload a document to extract information using Google Document AI.")
553
+
554
+ # Main content
555
+ uploaded_file = st.file_uploader(
556
+ "Upload a document (PDF, TIFF, JPG, PNG)",
557
+ type=["pdf", "tiff", "jpg", "jpeg", "png"]
558
+ )
559
+
560
+ if uploaded_file is not None:
561
+ # Display file details
562
+ file_details = {
563
+ "Filename": uploaded_file.name,
564
+ "File size": f"{uploaded_file.size / 1024:.2f} KB",
565
+ "File type": uploaded_file.type
566
+ }
567
+ st.write("### File Details")
568
+ for key, value in file_details.items():
569
+ st.write(f"**{key}:** {value}")
570
+
571
+ # If it's an image file, display it
572
+ if uploaded_file.type.startswith('image/'):
573
+ st.image(uploaded_file, caption="Uploaded Document", use_column_width=True)
574
+ else:
575
+ st.info("PDF document uploaded (preview not available)")
576
+
577
+ # Process button
578
+ if st.button("Process Document"):
579
+ with st.spinner("Processing document..."):
580
+ # Get processor ID based on selection
581
+ processor_id = processor_mapping[processor_type]
582
+
583
+ # Get file content
584
+ file_content = uploaded_file.getvalue()
585
+
586
+ # Process document
587
+ try:
588
+ text, entities, tables = analyze_document(file_content, processor_id)
589
+
590
+ # Display results
591
+ st.markdown("### Document Analysis Results")
592
+
593
+ # Show extracted information in tabs
594
+ tab1, tab2, tab3 = st.tabs(["Text", "Extracted Fields", "Tables"])
595
+
596
+ with tab1:
597
+ st.markdown("#### Extracted Text")
598
+ st.markdown('<div class="result-container">', unsafe_allow_html=True)
599
+ st.write(text)
600
+ st.markdown('</div>', unsafe_allow_html=True)
601
+
602
+ with tab2:
603
+ st.markdown("#### Extracted Fields")
604
+ st.markdown('<div class="result-container">', unsafe_allow_html=True)
605
+ if entities:
606
+ for entity_type, value in entities.items():
607
+ st.markdown(f"**{entity_type}:** {value}")
608
+ else:
609
+ st.info("No fields extracted from this document.")
610
+ st.markdown('</div>', unsafe_allow_html=True)
611
+
612
+ with tab3:
613
+ st.markdown("#### Extracted Tables")
614
+ if tables:
615
+ for i, table in enumerate(tables):
616
+ st.markdown(f"**Table {i+1}**")
617
+ df = pd.DataFrame(table["data"], columns=table["headers"])
618
+ st.dataframe(df)
619
+ else:
620
+ st.info("No tables found in this document.")
621
+
622
+ except Exception as e:
623
+ st.error(f"Error processing document: {str(e)}")
624
+
625
+ elif selected == "Data Analysis":
626
+ st.markdown('<div class="subheader">BigQuery CSV Data Analysis</div>', unsafe_allow_html=True)
627
+
628
+ # Sidebar controls for BigQuery
629
+ with st.sidebar:
630
+ st.markdown("### BigQuery Settings")
631
+
632
+ # Dataset and table settings
633
+ dataset_id = st.text_input("Dataset ID", "my_dataset")
634
+ table_id = st.text_input("Table ID", "my_table")
635
+
636
+ # Upload options
637
+ replace_data = st.radio(
638
+ "Upload Mode:",
639
+ ["Replace existing data", "Append to existing data"]
640
+ )
641
+
642
+ st.markdown("---")
643
+ st.info("Upload a CSV file to analyze with BigQuery.")
644
+
645
+ # Tabs for different actions
646
+ upload_tab, query_tab, visualization_tab = st.tabs(["Upload Data", "Query Data", "Visualize Data"])
647
+
648
+ with upload_tab:
649
+ st.markdown("### Upload CSV File to BigQuery")
650
+ uploaded_file = st.file_uploader("Choose a CSV file", type=["csv"])
651
+
652
+ if uploaded_file is not None:
653
+ # Preview the data
654
+ try:
655
+ df_preview = pd.read_csv(uploaded_file)
656
+ st.write("### Data Preview")
657
+ st.dataframe(df_preview.head())
658
+
659
+ # Display file details
660
+ st.write(f"**Rows:** {len(df_preview)}")
661
+ st.write(f"**Columns:** {len(df_preview.columns)}")
662
+ st.write("**Column Types:**")
663
+ st.write(df_preview.dtypes)
664
+
665
+ # Reset file pointer
666
+ uploaded_file.seek(0)
667
+
668
+ # Upload button
669
+ if st.button("Upload to BigQuery"):
670
+ with st.spinner("Uploading to BigQuery..."):
671
+ # Upload the file
672
+ append_mode = replace_data == "Append to existing data"
673
+ result = upload_csv_to_bigquery(uploaded_file, dataset_id, table_id, append=append_mode)
674
+
675
+ # Show results
676
+ st.success(f"Data uploaded successfully to {dataset_id}.{table_id}")
677
+ st.write(f"**Rows loaded:** {result['num_rows']}")
678
+ st.write(f"**Size:** {result['size_bytes']/1024/1024:.2f} MB")
679
+ st.write(f"**Schema:** {', '.join(result['schema'])}")
680
+
681
+ # Store table info in session state for querying
682
+ st.session_state["table_info"] = {
683
+ "dataset_id": dataset_id,
684
+ "table_id": table_id,
685
+ "schema": result["schema"]
686
+ }
687
+
688
+ except Exception as e:
689
+ st.error(f"Error processing CSV: {str(e)}")
690
+
691
+ with query_tab:
692
+ st.markdown("### Query Your Data with BigQuery")
693
+
694
+ # Check if table info exists in session state
695
+ if "table_info" in st.session_state:
696
+ table_info = st.session_state["table_info"]
697
+ st.info(f"Currently working with table: {table_info['dataset_id']}.{table_info['table_id']}")
698
+
699
+ # Create a default query
700
+ default_query = f"SELECT * FROM `{credentials.project_id}.{table_info['dataset_id']}.{table_info['table_id']}` LIMIT 100"
701
+
702
+ # Query editor
703
+ query = st.text_area("SQL Query", default_query, height=150)
704
+
705
+ # Example queries
706
+ with st.expander("Example Queries"):
707
+ st.markdown("""
708
+ #### Example Queries:
709
+
710
+ 1. **Get all data:**
711
+ ```sql
712
+ SELECT * FROM `{project}.{dataset}.{table}` LIMIT 1000
713
+ ```
714
+
715
+ 2. **Count rows:**
716
+ ```sql
717
+ SELECT COUNT(*) as count FROM `{project}.{dataset}.{table}`
718
+ ```
719
+
720
+ 3. **Get summary statistics:**
721
+ ```sql
722
+ SELECT
723
+ MIN({numeric_column}) as min_value,
724
+ MAX({numeric_column}) as max_value,
725
+ AVG({numeric_column}) as avg_value,
726
+ STDDEV({numeric_column}) as stddev_value
727
+ FROM `{project}.{dataset}.{table}`
728
+ ```
729
+
730
+ 4. **Group by and count:**
731
+ ```sql
732
+ SELECT
733
+ {category_column},
734
+ COUNT(*) as count
735
+ FROM `{project}.{dataset}.{table}`
736
+ GROUP BY {category_column}
737
+ ORDER BY count DESC
738
+ LIMIT 10
739
+ ```
740
+ """.replace("{project}", credentials.project_id)
741
+ .replace("{dataset}", table_info['dataset_id'])
742
+ .replace("{table}", table_info['table_id'])
743
+ .replace("{numeric_column}", table_info['schema'][0])
744
+ .replace("{category_column}", table_info['schema'][0]))
745
+
746
+ # Execute query button
747
+ if st.button("Run Query"):
748
+ with st.spinner("Running query..."):
749
+ try:
750
+ # Run the query
751
+ df_result = run_bigquery(query)
752
+
753
+ # Show results
754
+ if not df_result.empty:
755
+ st.write("### Query Results")
756
+ st.dataframe(df_result)
757
+
758
+ # Store results for visualization
759
+ st.session_state["query_results"] = df_result
760
+
761
+ # Download button
762
+ csv = df_result.to_csv(index=False)
763
+ st.download_button(
764
+ label="Download Results as CSV",
765
+ data=csv,
766
+ file_name="query_results.csv",
767
+ mime="text/csv"
768
+ )
769
+ else:
770
+ st.info("Query returned no results.")
771
+ except Exception as e:
772
+ st.error(f"Error running query: {str(e)}")
773
+ else:
774
+ st.warning("Please upload a CSV file in the 'Upload Data' tab first.")
775
+
776
+ with visualization_tab:
777
+ st.markdown("### Visualize Your Query Results")
778
+
779
+ if "query_results" in st.session_state and not st.session_state["query_results"].empty:
780
+ df = st.session_state["query_results"]
781
+
782
+ # Chart type selector
783
+ chart_type = st.selectbox(
784
+ "Select Chart Type",
785
+ ["Bar Chart", "Line Chart", "Scatter Plot", "Histogram", "Pie Chart"]
786
+ )
787
+
788
+ # Column selectors based on chart type
789
+ if chart_type in ["Bar Chart", "Line Chart", "Scatter Plot"]:
790
+ col1, col2 = st.columns(2)
791
+ with col1:
792
+ x_col = st.selectbox("X-axis", df.columns.tolist())
793
+ with col2:
794
+ y_col = st.selectbox("Y-axis", [c for c in df.columns.tolist() if c != x_col])
795
+
796
+ elif chart_type == "Histogram":
797
+ numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns.tolist()
798
+ if numeric_cols:
799
+ hist_col = st.selectbox("Column", numeric_cols)
800
+ else:
801
+ st.warning("No numeric columns available for histogram.")
802
+ hist_col = None
803
+
804
+ elif chart_type == "Pie Chart":
805
+ col1, col2 = st.columns(2)
806
+ with col1:
807
+ label_col = st.selectbox("Labels", df.columns.tolist())
808
+ with col2:
809
+ value_cols = df.select_dtypes(include=['int64', 'float64']).columns.tolist()
810
+ if value_cols:
811
+ value_col = st.selectbox("Values", value_cols)
812
+ else:
813
+ st.warning("No numeric columns available for values.")
814
+ value_col = None
815
+
816
+ # Generate the selected chart
817
+ st.write("### Data Visualization")
818
+
819
+ if chart_type == "Bar Chart" and x_col and y_col:
820
+ st.bar_chart(df.set_index(x_col)[y_col])
821
+
822
+ elif chart_type == "Line Chart" and x_col and y_col:
823
+ st.line_chart(df.set_index(x_col)[y_col])
824
+
825
+ elif chart_type == "Scatter Plot" and x_col and y_col:
826
+ st.write(f"Scatter Plot: {x_col} vs {y_col}")
827
+ fig, ax = plt.subplots()
828
+ ax.scatter(df[x_col], df[y_col])
829
+ ax.set_xlabel(x_col)
830
+ ax.set_ylabel(y_col)
831
+ st.pyplot(fig)
832
+
833
+ elif chart_type == "Histogram" and hist_col:
834
+ st.write(f"Histogram of {hist_col}")
835
+ fig, ax = plt.subplots()
836
+ ax.hist(df[hist_col].dropna(), bins=20)
837
+ ax.set_xlabel(hist_col)
838
+ ax.set_ylabel("Frequency")
839
+ st.pyplot(fig)
840
+
841
+ elif chart_type == "Pie Chart" and label_col and value_col:
842
+ st.write(f"Pie Chart: {value_col} by {label_col}")
843
+ # Limit to top 10 categories for pie chart clarity
844
+ top_data = df.groupby(label_col)[value_col].sum().nlargest(10).reset_index()
845
+ fig, ax = plt.subplots()
846
+ ax.pie(top_data[value_col], labels=top_data[label_col], autopct='%1.1f%%')
847
+ ax.axis('equal')
848
+ st.pyplot(fig)
849
+ else:
850
+ st.warning("Please run a query in the 'Query Data' tab first.")
851
+
852
  elif selected == "About":
853
  st.markdown("## About This App")
854
  st.write("""
855
+ This application uses Google Cloud Vision AI to analyze images and video streams. It can:
856
 
857
  - **Detect labels** in images
858
  - **Identify objects** and their locations
859
  - **Extract text** from images
860
  - **Detect faces** and facial landmarks
861
+ - **Analyze real-time video** from your camera
862
 
863
  To use this app, you need to:
864
  1. Set up Google Cloud Vision API credentials
865
+ 2. Upload an image or use your camera
866
  3. Select the types of analysis you want to perform
867
+ 4. Click "Analyze Image" or start the video stream
868
 
869
  The app is built with Streamlit and Google Cloud Vision API.
870
  """)