ggg4mless commited on
Commit
0906c14
·
verified ·
1 Parent(s): eb39f40
Files changed (4) hide show
  1. app.py +186 -0
  2. requirements.txt +7 -0
  3. v1.pt +3 -0
  4. v1l.pt +3 -0
app.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ from ultralytics import YOLO
5
+ from PIL import Image
6
+ import io
7
+ import os
8
+
9
+ # --- Load models ---
10
+ print("Loading models...")
11
+ try:
12
+ model_v1 = YOLO("v1.pt")
13
+ model_v1l = YOLO("v1l.pt")
14
+ print("Models loaded successfully!")
15
+ except Exception as e:
16
+ print(f"Error loading models: {e}")
17
+ raise
18
+
19
+
20
+ def ensemble_predict(image, conf_threshold=0.25):
21
+ """
22
+ Run ensemble inference on input image using v1 and v1l models
23
+ Returns annotated image and detection results
24
+ """
25
+ if image is None:
26
+ return None, "No image provided"
27
+
28
+ # Convert PIL Image to OpenCV format
29
+ if isinstance(image, Image.Image):
30
+ image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
31
+ else:
32
+ image_cv = image
33
+
34
+ # Predict dengan kedua model
35
+ results_v1 = model_v1.predict(
36
+ source=image_cv, conf=conf_threshold, save=False, verbose=False
37
+ )[0]
38
+ results_v1l = model_v1l.predict(
39
+ source=image_cv, conf=conf_threshold, save=False, verbose=False
40
+ )[0]
41
+
42
+ # Ensemble: voting class dengan weighted confidence
43
+ ensemble_results = []
44
+
45
+ for box_v1 in results_v1.boxes:
46
+ cls_v1 = int(box_v1.cls[0])
47
+ conf_v1 = float(box_v1.conf[0])
48
+ xyxy_v1 = box_v1.xyxy[0].cpu().numpy()
49
+
50
+ # Cek deteksi terkait di v1l (dengan IoU threshold)
51
+ best_match = None
52
+ best_iou = 0
53
+
54
+ for box_v1l in results_v1l.boxes:
55
+ xyxy_v1l = box_v1l.xyxy[0].cpu().numpy()
56
+
57
+ # Hitung IoU
58
+ x1_min, y1_min, x1_max, y1_max = xyxy_v1
59
+ x2_min, y2_min, x2_max, y2_max = xyxy_v1l
60
+
61
+ inter_x1 = max(x1_min, x2_min)
62
+ inter_y1 = max(y1_min, y2_min)
63
+ inter_x2 = min(x1_max, x2_max)
64
+ inter_y2 = min(y1_max, y2_max)
65
+
66
+ if inter_x2 > inter_x1 and inter_y2 > inter_y1:
67
+ inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1)
68
+ box1_area = (x1_max - x1_min) * (y1_max - y1_min)
69
+ box2_area = (x2_max - x2_min) * (y2_max - y2_min)
70
+ union_area = box1_area + box2_area - inter_area
71
+ iou = inter_area / union_area if union_area > 0 else 0
72
+
73
+ if iou > best_iou:
74
+ best_iou = iou
75
+ best_match = box_v1l
76
+
77
+ if best_iou > 0.3: # Jika ada overlap significant
78
+ cls_v1l = int(best_match.cls[0])
79
+ conf_v1l = float(best_match.conf[0])
80
+
81
+ # Voting: jika kedua model setuju class, gunakan weighted avg confidence
82
+ if cls_v1 == cls_v1l:
83
+ final_cls = cls_v1
84
+ final_conf = (conf_v1 + conf_v1l) / 2
85
+ else:
86
+ # Jika beda class, ambil yang confidence-nya lebih tinggi
87
+ if conf_v1 >= conf_v1l:
88
+ final_cls = cls_v1
89
+ final_conf = conf_v1
90
+ else:
91
+ final_cls = cls_v1l
92
+ final_conf = conf_v1l
93
+ else:
94
+ # Jika tidak ada match, gunakan v1 saja
95
+ final_cls = cls_v1
96
+ final_conf = conf_v1
97
+
98
+ ensemble_results.append((xyxy_v1, final_cls, final_conf))
99
+
100
+ # --- Create annotated image ---
101
+ annotated = image_cv.copy()
102
+
103
+ # Color mapping untuk setiap class
104
+ colors = [(0, 128, 0), (128, 0, 0), (0, 0, 128), (128, 128, 0)]
105
+
106
+ # Scale font based on image size
107
+ image_height = image_cv.shape[0]
108
+ base_font_scale = max(0.6, image_height / 1000.0) # Scale with image height
109
+ base_thickness = max(2, int(image_height / 500.0))
110
+
111
+ results_text = "=== Ensemble Prediction Results ===\n"
112
+
113
+ for xyxy, cls, conf in ensemble_results:
114
+ x1, y1, x2, y2 = xyxy.astype(int)
115
+ label = model_v1.names[cls]
116
+ text = f"{label} {conf*100:.1f}%"
117
+
118
+ results_text += f"Label: {label}\nConfidence: {conf:.3f}\nBBox: [{x1}, {y1}, {x2}, {y2}]\n\n"
119
+
120
+ # Pilih warna berdasarkan class
121
+ color = colors[cls % len(colors)]
122
+
123
+ # Draw bounding box with scaled thickness
124
+ cv2.rectangle(annotated, (x1, y1), (x2, y2), color, base_thickness)
125
+
126
+ # Draw label background with scaled font
127
+ font = cv2.FONT_HERSHEY_SIMPLEX
128
+ text_size = cv2.getTextSize(text, font, base_font_scale, base_thickness)[0]
129
+
130
+ cv2.rectangle(
131
+ annotated,
132
+ (x1, y1 - text_size[1] - 8),
133
+ (x1 + text_size[0] + 8, y1),
134
+ color,
135
+ -1,
136
+ )
137
+ cv2.putText(
138
+ annotated,
139
+ text,
140
+ (x1 + 4, y1 - 4),
141
+ font,
142
+ base_font_scale,
143
+ (255, 255, 255),
144
+ base_thickness,
145
+ )
146
+
147
+ if len(ensemble_results) == 0:
148
+ results_text += "No detections found."
149
+
150
+ # Convert BGR to RGB for display
151
+ annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
152
+
153
+ return annotated_rgb, results_text
154
+
155
+
156
+ # --- Create Gradio Interface ---
157
+ with gr.Blocks(title="YonkersNet") as demo:
158
+ gr.Markdown("# YonkersNet")
159
+ gr.Markdown(
160
+ "A Yolo model trained for detecting anime breast size, using ensemble method."
161
+ )
162
+
163
+ with gr.Row():
164
+ with gr.Column():
165
+ image_input = gr.Image(label="Upload Image", type="pil")
166
+ conf_slider = gr.Slider(
167
+ minimum=0.0,
168
+ maximum=1.0,
169
+ value=0.25,
170
+ step=0.05,
171
+ label="Confidence Threshold",
172
+ )
173
+ gr.Markdown("Sometimes at the rare moment the model isn't really confidence yet to predict. Lowering the threshold can be solve that.")
174
+ predict_btn = gr.Button("Run Detection", variant="primary")
175
+
176
+ with gr.Column():
177
+ image_output = gr.Image(label="Detection Result")
178
+ results_output = gr.Textbox(label="Detection Results", lines=10)
179
+
180
+ predict_btn.click(
181
+ fn=ensemble_predict, inputs=[image_input, conf_slider], outputs=[image_output, results_output]
182
+ )
183
+
184
+
185
+ if __name__ == "__main__":
186
+ demo.queue().launch(share=True)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ ultralytics>=8.0.0
2
+ gradio>=4.0.0
3
+ opencv-python>=4.8.0
4
+ numpy>=1.24.0
5
+ pillow>=10.0.0
6
+ torch>=2.0.0
7
+ torchvision>=0.15.0
v1.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4a8bfc0819093d2776f9045e6810ef163ca36f3d4bf9c631174db4d22d301e4f
3
+ size 6245354
v1l.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:90441396f0866b7ddeea70166c17368138a1294e7a19fbabcdb654f21e22f0f0
3
+ size 6252586