Update app.py
Browse files
app.py
CHANGED
@@ -34,331 +34,67 @@ from PIL import Image, ImageDraw
|
|
34 |
print("import scipy")
|
35 |
from scipy.ndimage import gaussian_filter
|
36 |
|
37 |
-
REPO_ID = "thoucentric/Shelf_Objects_Detection_Yolov7_Pytorch"
|
38 |
-
FILENAME = "best.pt"
|
39 |
-
|
40 |
-
yolov7_custom_weights = hf_hub_download(repo_id=REPO_ID, filename=FILENAME)
|
41 |
-
|
42 |
-
# Load YOLOv7 Custom Model
|
43 |
-
print("Load YOLOv7 Custom Model")
|
44 |
-
model = torch.hub.load('Owaiskhan9654/yolov7-1:main', model='custom', path_or_model=yolov7_custom_weights, force_reload=True)
|
45 |
-
|
46 |
-
# Roboflow
|
47 |
print("Initialize Roboflow")
|
48 |
rf = Roboflow(api_key="MjzjT2w8u8tlxjmUYDAd")
|
49 |
project = rf.workspace().project("sku-110k")
|
50 |
model = project.version(2).model
|
51 |
|
52 |
-
|
53 |
-
def roboflow(image, confidence, overlap, stroke_width=1, labels=False):
|
54 |
-
'''
|
55 |
-
Send the image to Roboflow API for inference.
|
56 |
-
Returns JSON and image with bounding boxes drawn on to it.
|
57 |
-
'''
|
58 |
-
json_url = f"https://detect.roboflow.com/sku-110k/2?api_key=MjzjT2w8u8tlxjmUYDAd&confidence={confidence}&overlap={overlap}&format=json"
|
59 |
-
image_url = f"https://detect.roboflow.com/sku-110k/2?api_key=MjzjT2w8u8tlxjmUYDAd&confidence={confidence}&overlap={overlap}&format=image&labels={str(labels).lower()}&stroke={stroke_width}"
|
60 |
-
|
61 |
-
encoded_image = encode_image(image)
|
62 |
-
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
63 |
-
|
64 |
-
json_request = requests.post(json_url, data=encoded_image, headers=headers)
|
65 |
-
image_request = requests.post(image_url, data=encoded_image, headers=headers)
|
66 |
-
|
67 |
-
print("JSON Response Headers:", json_request.headers)
|
68 |
-
print("Image Response Headers:", image_request.headers)
|
69 |
-
|
70 |
-
json_response = {}
|
71 |
-
image_response = None
|
72 |
-
|
73 |
-
if json_request.status_code == 200:
|
74 |
-
try:
|
75 |
-
json_response = json_request.json()
|
76 |
-
except json.JSONDecodeError:
|
77 |
-
json_response = {"error": "Invalid JSON response"}
|
78 |
-
else:
|
79 |
-
json_response = {"error": f"Failed to get JSON response, status code: {json_request.status_code}"}
|
80 |
-
|
81 |
-
if image_request.status_code == 200 and 'image' in image_request.headers.get('Content-Type', ''):
|
82 |
-
try:
|
83 |
-
image_response = Image.open(io.BytesIO(image_request.content))
|
84 |
-
except Exception as e:
|
85 |
-
image_response = None
|
86 |
-
print(f"Failed to open image: {e}")
|
87 |
-
else:
|
88 |
-
print(f"Failed to retrieve image, status code: {image_request.status_code}")
|
89 |
-
print("Image Response Content:", image_request.content)
|
90 |
-
|
91 |
-
return {
|
92 |
-
"json": json_response,
|
93 |
-
"image": image_response
|
94 |
-
}
|
95 |
-
|
96 |
-
# Image Splitting and Merging Functionality
|
97 |
-
def split_image(image, tile_size=640, overlap=160):
|
98 |
-
img_width, img_height = image.size
|
99 |
-
tiles = []
|
100 |
-
|
101 |
-
step = tile_size - overlap
|
102 |
-
|
103 |
-
for top in range(0, img_height, step):
|
104 |
-
for left in range(0, img_width, step):
|
105 |
-
box = (left, top, left + tile_size, top + tile_size)
|
106 |
-
tile = image.crop(box)
|
107 |
-
tiles.append((tile, box))
|
108 |
-
|
109 |
-
return tiles
|
110 |
-
|
111 |
-
def merge_bounding_boxes(results, box):
|
112 |
-
adjusted_bboxes = []
|
113 |
-
for idx, row in results.pandas().xyxy[0].iterrows():
|
114 |
-
adjusted_bbox = {
|
115 |
-
"class": row['name'],
|
116 |
-
"center_x": (row['xmin'] + row['xmax']) / 2 + box[0],
|
117 |
-
"center_y": (row['ymin'] + row['ymax']) / 2 + box[1],
|
118 |
-
"xmin": row['xmin'] + box[0],
|
119 |
-
"xmax": row['xmax'] + box[0],
|
120 |
-
"ymin": row['ymin'] + box[1],
|
121 |
-
"ymax": row['ymax'] + box[1],
|
122 |
-
"confidence": row['confidence'],
|
123 |
-
}
|
124 |
-
adjusted_bboxes.append(adjusted_bbox)
|
125 |
-
return adjusted_bboxes
|
126 |
-
|
127 |
-
def draw_bounding_boxes(image, bounding_boxes):
|
128 |
-
draw = ImageDraw.Draw(image)
|
129 |
-
for bbox in bounding_boxes:
|
130 |
-
color = "red"
|
131 |
-
draw.rectangle([bbox['xmin'], bbox['ymin'], bbox['xmax'], bbox['ymax']], outline=color, width=3)
|
132 |
-
draw.text((bbox['xmin'], bbox['ymin']), f"{bbox['class']} {bbox['confidence']:.2f}", fill=color)
|
133 |
-
return image
|
134 |
-
|
135 |
-
# Non-Max Suppression Implementations
|
136 |
-
def soft_nms(bounding_boxes, iou_threshold=0.3, sigma=0.5, score_threshold=0.001):
|
137 |
-
if not bounding_boxes:
|
138 |
-
return []
|
139 |
-
|
140 |
-
def iou(box1, box2):
|
141 |
-
x1 = max(box1['xmin'], box2['xmin'])
|
142 |
-
y1 = max(box1['ymin'], box2['ymin'])
|
143 |
-
x2 = min(box1['xmax'], box2['xmax'])
|
144 |
-
y2 = min(box1['ymax'], box2['ymax'])
|
145 |
-
|
146 |
-
intersection = max(0, x2 - x1) * max(0, y2 - y1)
|
147 |
-
box1_area = (box1['xmax'] - box1['xmin']) * (box1['ymax'] - box1['ymin'])
|
148 |
-
box2_area = (box2['xmax'] - box2['xmin']) * (box2['ymax'] - box2['ymin'])
|
149 |
-
|
150 |
-
union = box1_area + box2_area - intersection
|
151 |
-
return intersection / union
|
152 |
-
|
153 |
-
boxes = sorted(bounding_boxes, key=lambda x: x['confidence'], reverse=True)
|
154 |
-
final_boxes = []
|
155 |
-
|
156 |
-
while boxes:
|
157 |
-
chosen_box = boxes.pop(0)
|
158 |
-
final_boxes.append(chosen_box)
|
159 |
-
|
160 |
-
new_boxes = []
|
161 |
-
for box in boxes:
|
162 |
-
iou_score = iou(chosen_box, box)
|
163 |
-
if iou_score > iou_threshold:
|
164 |
-
box['confidence'] *= np.exp(-(iou_score ** 2) / sigma)
|
165 |
-
if box['confidence'] > score_threshold:
|
166 |
-
new_boxes.append(box)
|
167 |
-
boxes = new_boxes
|
168 |
-
|
169 |
-
return final_boxes
|
170 |
-
|
171 |
-
# Density Map Generation and Counting Functions
|
172 |
-
def generate_density_map(image, bounding_boxes, sigma=4):
|
173 |
-
density_map = np.zeros((image.height, image.width))
|
174 |
-
|
175 |
-
for bbox in bounding_boxes:
|
176 |
-
center_x = int((bbox['xmin'] + bbox['xmax']) / 2)
|
177 |
-
center_y = int((bbox['ymin'] + bbox['ymax']) / 2)
|
178 |
-
density_map[center_y, center_x] += 1
|
179 |
-
|
180 |
-
density_map = gaussian_filter(density_map, sigma=sigma)
|
181 |
-
return density_map
|
182 |
-
|
183 |
-
def count_from_density_map(density_map, threshold=0.05):
|
184 |
-
return np.sum(density_map > threshold)
|
185 |
-
|
186 |
-
# Edge Enhancement Functions
|
187 |
-
def apply_edge_enhancement(image):
|
188 |
-
gray_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
|
189 |
-
|
190 |
-
sobel_x = cv2.Sobel(gray_image, cv2.CV_64F, 1, 0, ksize=3)
|
191 |
-
sobel_y = cv2.Sobel(gray_image, cv2.CV_64F, 0, 1, ksize=3)
|
192 |
-
sobel_combined = np.sqrt(sobel_x**2 + sobel_y**2)
|
193 |
-
sobel_combined = np.uint8(sobel_combined / sobel_combined.max() * 255)
|
194 |
-
|
195 |
-
enhanced_image = cv2.cvtColor(sobel_combined, cv2.COLOR_GRAY2RGB)
|
196 |
-
return Image.fromarray(enhanced_image)
|
197 |
-
|
198 |
-
# Object Detection Functions
|
199 |
-
def object_detection(image, conf_threshold=0.25, iou_threshold=0.45):
|
200 |
-
image = Image.fromarray(image)
|
201 |
-
model.conf = conf_threshold
|
202 |
-
model.iou = iou_threshold
|
203 |
-
|
204 |
-
tiles = split_image(image, tile_size=640, overlap=160)
|
205 |
-
all_bounding_boxes = []
|
206 |
-
|
207 |
-
for tile, box in tiles:
|
208 |
-
results = model(tile)
|
209 |
-
adjusted_bboxes = merge_bounding_boxes(results, box)
|
210 |
-
all_bounding_boxes.extend(adjusted_bboxes)
|
211 |
-
|
212 |
-
final_bounding_boxes = soft_nms(all_bounding_boxes, iou_threshold=iou_threshold)
|
213 |
-
image_with_boxes = draw_bounding_boxes(image, final_bounding_boxes)
|
214 |
-
|
215 |
-
json_response = json.dumps(final_bounding_boxes, indent=4)
|
216 |
-
return image_with_boxes, json_response
|
217 |
-
|
218 |
-
|
219 |
-
def object_detection_with_edge_enhancement(image, conf_threshold=0.25, iou_threshold=0.45):
|
220 |
-
image = Image.fromarray(image)
|
221 |
-
image_enhanced = apply_edge_enhancement(image)
|
222 |
-
|
223 |
-
model.conf = conf_threshold
|
224 |
-
model.iou = iou_threshold
|
225 |
-
|
226 |
-
tiles = split_image(image_enhanced, tile_size=640, overlap=160)
|
227 |
-
all_bounding_boxes = []
|
228 |
-
|
229 |
-
for tile, box in tiles:
|
230 |
-
results = model(tile)
|
231 |
-
adjusted_bboxes = merge_bounding_boxes(results, box)
|
232 |
-
all_bounding_boxes.extend(adjusted_bboxes)
|
233 |
-
|
234 |
-
final_bounding_boxes = soft_nms(all_bounding_boxes, iou_threshold=iou_threshold)
|
235 |
-
image_with_boxes = draw_bounding_boxes(image_enhanced, final_bounding_boxes)
|
236 |
-
|
237 |
-
json_response = json.dumps(final_bounding_boxes, indent=4)
|
238 |
-
return image_with_boxes, json_response
|
239 |
-
|
240 |
-
def object_detection_density_edge(image, conf_threshold=0.25, iou_threshold=0.45):
|
241 |
-
"""Apply edge enhancement and density-based counting."""
|
242 |
-
image = Image.fromarray(image)
|
243 |
-
image_enhanced = apply_edge_enhancement(image)
|
244 |
-
|
245 |
-
model.conf = conf_threshold
|
246 |
-
model.iou = iou_threshold
|
247 |
-
|
248 |
-
tiles = split_image(image_enhanced, tile_size=640, overlap=160)
|
249 |
-
all_bounding_boxes = []
|
250 |
-
|
251 |
-
for tile, box in tiles:
|
252 |
-
results = model(tile)
|
253 |
-
adjusted_bboxes = merge_bounding_boxes(results, box)
|
254 |
-
all_bounding_boxes.extend(adjusted_bboxes)
|
255 |
-
|
256 |
-
final_bounding_boxes = soft_nms(all_bounding_boxes, iou_threshold=iou_threshold)
|
257 |
-
density_map = generate_density_map(image_enhanced, final_bounding_boxes)
|
258 |
-
|
259 |
-
density_map_rescaled = cv2.applyColorMap(
|
260 |
-
(density_map / np.max(density_map) * 255).astype(np.uint8),
|
261 |
-
cv2.COLORMAP_JET
|
262 |
-
)
|
263 |
-
density_map_pil = Image.fromarray(cv2.cvtColor(density_map_rescaled, cv2.COLOR_BGR2RGB))
|
264 |
-
image_with_density = Image.blend(image_enhanced, density_map_pil, alpha=0.5)
|
265 |
-
|
266 |
-
json_response = json.dumps(final_bounding_boxes, indent=4)
|
267 |
-
object_count = count_from_density_map(density_map)
|
268 |
-
|
269 |
-
summary = json.dumps({"object_count": int(object_count)}, indent=4)
|
270 |
-
return image_with_density, json_response, summary
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
# Function to resize and encode an image
|
276 |
def resize_image(image, max_size=1500):
|
277 |
-
print("resize_image initialized")
|
278 |
if max(image.size) > max_size:
|
279 |
-
|
280 |
-
print("Image size is over the max size, resizing")
|
281 |
ratio = max_size / float(max(image.size))
|
282 |
-
print("Ratio is: " + str(ratio))
|
283 |
-
|
284 |
new_size = tuple([int(x * ratio) for x in image.size])
|
285 |
-
print("New size is: " + str(new_size))
|
286 |
-
|
287 |
-
print("Resizing image")
|
288 |
image = image.resize(new_size, Image.LANCZOS)
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
print("Create io.BytesIO instance")
|
293 |
buffer = io.BytesIO()
|
294 |
-
|
295 |
-
print("Save image to BytesIO")
|
296 |
image.save(buffer, format="PNG")
|
297 |
buffered = buffer.getvalue()
|
298 |
-
|
299 |
-
print("Return base64 encoded image")
|
300 |
return base64.b64encode(buffered).decode("utf-8")
|
301 |
|
302 |
-
|
303 |
-
def infer(image, model, version, api_key, confidence=0.4, overlap=0.20, format="json", labels=False, stroke=1):
|
304 |
-
|
305 |
base_url = f"https://detect.roboflow.com/{model}/{version}?api_key={api_key}&confidence={confidence}&overlap={overlap}&format={format}"
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
base_url += "&labels=on"
|
310 |
-
base_url += f"&stroke={stroke}"
|
311 |
-
|
312 |
-
print(base_url)
|
313 |
-
|
314 |
-
|
315 |
image_data = resize_image(image)
|
316 |
-
|
317 |
response = requests.post(base_url, data=image_data, headers={"Content-Type": "application/x-www-form-urlencoded"})
|
318 |
-
|
319 |
if format == "json":
|
320 |
-
|
321 |
-
return json.dumps(response.json(), indent=4)
|
322 |
elif format == "image":
|
323 |
return Image.open(io.BytesIO(response.content))
|
324 |
|
325 |
-
|
326 |
-
def gradio_infer(image, model, version, api_key, confidence, overlap, format, labels, stroke):
|
327 |
-
result_json = infer(image, model, version, api_key, confidence, overlap, "json", labels, stroke)
|
328 |
-
result_image = infer(image, model, version, api_key, confidence, overlap, "image", labels, stroke)
|
329 |
-
|
330 |
-
return result_image, result_json
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
title = "<center>Cigarette Pack Counter</center>"
|
336 |
description = "<center><a href='http://counttek.online'><img width='25%' height='25%' src='https://mvp-83056e96f7ab.herokuapp.com/static/countteklogo2.png'></a><br><a href='https://nolenfelten.github.io'>Project by Nolen Felten</a></center>"
|
337 |
footer = ("<center><b>Item Classes it will detect (Total 140 Classes)</b></center>")
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
|
363 |
-
# Launch
|
364 |
interface.launch(debug=True)
|
|
|
34 |
print("import scipy")
|
35 |
from scipy.ndimage import gaussian_filter
|
36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
print("Initialize Roboflow")
|
38 |
rf = Roboflow(api_key="MjzjT2w8u8tlxjmUYDAd")
|
39 |
project = rf.workspace().project("sku-110k")
|
40 |
model = project.version(2).model
|
41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
def resize_image(image, max_size=1500):
|
|
|
43 |
if max(image.size) > max_size:
|
|
|
|
|
44 |
ratio = max_size / float(max(image.size))
|
|
|
|
|
45 |
new_size = tuple([int(x * ratio) for x in image.size])
|
|
|
|
|
|
|
46 |
image = image.resize(new_size, Image.LANCZOS)
|
|
|
|
|
|
|
|
|
47 |
buffer = io.BytesIO()
|
|
|
|
|
48 |
image.save(buffer, format="PNG")
|
49 |
buffered = buffer.getvalue()
|
|
|
|
|
50 |
return base64.b64encode(buffered).decode("utf-8")
|
51 |
|
52 |
+
def gradio_infer(image, model="sku-110k", version="2", api_key="gHiUgOSq9GqTnRy5mErk", confidence=0.4, overlap=0.2, format="json", labels=False, stroke=1):
|
|
|
|
|
53 |
base_url = f"https://detect.roboflow.com/{model}/{version}?api_key={api_key}&confidence={confidence}&overlap={overlap}&format={format}"
|
54 |
+
if format == "image" and labels:
|
55 |
+
base_url += "&labels=on"
|
56 |
+
base_url += f"&stroke={stroke}"
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
image_data = resize_image(image)
|
|
|
58 |
response = requests.post(base_url, data=image_data, headers={"Content-Type": "application/x-www-form-urlencoded"})
|
|
|
59 |
if format == "json":
|
60 |
+
return response.json()
|
|
|
61 |
elif format == "image":
|
62 |
return Image.open(io.BytesIO(response.content))
|
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
title = "<center>Cigarette Pack Counter</center>"
|
65 |
description = "<center><a href='http://counttek.online'><img width='25%' height='25%' src='https://mvp-83056e96f7ab.herokuapp.com/static/countteklogo2.png'></a><br><a href='https://nolenfelten.github.io'>Project by Nolen Felten</a></center>"
|
66 |
footer = ("<center><b>Item Classes it will detect (Total 140 Classes)</b></center>")
|
67 |
|
68 |
+
def create_interface(shelf_number):
|
69 |
+
return gr.Interface(
|
70 |
+
fn=gradio_infer,
|
71 |
+
inputs=[
|
72 |
+
gr.Image(type="pil", label=f"Shelf {shelf_number}"),
|
73 |
+
gr.Textbox(value="sku-110k", label=f"Model Name - Shelf {shelf_number}"),
|
74 |
+
gr.Textbox(value="2", label="Model Version"),
|
75 |
+
gr.Slider(0.0, 1.0, value=0.40, label="Confidence Threshold"),
|
76 |
+
gr.Slider(0.0, 1.0, value=0.20, label="Overlap Threshold"),
|
77 |
+
gr.Textbox(value="gHiUgOSq9GqTnRy5mErk", label="API Key"),
|
78 |
+
gr.Radio(["json", "image"], value="image", label="Output Format"),
|
79 |
+
gr.Checkbox(False, label="Include Labels"),
|
80 |
+
gr.Slider(1, 10, value=5, step=1, label="Stroke Width")
|
81 |
+
],
|
82 |
+
outputs=[
|
83 |
+
gr.Image(label=f"Roboflow Output Image - Shelf {shelf_number}"),
|
84 |
+
gr.JSON(label=f"Roboflow JSON Result - Shelf {shelf_number}")
|
85 |
+
],
|
86 |
+
title=title,
|
87 |
+
description=description,
|
88 |
+
article=footer,
|
89 |
+
cache_examples=False,
|
90 |
+
allow_flagging="never"
|
91 |
+
)
|
92 |
+
|
93 |
+
# Create a list of interfaces
|
94 |
+
interfaces = [create_interface(i) for i in range(1, 12)]
|
95 |
+
|
96 |
+
# Combine all interfaces into one layout
|
97 |
+
interface = gr.TabbedInterface(interfaces, tab_names=[f"Shelf {i}" for i in range(1, 12)])
|
98 |
|
99 |
+
# Launch the combined interface
|
100 |
interface.launch(debug=True)
|