Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import json | |
| import os | |
| import time | |
| from collections import defaultdict | |
| from PIL import Image | |
| import io | |
| BASE_URL = "https://api.jigsawstack.com/v1" | |
| headers = { | |
| "x-api-key": "sk_61635c6833032ef257aab9157e7376b0dadf30806aea75b18029878accaa31669dd074c3468d35d490a88c41944c2b94607a8a43207a1b7049db41bde8b8cefd024Am7v5COxYErMQyQU6u" | |
| } | |
| # Rate limiting configuration | |
| request_times = defaultdict(list) | |
| MAX_REQUESTS = 20 # Maximum requests per time window | |
| TIME_WINDOW = 3600 # Time window in seconds (1 hour) | |
| def get_real_ip(request: gr.Request): | |
| """Extract real IP address using x-forwarded-for header or fallback""" | |
| if not request: | |
| return "unknown" | |
| forwarded = request.headers.get("x-forwarded-for") | |
| if forwarded: | |
| ip = forwarded.split(",")[0].strip() # First IP in the list is the client's | |
| else: | |
| ip = request.client.host # fallback | |
| return ip | |
| def check_rate_limit(request: gr.Request): | |
| """Check if the current request exceeds rate limits""" | |
| if not request: | |
| return True, "Rate limit check failed - no request info" | |
| ip = get_real_ip(request) | |
| now = time.time() | |
| # Clean up old timestamps outside the time window | |
| request_times[ip] = [t for t in request_times[ip] if now - t < TIME_WINDOW] | |
| # Check if rate limit exceeded | |
| if len(request_times[ip]) >= MAX_REQUESTS: | |
| time_remaining = int(TIME_WINDOW - (now - request_times[ip][0])) | |
| time_remaining_minutes = round(time_remaining / 60, 1) | |
| time_window_minutes = round(TIME_WINDOW / 60, 1) | |
| return False, f"Rate limit exceeded. You can make {MAX_REQUESTS} requests per {time_window_minutes} minutes. Try again in {time_remaining_minutes} minutes." | |
| # Add current request timestamp | |
| request_times[ip].append(now) | |
| return True, "" | |
| def detect_objects(request: gr.Request, image_url=None, file_store_key=None, prompts=None, features=None): | |
| rate_limit_ok, rate_limit_msg = check_rate_limit(request) | |
| if not rate_limit_ok: | |
| return f"β {rate_limit_msg}", "", "", None | |
| if not image_url and not file_store_key: | |
| return "β Please provide either an image URL or file store key.", "", "", None | |
| if image_url and file_store_key: | |
| return "β Provide only one: image URL or file store key.", "", "", None | |
| try: | |
| payload = {} | |
| if image_url: | |
| payload["url"] = image_url | |
| if file_store_key: | |
| payload["file_store_key"] = file_store_key | |
| # Add optional parameters | |
| if prompts: | |
| payload["prompts"] = prompts | |
| if features: | |
| payload["features"] = features | |
| # Always return annotated image | |
| payload["annotated_image"] = True | |
| # Always use url as return_type | |
| payload["return_type"] = "url" | |
| response = requests.post(f"{BASE_URL}/object_detection", headers=headers, json=payload) | |
| if response.status_code != 200: | |
| return f"β Error: {response.status_code} - {response.text}", "", "", None | |
| result = response.json() | |
| if not result.get("success"): | |
| return "β Detection failed.", "", "", None | |
| status = "β Detection successful!" | |
| objects = result.get("objects", []) | |
| annotated_image_url = result.get("annotated_image") | |
| # Create description with object details | |
| description = f"Image Size: {result.get('width', 'Unknown')} x {result.get('height', 'Unknown')}\n\n" | |
| description += f"Total Objects Detected: {len(objects)}\n\n" | |
| for i, obj in enumerate(objects): | |
| bounds = obj.get("bounds", {}) | |
| label = obj.get("label", "Unknown") | |
| bound_text = "" | |
| if bounds: | |
| width = bounds.get("width", "Unknown") | |
| height = bounds.get("height", "Unknown") | |
| top_left = bounds.get("top_left", {}) | |
| if top_left: | |
| x, y = top_left.get("x", "?"), top_left.get("y", "?") | |
| bound_text = f"Position: ({x}, {y}), Size: {width}x{height}" | |
| description += f"β’ {label}\n {bound_text}\n" | |
| raw_json = json.dumps(result, indent=2) | |
| return status, description.strip(), raw_json, annotated_image_url | |
| except Exception as e: | |
| return f"β Error: {str(e)}", "", "", None | |
| with gr.Blocks() as demo: | |
| gr.Markdown(""" | |
| <div style='text-align: center; margin-bottom: 24px;'> | |
| <h1 style='font-size:2.2em; margin-bottom: 0.2em;'>π§© Object Detection</h1> | |
| <p style='font-size:1.2em; margin-top: 0;'>Detect objects within images with great accuracy using AI models.</p> | |
| <p style='font-size:1em; margin-top: 0.5em;'>For more details and API usage, see the <a href='https://jigsawstack.com/docs/api-reference/ai/object-detection' target='_blank'>documentation</a>.</p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| input_type = gr.Radio(choices=["Image URL", "File Store Key"], value="Image URL", label="Input Type") | |
| image_url = gr.Textbox(label="Image URL", placeholder="https://example.com/image.jpg", visible=True) | |
| file_store_key = gr.Textbox(label="File Store Key", placeholder="my-image.jpg", visible=False) | |
| # Advanced options | |
| prompts = gr.Textbox(label="Prompts (comma-separated)", placeholder="wine glass, bottle, cup", info="Targeted object detection prompts") | |
| features = gr.CheckboxGroup(choices=["object_detection", "gui"], value=["object_detection"], label="Features") | |
| detect_btn = gr.Button("π Detect Objects") | |
| clear_btn = gr.Button("Clear") | |
| with gr.Column(): | |
| status_box = gr.Textbox(label="Status", interactive=False) | |
| desc_display = gr.Textbox(label="Object Details", lines=10, interactive=False) | |
| # Annotated image display - always visible | |
| annotated_image_display = gr.Image(label="Annotated Image") | |
| json_box = gr.Accordion("Raw JSON Response", open=False) | |
| with json_box: | |
| json_output = gr.Textbox(show_label=False, lines=20, interactive=False) | |
| def toggle_inputs(choice): | |
| return ( | |
| gr.update(visible=(choice == "Image URL")), | |
| gr.update(visible=(choice == "File Store Key")) | |
| ) | |
| input_type.change(fn=toggle_inputs, inputs=input_type, outputs=[image_url, file_store_key]) | |
| def on_detect(input_mode, url, key, prompts_text, features_list, request: gr.Request): | |
| # Parse prompts | |
| prompts_list = None | |
| if prompts_text.strip(): | |
| prompts_list = [p.strip() for p in prompts_text.split(",") if p.strip()] | |
| if input_mode == "Image URL": | |
| return detect_objects( | |
| request=request, | |
| image_url=url.strip(), | |
| prompts=prompts_list, | |
| features=features_list | |
| ) | |
| else: | |
| return detect_objects( | |
| request=request, | |
| file_store_key=key.strip(), | |
| prompts=prompts_list, | |
| features=features_list | |
| ) | |
| detect_btn.click(fn=on_detect, inputs=[ | |
| input_type, image_url, file_store_key, prompts, features | |
| ], outputs=[status_box, desc_display, json_output, annotated_image_display]) | |
| def clear_all(): | |
| return "Image URL", "", "", "", "", ["object_detection"], "", "", "", None | |
| clear_btn.click(fn=clear_all, inputs=[], outputs=[ | |
| input_type, image_url, file_store_key, prompts, features, | |
| status_box, desc_display, json_output, annotated_image_display | |
| ]) | |
| demo.launch() | |