muhammadsalmanalfaridzi commited on
Commit
a05a995
·
verified ·
1 Parent(s): ab0680f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +173 -0
app.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from dotenv import load_dotenv
3
+ from roboflow import Roboflow
4
+ from PIL import Image
5
+ import tempfile
6
+ import os
7
+ import requests
8
+ import cv2
9
+ import numpy as np
10
+ import subprocess
11
+
12
+ # ========== Konfigurasi ==========
13
+ load_dotenv()
14
+
15
+ # Roboflow Config
16
+ rf_api_key = os.getenv("ROBOFLOW_API_KEY")
17
+ workspace = os.getenv("ROBOFLOW_WORKSPACE")
18
+ project_name = os.getenv("ROBOFLOW_PROJECT")
19
+ model_version = int(os.getenv("ROBOFLOW_MODEL_VERSION"))
20
+
21
+ # OWLv2 Config
22
+ OWLV2_API_KEY = os.getenv("COUNTGD_API_KEY")
23
+ OWLV2_PROMPTS = ["bottle", "tetra pak","cans", "carton drink"]
24
+
25
+ # Inisialisasi Model YOLO
26
+ rf = Roboflow(api_key=rf_api_key)
27
+ project = rf.workspace(workspace).project(project_name)
28
+ yolo_model = project.version(model_version).model
29
+
30
+ # ========== Fungsi Deteksi Kombinasi ==========
31
+ # Fungsi untuk deteksi dengan resize
32
+ def detect_combined(image):
33
+ # Simpan gambar input ke file sementara
34
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file:
35
+ image.save(temp_file, format="JPEG")
36
+ temp_path = temp_file.name
37
+
38
+ try:
39
+ # Resize gambar input menjadi 640x640
40
+ img = Image.open(temp_path)
41
+ img = img.resize((640, 640), Image.Resampling.LANCZOS) # Ganti ANTIALIAS dengan LANCZOS
42
+ img.save(temp_path, format="JPEG")
43
+
44
+ # ========== [1] YOLO: Deteksi Produk Nestlé (Per Class) ==========
45
+ yolo_pred = yolo_model.predict(temp_path, confidence=50, overlap=80).json()
46
+
47
+ # Hitung per class Nestlé dan simpan bounding box (format: (x_center, y_center, width, height))
48
+ nestle_class_count = {}
49
+ nestle_boxes = []
50
+ for pred in yolo_pred['predictions']:
51
+ class_name = pred['class']
52
+ nestle_class_count[class_name] = nestle_class_count.get(class_name, 0) + 1
53
+ nestle_boxes.append((pred['x'], pred['y'], pred['width'], pred['height']))
54
+
55
+ total_nestle = sum(nestle_class_count.values())
56
+
57
+ # ========== [2] OWLv2: Deteksi Kompetitor ==========
58
+
59
+ headers = {
60
+ "Authorization": "Basic " + OWLV2_API_KEY,
61
+ }
62
+ data = {
63
+ "prompts": OWLV2_PROMPTS,
64
+ "model": "owlv2",
65
+ "confidence": 0.25 # Set confidence threshold to 0.25
66
+ }
67
+ with open(temp_path, "rb") as f:
68
+ files = {"image": f}
69
+ response = requests.post("https://api.landing.ai/v1/tools/text-to-object-detection", files=files, data=data, headers=headers)
70
+ result = response.json()
71
+ owlv2_objects = result['data'][0] if 'data' in result else []
72
+
73
+ competitor_class_count = {}
74
+ competitor_boxes = []
75
+ for obj in owlv2_objects:
76
+ if 'bounding_box' in obj:
77
+ bbox = obj['bounding_box'] # Format: [x1, y1, x2, y2]
78
+ # Filter objek yang sudah terdeteksi oleh YOLO (Overlap detection)
79
+ if not is_overlap(bbox, nestle_boxes):
80
+ class_name = obj.get('label', 'unknown').strip().lower()
81
+ competitor_class_count[class_name] = competitor_class_count.get(class_name, 0) + 1
82
+ competitor_boxes.append({
83
+ "class": class_name,
84
+ "box": bbox,
85
+ "confidence": obj.get("score", 0)
86
+ })
87
+
88
+ total_competitor = sum(competitor_class_count.values())
89
+
90
+ # ========== [3] Format Output ==========
91
+ result_text = "Product Nestle\n\n"
92
+ for class_name, count in nestle_class_count.items():
93
+ result_text += f"{class_name}: {count}\n"
94
+ result_text += f"\nTotal Products Nestle: {total_nestle}\n\n"
95
+ if competitor_class_count:
96
+ result_text += f"Total Unclassified Products: {total_competitor}\n"
97
+ else:
98
+ result_text += "No Unclassified Products detected\n"
99
+
100
+ # ========== [4] Visualisasi ==========
101
+ img = cv2.imread(temp_path)
102
+
103
+ # Gambar bounding box untuk produk Nestlé (Hijau)
104
+ for pred in yolo_pred['predictions']:
105
+ x, y, w, h = pred['x'], pred['y'], pred['width'], pred['height']
106
+ cv2.rectangle(img, (int(x - w/2), int(y - h/2)), (int(x + w/2), int(y + h/2)), (0, 255, 0), 2)
107
+ cv2.putText(img, pred['class'], (int(x - w/2), int(y - h/2 - 10)),
108
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
109
+
110
+ # Gambar bounding box untuk kompetitor (Merah) dengan label 'unclassified' jika sesuai
111
+ for comp in competitor_boxes:
112
+ x1, y1, x2, y2 = comp['box']
113
+ unclassified_classes = ["cans"]
114
+ display_name = "unclassified" if any(cls in comp['class'].lower() for cls in unclassified_classes) else comp['class']
115
+ cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2)
116
+ cv2.putText(img, f"{display_name} {comp['confidence']:.2f}",
117
+ (int(x1), int(y1 - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
118
+
119
+ output_path = "/tmp/combined_output.jpg"
120
+ cv2.imwrite(output_path, img)
121
+
122
+ return output_path, result_text
123
+
124
+ except Exception as e:
125
+ return temp_path, f"Error: {str(e)}"
126
+ finally:
127
+ os.remove(temp_path)
128
+
129
+ def is_overlap(box1, boxes2, threshold=0.3):
130
+ """
131
+ Fungsi untuk mendeteksi overlap bounding box.
132
+ Parameter:
133
+ - box1: Bounding box pertama dengan format (x1, y1, x2, y2)
134
+ - boxes2: List bounding box lainnya dengan format (x_center, y_center, width, height)
135
+ """
136
+ x1_min, y1_min, x1_max, y1_max = box1
137
+ for b2 in boxes2:
138
+ x2, y2, w2, h2 = b2
139
+ x2_min = x2 - w2/2
140
+ x2_max = x2 + w2/2
141
+ y2_min = y2 - h2/2
142
+ y2_max = y2 + h2/2
143
+
144
+ dx = min(x1_max, x2_max) - max(x1_min, x2_min)
145
+ dy = min(y1_max, y2_max) - max(y1_min, y2_min)
146
+ if (dx >= 0) and (dy >= 0):
147
+ area_overlap = dx * dy
148
+ area_box1 = (x1_max - x1_min) * (y1_max - y1_min)
149
+ if area_overlap / area_box1 > threshold:
150
+ return True
151
+ return False
152
+
153
+ with gr.Blocks(theme=gr.themes.Base(primary_hue="teal", secondary_hue="teal", neutral_hue="slate")) as iface:
154
+ gr.Markdown("""<div style="text-align: center;"><h1>NESTLE - STOCK COUNTING</h1></div>""")
155
+ with gr.Row():
156
+ with gr.Column():
157
+ input_image = gr.Image(type="pil", label="Input Image")
158
+ with gr.Column():
159
+ output_image = gr.Image(label="Detect Object")
160
+ with gr.Column():
161
+ output_text = gr.Textbox(label="Counting Object")
162
+
163
+ # Tombol untuk memproses input
164
+ detect_button = gr.Button("Detect")
165
+
166
+ # Hubungkan tombol dengan fungsi deteksi
167
+ detect_button.click(
168
+ fn=detect_combined,
169
+ inputs=input_image,
170
+ outputs=[output_image, output_text]
171
+ )
172
+
173
+ iface.launch()