Update app.py
Browse files
app.py
CHANGED
@@ -1,20 +1,248 @@
|
|
1 |
-
import
|
2 |
-
import
|
3 |
-
import
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import base64
|
2 |
+
import io
|
3 |
+
import gradio as gr
|
4 |
+
from ultralytics import YOLO
|
5 |
+
import numpy as np
|
6 |
+
import cv2
|
7 |
+
from PIL import Image
|
8 |
+
import traceback
|
9 |
+
import json
|
10 |
+
import os
|
11 |
+
from huggingface_hub import hf_hub_download
|
12 |
+
|
13 |
+
# In a Hugging Face Space, authentication is handled by the environment
|
14 |
+
# No need to explicitly set a token in the Space environment
|
15 |
+
try:
|
16 |
+
# Try to download the model from Hugging Face Hub
|
17 |
+
print("Downloading model from Hugging Face Hub...")
|
18 |
+
try:
|
19 |
+
# First try with force_download
|
20 |
+
model_path = hf_hub_download(repo_id="tech4humans/yolov8s-signature-detector",
|
21 |
+
filename="yolov8s.pt",
|
22 |
+
force_download=True) # Force download for Space environment
|
23 |
+
except Exception as force_error:
|
24 |
+
print(f"Force download failed: {str(force_error)}")
|
25 |
+
# Try again without force_download
|
26 |
+
model_path = hf_hub_download(repo_id="tech4humans/yolov8s-signature-detector",
|
27 |
+
filename="yolov8s.pt",
|
28 |
+
force_download=False)
|
29 |
+
|
30 |
+
# Load the model from the downloaded path
|
31 |
+
model = YOLO(model_path)
|
32 |
+
print(f"Signature detector model loaded successfully from: {model_path}")
|
33 |
+
except Exception as e:
|
34 |
+
print(f"Error downloading/loading model: {str(e)}")
|
35 |
+
print("Falling back to default YOLOv8 model...")
|
36 |
+
try:
|
37 |
+
# Fallback to standard model
|
38 |
+
model = YOLO("yolov8s.pt")
|
39 |
+
print("Standard YOLOv8 model loaded successfully as fallback!")
|
40 |
+
except Exception as fallback_error:
|
41 |
+
print(f"Error loading fallback model: {str(fallback_error)}")
|
42 |
+
traceback.print_exc()
|
43 |
+
raise
|
44 |
+
|
45 |
+
def preprocess_image(image):
|
46 |
+
"""Convert image to correct format for YOLO."""
|
47 |
+
if image is None:
|
48 |
+
# Return a blank image if None is provided
|
49 |
+
blank_image = np.zeros((100, 100, 3), dtype=np.uint8)
|
50 |
+
return blank_image
|
51 |
+
elif isinstance(image, str):
|
52 |
+
# If image is a file path
|
53 |
+
return cv2.imread(image)
|
54 |
+
elif isinstance(image, np.ndarray):
|
55 |
+
# If image is already a numpy array
|
56 |
+
if len(image.shape) == 2: # Grayscale
|
57 |
+
return cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
|
58 |
+
elif image.shape[2] == 4: # RGBA
|
59 |
+
return cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
|
60 |
+
return image
|
61 |
+
elif isinstance(image, Image.Image):
|
62 |
+
# If image is a PIL Image
|
63 |
+
return np.array(image)
|
64 |
+
# Added support for base64 encoded images
|
65 |
+
elif isinstance(image, str) and image.startswith('data:image'):
|
66 |
+
try:
|
67 |
+
# Extract base64 part
|
68 |
+
encoded_data = image.split(',')[1]
|
69 |
+
binary_data = base64.b64decode(encoded_data)
|
70 |
+
image = Image.open(io.BytesIO(binary_data))
|
71 |
+
return np.array(image)
|
72 |
+
except Exception as e:
|
73 |
+
print(f"Error decoding base64 image: {str(e)}")
|
74 |
+
raise
|
75 |
+
else:
|
76 |
+
raise ValueError(f"Unsupported image type: {type(image)}")
|
77 |
+
|
78 |
+
def detect_signature(image):
|
79 |
+
try:
|
80 |
+
if image is None:
|
81 |
+
# Return empty results for None input
|
82 |
+
blank_image = np.zeros((100, 100, 3), dtype=np.uint8)
|
83 |
+
return blank_image, []
|
84 |
+
|
85 |
+
# Handle both regular images and base64 encoded ones
|
86 |
+
processed_image = preprocess_image(image)
|
87 |
+
|
88 |
+
# Save the processed image to a temporary file if it's not already a file path
|
89 |
+
image_path = None
|
90 |
+
if not isinstance(image, str) or not image.startswith('http'):
|
91 |
+
temp_img = Image.fromarray(processed_image)
|
92 |
+
image_path = 'temp_image.jpg'
|
93 |
+
temp_img.save(image_path)
|
94 |
+
else:
|
95 |
+
image_path = image
|
96 |
+
|
97 |
+
# Run prediction using the direct approach
|
98 |
+
results = model.predict(source=image_path, save=False, verbose=False)
|
99 |
+
|
100 |
+
if not results or len(results) == 0:
|
101 |
+
return processed_image, []
|
102 |
+
|
103 |
+
# Process results
|
104 |
+
result = results[0]
|
105 |
+
output = []
|
106 |
+
|
107 |
+
if hasattr(result, 'boxes'):
|
108 |
+
for box in result.boxes:
|
109 |
+
try:
|
110 |
+
conf = float(box.conf[0])
|
111 |
+
cls = int(box.cls[0])
|
112 |
+
class_name = model.names[cls]
|
113 |
+
|
114 |
+
if conf > 0.3: # Confidence threshold
|
115 |
+
output.append({
|
116 |
+
"confidence": round(conf, 3),
|
117 |
+
"label": class_name
|
118 |
+
})
|
119 |
+
except Exception as e:
|
120 |
+
print(f"Error processing box: {str(e)}")
|
121 |
+
traceback.print_exc()
|
122 |
+
continue
|
123 |
+
|
124 |
+
# Use the plotted image with annotations
|
125 |
+
annotated_image = result.plot()
|
126 |
+
|
127 |
+
return annotated_image, output
|
128 |
+
except Exception as e:
|
129 |
+
print(f"Error in detect_signature: {str(e)}")
|
130 |
+
traceback.print_exc()
|
131 |
+
# Return original image and empty results in case of error
|
132 |
+
if image is None:
|
133 |
+
return np.zeros((100, 100, 3), dtype=np.uint8), []
|
134 |
+
return image, []
|
135 |
+
|
136 |
+
# Add a direct API endpoint for our Node.js server
|
137 |
+
def api_detect_signature(image_data):
|
138 |
+
"""API endpoint for direct signature detection without UI"""
|
139 |
+
try:
|
140 |
+
# Handle None input
|
141 |
+
if image_data is None:
|
142 |
+
return {"success": False, "error": "No image data provided"}
|
143 |
+
|
144 |
+
# If data is base64 encoded
|
145 |
+
if isinstance(image_data, str) and image_data.startswith('data:image'):
|
146 |
+
# Use the existing function
|
147 |
+
result_img, detections = detect_signature(image_data)
|
148 |
+
|
149 |
+
# Convert result image to base64 for API response
|
150 |
+
buffered = io.BytesIO()
|
151 |
+
Image.fromarray(result_img).save(buffered, format="JPEG")
|
152 |
+
img_str = base64.b64encode(buffered.getvalue()).decode()
|
153 |
+
|
154 |
+
return {
|
155 |
+
"success": True,
|
156 |
+
"detections": detections,
|
157 |
+
"annotated_image": f"data:image/jpeg;base64,{img_str}"
|
158 |
+
}
|
159 |
+
else:
|
160 |
+
return {"success": False, "error": "Invalid image format. Send base64 encoded image."}
|
161 |
+
except Exception as e:
|
162 |
+
print(f"Error in api_detect_signature: {str(e)}")
|
163 |
+
traceback.print_exc()
|
164 |
+
return {"success": False, "error": str(e)}
|
165 |
+
|
166 |
+
# Create Gradio interface
|
167 |
+
interface = gr.Interface(
|
168 |
+
fn=detect_signature,
|
169 |
+
inputs=gr.Image(type="filepath", label="Upload an image"),
|
170 |
+
outputs=[
|
171 |
+
gr.Image(label="Detected Signatures"),
|
172 |
+
gr.JSON(label="Detection Results")
|
173 |
+
],
|
174 |
+
title="Signature Detector",
|
175 |
+
description="Upload an image to detect signatures",
|
176 |
+
examples=[
|
177 |
+
["temp_image.jpg"] if os.path.exists("temp_image.jpg") else None
|
178 |
+
],
|
179 |
+
flagging_mode="never",
|
180 |
+
cache_examples=True
|
181 |
+
)
|
182 |
+
|
183 |
+
# Create a dedicated API endpoint for direct access
|
184 |
+
api_interface = gr.Interface(
|
185 |
+
fn=api_detect_signature,
|
186 |
+
inputs=gr.Textbox(label="Base64 Image", placeholder="data:image/jpeg;base64,..."),
|
187 |
+
outputs=gr.JSON(label="API Response"),
|
188 |
+
title="Signature Detection API",
|
189 |
+
description="For programmatic access",
|
190 |
+
flagging_mode="never",
|
191 |
+
examples=[
|
192 |
+
["data:image/jpeg;base64,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"] if os.path.exists("temp_image.jpg") else None
|
193 |
+
]
|
194 |
+
)
|
195 |
+
|
196 |
+
# Create a Gradio Blocks app that includes both interfaces
|
197 |
+
with gr.Blocks() as app:
|
198 |
+
gr.Markdown("# Signature Detection Demo")
|
199 |
+
|
200 |
+
with gr.Tab("Interactive Demo"):
|
201 |
+
interface.render()
|
202 |
+
|
203 |
+
with gr.Tab("API Access"):
|
204 |
+
api_interface.render()
|
205 |
+
gr.Markdown("""
|
206 |
+
## API Usage Instructions
|
207 |
+
|
208 |
+
You can use this API endpoint from your applications by sending a POST request:
|
209 |
+
|
210 |
+
### Method 1 (Latest Gradio API, recommended):
|
211 |
+
```
|
212 |
+
POST /predict
|
213 |
+
|
214 |
+
{
|
215 |
+
"data": ["data:image/jpeg;base64,your_base64_encoded_image"]
|
216 |
+
}
|
217 |
+
```
|
218 |
+
|
219 |
+
### Method 2 (Standard API):
|
220 |
+
```
|
221 |
+
POST /api/predict
|
222 |
+
|
223 |
+
{
|
224 |
+
"data": ["data:image/jpeg;base64,your_base64_encoded_image"]
|
225 |
+
}
|
226 |
+
```
|
227 |
+
|
228 |
+
### Method 3 (Legacy format):
|
229 |
+
```
|
230 |
+
POST /run/predict
|
231 |
+
|
232 |
+
{
|
233 |
+
"fn_index": 0,
|
234 |
+
"data": ["data:image/jpeg;base64,your_base64_encoded_image"]
|
235 |
+
}
|
236 |
+
```
|
237 |
+
|
238 |
+
The response will contain detection results and an annotated image.
|
239 |
+
See README-API.md for more details.
|
240 |
+
""")
|
241 |
+
|
242 |
+
# Launch with specific configs for API access
|
243 |
+
# In Hugging Face Spaces, use Gradio's default launcher settings
|
244 |
+
app.launch(
|
245 |
+
server_name="0.0.0.0", # Bind to all network interfaces
|
246 |
+
show_api=True, # Enable API endpoints
|
247 |
+
allowed_paths=["*.jpg", "*.png", "*.jpeg"] # Allow access to image files
|
248 |
+
)
|