Update app.py
Browse files
app.py
CHANGED
|
@@ -6,14 +6,35 @@ from fpdf import FPDF
|
|
| 6 |
import tempfile
|
| 7 |
import cv2
|
| 8 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
# Load the pre-trained YOLOv8n model
|
| 11 |
-
model = YOLO('yolov8n.pt')
|
| 12 |
model.overrides['conf'] = 0.25 # Confidence threshold
|
| 13 |
model.overrides['iou'] = 0.45 # IoU threshold
|
| 14 |
model.overrides['agnostic_nms'] = False
|
| 15 |
model.overrides['max_det'] = 1000
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
def process_input(input_file):
|
| 18 |
# Handle video: Extract middle frame
|
| 19 |
if input_file.lower().endswith(('.mp4', '.avi', '.mov')):
|
|
@@ -35,25 +56,30 @@ def process_input(input_file):
|
|
| 35 |
boxes = results[0].boxes
|
| 36 |
class_names = results[0].names
|
| 37 |
|
| 38 |
-
# Count bottles
|
| 39 |
num_present = 0
|
| 40 |
-
|
| 41 |
-
empty_bboxes = []
|
| 42 |
for box in boxes:
|
| 43 |
cls_id = int(box.cls.item())
|
| 44 |
cls_name = class_names[cls_id]
|
| 45 |
if cls_name == 'bottle':
|
| 46 |
num_present += 1
|
| 47 |
-
|
| 48 |
|
| 49 |
-
# Load image for annotation
|
| 50 |
img = Image.open(process_img_path)
|
|
|
|
| 51 |
draw = ImageDraw.Draw(img)
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
# Generate PDF
|
| 59 |
pdf = FPDF()
|
|
@@ -62,30 +88,45 @@ def process_input(input_file):
|
|
| 62 |
pdf.cell(200, 10, txt="Wine Shop Inventory Report", ln=1, align='C')
|
| 63 |
pdf.ln(5)
|
| 64 |
pdf.cell(200, 10, txt=f"Number of bottles detected: {num_present}", ln=1)
|
| 65 |
-
pdf.cell(200, 10, txt="
|
| 66 |
pdf.ln(10)
|
| 67 |
|
| 68 |
# Add annotated full image
|
| 69 |
with tempfile.NamedTemporaryFile(suffix='.png') as tmp_annotated:
|
| 70 |
img.save(tmp_annotated.name)
|
| 71 |
-
pdf.image(tmp_annotated.name, x=10, y=pdf.get_y(), w=180)
|
|
|
|
|
|
|
| 72 |
pdf.add_page()
|
| 73 |
-
pdf.cell(200, 10, txt="Detected Bottles", ln=1, align='C')
|
| 74 |
pdf.ln(5)
|
| 75 |
y_pos = pdf.get_y()
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
# Output PDF
|
| 91 |
pdf_bytes = io.BytesIO()
|
|
|
|
| 6 |
import tempfile
|
| 7 |
import cv2
|
| 8 |
import os
|
| 9 |
+
import torch.serialization
|
| 10 |
+
from ultralytics.nn.tasks import DetectionModel
|
| 11 |
+
import numpy as np
|
| 12 |
+
|
| 13 |
+
# Allowlist the DetectionModel class to fix UnpicklingError
|
| 14 |
+
torch.serialization.add_safe_globals([DetectionModel])
|
| 15 |
|
| 16 |
# Load the pre-trained YOLOv8n model
|
| 17 |
+
model = YOLO('model/yolov8n.pt')
|
| 18 |
model.overrides['conf'] = 0.25 # Confidence threshold
|
| 19 |
model.overrides['iou'] = 0.45 # IoU threshold
|
| 20 |
model.overrides['agnostic_nms'] = False
|
| 21 |
model.overrides['max_det'] = 1000
|
| 22 |
|
| 23 |
+
def detect_empty_spots(img_width, img_height, bottle_bboxes, min_gap=50):
|
| 24 |
+
"""Simple logic to infer empty spots based on gaps between bottles"""
|
| 25 |
+
empty_bboxes = []
|
| 26 |
+
# Assume shelf spans full image width; split into horizontal segments
|
| 27 |
+
x_coords = sorted([bbox[0] for bbox in bottle_bboxes] + [bbox[2] for bbox in bottle_bboxes])
|
| 28 |
+
x_coords = [0] + x_coords + [img_width]
|
| 29 |
+
|
| 30 |
+
for i in range(len(x_coords) - 1):
|
| 31 |
+
gap_start = x_coords[i]
|
| 32 |
+
gap_end = x_coords[i + 1]
|
| 33 |
+
if gap_end - gap_start > min_gap: # If gap is large enough, consider it an empty spot
|
| 34 |
+
empty_bboxes.append([gap_start, 0, gap_end, img_height]) # Full height for simplicity
|
| 35 |
+
|
| 36 |
+
return empty_bboxes
|
| 37 |
+
|
| 38 |
def process_input(input_file):
|
| 39 |
# Handle video: Extract middle frame
|
| 40 |
if input_file.lower().endswith(('.mp4', '.avi', '.mov')):
|
|
|
|
| 56 |
boxes = results[0].boxes
|
| 57 |
class_names = results[0].names
|
| 58 |
|
| 59 |
+
# Count bottles and collect their bounding boxes
|
| 60 |
num_present = 0
|
| 61 |
+
bottle_bboxes = []
|
|
|
|
| 62 |
for box in boxes:
|
| 63 |
cls_id = int(box.cls.item())
|
| 64 |
cls_name = class_names[cls_id]
|
| 65 |
if cls_name == 'bottle':
|
| 66 |
num_present += 1
|
| 67 |
+
bottle_bboxes.append(box.xyxy[0].cpu().numpy()) # [x1, y1, x2, y2]
|
| 68 |
|
| 69 |
+
# Load image for annotation
|
| 70 |
img = Image.open(process_img_path)
|
| 71 |
+
img_width, img_height = img.size
|
| 72 |
draw = ImageDraw.Draw(img)
|
| 73 |
+
|
| 74 |
+
# Draw bottle bounding boxes
|
| 75 |
+
for bbox in bottle_bboxes:
|
| 76 |
+
draw.rectangle(((bbox[0], bbox[1]), (bbox[2], bbox[3])), outline="green", width=3)
|
| 77 |
+
|
| 78 |
+
# Infer empty spots
|
| 79 |
+
empty_bboxes = detect_empty_spots(img_width, img_height, bottle_bboxes)
|
| 80 |
+
num_empty = len(empty_bboxes)
|
| 81 |
+
for bbox in empty_bboxes:
|
| 82 |
+
draw.rectangle(((bbox[0], bbox[1]), (bbox[2], bbox[3])), outline="red", width=3)
|
| 83 |
|
| 84 |
# Generate PDF
|
| 85 |
pdf = FPDF()
|
|
|
|
| 88 |
pdf.cell(200, 10, txt="Wine Shop Inventory Report", ln=1, align='C')
|
| 89 |
pdf.ln(5)
|
| 90 |
pdf.cell(200, 10, txt=f"Number of bottles detected: {num_present}", ln=1)
|
| 91 |
+
pdf.cell(200, 10, txt=f"Number of empty spots (inferred): {num_empty}", ln=1)
|
| 92 |
pdf.ln(10)
|
| 93 |
|
| 94 |
# Add annotated full image
|
| 95 |
with tempfile.NamedTemporaryFile(suffix='.png') as tmp_annotated:
|
| 96 |
img.save(tmp_annotated.name)
|
| 97 |
+
pdf.image(tmp_annotated.name, x=10, y=pdf.get_y(), w=180) # Full width for readability
|
| 98 |
+
|
| 99 |
+
# Add screenshots of bottles and empty spots
|
| 100 |
pdf.add_page()
|
| 101 |
+
pdf.cell(200, 10, txt="Detected Bottles and Empty Spots", ln=1, align='C')
|
| 102 |
pdf.ln(5)
|
| 103 |
y_pos = pdf.get_y()
|
| 104 |
+
|
| 105 |
+
# Bottles
|
| 106 |
+
for i, bbox in enumerate(bottle_bboxes):
|
| 107 |
+
cropped_img = img.crop((bbox[0], bbox[1], bbox[2], bbox[3]))
|
| 108 |
+
with tempfile.NamedTemporaryFile(suffix='.png') as tmp_crop:
|
| 109 |
+
cropped_img.save(tmp_crop.name)
|
| 110 |
+
pdf.image(tmp_crop.name, x=10, y=y_pos, w=90) # Medium size for clear view
|
| 111 |
+
y_pos += 100
|
| 112 |
+
pdf.cell(200, 10, txt=f"Bottle {i+1} (Location: x1={int(bbox[0])}, y1={int(bbox[1])}, x2={int(bbox[2])}, y2={int(bbox[3])})", ln=1)
|
| 113 |
+
pdf.ln(5)
|
| 114 |
+
if y_pos > 200:
|
| 115 |
+
pdf.add_page()
|
| 116 |
+
y_pos = 10
|
| 117 |
+
|
| 118 |
+
# Empty spots
|
| 119 |
+
for i, bbox in enumerate(empty_bboxes):
|
| 120 |
+
cropped_img = img.crop((bbox[0], bbox[1], bbox[2], bbox[3]))
|
| 121 |
+
with tempfile.NamedTemporaryFile(suffix='.png') as tmp_crop:
|
| 122 |
+
cropped_img.save(tmp_crop.name)
|
| 123 |
+
pdf.image(tmp_crop.name, x=10, y=y_pos, w=90)
|
| 124 |
+
y_pos += 100
|
| 125 |
+
pdf.cell(200, 10, txt=f"Empty Spot {i+1} (Location: x1={int(bbox[0])}, y1={int(bbox[1])}, x2={int(bbox[2])}, y2={int(bbox[3])})", ln=1)
|
| 126 |
+
pdf.ln(5)
|
| 127 |
+
if y_pos > 200:
|
| 128 |
+
pdf.add_page()
|
| 129 |
+
y_pos = 10
|
| 130 |
|
| 131 |
# Output PDF
|
| 132 |
pdf_bytes = io.BytesIO()
|