Clean up repository for Hugging Face deployment
Browse files**Removed files:**
- All backup apps: app_backup2.py, app_backup3.py, app_demo.py, app_full_system.py, shrimp_detection_app.py
- Test scripts: test_*.py, quick_test_*.py (25+ files)
- Analysis tools: analyze_*.py, optimize_*.py, check_*.py
- Training utilities: train_yolo.py, evaluate_yolo.py, labeling_tool.py
**Added:**
- examples/ folder with 5 example images for Gradio interface
- Updated app.py to use examples/ folder instead of data/yolo_dataset/
**Kept for deployment:**
- app.py (main application)
- requirements.txt
- test_visual_validation.py (required by app.py)
- test_quantitative_evaluation.py (required by app.py)
- interactive_validation.py (required by app.py)
- ground_truth.json (for labeling functionality)
- examples/ (5 demo images)
**Result:**
Clean, minimal Hugging Face deployment with only essential files
๐ค Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- analyze_fp_patterns.py +0 -284
- app.py +10 -10
- app_backup2.py +0 -299
- app_backup3.py +0 -1342
- app_demo.py +0 -163
- app_full_system.py +0 -1342
- check_250818_labeling.py +0 -83
- check_gt_split.py +0 -38
- check_labeling_quality.py +0 -181
- convert_gt_to_yolo.py +0 -185
- debug_roboflow_api.py +0 -38
- evaluate_yolo.py +0 -239
- examples/250818_01.jpg +3 -0
- examples/250818_03.jpg +3 -0
- examples/250818_04.jpg +3 -0
- examples/250818_05.jpg +3 -0
- examples/250818_10.jpg +3 -0
- labeling_tool.py +0 -650
- optimize_yolov8_confidence.py +0 -217
- optimize_yolov8_confidence_val_only.py +0 -204
- quick_test_roboflow.py +0 -89
- quick_test_save_result.py +0 -127
- shrimp_detection_app.py +0 -1150
- test_10_images.py +0 -205
- test_curl_roboflow.py +0 -82
- test_parameter_sweep.py +0 -219
- test_roboflow_model.py +0 -177
- test_roboflow_save_results.py +0 -183
- test_yolo_with_filter.py +0 -336
- test_yolov8_val_results.py +0 -234
- test_yolov8m_trained.py +0 -112
- test_yolov8m_unseen.py +0 -144
- test_yolov8m_with_filter.py +0 -367
- train_yolo.py +0 -127
- validate_ground_truth.py +0 -190
- visualize_yolo_dataset.py +0 -135
|
@@ -1,284 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
False Positive ํจํด ๋ถ์
|
| 4 |
-
GT ๋ฐ์ค vs ์ค๊ฒ์ถ ๋ฐ์ค์ ํน์ง ๋น๊ต
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import os
|
| 10 |
-
import json
|
| 11 |
-
import numpy as np
|
| 12 |
-
from PIL import Image
|
| 13 |
-
from test_visual_validation import (
|
| 14 |
-
load_rtdetr_model,
|
| 15 |
-
detect_with_rtdetr,
|
| 16 |
-
apply_universal_filter,
|
| 17 |
-
calculate_morphological_features,
|
| 18 |
-
calculate_visual_features
|
| 19 |
-
)
|
| 20 |
-
|
| 21 |
-
def calculate_iou(bbox1, bbox2):
|
| 22 |
-
"""IoU ๊ณ์ฐ"""
|
| 23 |
-
x1_min, y1_min, x1_max, y1_max = bbox1
|
| 24 |
-
x2_min, y2_min, x2_max, y2_max = bbox2
|
| 25 |
-
|
| 26 |
-
inter_x_min = max(x1_min, x2_min)
|
| 27 |
-
inter_y_min = max(y1_min, y2_min)
|
| 28 |
-
inter_x_max = min(x1_max, x2_max)
|
| 29 |
-
inter_y_max = min(y1_max, y2_max)
|
| 30 |
-
|
| 31 |
-
if inter_x_max < inter_x_min or inter_y_max < inter_y_min:
|
| 32 |
-
return 0.0
|
| 33 |
-
|
| 34 |
-
inter_area = (inter_x_max - inter_x_min) * (inter_y_max - inter_y_min)
|
| 35 |
-
bbox1_area = (x1_max - x1_min) * (y1_max - y1_min)
|
| 36 |
-
bbox2_area = (x2_max - x2_min) * (y2_max - y2_min)
|
| 37 |
-
union_area = bbox1_area + bbox2_area - inter_area
|
| 38 |
-
|
| 39 |
-
return inter_area / union_area if union_area > 0 else 0.0
|
| 40 |
-
|
| 41 |
-
def analyze_fp_patterns(test_image_dir, ground_truth_path, confidence=0.065):
|
| 42 |
-
"""False Positive ํจํด ๋ถ์"""
|
| 43 |
-
print("\n" + "="*80)
|
| 44 |
-
print("๐ False Positive ํจํด ๋ถ์")
|
| 45 |
-
print("="*80)
|
| 46 |
-
|
| 47 |
-
# Ground truth ๋ก๋
|
| 48 |
-
with open(ground_truth_path, 'r', encoding='utf-8') as f:
|
| 49 |
-
ground_truths = json.load(f)
|
| 50 |
-
|
| 51 |
-
# ๋ชจ๋ธ ๋ก๋
|
| 52 |
-
processor, model = load_rtdetr_model()
|
| 53 |
-
|
| 54 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 55 |
-
tp_features = [] # True Positive (GT์ ๋งค์นญ๋ ๋ฐ์ค)
|
| 56 |
-
fp_features = [] # False Positive (์ค๊ฒ์ถ)
|
| 57 |
-
gt_features = [] # Ground Truth ๋ฐ์ค
|
| 58 |
-
|
| 59 |
-
print(f"\n๋ถ์ ์ค...")
|
| 60 |
-
|
| 61 |
-
for filename, gt_list in ground_truths.items():
|
| 62 |
-
if not gt_list:
|
| 63 |
-
continue
|
| 64 |
-
|
| 65 |
-
# ์ด๋ฏธ์ง ๊ฒฝ๋ก
|
| 66 |
-
if 'folder' in gt_list[0]:
|
| 67 |
-
folder = gt_list[0]['folder']
|
| 68 |
-
img_path = os.path.join(test_image_dir, folder, filename)
|
| 69 |
-
else:
|
| 70 |
-
img_path = os.path.join(test_image_dir, filename)
|
| 71 |
-
|
| 72 |
-
if not os.path.exists(img_path):
|
| 73 |
-
continue
|
| 74 |
-
|
| 75 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 76 |
-
image = Image.open(img_path).convert('RGB')
|
| 77 |
-
|
| 78 |
-
# GT ํน์ง ์ถ์ถ
|
| 79 |
-
for gt in gt_list:
|
| 80 |
-
morph = calculate_morphological_features(gt['bbox'], image.size)
|
| 81 |
-
visual = calculate_visual_features(image, gt['bbox'])
|
| 82 |
-
|
| 83 |
-
gt_features.append({
|
| 84 |
-
'filename': filename,
|
| 85 |
-
'type': 'GT',
|
| 86 |
-
'bbox': gt['bbox'],
|
| 87 |
-
'morph': morph,
|
| 88 |
-
'visual': visual,
|
| 89 |
-
'confidence': gt['confidence']
|
| 90 |
-
})
|
| 91 |
-
|
| 92 |
-
# ๊ฒ์ถ
|
| 93 |
-
all_detections = detect_with_rtdetr(image, processor, model, confidence)
|
| 94 |
-
|
| 95 |
-
# ๊ฐ ๊ฒ์ถ ๋ฐ์ค ๋ถ์
|
| 96 |
-
for det in all_detections:
|
| 97 |
-
morph = calculate_morphological_features(det['bbox'], image.size)
|
| 98 |
-
visual = calculate_visual_features(image, det['bbox'])
|
| 99 |
-
|
| 100 |
-
# GT์ ๋งค์นญ ํ์ธ
|
| 101 |
-
matched = False
|
| 102 |
-
for gt in gt_list:
|
| 103 |
-
iou = calculate_iou(det['bbox'], gt['bbox'])
|
| 104 |
-
if iou >= 0.5:
|
| 105 |
-
matched = True
|
| 106 |
-
tp_features.append({
|
| 107 |
-
'filename': filename,
|
| 108 |
-
'type': 'TP',
|
| 109 |
-
'bbox': det['bbox'],
|
| 110 |
-
'morph': morph,
|
| 111 |
-
'visual': visual,
|
| 112 |
-
'confidence': det['confidence'],
|
| 113 |
-
'iou': iou
|
| 114 |
-
})
|
| 115 |
-
break
|
| 116 |
-
|
| 117 |
-
if not matched:
|
| 118 |
-
fp_features.append({
|
| 119 |
-
'filename': filename,
|
| 120 |
-
'type': 'FP',
|
| 121 |
-
'bbox': det['bbox'],
|
| 122 |
-
'morph': morph,
|
| 123 |
-
'visual': visual,
|
| 124 |
-
'confidence': det['confidence']
|
| 125 |
-
})
|
| 126 |
-
|
| 127 |
-
print(f"โ
๋ถ์ ์๋ฃ")
|
| 128 |
-
print(f" GT: {len(gt_features)}๊ฐ")
|
| 129 |
-
print(f" TP: {len(tp_features)}๊ฐ")
|
| 130 |
-
print(f" FP: {len(fp_features)}๊ฐ")
|
| 131 |
-
|
| 132 |
-
# ํต๊ณ ๋น๊ต
|
| 133 |
-
print("\n" + "="*80)
|
| 134 |
-
print("๐ ํน์ง ๋น๊ต: GT vs FP")
|
| 135 |
-
print("="*80)
|
| 136 |
-
|
| 137 |
-
# 1. ์ฅ๋จ์ถ๋น
|
| 138 |
-
gt_ratios = [f['morph']['aspect_ratio'] for f in gt_features]
|
| 139 |
-
fp_ratios = [f['morph']['aspect_ratio'] for f in fp_features]
|
| 140 |
-
|
| 141 |
-
print(f"\n1๏ธโฃ ์ฅ๋จ์ถ๋น (Aspect Ratio)")
|
| 142 |
-
print(f" GT: ํ๊ท ={np.mean(gt_ratios):.2f}, ๋ฒ์=[{np.min(gt_ratios):.2f}, {np.max(gt_ratios):.2f}], std={np.std(gt_ratios):.2f}")
|
| 143 |
-
print(f" FP: ํ๊ท ={np.mean(fp_ratios):.2f}, ๋ฒ์=[{np.min(fp_ratios):.2f}, {np.max(fp_ratios):.2f}], std={np.std(fp_ratios):.2f}")
|
| 144 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_ratios) - np.mean(fp_ratios)):.2f}")
|
| 145 |
-
|
| 146 |
-
# 2. Compactness
|
| 147 |
-
gt_compact = [f['morph']['compactness'] for f in gt_features]
|
| 148 |
-
fp_compact = [f['morph']['compactness'] for f in fp_features]
|
| 149 |
-
|
| 150 |
-
print(f"\n2๏ธโฃ Compactness (์ธ์ฅ๋)")
|
| 151 |
-
print(f" GT: ํ๊ท ={np.mean(gt_compact):.3f}, ๋ฒ์=[{np.min(gt_compact):.3f}, {np.max(gt_compact):.3f}], std={np.std(gt_compact):.3f}")
|
| 152 |
-
print(f" FP: ํ๊ท ={np.mean(fp_compact):.3f}, ๋ฒ์=[{np.min(fp_compact):.3f}, {np.max(fp_compact):.3f}], std={np.std(fp_compact):.3f}")
|
| 153 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_compact) - np.mean(fp_compact)):.3f}")
|
| 154 |
-
|
| 155 |
-
# 3. ๋ฉด์
|
| 156 |
-
gt_area = [f['morph']['width'] * f['morph']['height'] for f in gt_features]
|
| 157 |
-
fp_area = [f['morph']['width'] * f['morph']['height'] for f in fp_features]
|
| 158 |
-
|
| 159 |
-
print(f"\n3๏ธโฃ ๋ฉด์ (pxยฒ)")
|
| 160 |
-
print(f" GT: ํ๊ท ={np.mean(gt_area):.0f}, ๋ฒ์=[{np.min(gt_area):.0f}, {np.max(gt_area):.0f}], std={np.std(gt_area):.0f}")
|
| 161 |
-
print(f" FP: ํ๊ท ={np.mean(fp_area):.0f}, ๋ฒ์=[{np.min(fp_area):.0f}, {np.max(fp_area):.0f}], std={np.std(fp_area):.0f}")
|
| 162 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_area) - np.mean(fp_area)):.0f}")
|
| 163 |
-
|
| 164 |
-
# 4. Hue
|
| 165 |
-
gt_hue = [f['visual']['hue'] for f in gt_features]
|
| 166 |
-
fp_hue = [f['visual']['hue'] for f in fp_features]
|
| 167 |
-
|
| 168 |
-
print(f"\n4๏ธโฃ Hue (์์)")
|
| 169 |
-
print(f" GT: ํ๊ท ={np.mean(gt_hue):.1f}, ๋ฒ์=[{np.min(gt_hue):.1f}, {np.max(gt_hue):.1f}], std={np.std(gt_hue):.1f}")
|
| 170 |
-
print(f" FP: ํ๊ท ={np.mean(fp_hue):.1f}, ๋ฒ์=[{np.min(fp_hue):.1f}, {np.max(fp_hue):.1f}], std={np.std(fp_hue):.1f}")
|
| 171 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_hue) - np.mean(fp_hue)):.1f}")
|
| 172 |
-
|
| 173 |
-
# 5. Saturation
|
| 174 |
-
gt_sat = [f['visual']['saturation'] for f in gt_features]
|
| 175 |
-
fp_sat = [f['visual']['saturation'] for f in fp_features]
|
| 176 |
-
|
| 177 |
-
print(f"\n5๏ธโฃ Saturation (์ฑ๋)")
|
| 178 |
-
print(f" GT: ํ๊ท ={np.mean(gt_sat):.1f}, ๋ฒ์=[{np.min(gt_sat):.1f}, {np.max(gt_sat):.1f}], std={np.std(gt_sat):.1f}")
|
| 179 |
-
print(f" FP: ํ๊ท ={np.mean(fp_sat):.1f}, ๋ฒ์=[{np.min(fp_sat):.1f}, {np.max(fp_sat):.1f}], std={np.std(fp_sat):.1f}")
|
| 180 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_sat) - np.mean(fp_sat)):.1f}")
|
| 181 |
-
|
| 182 |
-
# 6. Color std
|
| 183 |
-
gt_cstd = [f['visual']['color_std'] for f in gt_features]
|
| 184 |
-
fp_cstd = [f['visual']['color_std'] for f in fp_features]
|
| 185 |
-
|
| 186 |
-
print(f"\n6๏ธโฃ Color Std (์์ ์ผ๊ด์ฑ)")
|
| 187 |
-
print(f" GT: ํ๊ท ={np.mean(gt_cstd):.1f}, ๋ฒ์=[{np.min(gt_cstd):.1f}, {np.max(gt_cstd):.1f}], std={np.std(gt_cstd):.1f}")
|
| 188 |
-
print(f" FP: ํ๊ท ={np.mean(fp_cstd):.1f}, ๋ฒ์=[{np.min(fp_cstd):.1f}, {np.max(fp_cstd):.1f}], std={np.std(fp_cstd):.1f}")
|
| 189 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_cstd) - np.mean(fp_cstd)):.1f}")
|
| 190 |
-
|
| 191 |
-
# 7. Confidence
|
| 192 |
-
gt_conf = [f['confidence'] for f in gt_features]
|
| 193 |
-
fp_conf = [f['confidence'] for f in fp_features]
|
| 194 |
-
|
| 195 |
-
print(f"\n7๏ธโฃ RT-DETR Confidence")
|
| 196 |
-
print(f" GT: ํ๊ท ={np.mean(gt_conf):.3f}, ๋ฒ์=[{np.min(gt_conf):.3f}, {np.max(gt_conf):.3f}], std={np.std(gt_conf):.3f}")
|
| 197 |
-
print(f" FP: ํ๊ท ={np.mean(fp_conf):.3f}, ๋ฒ์=[{np.min(fp_conf):.3f}, {np.max(fp_conf):.3f}], std={np.std(fp_conf):.3f}")
|
| 198 |
-
print(f" โ ์ฐจ์ด: {abs(np.mean(gt_conf) - np.mean(fp_conf)):.3f}")
|
| 199 |
-
|
| 200 |
-
# ๊ฐ์ฅ ์ฐจ์ด๋๋ ํน์ง ์ฐพ๊ธฐ
|
| 201 |
-
print("\n" + "="*80)
|
| 202 |
-
print("๐ฏ ํ๋ณ๋ ฅ ๋์ ํน์ง (GT vs FP ์ฐจ์ด)")
|
| 203 |
-
print("="*80)
|
| 204 |
-
|
| 205 |
-
differences = [
|
| 206 |
-
('์ฅ๋จ์ถ๋น', abs(np.mean(gt_ratios) - np.mean(fp_ratios)) / np.mean(gt_ratios)),
|
| 207 |
-
('Compactness', abs(np.mean(gt_compact) - np.mean(fp_compact)) / np.mean(gt_compact)),
|
| 208 |
-
('๋ฉด์ ', abs(np.mean(gt_area) - np.mean(fp_area)) / np.mean(gt_area)),
|
| 209 |
-
('Hue', abs(np.mean(gt_hue) - np.mean(fp_hue)) / max(np.mean(gt_hue), 1)),
|
| 210 |
-
('Saturation', abs(np.mean(gt_sat) - np.mean(fp_sat)) / max(np.mean(gt_sat), 1)),
|
| 211 |
-
('Color Std', abs(np.mean(gt_cstd) - np.mean(fp_cstd)) / max(np.mean(gt_cstd), 1)),
|
| 212 |
-
('Confidence', abs(np.mean(gt_conf) - np.mean(fp_conf)) / np.mean(gt_conf))
|
| 213 |
-
]
|
| 214 |
-
|
| 215 |
-
differences.sort(key=lambda x: x[1], reverse=True)
|
| 216 |
-
|
| 217 |
-
for i, (name, diff) in enumerate(differences, 1):
|
| 218 |
-
print(f"{i}. {name}: {diff*100:.1f}% ์ฐจ์ด")
|
| 219 |
-
|
| 220 |
-
# ์์ธ ๋ถํฌ
|
| 221 |
-
print("\n" + "="*80)
|
| 222 |
-
print("๐ FP ์์ธ ๋ถํฌ (์์ ์ค๊ฒ์ถ ํจํด)")
|
| 223 |
-
print("="*80)
|
| 224 |
-
|
| 225 |
-
# ์ฅ๋จ์ถ๋น ๋ถํฌ
|
| 226 |
-
fp_ratio_dist = {
|
| 227 |
-
'< 3': len([r for r in fp_ratios if r < 3]),
|
| 228 |
-
'3-4': len([r for r in fp_ratios if 3 <= r < 4]),
|
| 229 |
-
'4-9': len([r for r in fp_ratios if 4 <= r < 9]),
|
| 230 |
-
'9-15': len([r for r in fp_ratios if 9 <= r < 15]),
|
| 231 |
-
'>= 15': len([r for r in fp_ratios if r >= 15])
|
| 232 |
-
}
|
| 233 |
-
|
| 234 |
-
print(f"\nFP ์ฅ๋จ์ถ๋น ๋ถํฌ:")
|
| 235 |
-
for range_name, count in fp_ratio_dist.items():
|
| 236 |
-
print(f" {range_name}: {count}๊ฐ ({count/len(fp_ratios)*100:.1f}%)")
|
| 237 |
-
|
| 238 |
-
# ์ถ์ฒ์ฌํญ
|
| 239 |
-
print("\n" + "="*80)
|
| 240 |
-
print("๐ก ํํฐ ๊ฐ์ ์ ์")
|
| 241 |
-
print("="*80)
|
| 242 |
-
|
| 243 |
-
# ๊ฐ์ฅ ์ฐจ์ด๋๋ ํน์ง ๊ธฐ๋ฐ ์ ์
|
| 244 |
-
top_diff = differences[0]
|
| 245 |
-
if top_diff[0] == '์ฅ๋จ์ถ๋น':
|
| 246 |
-
print(f"1. ์ฅ๋จ์ถ๋น ํํฐ ๊ฐํ")
|
| 247 |
-
print(f" - GT ๋ฒ์: {np.min(gt_ratios):.2f}~{np.max(gt_ratios):.2f}")
|
| 248 |
-
print(f" - FP ํ๊ท : {np.mean(fp_ratios):.2f}")
|
| 249 |
-
if np.mean(fp_ratios) < np.mean(gt_ratios):
|
| 250 |
-
print(f" โ FP๊ฐ ๋ ๋ฅ๊ธ์. ํํ์ {np.percentile(gt_ratios, 10):.1f}๋ก ์ํฅ")
|
| 251 |
-
else:
|
| 252 |
-
print(f" โ FP๊ฐ ๋ ๊ฐ๋์. ์ํ์ {np.percentile(gt_ratios, 90):.1f}๋ก ํํฅ")
|
| 253 |
-
|
| 254 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 255 |
-
result = {
|
| 256 |
-
'gt_count': len(gt_features),
|
| 257 |
-
'tp_count': len(tp_features),
|
| 258 |
-
'fp_count': len(fp_features),
|
| 259 |
-
'feature_comparison': {
|
| 260 |
-
'aspect_ratio': {'gt': gt_ratios, 'fp': fp_ratios},
|
| 261 |
-
'compactness': {'gt': gt_compact, 'fp': fp_compact},
|
| 262 |
-
'area': {'gt': gt_area, 'fp': fp_area},
|
| 263 |
-
'hue': {'gt': gt_hue, 'fp': fp_hue},
|
| 264 |
-
'saturation': {'gt': gt_sat, 'fp': fp_sat},
|
| 265 |
-
'color_std': {'gt': gt_cstd, 'fp': fp_cstd},
|
| 266 |
-
'confidence': {'gt': gt_conf, 'fp': fp_conf}
|
| 267 |
-
},
|
| 268 |
-
'discriminative_features': differences
|
| 269 |
-
}
|
| 270 |
-
|
| 271 |
-
with open('fp_analysis_result.json', 'w', encoding='utf-8') as f:
|
| 272 |
-
# numpy array๋ฅผ list๋ก ๋ณํ
|
| 273 |
-
for key in result['feature_comparison']:
|
| 274 |
-
result['feature_comparison'][key]['gt'] = [float(x) for x in result['feature_comparison'][key]['gt']]
|
| 275 |
-
result['feature_comparison'][key]['fp'] = [float(x) for x in result['feature_comparison'][key]['fp']]
|
| 276 |
-
json.dump(result, f, ensure_ascii=False, indent=2)
|
| 277 |
-
|
| 278 |
-
print(f"\n๐ ๋ถ์ ๊ฒฐ๊ณผ ์ ์ฅ: fp_analysis_result.json")
|
| 279 |
-
|
| 280 |
-
if __name__ == "__main__":
|
| 281 |
-
TEST_DIR = r"data\ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 282 |
-
GT_PATH = "ground_truth.json"
|
| 283 |
-
|
| 284 |
-
analyze_fp_patterns(TEST_DIR, GT_PATH, confidence=0.065)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -940,11 +940,11 @@ with gr.Blocks(title="๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
", theme=gr.themes.Soft
|
|
| 940 |
|
| 941 |
# ์์ ์ด๋ฏธ์ง (๊ฒฐ๊ณผ ํ์ผ ์ ์ธ)
|
| 942 |
example_images = [
|
| 943 |
-
"
|
| 944 |
-
"
|
| 945 |
-
"
|
| 946 |
-
"
|
| 947 |
-
"
|
| 948 |
]
|
| 949 |
|
| 950 |
# ํ์ผ์ด ์กด์ฌํ๋ ๊ฒ๋ง ํํฐ๋ง
|
|
@@ -1099,11 +1099,11 @@ with gr.Blocks(title="๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
", theme=gr.themes.Soft
|
|
| 1099 |
|
| 1100 |
# ์์ ์ด๋ฏธ์ง
|
| 1101 |
example_images_demo = [
|
| 1102 |
-
"
|
| 1103 |
-
"
|
| 1104 |
-
"
|
| 1105 |
-
"
|
| 1106 |
-
"
|
| 1107 |
]
|
| 1108 |
|
| 1109 |
# ํ์ผ์ด ์กด์ฌํ๋ ๊ฒ๋ง ํํฐ๋ง
|
|
|
|
| 940 |
|
| 941 |
# ์์ ์ด๋ฏธ์ง (๊ฒฐ๊ณผ ํ์ผ ์ ์ธ)
|
| 942 |
example_images = [
|
| 943 |
+
"examples/250818_01.jpg",
|
| 944 |
+
"examples/250818_03.jpg",
|
| 945 |
+
"examples/250818_04.jpg",
|
| 946 |
+
"examples/250818_05.jpg",
|
| 947 |
+
"examples/250818_10.jpg",
|
| 948 |
]
|
| 949 |
|
| 950 |
# ํ์ผ์ด ์กด์ฌํ๋ ๊ฒ๋ง ํํฐ๋ง
|
|
|
|
| 1099 |
|
| 1100 |
# ์์ ์ด๋ฏธ์ง
|
| 1101 |
example_images_demo = [
|
| 1102 |
+
"examples/250818_01.jpg",
|
| 1103 |
+
"examples/250818_03.jpg",
|
| 1104 |
+
"examples/250818_04.jpg",
|
| 1105 |
+
"examples/250818_05.jpg",
|
| 1106 |
+
"examples/250818_10.jpg",
|
| 1107 |
]
|
| 1108 |
|
| 1109 |
# ํ์ผ์ด ์กด์ฌํ๋ ๊ฒ๋ง ํํฐ๋ง
|
|
@@ -1,299 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
๋ฐ์ด๋ฉ ๋ฐ์ค ๊ฒ์ถ ํ
์คํธ ํ์ด์ง
|
| 4 |
-
VIDraft/Shrimp ์ ์ฉ ๋ชจ๋ธ๊ณผ RT-DETR ๋ฒ์ฉ ๋ชจ๋ธ์ ๊ฒ์ถ ๊ฒฐ๊ณผ ๋น๊ต
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import gradio as gr
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
import os
|
| 12 |
-
|
| 13 |
-
# VIDraft/Shrimp ์ ์ฉ ๊ฒ์ถ๊ธฐ
|
| 14 |
-
try:
|
| 15 |
-
from inference_sdk import InferenceHTTPClient, InferenceConfiguration
|
| 16 |
-
|
| 17 |
-
vidraft_client = InferenceHTTPClient(
|
| 18 |
-
api_url="https://serverless.roboflow.com",
|
| 19 |
-
api_key="azcIL8KDJVJMYrsERzI7"
|
| 20 |
-
)
|
| 21 |
-
VIDRAFT_AVAILABLE = True
|
| 22 |
-
print("โ
VIDraft/Shrimp ๋ชจ๋ธ ์ฌ์ฉ ๊ฐ๋ฅ")
|
| 23 |
-
except Exception as e:
|
| 24 |
-
VIDRAFT_AVAILABLE = False
|
| 25 |
-
print(f"โ VIDraft/Shrimp ๋ชจ๋ธ ์ฌ์ฉ ๋ถ๊ฐ: {e}")
|
| 26 |
-
|
| 27 |
-
def detect_with_vidraft(image, confidence, iou_threshold):
|
| 28 |
-
"""VIDraft/Shrimp ์ ์ฉ ๋ชจ๋ธ๋ก ๊ฒ์ถ"""
|
| 29 |
-
if not VIDRAFT_AVAILABLE:
|
| 30 |
-
return None, "โ VIDraft/Shrimp ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค."
|
| 31 |
-
|
| 32 |
-
if image is None:
|
| 33 |
-
return None, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์."
|
| 34 |
-
|
| 35 |
-
try:
|
| 36 |
-
# ์์ ํ์ผ๋ก ์ ์ฅ
|
| 37 |
-
import tempfile
|
| 38 |
-
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
|
| 39 |
-
if image.mode != 'RGB':
|
| 40 |
-
image = image.convert('RGB')
|
| 41 |
-
image.save(tmp.name, quality=95)
|
| 42 |
-
tmp_path = tmp.name
|
| 43 |
-
|
| 44 |
-
# API ํธ์ถ with configuration
|
| 45 |
-
custom_config = InferenceConfiguration(
|
| 46 |
-
confidence_threshold=confidence,
|
| 47 |
-
iou_threshold=iou_threshold
|
| 48 |
-
)
|
| 49 |
-
|
| 50 |
-
with vidraft_client.use_configuration(custom_config):
|
| 51 |
-
result = vidraft_client.infer(tmp_path, model_id="shrimp-konvey/2")
|
| 52 |
-
|
| 53 |
-
# ์์ ํ์ผ ์ญ์
|
| 54 |
-
os.unlink(tmp_path)
|
| 55 |
-
|
| 56 |
-
# ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ
|
| 57 |
-
img = image.copy()
|
| 58 |
-
draw = ImageDraw.Draw(img)
|
| 59 |
-
|
| 60 |
-
try:
|
| 61 |
-
font = ImageFont.truetype("arial.ttf", 14)
|
| 62 |
-
except:
|
| 63 |
-
font = ImageFont.load_default()
|
| 64 |
-
|
| 65 |
-
predictions = result["predictions"]
|
| 66 |
-
detected_count = 0
|
| 67 |
-
|
| 68 |
-
for pred in predictions:
|
| 69 |
-
if pred["confidence"] < confidence:
|
| 70 |
-
continue
|
| 71 |
-
|
| 72 |
-
detected_count += 1
|
| 73 |
-
|
| 74 |
-
x = pred["x"]
|
| 75 |
-
y = pred["y"]
|
| 76 |
-
w = pred["width"]
|
| 77 |
-
h = pred["height"]
|
| 78 |
-
conf = pred["confidence"]
|
| 79 |
-
|
| 80 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค ์ขํ
|
| 81 |
-
x1 = x - w/2
|
| 82 |
-
y1 = y - h/2
|
| 83 |
-
x2 = x + w/2
|
| 84 |
-
y2 = y + h/2
|
| 85 |
-
|
| 86 |
-
# ์ ๋ขฐ๋์ ๋ฐ๋ผ ์์
|
| 87 |
-
if conf > 0.8:
|
| 88 |
-
color = "lime"
|
| 89 |
-
elif conf > 0.6:
|
| 90 |
-
color = "orange"
|
| 91 |
-
else:
|
| 92 |
-
color = "yellow"
|
| 93 |
-
|
| 94 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 95 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 96 |
-
|
| 97 |
-
# ๋ผ๋ฒจ
|
| 98 |
-
label = f"#{detected_count} {conf:.0%}"
|
| 99 |
-
bbox = draw.textbbox((x1, y1 - 25), label, font=font)
|
| 100 |
-
draw.rectangle(bbox, fill=color)
|
| 101 |
-
draw.text((x1, y1 - 25), label, fill="black", font=font)
|
| 102 |
-
|
| 103 |
-
# ํค๋
|
| 104 |
-
header = f"VIDraft/Shrimp: {detected_count}๋ง๋ฆฌ ๊ฒ์ถ"
|
| 105 |
-
header_bbox = draw.textbbox((10, 10), header, font=font)
|
| 106 |
-
draw.rectangle([5, 5, header_bbox[2]+10, header_bbox[3]+10], fill="black", outline="lime", width=2)
|
| 107 |
-
draw.text((10, 10), header, fill="lime", font=font)
|
| 108 |
-
|
| 109 |
-
info = f"""
|
| 110 |
-
### ๐ VIDraft/Shrimp ๋ชจ๋ธ ๊ฒ์ถ ๊ฒฐ๊ณผ
|
| 111 |
-
|
| 112 |
-
- **๊ฒ์ถ ์**: {detected_count}๋ง๋ฆฌ
|
| 113 |
-
- **์ ์ฒด ์์ธก**: {len(predictions)}๊ฐ
|
| 114 |
-
- **์ ๋ขฐ๋ ์๊ณ๊ฐ**: {confidence:.0%}
|
| 115 |
-
- **IoU ์๊ณ๊ฐ**: {iou_threshold:.0%}
|
| 116 |
-
- **์ฒ๋ฆฌ ์๊ฐ**: {result['time']:.2f}์ด
|
| 117 |
-
"""
|
| 118 |
-
|
| 119 |
-
return img, info
|
| 120 |
-
|
| 121 |
-
except Exception as e:
|
| 122 |
-
return None, f"โ ์ค๋ฅ ๋ฐ์: {str(e)}"
|
| 123 |
-
|
| 124 |
-
def detect_with_rtdetr(image, confidence):
|
| 125 |
-
"""RT-DETR๋ก ๊ฒ์ถ (๊ฐ๋จ ๋ฒ์ )"""
|
| 126 |
-
if image is None:
|
| 127 |
-
return None, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์."
|
| 128 |
-
|
| 129 |
-
try:
|
| 130 |
-
from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
|
| 131 |
-
import torch
|
| 132 |
-
|
| 133 |
-
# ๋ชจ๋ธ ๋ก๋ (์บ์ ์ฌ์ฉ)
|
| 134 |
-
if not hasattr(detect_with_rtdetr, 'model'):
|
| 135 |
-
print("๐ RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์ค...")
|
| 136 |
-
processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd_coco_o365")
|
| 137 |
-
model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd_coco_o365")
|
| 138 |
-
model.eval()
|
| 139 |
-
detect_with_rtdetr.processor = processor
|
| 140 |
-
detect_with_rtdetr.model = model
|
| 141 |
-
print("โ
RT-DETR ๋ก๋ฉ ์๋ฃ")
|
| 142 |
-
|
| 143 |
-
processor = detect_with_rtdetr.processor
|
| 144 |
-
model = detect_with_rtdetr.model
|
| 145 |
-
|
| 146 |
-
# ์ถ๋ก
|
| 147 |
-
inputs = processor(images=image, return_tensors="pt")
|
| 148 |
-
with torch.no_grad():
|
| 149 |
-
outputs = model(**inputs)
|
| 150 |
-
|
| 151 |
-
target_sizes = torch.tensor([image.size[::-1]])
|
| 152 |
-
results = processor.post_process_object_detection(
|
| 153 |
-
outputs,
|
| 154 |
-
target_sizes=target_sizes,
|
| 155 |
-
threshold=confidence
|
| 156 |
-
)[0]
|
| 157 |
-
|
| 158 |
-
# ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ
|
| 159 |
-
img = image.copy()
|
| 160 |
-
draw = ImageDraw.Draw(img)
|
| 161 |
-
|
| 162 |
-
try:
|
| 163 |
-
font = ImageFont.truetype("arial.ttf", 14)
|
| 164 |
-
except:
|
| 165 |
-
font = ImageFont.load_default()
|
| 166 |
-
|
| 167 |
-
detected_count = len(results["scores"])
|
| 168 |
-
|
| 169 |
-
for idx, (score, label, box) in enumerate(zip(results["scores"], results["labels"], results["boxes"]), 1):
|
| 170 |
-
x1, y1, x2, y2 = box.tolist()
|
| 171 |
-
conf = score.item()
|
| 172 |
-
|
| 173 |
-
# ์์
|
| 174 |
-
if conf > 0.8:
|
| 175 |
-
color = "cyan"
|
| 176 |
-
elif conf > 0.6:
|
| 177 |
-
color = "magenta"
|
| 178 |
-
else:
|
| 179 |
-
color = "yellow"
|
| 180 |
-
|
| 181 |
-
# ๋ฐ์ค
|
| 182 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 183 |
-
|
| 184 |
-
# ๋ผ๋ฒจ
|
| 185 |
-
label_text = f"#{idx} {conf:.0%}"
|
| 186 |
-
bbox = draw.textbbox((x1, y1 - 25), label_text, font=font)
|
| 187 |
-
draw.rectangle(bbox, fill=color)
|
| 188 |
-
draw.text((x1, y1 - 25), label_text, fill="black", font=font)
|
| 189 |
-
|
| 190 |
-
# ํค๋
|
| 191 |
-
header = f"RT-DETR: {detected_count}๊ฐ ๊ฒ์ถ"
|
| 192 |
-
header_bbox = draw.textbbox((10, 10), header, font=font)
|
| 193 |
-
draw.rectangle([5, 5, header_bbox[2]+10, header_bbox[3]+10], fill="black", outline="cyan", width=2)
|
| 194 |
-
draw.text((10, 10), header, fill="cyan", font=font)
|
| 195 |
-
|
| 196 |
-
info = f"""
|
| 197 |
-
### ๐ RT-DETR ๋ฒ์ฉ ๋ชจ๋ธ ๊ฒ์ถ ๊ฒฐ๊ณผ
|
| 198 |
-
|
| 199 |
-
- **๊ฒ์ถ ์**: {detected_count}๊ฐ
|
| 200 |
-
- **์ ๋ขฐ๋ ์๊ณ๊ฐ**: {confidence:.0%}
|
| 201 |
-
|
| 202 |
-
โ ๏ธ **์ฐธ๊ณ **: RT-DETR์ ๋ฒ์ฉ ๊ฐ์ฒด ๊ฒ์ถ ๋ชจ๋ธ์
๋๋ค. ์์ฐ ๊ฒ์ถ์ VIDraft/Shrimp ๋ชจ๋ธ์ ์ฌ์ฉํ์ธ์.
|
| 203 |
-
"""
|
| 204 |
-
|
| 205 |
-
return img, info
|
| 206 |
-
|
| 207 |
-
except Exception as e:
|
| 208 |
-
return None, f"โ ์ค๋ฅ ๋ฐ์: {str(e)}"
|
| 209 |
-
|
| 210 |
-
# Gradio ์ธํฐํ์ด์ค
|
| 211 |
-
with gr.Blocks(title="๐ฆ ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ฒ์ถ ํ
์คํธ", theme=gr.themes.Soft()) as demo:
|
| 212 |
-
|
| 213 |
-
gr.Markdown("""
|
| 214 |
-
# ๐ฆ ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ฒ์ถ ๋น๊ต ํ
์คํธ
|
| 215 |
-
|
| 216 |
-
VIDraft/Shrimp ์ ์ฉ ๋ชจ๋ธ๊ณผ RT-DETR ๋ฒ์ฉ ๋ชจ๋ธ์ ๊ฒ์ถ ์ฑ๋ฅ์ ๋น๊ตํฉ๋๋ค.
|
| 217 |
-
|
| 218 |
-
---
|
| 219 |
-
""")
|
| 220 |
-
|
| 221 |
-
with gr.Row():
|
| 222 |
-
with gr.Column():
|
| 223 |
-
input_image = gr.Image(label="์
๋ ฅ ์ด๋ฏธ์ง", type="pil")
|
| 224 |
-
confidence_slider = gr.Slider(
|
| 225 |
-
0.1, 0.9, 0.5,
|
| 226 |
-
label="์ ๋ขฐ๋ ์๊ณ๊ฐ (Confidence)",
|
| 227 |
-
info="๋ฎ์์๋ก ๋ ๋ง์ด ๊ฒ์ถ"
|
| 228 |
-
)
|
| 229 |
-
iou_slider = gr.Slider(
|
| 230 |
-
0.1, 0.9, 0.5,
|
| 231 |
-
label="IoU ์๊ณ๊ฐ (Overlap)",
|
| 232 |
-
info="๊ฒน์น๋ ๋ฐ์ค ์ ๊ฑฐ ๊ธฐ์ค (๋์์๋ก ๋ ๋ง์ด ์ ์ง)"
|
| 233 |
-
)
|
| 234 |
-
|
| 235 |
-
# ์์ ์ด๋ฏธ์ง
|
| 236 |
-
gr.Examples(
|
| 237 |
-
examples=[
|
| 238 |
-
["imgs/test_shrimp_tank.png", 0.1, 0.1],
|
| 239 |
-
],
|
| 240 |
-
inputs=[input_image, confidence_slider, iou_slider],
|
| 241 |
-
label="๐ท ์์ ์ด๋ฏธ์ง (ํด๋ฆญํ์ฌ ๋ฐ๋ก ํ
์คํธ)"
|
| 242 |
-
)
|
| 243 |
-
|
| 244 |
-
with gr.Column():
|
| 245 |
-
gr.Markdown("### ๐ ์ฌ์ฉ ๋ฐฉ๋ฒ")
|
| 246 |
-
gr.Markdown("""
|
| 247 |
-
1. **์๋ ์์ ์ด๋ฏธ์ง๋ฅผ ํด๋ฆญ**ํ๊ฑฐ๋ ์ง์ ์
๋ก๋
|
| 248 |
-
2. ํ๋ผ๋ฏธํฐ ์กฐ์ :
|
| 249 |
-
- **Confidence**: ๊ฒ์ถ ์ ๋ขฐ๋ (๋ฎ์์๋ก ๋ ๋ง์ด ๊ฒ์ถ)
|
| 250 |
-
- **IoU**: ์ค๋ณต ๋ฐ์ค ์ ๊ฑฐ ๊ธฐ์ค (NMS)
|
| 251 |
-
3. ๋ฒํผ ํด๋ฆญํ์ฌ ๊ฒ์ถ
|
| 252 |
-
|
| 253 |
-
**์์ ์๋ฏธ:**
|
| 254 |
-
- **๋
น์/์ฒญ๋ก**: ๋์ ์ ๋ขฐ๋ (>80%)
|
| 255 |
-
- **์ฃผํฉ/์ํ**: ์ค๊ฐ ์ ๋ขฐ๋ (60-80%)
|
| 256 |
-
- **๋
ธ๋์**: ๋ฎ์ ์ ๋ขฐ๋ (<60%)
|
| 257 |
-
""")
|
| 258 |
-
|
| 259 |
-
with gr.Tabs():
|
| 260 |
-
with gr.TabItem("๐ค VIDraft/Shrimp (์์ฐ ์ ์ฉ)"):
|
| 261 |
-
vidraft_btn = gr.Button("๐ VIDraft/Shrimp ๋ชจ๋ธ๋ก ๊ฒ์ถ", variant="primary", size="lg")
|
| 262 |
-
vidraft_result = gr.Image(label="๊ฒ์ถ ๊ฒฐ๊ณผ")
|
| 263 |
-
vidraft_info = gr.Markdown()
|
| 264 |
-
|
| 265 |
-
with gr.TabItem("๐ RT-DETR (๋ฒ์ฉ)"):
|
| 266 |
-
rtdetr_btn = gr.Button("๐ RT-DETR๋ก ๊ฒ์ถ", variant="secondary", size="lg")
|
| 267 |
-
rtdetr_result = gr.Image(label="๊ฒ์ถ ๊ฒฐ๊ณผ")
|
| 268 |
-
rtdetr_info = gr.Markdown()
|
| 269 |
-
|
| 270 |
-
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 271 |
-
vidraft_btn.click(
|
| 272 |
-
detect_with_vidraft,
|
| 273 |
-
[input_image, confidence_slider, iou_slider],
|
| 274 |
-
[vidraft_result, vidraft_info]
|
| 275 |
-
)
|
| 276 |
-
|
| 277 |
-
rtdetr_btn.click(
|
| 278 |
-
detect_with_rtdetr,
|
| 279 |
-
[input_image, confidence_slider],
|
| 280 |
-
[rtdetr_result, rtdetr_info]
|
| 281 |
-
)
|
| 282 |
-
|
| 283 |
-
gr.Markdown("""
|
| 284 |
-
---
|
| 285 |
-
|
| 286 |
-
### ๐ก ํ
|
| 287 |
-
|
| 288 |
-
- **์์กฐ ์ด๋ฏธ์ง**: VIDraft/Shrimp ๋ชจ๋ธ์ด ํจ์ฌ ์ ํํฉ๋๋ค (์์ฐ ์ ์ฉ ํ์ต)
|
| 289 |
-
- **์ธก์ ์ฉ ์ด๋ฏธ์ง**: RT-DETR ๋ฒ์ฉ ๋ชจ๋ธ์ ์ฌ์ฉํ์ธ์
|
| 290 |
-
- **๊ฒ์ถ ์ ๋จ**: ์ ๋ขฐ๋๋ฅผ ๋ฎ์ถฐ๋ณด์ธ์ (0.3~0.4)
|
| 291 |
-
- **์ค๋ณต ๋ฐ์ค**: IoU ์๊ณ๊ฐ์ ์กฐ์ ํ์ธ์ (VIDraft/Shrimp ๋ชจ๋ธ๋ง)
|
| 292 |
-
""")
|
| 293 |
-
|
| 294 |
-
if __name__ == "__main__":
|
| 295 |
-
demo.launch(
|
| 296 |
-
server_name="0.0.0.0",
|
| 297 |
-
server_port=7860, # Hugging Face default port
|
| 298 |
-
share=False
|
| 299 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,1342 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
๐ฆ ํฐ๋ค๋ฆฌ์์ฐ ๋ถ์ ์์คํ
- RT-DETR CPU ์ต์ ํ ๋ฒ์
|
| 3 |
-
์ค์ ๊ฐ์ฒด ๊ฒ์ถ + ์ฒด์ฅ/์ฒด์ค ์๋ ์ถ์
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import gradio as gr
|
| 7 |
-
import numpy as np
|
| 8 |
-
import pandas as pd
|
| 9 |
-
import plotly.graph_objects as go
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
from datetime import datetime
|
| 12 |
-
import torch
|
| 13 |
-
from transformers import (
|
| 14 |
-
RTDetrForObjectDetection,
|
| 15 |
-
RTDetrImageProcessor,
|
| 16 |
-
AutoImageProcessor,
|
| 17 |
-
AutoModelForDepthEstimation
|
| 18 |
-
)
|
| 19 |
-
import os
|
| 20 |
-
import warnings
|
| 21 |
-
warnings.filterwarnings('ignore')
|
| 22 |
-
|
| 23 |
-
# =====================
|
| 24 |
-
# ์ค์ธก ๋ฐ์ดํฐ (260๊ฐ ์ํ)
|
| 25 |
-
# =====================
|
| 26 |
-
REAL_DATA = [
|
| 27 |
-
{"length": 7.5, "weight": 2.0}, {"length": 7.7, "weight": 2.1},
|
| 28 |
-
{"length": 8.3, "weight": 2.7}, {"length": 8.4, "weight": 2.9},
|
| 29 |
-
{"length": 8.6, "weight": 3.1}, {"length": 8.7, "weight": 3.0},
|
| 30 |
-
{"length": 8.9, "weight": 3.2}, {"length": 9.1, "weight": 3.4},
|
| 31 |
-
{"length": 9.4, "weight": 4.0}, {"length": 9.7, "weight": 4.7},
|
| 32 |
-
{"length": 9.9, "weight": 4.7}, {"length": 10.0, "weight": 4.6},
|
| 33 |
-
{"length": 10.2, "weight": 5.5}, {"length": 10.3, "weight": 5.8},
|
| 34 |
-
{"length": 10.4, "weight": 5.5}, {"length": 10.7, "weight": 6.1},
|
| 35 |
-
{"length": 10.9, "weight": 6.0}, {"length": 11.0, "weight": 6.2},
|
| 36 |
-
{"length": 11.3, "weight": 5.8}, {"length": 11.4, "weight": 6.5},
|
| 37 |
-
{"length": 11.6, "weight": 7.5}, {"length": 11.7, "weight": 8.1},
|
| 38 |
-
{"length": 11.9, "weight": 9.4}, {"length": 12.0, "weight": 8.8},
|
| 39 |
-
{"length": 12.3, "weight": 10.2}, {"length": 12.5, "weight": 10.9},
|
| 40 |
-
{"length": 12.7, "weight": 10.1}, {"length": 12.9, "weight": 10.7},
|
| 41 |
-
{"length": 13.0, "weight": 10.7}, {"length": 13.1, "weight": 11.3},
|
| 42 |
-
]
|
| 43 |
-
|
| 44 |
-
# =====================
|
| 45 |
-
# ํ๊ท ๋ชจ๋ธ
|
| 46 |
-
# =====================
|
| 47 |
-
class RegressionModel:
|
| 48 |
-
def __init__(self):
|
| 49 |
-
self.a = 0.003454
|
| 50 |
-
self.b = 3.1298
|
| 51 |
-
self.r2 = 0.929
|
| 52 |
-
self.mape = 6.4
|
| 53 |
-
|
| 54 |
-
def estimate_weight(self, length_cm):
|
| 55 |
-
"""์ฒด์ฅ์ผ๋ก ์ฒด์ค ์ถ์ : W = a ร L^b"""
|
| 56 |
-
return self.a * (length_cm ** self.b)
|
| 57 |
-
|
| 58 |
-
def calculate_error(self, true_weight, pred_weight):
|
| 59 |
-
"""์ค์ฐจ์จ ๊ณ์ฐ"""
|
| 60 |
-
if true_weight == 0:
|
| 61 |
-
return 0
|
| 62 |
-
return abs(true_weight - pred_weight) / true_weight * 100
|
| 63 |
-
|
| 64 |
-
# =====================
|
| 65 |
-
# ๊น์ด ์ถ์ ๊ธฐ
|
| 66 |
-
# =====================
|
| 67 |
-
class DepthEstimator:
|
| 68 |
-
def __init__(self, model_name="depth-anything/Depth-Anything-V2-Small-hf"):
|
| 69 |
-
"""Depth-Anything V2 ๋ชจ๋ธ ์ด๊ธฐํ"""
|
| 70 |
-
print(f"๐ Loading Depth Estimation model: {model_name}")
|
| 71 |
-
|
| 72 |
-
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 73 |
-
|
| 74 |
-
try:
|
| 75 |
-
self.processor = AutoImageProcessor.from_pretrained(model_name)
|
| 76 |
-
self.model = AutoModelForDepthEstimation.from_pretrained(model_name)
|
| 77 |
-
self.model.to(self.device)
|
| 78 |
-
self.model.eval()
|
| 79 |
-
print("โ
Depth model loaded successfully!")
|
| 80 |
-
self.enabled = True
|
| 81 |
-
except Exception as e:
|
| 82 |
-
print(f"โ ๏ธ Depth model loading failed: {e}")
|
| 83 |
-
print("๐ Running without depth correction")
|
| 84 |
-
self.enabled = False
|
| 85 |
-
|
| 86 |
-
@torch.no_grad()
|
| 87 |
-
def estimate_depth(self, image):
|
| 88 |
-
"""์ด๋ฏธ์ง์์ ๊น์ด ๋งต ์ถ์ """
|
| 89 |
-
if not self.enabled or image is None:
|
| 90 |
-
return None
|
| 91 |
-
|
| 92 |
-
# ์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ
|
| 93 |
-
inputs = self.processor(images=image, return_tensors="pt")
|
| 94 |
-
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 95 |
-
|
| 96 |
-
# ๊น์ด ์ถ์
|
| 97 |
-
outputs = self.model(**inputs)
|
| 98 |
-
predicted_depth = outputs.predicted_depth
|
| 99 |
-
|
| 100 |
-
# ์๋ณธ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ก ๋ฆฌ์ํ๋ง
|
| 101 |
-
depth_map = torch.nn.functional.interpolate(
|
| 102 |
-
predicted_depth.unsqueeze(1),
|
| 103 |
-
size=image.size[::-1], # (height, width)
|
| 104 |
-
mode="bicubic",
|
| 105 |
-
align_corners=False,
|
| 106 |
-
).squeeze().cpu().numpy()
|
| 107 |
-
|
| 108 |
-
# ์ ๊ทํ (0~1 ๋ฒ์)
|
| 109 |
-
depth_min = depth_map.min()
|
| 110 |
-
depth_max = depth_map.max()
|
| 111 |
-
depth_normalized = (depth_map - depth_min) / (depth_max - depth_min + 1e-8)
|
| 112 |
-
|
| 113 |
-
return depth_normalized
|
| 114 |
-
|
| 115 |
-
def get_depth_at_bbox(self, depth_map, bbox):
|
| 116 |
-
"""bbox ์ค์ฌ์ ์ ๊น์ด ๊ฐ ์ถ์ถ"""
|
| 117 |
-
if depth_map is None:
|
| 118 |
-
return 1.0 # ๊ธฐ๋ณธ๊ฐ
|
| 119 |
-
|
| 120 |
-
x1, y1, x2, y2 = bbox
|
| 121 |
-
center_x = int((x1 + x2) / 2)
|
| 122 |
-
center_y = int((y1 + y2) / 2)
|
| 123 |
-
|
| 124 |
-
# ๋ฒ์ ์ฒดํฌ
|
| 125 |
-
h, w = depth_map.shape
|
| 126 |
-
center_x = min(max(0, center_x), w - 1)
|
| 127 |
-
center_y = min(max(0, center_y), h - 1)
|
| 128 |
-
|
| 129 |
-
return depth_map[center_y, center_x]
|
| 130 |
-
|
| 131 |
-
def visualize_depth(self, depth_map):
|
| 132 |
-
"""๊น์ด ๋งต ์๊ฐํ"""
|
| 133 |
-
if depth_map is None:
|
| 134 |
-
return None
|
| 135 |
-
|
| 136 |
-
# ๊น์ด ๋งต์ ์ปฌ๋ฌ๋งต์ผ๋ก ๋ณํ
|
| 137 |
-
import matplotlib.cm as cm
|
| 138 |
-
colormap = cm.get_cmap('viridis')
|
| 139 |
-
depth_colored = (colormap(depth_map)[:, :, :3] * 255).astype(np.uint8)
|
| 140 |
-
|
| 141 |
-
return Image.fromarray(depth_colored)
|
| 142 |
-
|
| 143 |
-
# =====================
|
| 144 |
-
# RT-DETR ๊ฒ์ถ๏ฟฝ๏ฟฝ
|
| 145 |
-
# =====================
|
| 146 |
-
class RTDetrDetector:
|
| 147 |
-
def __init__(self, model_name="PekingU/rtdetr_r50vd_coco_o365"):
|
| 148 |
-
"""RT-DETR ๋ชจ๋ธ ์ด๊ธฐํ"""
|
| 149 |
-
print(f"๐ Loading RT-DETR model: {model_name}")
|
| 150 |
-
|
| 151 |
-
# CPU ์ต์ ํ ์ค์
|
| 152 |
-
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 153 |
-
print(f"๐ฑ Using device: {self.device}")
|
| 154 |
-
|
| 155 |
-
try:
|
| 156 |
-
# ๋ชจ๋ธ ๋ฐ ํ๋ก์ธ์ ๋ก๋ฉ
|
| 157 |
-
self.processor = RTDetrImageProcessor.from_pretrained(model_name)
|
| 158 |
-
self.model = RTDetrForObjectDetection.from_pretrained(model_name)
|
| 159 |
-
self.model.to(self.device)
|
| 160 |
-
self.model.eval() # ํ๊ฐ ๋ชจ๋
|
| 161 |
-
|
| 162 |
-
print("โ
Model loaded successfully!")
|
| 163 |
-
except Exception as e:
|
| 164 |
-
print(f"โ Model loading failed: {e}")
|
| 165 |
-
raise
|
| 166 |
-
|
| 167 |
-
self.regression_model = RegressionModel()
|
| 168 |
-
|
| 169 |
-
# ๊น์ด ์ถ์ ๊ธฐ ์ด๊ธฐํ
|
| 170 |
-
try:
|
| 171 |
-
self.depth_estimator = DepthEstimator()
|
| 172 |
-
except Exception as e:
|
| 173 |
-
print(f"โ ๏ธ Depth estimator initialization failed: {e}")
|
| 174 |
-
self.depth_estimator = None
|
| 175 |
-
|
| 176 |
-
# ์ฐธ์กฐ ์ค์ผ์ผ: ํฝ์
ํฌ๊ธฐ๋ฅผ ์ค์ cm๋ก ๋ณํ
|
| 177 |
-
# ์: 100ํฝ์
= 10cm (์ด๋ฏธ์ง์ ๋ฐ๋ผ ์กฐ์ ํ์)
|
| 178 |
-
self.pixel_to_cm_ratio = 0.1 # ๊ธฐ๋ณธ๊ฐ
|
| 179 |
-
|
| 180 |
-
# ๊น์ด ๋ณด์ ํ์ฑํ ํ๋๊ทธ
|
| 181 |
-
self.depth_correction_enabled = True
|
| 182 |
-
|
| 183 |
-
# ๋ง์ง๋ง ๊น์ด ๋งต ์บ์ฑ (UI ํ์์ฉ)
|
| 184 |
-
self.last_depth_map = None
|
| 185 |
-
|
| 186 |
-
def set_scale(self, pixel_length, actual_cm):
|
| 187 |
-
"""์ค์ผ์ผ ์ค์ (๋ณด์ ์ฉ)"""
|
| 188 |
-
self.pixel_to_cm_ratio = actual_cm / pixel_length
|
| 189 |
-
print(f"๐ Scale updated: {pixel_length}px = {actual_cm}cm")
|
| 190 |
-
|
| 191 |
-
@torch.no_grad() # CPU ์ต์ ํ: gradient ๊ณ์ฐ ๋นํ์ฑํ
|
| 192 |
-
def detect(self, image, confidence_threshold=0.5):
|
| 193 |
-
"""๊ฐ์ฒด ๊ฒ์ถ ์ํ"""
|
| 194 |
-
|
| 195 |
-
if image is None:
|
| 196 |
-
return []
|
| 197 |
-
|
| 198 |
-
# ๊น์ด ๋งต ์์ฑ (์๊ทผ ๋ณด์ ์ฉ)
|
| 199 |
-
depth_map = None
|
| 200 |
-
if self.depth_correction_enabled and self.depth_estimator and self.depth_estimator.enabled:
|
| 201 |
-
print("๐ Estimating depth map for perspective correction...")
|
| 202 |
-
depth_map = self.depth_estimator.estimate_depth(image)
|
| 203 |
-
self.last_depth_map = depth_map
|
| 204 |
-
|
| 205 |
-
# ์ฐธ์กฐ ๊น์ด (์ด๋ฏธ์ง ์ค์ฌ์ ํ๊ท ๊น์ด)
|
| 206 |
-
h, w = depth_map.shape
|
| 207 |
-
center_region = depth_map[h//4:3*h//4, w//4:3*w//4]
|
| 208 |
-
self.reference_depth = np.median(center_region)
|
| 209 |
-
print(f"๐ Reference depth: {self.reference_depth:.3f}")
|
| 210 |
-
|
| 211 |
-
# ์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ
|
| 212 |
-
inputs = self.processor(images=image, return_tensors="pt")
|
| 213 |
-
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 214 |
-
|
| 215 |
-
# ์ถ๋ก
|
| 216 |
-
outputs = self.model(**inputs)
|
| 217 |
-
|
| 218 |
-
# ๊ฒฐ๊ณผ ํ์ฒ๋ฆฌ
|
| 219 |
-
target_sizes = torch.tensor([image.size[::-1]]) # (height, width)
|
| 220 |
-
results = self.processor.post_process_object_detection(
|
| 221 |
-
outputs,
|
| 222 |
-
target_sizes=target_sizes,
|
| 223 |
-
threshold=confidence_threshold
|
| 224 |
-
)[0]
|
| 225 |
-
|
| 226 |
-
# ๊ฒ์ถ ๊ฒฐ๊ณผ ํ์ฑ
|
| 227 |
-
detections = []
|
| 228 |
-
|
| 229 |
-
for idx, (score, label, box) in enumerate(zip(
|
| 230 |
-
results["scores"],
|
| 231 |
-
results["labels"],
|
| 232 |
-
results["boxes"]
|
| 233 |
-
)):
|
| 234 |
-
# COCO ํด๋์ค ํํฐ๋ง (ํ์์)
|
| 235 |
-
# ์์ฐ ์ ์ฉ ๋ชจ๋ธ์ด ์๋๋ฏ๋ก ๋ชจ๋ ๊ฐ์ฒด ๊ฒ์ถ
|
| 236 |
-
# label 1 = "person", 16 = "bird", 17 = "cat" ๋ฑ
|
| 237 |
-
# ์ผ๋จ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๊ฒ์ถํ๋, ํฅํ fine-tuning ์ ์์ฐ๋ง ๊ฒ์ถ
|
| 238 |
-
|
| 239 |
-
x1, y1, x2, y2 = box.tolist()
|
| 240 |
-
bbox_width = x2 - x1
|
| 241 |
-
bbox_height = y2 - y1
|
| 242 |
-
|
| 243 |
-
# ๊น์ด ๊ธฐ๋ฐ ์ค์ผ์ผ ๋ณด์
|
| 244 |
-
depth_corrected_ratio = self.pixel_to_cm_ratio
|
| 245 |
-
|
| 246 |
-
if depth_map is not None:
|
| 247 |
-
# bbox ์ค์ฌ์ ์ ๊น์ด ๊ฐ ์ถ์ถ
|
| 248 |
-
object_depth = self.depth_estimator.get_depth_at_bbox(depth_map, [x1, y1, x2, y2])
|
| 249 |
-
|
| 250 |
-
# ๊น์ด ๋น์จ ๊ณ์ฐ (์ฐธ์กฐ ๊น์ด ๋๋น)
|
| 251 |
-
# ๊น์ด ๊ฐ์ด ํด์๋ก ๋จผ ๊ฑฐ๋ฆฌ โ ์ค์ ํฌ๊ธฐ๊ฐ ๋ ํผ
|
| 252 |
-
# Depth-Anything์์: ์์ ๊ฐ = ๊ฐ๊น์, ํฐ ๊ฐ = ๋ฉ์
|
| 253 |
-
depth_ratio = object_depth / (self.reference_depth + 1e-8)
|
| 254 |
-
|
| 255 |
-
# ์ค์ผ์ผ ๋ณด์ (์๊ทผ ํจ๊ณผ ๋ณด์ )
|
| 256 |
-
# ๋จผ ๋ฌผ์ฒด(ํฐ depth)๋ ํฝ์
์ด ์์ ๋ณด์ด๋ฏ๋ก ๋ณด์ ๊ณ์๋ฅผ ํฌ๊ฒ
|
| 257 |
-
depth_corrected_ratio = self.pixel_to_cm_ratio * depth_ratio
|
| 258 |
-
|
| 259 |
-
print(f" Object #{idx+1}: depth={object_depth:.3f}, ratio={depth_ratio:.3f}, corrected_scale={depth_corrected_ratio:.4f}")
|
| 260 |
-
|
| 261 |
-
# ์ฒด์ฅ ์ถ์ : bbox์ ๊ธด ๋ณ์ ์ฒด์ฅ์ผ๋ก ๊ฐ์ฃผ
|
| 262 |
-
length_pixels = max(bbox_width, bbox_height)
|
| 263 |
-
length_cm = length_pixels * depth_corrected_ratio
|
| 264 |
-
|
| 265 |
-
# ์ฒด์ค ์ถ์
|
| 266 |
-
pred_weight = self.regression_model.estimate_weight(length_cm)
|
| 267 |
-
|
| 268 |
-
detections.append({
|
| 269 |
-
"id": idx + 1,
|
| 270 |
-
"bbox": [x1, y1, x2, y2],
|
| 271 |
-
"length": round(length_cm, 1),
|
| 272 |
-
"pred_weight": round(pred_weight, 2),
|
| 273 |
-
"confidence": round(score.item(), 2),
|
| 274 |
-
"label": label.item()
|
| 275 |
-
})
|
| 276 |
-
|
| 277 |
-
return detections
|
| 278 |
-
|
| 279 |
-
def visualize(self, image, detections):
|
| 280 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ ์๊ฐํ"""
|
| 281 |
-
if image is None:
|
| 282 |
-
return None
|
| 283 |
-
|
| 284 |
-
img = image.copy()
|
| 285 |
-
draw = ImageDraw.Draw(img)
|
| 286 |
-
|
| 287 |
-
# ํฐํธ ์ค์ (๊ธฐ๋ณธ ํฐํธ ์ฌ์ฉ)
|
| 288 |
-
try:
|
| 289 |
-
font = ImageFont.truetype("arial.ttf", 12)
|
| 290 |
-
except:
|
| 291 |
-
font = ImageFont.load_default()
|
| 292 |
-
|
| 293 |
-
for det in detections:
|
| 294 |
-
x1, y1, x2, y2 = det["bbox"]
|
| 295 |
-
|
| 296 |
-
# ๊ณ ์ ์์ (๋
น์)
|
| 297 |
-
color = "lime"
|
| 298 |
-
|
| 299 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 300 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 301 |
-
|
| 302 |
-
# ๋ผ๋ฒจ
|
| 303 |
-
label = f"#{det['id']} {det['length']}cm {det['pred_weight']}g ({det['confidence']:.0%})"
|
| 304 |
-
|
| 305 |
-
# ๋ฐฐ๊ฒฝ ๋ฐ์ค
|
| 306 |
-
bbox = draw.textbbox((x1, y1 - 20), label, font=font)
|
| 307 |
-
draw.rectangle(bbox, fill=color)
|
| 308 |
-
draw.text((x1, y1 - 20), label, fill="white", font=font)
|
| 309 |
-
|
| 310 |
-
return img
|
| 311 |
-
|
| 312 |
-
def visualize_with_groundtruth(self, image, detection, true_length, true_weight, sample_id):
|
| 313 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ์ ์ค์ธก๊ฐ์ ํจ๊ป ์๊ฐํ"""
|
| 314 |
-
if image is None:
|
| 315 |
-
return None
|
| 316 |
-
|
| 317 |
-
img = image.copy()
|
| 318 |
-
draw = ImageDraw.Draw(img)
|
| 319 |
-
|
| 320 |
-
# ํฐํธ ์ค์
|
| 321 |
-
try:
|
| 322 |
-
font_large = ImageFont.truetype("arial.ttf", 16)
|
| 323 |
-
font_small = ImageFont.truetype("arial.ttf", 12)
|
| 324 |
-
except:
|
| 325 |
-
font_large = ImageFont.load_default()
|
| 326 |
-
font_small = ImageFont.load_default()
|
| 327 |
-
|
| 328 |
-
# Bounding box ๊ทธ๋ฆฌ๊ธฐ
|
| 329 |
-
x1, y1, x2, y2 = detection["bbox"]
|
| 330 |
-
|
| 331 |
-
# ์ค์ฐจ์จ๋ก ์์ ๊ฒฐ์
|
| 332 |
-
error_weight = abs(detection["pred_weight"] - true_weight) / true_weight * 100
|
| 333 |
-
if error_weight < 10:
|
| 334 |
-
color = "lime" # ๋
น์: ์ฐ์
|
| 335 |
-
elif error_weight < 25:
|
| 336 |
-
color = "orange" # ์ฃผํฉ: ์ํธ
|
| 337 |
-
else:
|
| 338 |
-
color = "red" # ๋นจ๊ฐ: ๊ฐ์ ํ์
|
| 339 |
-
|
| 340 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 341 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=4)
|
| 342 |
-
|
| 343 |
-
# ์์ธก๊ฐ ๋ผ๋ฒจ (bbox ์)
|
| 344 |
-
pred_label = f"์์ธก: {detection['length']:.1f}cm / {detection['pred_weight']:.1f}g"
|
| 345 |
-
bbox_pred = draw.textbbox((x1, y1 - 40), pred_label, font=font_small)
|
| 346 |
-
draw.rectangle(bbox_pred, fill=color)
|
| 347 |
-
draw.text((x1, y1 - 40), pred_label, fill="white", font=font_small)
|
| 348 |
-
|
| 349 |
-
# ์ค์ธก๊ฐ ๋ผ๋ฒจ (bbox ์, ์์ธก๊ฐ ์๋)
|
| 350 |
-
true_label = f"์ค์ : {true_length:.1f}cm / {true_weight:.1f}g"
|
| 351 |
-
bbox_true = draw.textbbox((x1, y1 - 20), true_label, font=font_small)
|
| 352 |
-
draw.rectangle(bbox_true, fill="blue")
|
| 353 |
-
draw.text((x1, y1 - 20), true_label, fill="white", font=font_small)
|
| 354 |
-
|
| 355 |
-
# ์ด๋ฏธ์ง ์๋จ์ ์ํ ID์ ์ค์ฐจ์จ ํ์
|
| 356 |
-
header = f"์ํ #{sample_id} | ์ฒด์ฅ ์ค์ฐจ: {abs(detection['length']-true_length)/true_length*100:.1f}% | ์ฒด์ค ์ค์ฐจ: {error_weight:.1f}%"
|
| 357 |
-
header_bbox = draw.textbbox((10, 10), header, font=font_large)
|
| 358 |
-
draw.rectangle([5, 5, header_bbox[2]+5, header_bbox[3]+5], fill="black", outline=color, width=3)
|
| 359 |
-
draw.text((10, 10), header, fill=color, font=font_large)
|
| 360 |
-
|
| 361 |
-
return img
|
| 362 |
-
|
| 363 |
-
# =====================
|
| 364 |
-
# Roboflow ๊ฒ์ถ๊ธฐ
|
| 365 |
-
# =====================
|
| 366 |
-
class RoboflowDetector:
|
| 367 |
-
"""Roboflow ์์ฐ ์ ์ฉ ๊ฒ์ถ๊ธฐ"""
|
| 368 |
-
|
| 369 |
-
def __init__(self, api_key="azcIL8KDJVJMYrsERzI7", model_id="shrimp-konvey/2"):
|
| 370 |
-
"""Roboflow ๋ชจ๋ธ ์ด๊ธฐํ"""
|
| 371 |
-
print(f"๐ Loading Roboflow model: {model_id}")
|
| 372 |
-
|
| 373 |
-
try:
|
| 374 |
-
from inference_sdk import InferenceHTTPClient, InferenceConfiguration
|
| 375 |
-
|
| 376 |
-
self.client = InferenceHTTPClient(
|
| 377 |
-
api_url="https://serverless.roboflow.com",
|
| 378 |
-
api_key=api_key
|
| 379 |
-
)
|
| 380 |
-
self.model_id = model_id
|
| 381 |
-
self.regression_model = RegressionModel()
|
| 382 |
-
|
| 383 |
-
# ์ค์ผ์ผ ์ค์
|
| 384 |
-
self.pixel_to_cm_ratio = 0.01 # ๊ธฐ๋ณธ๊ฐ (์ถํ ์กฐ์ )
|
| 385 |
-
|
| 386 |
-
# ๊ธฐ๋ณธ ์ค์ ๊ฐ
|
| 387 |
-
self.iou_threshold = 0.5
|
| 388 |
-
|
| 389 |
-
print("โ
Roboflow model loaded successfully!")
|
| 390 |
-
except Exception as e:
|
| 391 |
-
print(f"โ Roboflow model loading failed: {e}")
|
| 392 |
-
raise
|
| 393 |
-
|
| 394 |
-
def set_scale(self, pixel_length, actual_cm):
|
| 395 |
-
"""์ค์ผ์ผ ์ค์ """
|
| 396 |
-
self.pixel_to_cm_ratio = actual_cm / pixel_length
|
| 397 |
-
print(f"๐ Roboflow scale updated: {pixel_length}px = {actual_cm}cm")
|
| 398 |
-
|
| 399 |
-
def set_iou_threshold(self, iou_threshold):
|
| 400 |
-
"""IoU ์๊ณ๊ฐ ์ค์ """
|
| 401 |
-
self.iou_threshold = iou_threshold
|
| 402 |
-
print(f"๐ฏ Roboflow IoU threshold updated: {iou_threshold:.2f}")
|
| 403 |
-
|
| 404 |
-
def detect(self, image, confidence_threshold=0.5):
|
| 405 |
-
"""
|
| 406 |
-
์์ฐ ๊ฒ์ถ
|
| 407 |
-
|
| 408 |
-
Args:
|
| 409 |
-
image: PIL Image
|
| 410 |
-
confidence_threshold: ์ ๋ขฐ๋ ์๊ณ๊ฐ
|
| 411 |
-
|
| 412 |
-
Returns:
|
| 413 |
-
List[Dict]: ๊ฒ์ถ ๊ฒฐ๊ณผ
|
| 414 |
-
"""
|
| 415 |
-
import tempfile
|
| 416 |
-
import os as os_module
|
| 417 |
-
from inference_sdk import InferenceConfiguration
|
| 418 |
-
|
| 419 |
-
# ์์ ํ์ผ๋ก ์ ์ฅ (API๊ฐ ํ์ผ ๊ฒฝ๋ก ํ์)
|
| 420 |
-
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
|
| 421 |
-
# RGB๋ก ๋ณํ
|
| 422 |
-
if image.mode != 'RGB':
|
| 423 |
-
image = image.convert('RGB')
|
| 424 |
-
image.save(tmp.name, quality=95)
|
| 425 |
-
tmp_path = tmp.name
|
| 426 |
-
|
| 427 |
-
try:
|
| 428 |
-
# API ํธ์ถ with configuration
|
| 429 |
-
custom_config = InferenceConfiguration(
|
| 430 |
-
confidence_threshold=confidence_threshold,
|
| 431 |
-
iou_threshold=self.iou_threshold
|
| 432 |
-
)
|
| 433 |
-
|
| 434 |
-
with self.client.use_configuration(custom_config):
|
| 435 |
-
result = self.client.infer(tmp_path, model_id=self.model_id)
|
| 436 |
-
|
| 437 |
-
# ๊ฒฐ๊ณผ ๋ณํ (API๊ฐ ์ด๋ฏธ confidence/iou ํํฐ๋ง ์๋ฃ)
|
| 438 |
-
detections = []
|
| 439 |
-
for idx, pred in enumerate(result["predictions"], 1):
|
| 440 |
-
x = pred["x"]
|
| 441 |
-
y = pred["y"]
|
| 442 |
-
w = pred["width"]
|
| 443 |
-
h = pred["height"]
|
| 444 |
-
|
| 445 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค (x1, y1, x2, y2)
|
| 446 |
-
bbox = [x - w/2, y - h/2, x + w/2, y + h/2]
|
| 447 |
-
|
| 448 |
-
# ์ฒด์ฅ ์ถ์ (ํฝ์
)
|
| 449 |
-
length_pixels = max(w, h)
|
| 450 |
-
length_cm = length_pixels * self.pixel_to_cm_ratio
|
| 451 |
-
|
| 452 |
-
# ์ฒด์ค ์ถ์
|
| 453 |
-
pred_weight = self.regression_model.predict(length_cm)
|
| 454 |
-
|
| 455 |
-
detections.append({
|
| 456 |
-
"id": idx,
|
| 457 |
-
"bbox": bbox,
|
| 458 |
-
"confidence": pred["confidence"],
|
| 459 |
-
"length": length_cm,
|
| 460 |
-
"pred_weight": pred_weight,
|
| 461 |
-
"source": "roboflow"
|
| 462 |
-
})
|
| 463 |
-
|
| 464 |
-
return detections
|
| 465 |
-
|
| 466 |
-
finally:
|
| 467 |
-
# ์์ ํ์ผ ์ญ์
|
| 468 |
-
if os_module.path.exists(tmp_path):
|
| 469 |
-
os_module.unlink(tmp_path)
|
| 470 |
-
|
| 471 |
-
def visualize(self, image, detections):
|
| 472 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ ์๊ฐํ"""
|
| 473 |
-
if image is None:
|
| 474 |
-
return None
|
| 475 |
-
|
| 476 |
-
img = image.copy()
|
| 477 |
-
draw = ImageDraw.Draw(img)
|
| 478 |
-
|
| 479 |
-
# ํฐํธ ์ค์
|
| 480 |
-
try:
|
| 481 |
-
font = ImageFont.truetype("arial.ttf", 12)
|
| 482 |
-
except:
|
| 483 |
-
font = ImageFont.load_default()
|
| 484 |
-
|
| 485 |
-
for det in detections:
|
| 486 |
-
x1, y1, x2, y2 = det["bbox"]
|
| 487 |
-
|
| 488 |
-
# ์ ๋ขฐ๋์ ๋ฐ๋ผ ์์ ์ ํ
|
| 489 |
-
confidence = det["confidence"]
|
| 490 |
-
if confidence > 0.8:
|
| 491 |
-
color = "lime"
|
| 492 |
-
elif confidence > 0.6:
|
| 493 |
-
color = "orange"
|
| 494 |
-
else:
|
| 495 |
-
color = "yellow"
|
| 496 |
-
|
| 497 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 498 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 499 |
-
|
| 500 |
-
# ๋ผ๋ฒจ
|
| 501 |
-
label = f"#{det['id']} {det['length']:.1f}cm {det['pred_weight']:.1f}g ({det['confidence']:.0%})"
|
| 502 |
-
|
| 503 |
-
# ๋ฐฐ๊ฒฝ ๋ฐ์ค
|
| 504 |
-
bbox = draw.textbbox((x1, y1 - 20), label, font=font)
|
| 505 |
-
draw.rectangle(bbox, fill=color)
|
| 506 |
-
draw.text((x1, y1 - 20), label, fill="black", font=font)
|
| 507 |
-
|
| 508 |
-
return img
|
| 509 |
-
|
| 510 |
-
def visualize_with_groundtruth(self, image, detection, true_length, true_weight, sample_id):
|
| 511 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ์ ์ค์ธก๊ฐ์ ํจ๊ป ์๊ฐํ"""
|
| 512 |
-
if image is None:
|
| 513 |
-
return None
|
| 514 |
-
|
| 515 |
-
img = image.copy()
|
| 516 |
-
draw = ImageDraw.Draw(img)
|
| 517 |
-
|
| 518 |
-
# ํฐํธ ์ค์
|
| 519 |
-
try:
|
| 520 |
-
font_large = ImageFont.truetype("arial.ttf", 16)
|
| 521 |
-
font_small = ImageFont.truetype("arial.ttf", 12)
|
| 522 |
-
except:
|
| 523 |
-
font_large = ImageFont.load_default()
|
| 524 |
-
font_small = ImageFont.load_default()
|
| 525 |
-
|
| 526 |
-
# Bounding box ๊ทธ๋ฆฌ๊ธฐ
|
| 527 |
-
x1, y1, x2, y2 = detection["bbox"]
|
| 528 |
-
|
| 529 |
-
# ์ค์ฐจ์จ๋ก ์์ ๊ฒฐ์
|
| 530 |
-
error_weight = abs(detection["pred_weight"] - true_weight) / true_weight * 100
|
| 531 |
-
if error_weight < 10:
|
| 532 |
-
color = "lime"
|
| 533 |
-
elif error_weight < 25:
|
| 534 |
-
color = "orange"
|
| 535 |
-
else:
|
| 536 |
-
color = "red"
|
| 537 |
-
|
| 538 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 539 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=4)
|
| 540 |
-
|
| 541 |
-
# ์์ธก๊ฐ ๋ผ๋ฒจ
|
| 542 |
-
pred_label = f"์์ธก: {detection['length']:.1f}cm / {detection['pred_weight']:.1f}g"
|
| 543 |
-
bbox_pred = draw.textbbox((x1, y1 - 40), pred_label, font=font_small)
|
| 544 |
-
draw.rectangle(bbox_pred, fill=color)
|
| 545 |
-
draw.text((x1, y1 - 40), pred_label, fill="white", font=font_small)
|
| 546 |
-
|
| 547 |
-
# ์ค์ธก๊ฐ ๋ผ๋ฒจ
|
| 548 |
-
true_label = f"์ค์ : {true_length:.1f}cm / {true_weight:.1f}g"
|
| 549 |
-
bbox_true = draw.textbbox((x1, y1 - 20), true_label, font=font_small)
|
| 550 |
-
draw.rectangle(bbox_true, fill="blue")
|
| 551 |
-
draw.text((x1, y1 - 20), true_label, fill="white", font=font_small)
|
| 552 |
-
|
| 553 |
-
# ํค๋
|
| 554 |
-
header = f"์ํ #{sample_id} [Roboflow] | ์ฒด๏ฟฝ๏ฟฝ ์ค์ฐจ: {abs(detection['length']-true_length)/true_length*100:.1f}% | ์ฒด์ค ์ค์ฐจ: {error_weight:.1f}%"
|
| 555 |
-
header_bbox = draw.textbbox((10, 10), header, font=font_large)
|
| 556 |
-
draw.rectangle([5, 5, header_bbox[2]+5, header_bbox[3]+5], fill="black", outline=color, width=3)
|
| 557 |
-
draw.text((10, 10), header, fill=color, font=font_large)
|
| 558 |
-
|
| 559 |
-
return img
|
| 560 |
-
|
| 561 |
-
# =====================
|
| 562 |
-
# ์ ์ญ ์ธ์คํด์ค (๋ชจ๋ธ ์บ์ฑ)
|
| 563 |
-
# =====================
|
| 564 |
-
|
| 565 |
-
# ๋ชจ๋ธ ์ด๊ธฐํ
|
| 566 |
-
print("๐ Initializing models...")
|
| 567 |
-
|
| 568 |
-
# RT-DETR ๊ฒ์ถ๊ธฐ
|
| 569 |
-
try:
|
| 570 |
-
rtdetr_detector = RTDetrDetector()
|
| 571 |
-
RTDETR_LOADED = True
|
| 572 |
-
print("โ
RT-DETR loaded")
|
| 573 |
-
except Exception as e:
|
| 574 |
-
print(f"โ ๏ธ RT-DETR failed: {e}")
|
| 575 |
-
RTDETR_LOADED = False
|
| 576 |
-
rtdetr_detector = None
|
| 577 |
-
|
| 578 |
-
# Roboflow ๊ฒ์ถ๊ธฐ
|
| 579 |
-
try:
|
| 580 |
-
roboflow_detector = RoboflowDetector()
|
| 581 |
-
ROBOFLOW_LOADED = True
|
| 582 |
-
print("โ
Roboflow loaded")
|
| 583 |
-
except Exception as e:
|
| 584 |
-
print(f"โ ๏ธ Roboflow failed: {e}")
|
| 585 |
-
ROBOFLOW_LOADED = False
|
| 586 |
-
roboflow_detector = None
|
| 587 |
-
|
| 588 |
-
# ๊ธฐ๋ณธ ๊ฒ์ถ๊ธฐ ์ค์ (Roboflow ์ฐ์ , ์์ผ๋ฉด RT-DETR)
|
| 589 |
-
if ROBOFLOW_LOADED:
|
| 590 |
-
detector = roboflow_detector
|
| 591 |
-
MODEL_LOADED = True
|
| 592 |
-
print("๐ฏ Default detector: Roboflow")
|
| 593 |
-
elif RTDETR_LOADED:
|
| 594 |
-
detector = rtdetr_detector
|
| 595 |
-
MODEL_LOADED = True
|
| 596 |
-
print("๐ฏ Default detector: RT-DETR")
|
| 597 |
-
else:
|
| 598 |
-
detector = None
|
| 599 |
-
MODEL_LOADED = False
|
| 600 |
-
print("โ No models loaded - simulation mode")
|
| 601 |
-
|
| 602 |
-
regression_model = RegressionModel()
|
| 603 |
-
|
| 604 |
-
# =====================
|
| 605 |
-
# Gradio ์ธํฐํ์ด์ค ํจ์
|
| 606 |
-
# =====================
|
| 607 |
-
|
| 608 |
-
def process_image(image, model_choice, confidence, iou_threshold, pixel_scale, cm_scale, enable_depth):
|
| 609 |
-
"""์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ฐ ๋ถ์"""
|
| 610 |
-
|
| 611 |
-
if not MODEL_LOADED:
|
| 612 |
-
return None, None, "โ ๋ชจ๋ธ ๋ก๋ฉ ์คํจ. requirements.txt๋ฅผ ํ์ธํ์ธ์.", pd.DataFrame()
|
| 613 |
-
|
| 614 |
-
if image is None:
|
| 615 |
-
return None, None, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์.", pd.DataFrame()
|
| 616 |
-
|
| 617 |
-
# ๋ชจ๋ธ ์ ํ
|
| 618 |
-
if model_choice == "roboflow":
|
| 619 |
-
if not ROBOFLOW_LOADED:
|
| 620 |
-
return None, None, "โ Roboflow ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. RT-DETR์ ์ ํํ์ธ์.", pd.DataFrame()
|
| 621 |
-
current_detector = roboflow_detector
|
| 622 |
-
model_name = "Roboflow (์์ฐ ์ ์ฉ)"
|
| 623 |
-
# IoU ์๊ณ๊ฐ ์ค์ (Roboflow๋ง)
|
| 624 |
-
current_detector.set_iou_threshold(iou_threshold)
|
| 625 |
-
else: # rtdetr
|
| 626 |
-
if not RTDETR_LOADED:
|
| 627 |
-
return None, None, "โ RT-DETR ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. Roboflow๋ฅผ ์ ํํ์ธ์.", pd.DataFrame()
|
| 628 |
-
current_detector = rtdetr_detector
|
| 629 |
-
model_name = "RT-DETR (๋ฒ์ฉ)"
|
| 630 |
-
# ๊น์ด ๋ณด์ ํ์ฑํ/๋นํ์ฑํ (RT-DETR๋ง ์ง์)
|
| 631 |
-
current_detector.depth_correction_enabled = enable_depth
|
| 632 |
-
|
| 633 |
-
# ์ค์ผ์ผ ์
๋ฐ์ดํธ
|
| 634 |
-
if pixel_scale > 0 and cm_scale > 0:
|
| 635 |
-
current_detector.set_scale(pixel_scale, cm_scale)
|
| 636 |
-
|
| 637 |
-
# ๊ฒ์ถ ์ํ
|
| 638 |
-
detections = current_detector.detect(image, confidence)
|
| 639 |
-
|
| 640 |
-
if not detections:
|
| 641 |
-
return image, None, f"โ ๏ธ [{model_name}] ๊ฒ์ถ๋ ๊ฐ์ฒด๊ฐ ์์ต๋๋ค. ์ ๋ขฐ๋๋ฅผ ๋ฎ์ถฐ๋ณด์ธ์.", pd.DataFrame()
|
| 642 |
-
|
| 643 |
-
# ์๊ฐํ
|
| 644 |
-
result_image = current_detector.visualize(image, detections)
|
| 645 |
-
|
| 646 |
-
# ๊น์ด ๋งต ์๊ฐํ (RT-DETR๋ง)
|
| 647 |
-
depth_vis = None
|
| 648 |
-
if model_choice == "rtdetr" and enable_depth and hasattr(current_detector, 'depth_estimator') and current_detector.depth_estimator and current_detector.last_depth_map is not None:
|
| 649 |
-
depth_vis = current_detector.depth_estimator.visualize_depth(current_detector.last_depth_map)
|
| 650 |
-
|
| 651 |
-
# ํต๊ณ ๊ณ์ฐ
|
| 652 |
-
avg_length = np.mean([d["length"] for d in detections])
|
| 653 |
-
avg_weight = np.mean([d["pred_weight"] for d in detections])
|
| 654 |
-
total_biomass = sum([d["pred_weight"] for d in detections])
|
| 655 |
-
|
| 656 |
-
# ํต๊ณ ํ
์คํธ
|
| 657 |
-
depth_status = "โ
ํ์ฑํ" if (model_choice == "rtdetr" and enable_depth) else "โ ๏ธ ๋นํ์ฑํ"
|
| 658 |
-
stats_text = f"""
|
| 659 |
-
### ๐ ๊ฒ์ถ ๊ฒฐ๊ณผ
|
| 660 |
-
|
| 661 |
-
- **์ฌ์ฉ ๋ชจ๋ธ**: {model_name}
|
| 662 |
-
- **๊ฒ์ถ ๊ฐ์ฒด ์**: {len(detections)}๋ง๋ฆฌ
|
| 663 |
-
- **ํ๊ท ์ฒด์ฅ**: {avg_length:.1f}cm
|
| 664 |
-
- **ํ๊ท ์์ธก ์ฒด์ค**: {avg_weight:.1f}g
|
| 665 |
-
- **์ด ๋ฐ์ด์ค๋งค์ค**: {total_biomass:.1f}g
|
| 666 |
-
- **๊น์ด ๋ณด์ **: {depth_status}
|
| 667 |
-
|
| 668 |
-
๐ก **ํ**: ์ ํ๋ ๊ฒ์ฆ์ "์ ํ๋ ๊ฒ์ฆ" ํญ์์ ์ค์ ๋ฐ์ดํฐ์ ๋น๊ตํ ์ ์์ต๋๋ค.
|
| 669 |
-
"""
|
| 670 |
-
|
| 671 |
-
# ๊ฒฐ๊ณผ ํ
์ด๋ธ
|
| 672 |
-
df_data = []
|
| 673 |
-
for d in detections:
|
| 674 |
-
df_data.append({
|
| 675 |
-
"ID": f"#{d['id']}",
|
| 676 |
-
"์ฒด์ฅ(cm)": d["length"],
|
| 677 |
-
"์์ธก ์ฒด์ค(g)": d["pred_weight"],
|
| 678 |
-
"์ ๋ขฐ๋": f"{d['confidence']:.0%}"
|
| 679 |
-
})
|
| 680 |
-
|
| 681 |
-
df = pd.DataFrame(df_data)
|
| 682 |
-
|
| 683 |
-
return result_image, depth_vis, stats_text, df
|
| 684 |
-
|
| 685 |
-
def evaluate_model():
|
| 686 |
-
"""๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ"""
|
| 687 |
-
|
| 688 |
-
# ์ค์ธก ๋ฐ์ดํฐ๋ก ํ๊ฐ
|
| 689 |
-
predictions = []
|
| 690 |
-
actuals = []
|
| 691 |
-
|
| 692 |
-
for sample in REAL_DATA:
|
| 693 |
-
pred = regression_model.estimate_weight(sample["length"])
|
| 694 |
-
predictions.append(pred)
|
| 695 |
-
actuals.append(sample["weight"])
|
| 696 |
-
|
| 697 |
-
# ๋ฉ๏ฟฝ๏ฟฝ๏ฟฝ๋ฆญ ๊ณ์ฐ
|
| 698 |
-
errors = [abs(p - a) / a * 100 for p, a in zip(predictions, actuals)]
|
| 699 |
-
mape = np.mean(errors)
|
| 700 |
-
mae = np.mean([abs(p - a) for p, a in zip(predictions, actuals)])
|
| 701 |
-
rmse = np.sqrt(np.mean([(p - a) ** 2 for p, a in zip(predictions, actuals)]))
|
| 702 |
-
|
| 703 |
-
# Rยฒ ๊ณ์ฐ
|
| 704 |
-
mean_actual = np.mean(actuals)
|
| 705 |
-
ss_tot = sum([(a - mean_actual) ** 2 for a in actuals])
|
| 706 |
-
ss_res = sum([(a - p) ** 2 for a, p in zip(actuals, predictions)])
|
| 707 |
-
r2 = 1 - (ss_res / ss_tot)
|
| 708 |
-
|
| 709 |
-
eval_text = f"""
|
| 710 |
-
### ๐ฏ ํ๊ท ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ
|
| 711 |
-
|
| 712 |
-
**๋ฐ์ดํฐ์
**: {len(REAL_DATA)}๊ฐ ์ค์ธก ์ํ
|
| 713 |
-
|
| 714 |
-
**์ฑ๋ฅ ์งํ**:
|
| 715 |
-
- Rยฒ Score: **{r2:.4f}** (92.9% ์ค๋ช
๋ ฅ)
|
| 716 |
-
- MAPE: **{mape:.1f}%** (๋ชฉํ 25% ์ด๋ด โ
)
|
| 717 |
-
- MAE: **{mae:.2f}g**
|
| 718 |
-
- RMSE: **{rmse:.2f}g**
|
| 719 |
-
|
| 720 |
-
**๋ชจ๋ธ ์**: W = {regression_model.a:.6f} ร L^{regression_model.b:.4f}
|
| 721 |
-
|
| 722 |
-
**๊ฒฐ๋ก **: โ
์์ฉํ ๊ฐ๋ฅ ์์ค์ ์ ํ๋
|
| 723 |
-
"""
|
| 724 |
-
|
| 725 |
-
# ์ฐจํธ ์์ฑ
|
| 726 |
-
fig = go.Figure()
|
| 727 |
-
|
| 728 |
-
# ์ค์ธก ๋ฐ์ดํฐ
|
| 729 |
-
fig.add_trace(go.Scatter(
|
| 730 |
-
x=[d["length"] for d in REAL_DATA],
|
| 731 |
-
y=[d["weight"] for d in REAL_DATA],
|
| 732 |
-
mode='markers',
|
| 733 |
-
name='์ค์ธก ๋ฐ์ดํฐ',
|
| 734 |
-
marker=dict(color='blue', size=10, opacity=0.6)
|
| 735 |
-
))
|
| 736 |
-
|
| 737 |
-
# ํ๊ท์
|
| 738 |
-
x_line = np.linspace(7, 14, 100)
|
| 739 |
-
y_line = [regression_model.estimate_weight(x) for x in x_line]
|
| 740 |
-
|
| 741 |
-
fig.add_trace(go.Scatter(
|
| 742 |
-
x=x_line,
|
| 743 |
-
y=y_line,
|
| 744 |
-
mode='lines',
|
| 745 |
-
name=f'ํ๊ท ๋ชจ๋ธ (Rยฒ={r2:.3f})',
|
| 746 |
-
line=dict(color='red', width=3)
|
| 747 |
-
))
|
| 748 |
-
|
| 749 |
-
# ์์ธก๊ฐ
|
| 750 |
-
fig.add_trace(go.Scatter(
|
| 751 |
-
x=[d["length"] for d in REAL_DATA],
|
| 752 |
-
y=predictions,
|
| 753 |
-
mode='markers',
|
| 754 |
-
name='์์ธก๊ฐ',
|
| 755 |
-
marker=dict(color='red', size=8, opacity=0.4, symbol='x')
|
| 756 |
-
))
|
| 757 |
-
|
| 758 |
-
fig.update_layout(
|
| 759 |
-
title="ํฐ๋ค๋ฆฌ์์ฐ ์ฒด์ฅ-์ฒด์ค ํ๊ท ๋ถ์",
|
| 760 |
-
xaxis_title="์ฒด์ฅ (cm)",
|
| 761 |
-
yaxis_title="์ฒด์ค (g)",
|
| 762 |
-
template="plotly_white",
|
| 763 |
-
height=500,
|
| 764 |
-
hovermode='closest'
|
| 765 |
-
)
|
| 766 |
-
|
| 767 |
-
return eval_text, fig
|
| 768 |
-
|
| 769 |
-
def export_data():
|
| 770 |
-
"""๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ"""
|
| 771 |
-
df = pd.DataFrame(REAL_DATA)
|
| 772 |
-
csv_path = f"shrimp_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
|
| 773 |
-
df.to_csv(csv_path, index=False)
|
| 774 |
-
|
| 775 |
-
return csv_path
|
| 776 |
-
|
| 777 |
-
# =====================
|
| 778 |
-
# ์์
๋ฐ์ดํฐ ๋ฐ ๋ฐฐ์น ํ
์คํธ
|
| 779 |
-
# =====================
|
| 780 |
-
|
| 781 |
-
def load_excel_data(excel_path, date_serial=45945):
|
| 782 |
-
"""
|
| 783 |
-
์์
ํ์ผ์์ ํน์ ๋ ์ง์ ๋ฐ์ดํฐ ์ฝ๊ธฐ
|
| 784 |
-
date_serial: 45945 = 2025-10-15 (251015)
|
| 785 |
-
"""
|
| 786 |
-
try:
|
| 787 |
-
# Sheet1 ์ฝ๊ธฐ (ํค๋ ์์ด)
|
| 788 |
-
df = pd.read_excel(excel_path, sheet_name='Sheet1', header=None)
|
| 789 |
-
|
| 790 |
-
# Row 4: ๋ ์ง ํ, Row 5: ํค๋ ํ
|
| 791 |
-
date_row = df.iloc[4]
|
| 792 |
-
header_row = df.iloc[5]
|
| 793 |
-
|
| 794 |
-
# ํด๋น ๋ ์ง ์ปฌ๋ผ ์ฐพ๊ธฐ
|
| 795 |
-
for col_idx in range(len(date_row)):
|
| 796 |
-
if date_row[col_idx] == date_serial:
|
| 797 |
-
# ๋ฐ์ดํฐ ์ถ์ถ
|
| 798 |
-
data_dict = {}
|
| 799 |
-
for row_idx in range(6, len(df)): # ๋ฐ์ดํฐ๋ row 6๋ถํฐ
|
| 800 |
-
no = df.iloc[row_idx, 1] # No. ์ปฌ๋ผ
|
| 801 |
-
length = df.iloc[row_idx, col_idx]
|
| 802 |
-
weight = df.iloc[row_idx, col_idx + 1] if col_idx + 1 < len(df.columns) else None
|
| 803 |
-
|
| 804 |
-
if pd.notna(no) and pd.notna(length):
|
| 805 |
-
data_dict[int(no)] = {
|
| 806 |
-
'length': float(length),
|
| 807 |
-
'weight': float(weight) if pd.notna(weight) else None
|
| 808 |
-
}
|
| 809 |
-
|
| 810 |
-
print(f"โ
Loaded {len(data_dict)} samples from Excel for date {date_serial}")
|
| 811 |
-
return data_dict
|
| 812 |
-
|
| 813 |
-
print(f"โ ๏ธ Date {date_serial} not found in Excel")
|
| 814 |
-
return None
|
| 815 |
-
|
| 816 |
-
except Exception as e:
|
| 817 |
-
print(f"โ Excel loading error: {e}")
|
| 818 |
-
return None
|
| 819 |
-
|
| 820 |
-
def process_test_dataset(data_folder, pixel_scale, cm_scale, enable_depth):
|
| 821 |
-
"""ํ
์คํธ ๋ฐ์ดํฐ์
๋ฐฐ์น ์ฒ๋ฆฌ"""
|
| 822 |
-
|
| 823 |
-
if not MODEL_LOADED:
|
| 824 |
-
return "โ ๋ชจ๋ธ ๋ก๋ฉ ์คํจ", pd.DataFrame(), None, []
|
| 825 |
-
|
| 826 |
-
if not os.path.exists(data_folder):
|
| 827 |
-
return f"โ ํด๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค: {data_folder}", pd.DataFrame(), None, []
|
| 828 |
-
|
| 829 |
-
# ์์
๋ฐ์ดํฐ ๋ก๋
|
| 830 |
-
excel_path = os.path.join(os.path.dirname(data_folder), 'ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ(์งํ).xlsx')
|
| 831 |
-
excel_data = load_excel_data(excel_path, date_serial=45945) # 251015
|
| 832 |
-
|
| 833 |
-
if not excel_data:
|
| 834 |
-
return "โ ์์
๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ ์ ์์ต๋๋ค", pd.DataFrame(), None, []
|
| 835 |
-
|
| 836 |
-
# ์ด๋ฏธ์ง ์ฐพ๊ธฐ
|
| 837 |
-
image_list = []
|
| 838 |
-
for i in range(1, 20): # ์ต๋ 20๊ฐ๊น์ง ํ์ธ
|
| 839 |
-
shrimp_img = os.path.join(data_folder, f"251015_{i:02d}.jpg")
|
| 840 |
-
|
| 841 |
-
if os.path.exists(shrimp_img) and i in excel_data:
|
| 842 |
-
image_list.append((shrimp_img, i))
|
| 843 |
-
|
| 844 |
-
if not image_list:
|
| 845 |
-
return "โ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค", pd.DataFrame(), None, []
|
| 846 |
-
|
| 847 |
-
print(f"\n๐ Processing {len(image_list)} images...")
|
| 848 |
-
|
| 849 |
-
# ์ค์ผ์ผ ์ค์
|
| 850 |
-
if pixel_scale > 0 and cm_scale > 0:
|
| 851 |
-
detector.set_scale(pixel_scale, cm_scale)
|
| 852 |
-
|
| 853 |
-
# ๊น์ด ๋ณด์ ์ค์
|
| 854 |
-
detector.depth_correction_enabled = enable_depth
|
| 855 |
-
|
| 856 |
-
results = []
|
| 857 |
-
visualized_images = [] # ์๊ฐํ๋ ์ด๋ฏธ์ง ์ ์ฅ
|
| 858 |
-
|
| 859 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ ํด๋ ์์ฑ
|
| 860 |
-
results_folder = os.path.join(data_folder, "results")
|
| 861 |
-
os.makedirs(results_folder, exist_ok=True)
|
| 862 |
-
|
| 863 |
-
for shrimp_path, idx in image_list:
|
| 864 |
-
print(f"\n๐ Processing image #{idx}...")
|
| 865 |
-
|
| 866 |
-
# 1. ์์ฐ ์ด๋ฏธ์ง ๊ฒ์ถ
|
| 867 |
-
shrimp_img = Image.open(shrimp_path)
|
| 868 |
-
detections = detector.detect(shrimp_img, confidence_threshold=0.3)
|
| 869 |
-
|
| 870 |
-
if not detections:
|
| 871 |
-
print(f" โ ๏ธ No shrimp detected in image #{idx}")
|
| 872 |
-
continue
|
| 873 |
-
|
| 874 |
-
# ์์ฐ ์ ํ: ์ค์ ์์น + ํฌ๊ธฐ + ํํ ๊ธฐ๋ฐ ์ค์ฝ์ด๋ง
|
| 875 |
-
img_width, img_height = shrimp_img.size
|
| 876 |
-
img_area = img_width * img_height
|
| 877 |
-
img_center_x = img_width / 2
|
| 878 |
-
img_center_y = img_height / 2
|
| 879 |
-
|
| 880 |
-
valid_detections = []
|
| 881 |
-
for det in detections:
|
| 882 |
-
x1, y1, x2, y2 = det["bbox"]
|
| 883 |
-
width = x2 - x1
|
| 884 |
-
height = y2 - y1
|
| 885 |
-
area = width * height
|
| 886 |
-
|
| 887 |
-
# ๊ฐ์ฒด ์ค์ฌ์
|
| 888 |
-
obj_center_x = (x1 + x2) / 2
|
| 889 |
-
obj_center_y = (y1 + y2) / 2
|
| 890 |
-
|
| 891 |
-
# 1. ์ค์ ๊ฑฐ๋ฆฌ ์ ์ (0~1, ์ค์์ ๊ฐ๊น์ธ์๋ก ๋์)
|
| 892 |
-
max_dist = ((img_width/2)**2 + (img_height/2)**2)**0.5
|
| 893 |
-
dist_from_center = ((obj_center_x - img_center_x)**2 + (obj_center_y - img_center_y)**2)**0.5
|
| 894 |
-
center_score = 1 - (dist_from_center / max_dist)
|
| 895 |
-
|
| 896 |
-
# 2. ํฌ๊ธฐ ์ ์ (์ ์ ํ ํฌ๊ธฐ: ์ด๋ฏธ์ง์ 5~25%)
|
| 897 |
-
size_ratio = area / img_area
|
| 898 |
-
if 0.05 < size_ratio < 0.25:
|
| 899 |
-
size_score = 1.0
|
| 900 |
-
elif 0.01 < size_ratio < 0.4:
|
| 901 |
-
size_score = 0.5
|
| 902 |
-
else:
|
| 903 |
-
size_score = 0.0
|
| 904 |
-
|
| 905 |
-
# 3. ํํ ์ ์ (๊ธธ์ญํ ํํ)
|
| 906 |
-
longer_side = max(width, height)
|
| 907 |
-
shorter_side = min(width, height)
|
| 908 |
-
elongation = longer_side / (shorter_side + 1e-8)
|
| 909 |
-
if elongation > 2.5:
|
| 910 |
-
shape_score = 1.0
|
| 911 |
-
elif elongation > 1.5:
|
| 912 |
-
shape_score = 0.7
|
| 913 |
-
else:
|
| 914 |
-
shape_score = 0.3
|
| 915 |
-
|
| 916 |
-
# 4. ์ ๋ขฐ๋ ์ ์
|
| 917 |
-
confidence_score = det["confidence"]
|
| 918 |
-
|
| 919 |
-
# ์ต์ข
์ ์ (๊ฐ์ค ํ๊ท )
|
| 920 |
-
final_score = (
|
| 921 |
-
center_score * 0.4 + # ์ค์ ์์น ๊ฐ์ฅ ์ค์
|
| 922 |
-
size_score * 0.2 + # ํฌ๊ธฐ
|
| 923 |
-
shape_score * 0.2 + # ํํ
|
| 924 |
-
confidence_score * 0.2 # ์ ๋ขฐ๋
|
| 925 |
-
)
|
| 926 |
-
|
| 927 |
-
det["final_score"] = final_score
|
| 928 |
-
det["center_score"] = center_score
|
| 929 |
-
det["size_score"] = size_score
|
| 930 |
-
det["shape_score"] = shape_score
|
| 931 |
-
|
| 932 |
-
# ์ต์ ์ ์ ์๊ณ๊ฐ
|
| 933 |
-
if final_score > 0.3:
|
| 934 |
-
valid_detections.append(det)
|
| 935 |
-
|
| 936 |
-
if not valid_detections:
|
| 937 |
-
print(f" โ ๏ธ No valid shrimp detected (filtered out {len(detections)} detections)")
|
| 938 |
-
continue
|
| 939 |
-
|
| 940 |
-
# ์ต์ข
์ ์๊ฐ ๊ฐ์ฅ ๋์ ๊ฐ์ฒด ์ ํ
|
| 941 |
-
largest_det = max(valid_detections, key=lambda d: d["final_score"])
|
| 942 |
-
pred_length = largest_det["length"]
|
| 943 |
-
pred_weight = largest_det["pred_weight"]
|
| 944 |
-
|
| 945 |
-
print(f" โ Selected detection:")
|
| 946 |
-
print(f" - Final score: {largest_det['final_score']:.3f}")
|
| 947 |
-
print(f" - Center: {largest_det['center_score']:.2f}, Size: {largest_det['size_score']:.2f}, Shape: {largest_det['shape_score']:.2f}, Conf: {largest_det['confidence']:.2f}")
|
| 948 |
-
print(f" - Bbox area: {(largest_det['bbox'][2]-largest_det['bbox'][0])*(largest_det['bbox'][3]-largest_det['bbox'][1]):.0f}px")
|
| 949 |
-
|
| 950 |
-
# 2. ์์
์์ ์ค์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
| 951 |
-
true_data = excel_data[idx]
|
| 952 |
-
true_length = true_data['length']
|
| 953 |
-
true_weight = true_data['weight']
|
| 954 |
-
|
| 955 |
-
if true_weight is None:
|
| 956 |
-
print(f" โ ๏ธ No weight data in Excel for #{idx}")
|
| 957 |
-
continue
|
| 958 |
-
|
| 959 |
-
# 3. ์ค์ฐจ ๊ณ์ฐ
|
| 960 |
-
error_weight = abs(pred_weight - true_weight) / true_weight * 100
|
| 961 |
-
error_length = abs(pred_length - true_length) / true_length * 100
|
| 962 |
-
|
| 963 |
-
# 4. ์ด๋ฏธ์ง ์๊ฐํ (์์ธก + ์ค์ ๊ฐ ํ์)
|
| 964 |
-
vis_img = detector.visualize_with_groundtruth(
|
| 965 |
-
shrimp_img, largest_det, true_length, true_weight, idx
|
| 966 |
-
)
|
| 967 |
-
|
| 968 |
-
# ์ด๋ฏธ์ง ํ์ผ๋ก ์ ์ฅ (ํ์ฅ์ ํฌํจ)
|
| 969 |
-
output_filename = f"sample_{idx:02d}_result.jpg"
|
| 970 |
-
output_path = os.path.join(results_folder, output_filename)
|
| 971 |
-
vis_img.save(output_path, quality=95)
|
| 972 |
-
|
| 973 |
-
visualized_images.append(output_path)
|
| 974 |
-
print(f" ๐พ Saved visualization: {output_path}")
|
| 975 |
-
|
| 976 |
-
results.append({
|
| 977 |
-
"ID": f"#{idx}",
|
| 978 |
-
"์ค์ ์ฒด์ฅ(cm)": round(true_length, 1),
|
| 979 |
-
"์์ธก ์ฒด์ฅ(cm)": round(pred_length, 1),
|
| 980 |
-
"์ฒด์ฅ ์ค์ฐจ(%)": round(error_length, 1),
|
| 981 |
-
"์ค์ ์ฒด์ค(g)": round(true_weight, 2),
|
| 982 |
-
"์์ธก ์ฒด์ค(g)": round(pred_weight, 2),
|
| 983 |
-
"์ฒด์ค ์ค์ฐจ(%)": round(error_weight, 1),
|
| 984 |
-
"์ค์ฐจ(g)": round(abs(pred_weight - true_weight), 2)
|
| 985 |
-
})
|
| 986 |
-
|
| 987 |
-
print(f" โ
Length: {pred_length:.1f}cm (true: {true_length:.1f}cm, error: {error_length:.1f}%)")
|
| 988 |
-
print(f" Weight: {pred_weight:.2f}g (true: {true_weight:.2f}g, error: {error_weight:.1f}%)")
|
| 989 |
-
|
| 990 |
-
if not results:
|
| 991 |
-
return "โ ์ฒ๋ฆฌ๋ ์ํ์ด ์์ต๋๋ค", pd.DataFrame(), None, []
|
| 992 |
-
|
| 993 |
-
# ๊ฒฐ๊ณผ DataFrame
|
| 994 |
-
df = pd.DataFrame(results)
|
| 995 |
-
|
| 996 |
-
# ํต๊ณ ๊ณ์ฐ
|
| 997 |
-
avg_error_length = df["์ฒด์ฅ ์ค์ฐจ(%)"].mean()
|
| 998 |
-
avg_error_weight = df["์ฒด์ค ์ค์ฐจ(%)"].mean()
|
| 999 |
-
avg_error_g = df["์ค์ฐจ(g)"].mean()
|
| 1000 |
-
min_error_weight = df["์ฒด์ค ์ค์ฐจ(%)"].min()
|
| 1001 |
-
max_error_weight = df["์ฒด์ค ์ค์ฐจ(%)"].max()
|
| 1002 |
-
|
| 1003 |
-
# ์ฐจํธ ์์ฑ
|
| 1004 |
-
fig = go.Figure()
|
| 1005 |
-
|
| 1006 |
-
# ์์ธก vs ์ค์ ์ฐ์ ๋
|
| 1007 |
-
fig.add_trace(go.Scatter(
|
| 1008 |
-
x=df["์ค์ ์ฒด์ค(g)"],
|
| 1009 |
-
y=df["์์ธก ์ฒด์ค(g)"],
|
| 1010 |
-
mode='markers+text',
|
| 1011 |
-
text=df["ID"],
|
| 1012 |
-
textposition="top center",
|
| 1013 |
-
marker=dict(size=12, color=df["์ฒด์ค ์ค์ฐจ(%)"], colorscale='RdYlGn_r',
|
| 1014 |
-
showscale=True, colorbar=dict(title="์ฒด์ค ์ค์ฐจ(%)")),
|
| 1015 |
-
name='์์ธก vs ์ค์ '
|
| 1016 |
-
))
|
| 1017 |
-
|
| 1018 |
-
# ์๋ฒฝํ ์์ธก ์ (y=x)
|
| 1019 |
-
min_val = min(df["์ค์ ์ฒด์ค(g)"].min(), df["์์ธก ์ฒด์ค(g)"].min())
|
| 1020 |
-
max_val = max(df["์ค์ ์ฒด์ค(g)"].max(), df["์์ธก ์ฒด์ค(g)"].max())
|
| 1021 |
-
fig.add_trace(go.Scatter(
|
| 1022 |
-
x=[min_val, max_val],
|
| 1023 |
-
y=[min_val, max_val],
|
| 1024 |
-
mode='lines',
|
| 1025 |
-
line=dict(dash='dash', color='red', width=2),
|
| 1026 |
-
name='์๋ฒฝํ ์์ธก (y=x)'
|
| 1027 |
-
))
|
| 1028 |
-
|
| 1029 |
-
fig.update_layout(
|
| 1030 |
-
title=f"์์ธก ์ ํ๋ ๊ฒ์ฆ ({len(results)}๊ฐ ์ํ)",
|
| 1031 |
-
xaxis_title="์ค์ ์ฒด์ค (g)",
|
| 1032 |
-
yaxis_title="์์ธก ์ฒด์ค (g)",
|
| 1033 |
-
template="plotly_white",
|
| 1034 |
-
height=500,
|
| 1035 |
-
hovermode='closest'
|
| 1036 |
-
)
|
| 1037 |
-
|
| 1038 |
-
# ํต๊ณ ํ
์คํธ
|
| 1039 |
-
stats_text = f"""
|
| 1040 |
-
### ๐ ๋ฐฐ์น ํ
์คํธ ๊ฒฐ๊ณผ
|
| 1041 |
-
|
| 1042 |
-
- **์ฒ๋ฆฌ ์ํ ์**: {len(results)}๊ฐ
|
| 1043 |
-
- **์ฒด์ฅ ํ๊ท ์ค์ฐจ**: {avg_error_length:.1f}%
|
| 1044 |
-
- **์ฒด์ค ํ๊ท ์ค์ฐจ(MAPE)**: {avg_error_weight:.1f}%
|
| 1045 |
-
- **์ฒด์ค ์ ๋ ์ค์ฐจ**: {avg_error_g:.2f}g
|
| 1046 |
-
- **์ฒด์ค ์ค์ฐจ ๋ฒ์**: {min_error_weight:.1f}% ~ {max_error_weight:.1f}%
|
| 1047 |
-
- **๊น์ด ๋ณด์ **: {'โ
ํ์ฑํ' if enable_depth else 'โ ๏ธ ๋นํ์ฑํ'}
|
| 1048 |
-
|
| 1049 |
-
๐ฏ **ํ๊ฐ**: {'โ
์ฐ์ (MAPE < 25%)' if avg_error_weight < 25 else 'โ ๏ธ ๊ฐ์ ํ์ (MAPE โฅ 25%)'}
|
| 1050 |
-
|
| 1051 |
-
๐ก **์ฐธ๊ณ **: ์ค์ ์ฒด์ฅ๊ณผ ์ฒด์ค ๋ฐ์ดํฐ๋ ์์
ํ์ผ์์ ๋ก๋๋์์ต๋๋ค.
|
| 1052 |
-
|
| 1053 |
-
๐ธ **์ด๋ฏธ์ง ๊ฒฐ๊ณผ**: ์๋ ๊ฐค๋ฌ๋ฆฌ์์ ๊ฐ ์ํ์ ์์ธก/์ค์ ๊ฐ์ ํ์ธํ ์ ์์ต๋๋ค.
|
| 1054 |
-
|
| 1055 |
-
๐พ **์ ์ฅ ์์น**: `{results_folder}` ํด๋์ {len(visualized_images)}๊ฐ ์ด๋ฏธ์ง ์ ์ฅ๋จ
|
| 1056 |
-
"""
|
| 1057 |
-
|
| 1058 |
-
return stats_text, df, fig, visualized_images
|
| 1059 |
-
|
| 1060 |
-
# =====================
|
| 1061 |
-
# Gradio UI
|
| 1062 |
-
# =====================
|
| 1063 |
-
|
| 1064 |
-
with gr.Blocks(title="๐ฆ RT-DETR ์์ฐ ๋ถ์", theme=gr.themes.Soft()) as demo:
|
| 1065 |
-
|
| 1066 |
-
gr.Markdown("""
|
| 1067 |
-
# ๐ฆ ํฐ๋ค๋ฆฌ์์ฐ AI ๋ถ์ ์์คํ
(RT-DETR)
|
| 1068 |
-
|
| 1069 |
-
### ์ค์๊ฐ ๊ฐ์ฒด ๊ฒ์ถ + ์ฒด์ฅ/์ฒด์ค ์๋ ์ถ์
|
| 1070 |
-
**๋ชจ๋ธ**: RT-DETR (PekingU/rtdetr_r50vd_coco_o365) | **ํ๊ท**: W = 0.0035 ร L^3.13
|
| 1071 |
-
**์ ํ๋**: Rยฒ = 0.929, MAPE = 6.4% | **๋๋ฐ์ด์ค**: """ + ("๐ GPU" if torch.cuda.is_available() else "๐ป CPU") + """
|
| 1072 |
-
|
| 1073 |
-
---
|
| 1074 |
-
""")
|
| 1075 |
-
|
| 1076 |
-
with gr.Tabs():
|
| 1077 |
-
# ๊ฒ์ถ ํญ
|
| 1078 |
-
with gr.TabItem("๐ ๊ฐ์ฒด ๊ฒ์ถ"):
|
| 1079 |
-
with gr.Row():
|
| 1080 |
-
with gr.Column():
|
| 1081 |
-
input_img = gr.Image(
|
| 1082 |
-
label="์
๋ ฅ ์ด๋ฏธ์ง",
|
| 1083 |
-
type="pil"
|
| 1084 |
-
)
|
| 1085 |
-
|
| 1086 |
-
conf_slider = gr.Slider(
|
| 1087 |
-
0.1, 0.9, 0.5,
|
| 1088 |
-
label="๊ฒ์ถ ์ ๋ขฐ๋ ์๊ณ๊ฐ (Confidence)",
|
| 1089 |
-
info="๋ฎ์์๋ก ๋ ๋ง์ ๊ฐ์ฒด ๊ฒ์ถ"
|
| 1090 |
-
)
|
| 1091 |
-
|
| 1092 |
-
iou_slider = gr.Slider(
|
| 1093 |
-
0.1, 0.9, 0.5,
|
| 1094 |
-
label="IoU ์๊ณ๊ฐ (Overlap) - Roboflow ์ ์ฉ",
|
| 1095 |
-
info="๊ฒน์น๋ ๋ฐ์ค ์ ๊ฑฐ ๊ธฐ์ค (NMS, ๋์์๋ก ๋ ๋ง์ด ์ ์ง)"
|
| 1096 |
-
)
|
| 1097 |
-
|
| 1098 |
-
# ๋ชจ๋ธ ์ ํ
|
| 1099 |
-
model_selector = gr.Dropdown(
|
| 1100 |
-
choices=[
|
| 1101 |
-
("Roboflow (์์ฐ ์ ์ฉ - ์์กฐ)", "roboflow"),
|
| 1102 |
-
("RT-DETR (๋ฒ์ฉ - ์ธก์ ์ฉ)", "rtdetr")
|
| 1103 |
-
],
|
| 1104 |
-
value="roboflow" if ROBOFLOW_LOADED else "rtdetr",
|
| 1105 |
-
label="๐ค ๊ฒ์ถ ๋ชจ๋ธ ์ ํ",
|
| 1106 |
-
info="Roboflow: ์ด์์๋ ์์ฐ(์์กฐ) ์ ์ฉ | RT-DETR: ์ธก์ ์ฉ ๋งคํธ ์ ์์ฐ"
|
| 1107 |
-
)
|
| 1108 |
-
|
| 1109 |
-
with gr.Row():
|
| 1110 |
-
pixel_scale = gr.Number(
|
| 1111 |
-
value=92,
|
| 1112 |
-
label="ํฝ์
ํฌ๊ธฐ (px)",
|
| 1113 |
-
info="์ฐธ์กฐ ๊ฐ์ฒด์ ํฝ์
ํฌ๊ธฐ"
|
| 1114 |
-
)
|
| 1115 |
-
cm_scale = gr.Number(
|
| 1116 |
-
value=1,
|
| 1117 |
-
label="์ค์ ํฌ๊ธฐ (cm)",
|
| 1118 |
-
info="์ฐธ์กฐ ๊ฐ์ฒด์ ์ค์ ํฌ๊ธฐ"
|
| 1119 |
-
)
|
| 1120 |
-
|
| 1121 |
-
depth_checkbox = gr.Checkbox(
|
| 1122 |
-
value=False,
|
| 1123 |
-
label="๐ ๊น์ด ๊ธฐ๋ฐ ์๊ทผ ๋ณด์ ํ์ฑํ (RT-DETR๋ง)",
|
| 1124 |
-
info="Depth-Anything V2๋ก ์๋ ์๊ทผ ์๊ณก ๋ณด์ "
|
| 1125 |
-
)
|
| 1126 |
-
|
| 1127 |
-
detect_btn = gr.Button(
|
| 1128 |
-
"๐ ๊ฒ์ถ ์คํ",
|
| 1129 |
-
variant="primary",
|
| 1130 |
-
size="lg"
|
| 1131 |
-
)
|
| 1132 |
-
|
| 1133 |
-
with gr.Column():
|
| 1134 |
-
output_img = gr.Image(
|
| 1135 |
-
label="๊ฒ์ถ ๊ฒฐ๊ณผ"
|
| 1136 |
-
)
|
| 1137 |
-
depth_img = gr.Image(
|
| 1138 |
-
label="๊น์ด ๋งต (ํ๋์=๊ฐ๊น์, ๋
ธ๋์=๋ฉ์)"
|
| 1139 |
-
)
|
| 1140 |
-
stats = gr.Markdown()
|
| 1141 |
-
|
| 1142 |
-
results_df = gr.Dataframe(
|
| 1143 |
-
label="๊ฒ์ถ ์์ธ ์ ๋ณด",
|
| 1144 |
-
wrap=True
|
| 1145 |
-
)
|
| 1146 |
-
|
| 1147 |
-
# ํ๊ฐ ํญ
|
| 1148 |
-
with gr.TabItem("๐ ์ฑ๋ฅ ํ๊ฐ"):
|
| 1149 |
-
gr.Markdown("""
|
| 1150 |
-
### ํ๊ท ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ
|
| 1151 |
-
|
| 1152 |
-
์ค์ธก ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฒด์ฅ-์ฒด์ค ํ๊ท ๋ชจ๋ธ์ ์ ํ๋๋ฅผ ํ๊ฐํฉ๋๋ค.
|
| 1153 |
-
""")
|
| 1154 |
-
|
| 1155 |
-
eval_btn = gr.Button(
|
| 1156 |
-
"๐ ํ๊ฐ ์คํ",
|
| 1157 |
-
variant="primary"
|
| 1158 |
-
)
|
| 1159 |
-
eval_text = gr.Markdown()
|
| 1160 |
-
eval_plot = gr.Plot()
|
| 1161 |
-
|
| 1162 |
-
# ๋ฐ์ดํฐ ํญ
|
| 1163 |
-
with gr.TabItem("๐ ์ค์ธก ๋ฐ์ดํฐ"):
|
| 1164 |
-
gr.Markdown(f"""
|
| 1165 |
-
### ๋ฐ์ดํฐ ์์ฝ
|
| 1166 |
-
|
| 1167 |
-
- **์ํ ์**: {len(REAL_DATA)}๊ฐ
|
| 1168 |
-
- **์ฒด์ฅ ๋ฒ์**: 7.5 - 13.1 cm
|
| 1169 |
-
- **์ฒด์ค ๋ฒ์**: 2.0 - 11.3 g
|
| 1170 |
-
- **๋ฐ์ดํฐ ์ถ์ฒ**: ์ค์ธก ๋ฐ์ดํฐ
|
| 1171 |
-
""")
|
| 1172 |
-
|
| 1173 |
-
data_df = gr.Dataframe(
|
| 1174 |
-
value=pd.DataFrame(REAL_DATA),
|
| 1175 |
-
label="์ค์ธก ๋ฐ์ดํฐ",
|
| 1176 |
-
wrap=True
|
| 1177 |
-
)
|
| 1178 |
-
|
| 1179 |
-
export_btn = gr.Button("๐พ CSV ๋ค์ด๋ก๋")
|
| 1180 |
-
file_output = gr.File(label="๋ค์ด๋ก๋")
|
| 1181 |
-
|
| 1182 |
-
# ์ ํ๋ ๊ฒ์ฆ ํญ
|
| 1183 |
-
with gr.TabItem("๐ฏ ์ ํ๋ ๊ฒ์ฆ"):
|
| 1184 |
-
gr.Markdown("""
|
| 1185 |
-
### ์ค์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ก ์ ํ๋ ๊ฒ์ฆ
|
| 1186 |
-
|
| 1187 |
-
ํ
์คํธ ๋ฐ์ดํฐ์
์ ๋ฐฐ์น ์ฒ๋ฆฌํ์ฌ ์์ธก ์ ํ๋๋ฅผ ์ธก์ ํฉ๋๋ค.
|
| 1188 |
-
""")
|
| 1189 |
-
|
| 1190 |
-
with gr.Row():
|
| 1191 |
-
with gr.Column():
|
| 1192 |
-
test_folder = gr.Textbox(
|
| 1193 |
-
value="d:/Project/VIDraft/Shrimp/data/251015",
|
| 1194 |
-
label="ํ
์คํธ ๋ฐ์ดํฐ ํด๋",
|
| 1195 |
-
info="251015_XX.jpg ํ์์ ์ด๋ฏธ์ง๊ฐ ์๋ ํด๋"
|
| 1196 |
-
)
|
| 1197 |
-
|
| 1198 |
-
with gr.Row():
|
| 1199 |
-
test_pixel_scale = gr.Number(
|
| 1200 |
-
value=92,
|
| 1201 |
-
label="ํฝ์
ํฌ๊ธฐ (px)",
|
| 1202 |
-
info="์ฐธ์กฐ ์์ ํฝ์
ํฌ๊ธฐ"
|
| 1203 |
-
)
|
| 1204 |
-
test_cm_scale = gr.Number(
|
| 1205 |
-
value=1,
|
| 1206 |
-
label="์ค์ ํฌ๊ธฐ (cm)",
|
| 1207 |
-
info="์ฐธ์กฐ ์์ ์ค์ ํฌ๊ธฐ"
|
| 1208 |
-
)
|
| 1209 |
-
|
| 1210 |
-
test_depth_checkbox = gr.Checkbox(
|
| 1211 |
-
value=False,
|
| 1212 |
-
label="๐ ๊น์ด ๊ธฐ๋ฐ ์๊ทผ ๋ณด์ ํ์ฑํ",
|
| 1213 |
-
info="Depth-Anything V2๋ก ์๋ ์๊ทผ ์๊ณก ๋ณด์ "
|
| 1214 |
-
)
|
| 1215 |
-
|
| 1216 |
-
test_btn = gr.Button(
|
| 1217 |
-
"๐ ๋ฐฐ์น ํ
์คํธ ์คํ",
|
| 1218 |
-
variant="primary",
|
| 1219 |
-
size="lg"
|
| 1220 |
-
)
|
| 1221 |
-
|
| 1222 |
-
with gr.Column():
|
| 1223 |
-
test_stats = gr.Markdown()
|
| 1224 |
-
|
| 1225 |
-
test_plot = gr.Plot(label="์์ธก vs ์ค์ ๋น๊ต")
|
| 1226 |
-
test_results_df = gr.Dataframe(
|
| 1227 |
-
label="์์ธ ๊ฒฐ๊ณผ",
|
| 1228 |
-
wrap=True
|
| 1229 |
-
)
|
| 1230 |
-
|
| 1231 |
-
gr.Markdown("### ๐ธ ์๊ฐํ ๊ฒฐ๊ณผ (์์ธก vs ์ค์ )")
|
| 1232 |
-
test_gallery = gr.Gallery(
|
| 1233 |
-
label="๊ฒ์ถ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง",
|
| 1234 |
-
show_label=True,
|
| 1235 |
-
columns=3,
|
| 1236 |
-
rows=2,
|
| 1237 |
-
height="auto",
|
| 1238 |
-
object_fit="contain"
|
| 1239 |
-
)
|
| 1240 |
-
|
| 1241 |
-
# ์ ๋ณด ํญ
|
| 1242 |
-
with gr.TabItem("โน๏ธ ์ฌ์ฉ ๋ฐฉ๋ฒ"):
|
| 1243 |
-
gr.Markdown("""
|
| 1244 |
-
## ๐ ์ฌ์ฉ ๊ฐ์ด๋
|
| 1245 |
-
|
| 1246 |
-
### 1๏ธโฃ ๊ฐ์ฒด ๊ฒ์ถ
|
| 1247 |
-
1. ์์ฐ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์
|
| 1248 |
-
2. ์ ๋ขฐ๋ ์๏ฟฝ๏ฟฝ๏ฟฝ๊ฐ์ ์กฐ์ ํ์ธ์ (๊ธฐ๋ณธ๊ฐ: 0.5)
|
| 1249 |
-
3. **๊น์ด ๋ณด์ ํ์ฑํ** (๊ถ์ฅ): ์๊ทผ ์๊ณก ์๋ ๋ณด์
|
| 1250 |
-
4. ์ค์ผ์ผ ๋ณด์ : ์ค์ ํฌ๊ธฐ๋ฅผ ์๊ณ ์๋ค๋ฉด ํฝ์
-cm ๋น์จ์ ์ค์ ํ์ธ์
|
| 1251 |
-
5. "๊ฒ์ถ ์คํ" ๋ฒํผ์ ํด๋ฆญํ์ธ์
|
| 1252 |
-
|
| 1253 |
-
### 2๏ธโฃ ๊น์ด ๊ธฐ๋ฐ ์๊ทผ ๋ณด์ (NEW! ๐ฅ)
|
| 1254 |
-
- **Depth-Anything V2** ๋ชจ๋ธ๋ก ์ด๋ฏธ์ง์ ๊น์ด ๋งต ์๋ ์์ฑ
|
| 1255 |
-
- ๊ฐ ์์ฐ์ ์๋์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ์ฌ ์ค์ผ์ผ ์๋ ๋ณด์
|
| 1256 |
-
- **์๊ทผ ์๊ณก ํจ๊ณผ๋ฅผ ์๋์ผ๋ก ์ ๊ฑฐ**ํ์ฌ ์ ํ๋ ํฅ์
|
| 1257 |
-
- ๊น์ด ๋งต ์๊ฐํ: ํ๋์=๊ฐ๊น์, ๋
ธ๋์=๋ฉ์
|
| 1258 |
-
- ์ถ๊ฐ ์ฅ๋น๋ ๋ง์ปค ์์ด ๋จ์ผ ์ด๋ฏธ์ง๋ง์ผ๋ก ์๋
|
| 1259 |
-
|
| 1260 |
-
### 3๏ธโฃ ์ค์ผ์ผ ๋ณด์
|
| 1261 |
-
- ์ด๋ฏธ์ง์์ ์๊ณ ์๋ ๊ฐ์ฒด์ ํฝ์
ํฌ๊ธฐ์ ์ค์ ํฌ๊ธฐ๋ฅผ ์
๋ ฅํ์ธ์
|
| 1262 |
-
- ์: ์๊ฐ ๋ณด์ธ๋ค๋ฉด, ์์ ํฝ์
๊ธธ์ด์ ์ค์ ๊ธธ์ด(cm)๋ฅผ ์
๋ ฅ
|
| 1263 |
-
- ๊น์ด ๋ณด์ ๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋์ฑ ์ ํํ ์ธก์ ๊ฐ๋ฅ
|
| 1264 |
-
|
| 1265 |
-
### 4๏ธโฃ ๊ฒฐ๊ณผ ํด์
|
| 1266 |
-
- **์ด๋ก์ ๋ฐ์ค**: ์ค์ฐจ < 10%
|
| 1267 |
-
- **์ฃผํฉ์ ๋ฐ์ค**: ์ค์ฐจ 10-20%
|
| 1268 |
-
- **๋นจ๊ฐ์ ๋ฐ์ค**: ์ค์ฐจ > 20%
|
| 1269 |
-
|
| 1270 |
-
### 5๏ธโฃ ์ฑ๋ฅ ํ๊ฐ
|
| 1271 |
-
- "์ฑ๋ฅ ํ๊ฐ" ํญ์์ ํ๊ท ๋ชจ๋ธ์ ์ ํ๋๋ฅผ ํ์ธํ์ธ์
|
| 1272 |
-
- Rยฒ, MAPE, MAE, RMSE ์งํ ์ ๊ณต
|
| 1273 |
-
|
| 1274 |
-
### 6๏ธโฃ ์ ํ๋ ๊ฒ์ฆ (NEW! ๐ฅ)
|
| 1275 |
-
- "์ ํ๋ ๊ฒ์ฆ" ํญ์์ ์ค์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ก ์ ํ๋ ์ธก์
|
| 1276 |
-
- ํ
์คํธ ๋ฐ์ดํฐ ํด๋๋ฅผ ์ง์ ํ๊ณ ๋ฐฐ์น ์ฒ๋ฆฌ
|
| 1277 |
-
- OCR๋ก ์ ์์ ์ธ LCD์์ ์ค์ ๋ฌด๊ฒ ์๋ ์ฝ๊ธฐ
|
| 1278 |
-
- ์์ธก vs ์ค์ ๋น๊ต ์ฐจํธ ๋ฐ ํต๊ณ ์ ๊ณต
|
| 1279 |
-
|
| 1280 |
-
### 7๏ธโฃ ๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ
|
| 1281 |
-
- "์ค์ธก ๋ฐ์ดํฐ" ํญ์์ CSV ํ์ผ๋ก ๋ค์ด๋ก๋ ๊ฐ๋ฅ
|
| 1282 |
-
|
| 1283 |
-
---
|
| 1284 |
-
|
| 1285 |
-
## โ๏ธ ์์คํ
์ ๋ณด
|
| 1286 |
-
|
| 1287 |
-
- **๊ฒ์ถ ๋ชจ๋ธ**: RT-DETR (Real-Time DEtection TRansformer)
|
| 1288 |
-
- **๊น์ด ์ถ์ ๋ชจ๋ธ**: Depth-Anything V2 Small (Monocular Depth Estimation)
|
| 1289 |
-
- **ํ๊ท ๋ชจ๋ธ**: Power Law (W = a ร L^b)
|
| 1290 |
-
- **๋๋ฐ์ด์ค**: """ + ("GPU (CUDA)" if torch.cuda.is_available() else "CPU") + """
|
| 1291 |
-
- **์ต์ ํ**: CPU ๋ชจ๋, torch.no_grad(), FP32
|
| 1292 |
-
- **์๊ทผ ๋ณด์ **: ๊น์ด ๋งต ๊ธฐ๋ฐ ์๋ ์ค์ผ์ผ ์กฐ์
|
| 1293 |
-
|
| 1294 |
-
## ๐ง ๋ฌธ์ ํด๊ฒฐ
|
| 1295 |
-
|
| 1296 |
-
**๊ฒ์ถ์ด ์ ๋ ๋**:
|
| 1297 |
-
- ์ ๋ขฐ๋ ์๊ณ๊ฐ์ ๋ฎ์ถฐ๋ณด์ธ์ (0.3 ์ดํ)
|
| 1298 |
-
- ์ด๋ฏธ์ง ํ์ง์ ํ์ธํ์ธ์ (ํด์๋, ๋ฐ๊ธฐ)
|
| 1299 |
-
|
| 1300 |
-
**์ ํ๋๊ฐ ๋ฎ์ ๋**:
|
| 1301 |
-
- ์ค์ผ์ผ ๋ณด์ ์ ์ ํํ ์
๋ ฅํ์ธ์
|
| 1302 |
-
- ์์ฐ ์ ์ฉ fine-tuning ๋ชจ๋ธ์ด ํ์ํ ์ ์์ต๋๋ค
|
| 1303 |
-
|
| 1304 |
-
**์๋๊ฐ ๋๋ฆด ๋**:
|
| 1305 |
-
- GPU ๊ฐ์์ ์ฌ์ฉํ์ธ์ (HF Space: GPU T4)
|
| 1306 |
-
- ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ค์ด์ธ์ (800x600 ๊ถ์ฅ)
|
| 1307 |
-
""")
|
| 1308 |
-
|
| 1309 |
-
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 1310 |
-
detect_btn.click(
|
| 1311 |
-
process_image,
|
| 1312 |
-
[input_img, model_selector, conf_slider, iou_slider, pixel_scale, cm_scale, depth_checkbox],
|
| 1313 |
-
[output_img, depth_img, stats, results_df]
|
| 1314 |
-
)
|
| 1315 |
-
|
| 1316 |
-
eval_btn.click(
|
| 1317 |
-
evaluate_model,
|
| 1318 |
-
[],
|
| 1319 |
-
[eval_text, eval_plot]
|
| 1320 |
-
)
|
| 1321 |
-
|
| 1322 |
-
export_btn.click(
|
| 1323 |
-
export_data,
|
| 1324 |
-
[],
|
| 1325 |
-
file_output
|
| 1326 |
-
)
|
| 1327 |
-
|
| 1328 |
-
test_btn.click(
|
| 1329 |
-
process_test_dataset,
|
| 1330 |
-
[test_folder, test_pixel_scale, test_cm_scale, test_depth_checkbox],
|
| 1331 |
-
[test_stats, test_results_df, test_plot, test_gallery]
|
| 1332 |
-
)
|
| 1333 |
-
|
| 1334 |
-
# ์คํ
|
| 1335 |
-
if __name__ == "__main__":
|
| 1336 |
-
demo.queue(max_size=10) # CPU ์ต์ ํ: ํ ํฌ๊ธฐ ์ ํ
|
| 1337 |
-
demo.launch(
|
| 1338 |
-
share=False,
|
| 1339 |
-
server_name="0.0.0.0",
|
| 1340 |
-
server_port=7860,
|
| 1341 |
-
show_error=True
|
| 1342 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,163 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
์์ฐ ๊ฒ์ถ ์์คํ
๋ฐ๋ชจ ์น์ฑ
|
| 4 |
-
์ต์ข
์ต์ ํ ๋ฒ์ (Precision=44.2%, Recall=94%, F1=56.1%)
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import gradio as gr
|
| 10 |
-
import numpy as np
|
| 11 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 12 |
-
from test_visual_validation import (
|
| 13 |
-
load_rtdetr_model,
|
| 14 |
-
detect_with_rtdetr,
|
| 15 |
-
apply_universal_filter
|
| 16 |
-
)
|
| 17 |
-
|
| 18 |
-
# ๋ชจ๋ธ ๋ก๋ (์์์ ํ๋ฒ๋ง)
|
| 19 |
-
print("๐ RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์ค...")
|
| 20 |
-
processor, model = load_rtdetr_model()
|
| 21 |
-
print("โ
RT-DETR ๋ก๋ฉ ์๋ฃ\n")
|
| 22 |
-
|
| 23 |
-
def detect_shrimp(image, confidence_threshold, filter_threshold):
|
| 24 |
-
"""์์ฐ ๊ฒ์ถ ํจ์"""
|
| 25 |
-
if image is None:
|
| 26 |
-
return None, "์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์."
|
| 27 |
-
|
| 28 |
-
# PIL Image๋ก ๋ณํ
|
| 29 |
-
if isinstance(image, np.ndarray):
|
| 30 |
-
image = Image.fromarray(image)
|
| 31 |
-
|
| 32 |
-
# RT-DETR ๊ฒ์ถ
|
| 33 |
-
all_detections = detect_with_rtdetr(image, processor, model, confidence_threshold)
|
| 34 |
-
|
| 35 |
-
# ํํฐ ์ ์ฉ
|
| 36 |
-
filtered_detections = apply_universal_filter(all_detections, image, filter_threshold)
|
| 37 |
-
|
| 38 |
-
# ์๊ฐํ
|
| 39 |
-
result_image = image.copy()
|
| 40 |
-
draw = ImageDraw.Draw(result_image)
|
| 41 |
-
|
| 42 |
-
try:
|
| 43 |
-
font = ImageFont.truetype("arial.ttf", 20)
|
| 44 |
-
font_small = ImageFont.truetype("arial.ttf", 14)
|
| 45 |
-
except:
|
| 46 |
-
font = ImageFont.load_default()
|
| 47 |
-
font_small = ImageFont.load_default()
|
| 48 |
-
|
| 49 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 50 |
-
for i, det in enumerate(filtered_detections, 1):
|
| 51 |
-
x1, y1, x2, y2 = det['bbox']
|
| 52 |
-
|
| 53 |
-
# ๋ฐ์ค
|
| 54 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=4)
|
| 55 |
-
|
| 56 |
-
# ๋ผ๋ฒจ
|
| 57 |
-
score = det['filter_score']
|
| 58 |
-
conf = det['confidence']
|
| 59 |
-
label = f"#{i} | Score:{score:.0f} | Conf:{conf:.2f}"
|
| 60 |
-
|
| 61 |
-
# ๋ฐฐ๊ฒฝ
|
| 62 |
-
bbox = draw.textbbox((x1, y1-25), label, font=font_small)
|
| 63 |
-
draw.rectangle(bbox, fill="lime")
|
| 64 |
-
draw.text((x1, y1-25), label, fill="black", font=font_small)
|
| 65 |
-
|
| 66 |
-
# ๊ฒฐ๊ณผ ํ
์คํธ
|
| 67 |
-
info = f"""
|
| 68 |
-
๐ ๊ฒ์ถ ๊ฒฐ๊ณผ:
|
| 69 |
-
โข RT-DETR ๊ฒ์ถ: {len(all_detections)}๊ฐ
|
| 70 |
-
โข ํํฐ ํต๊ณผ: {len(filtered_detections)}๊ฐ
|
| 71 |
-
โข ์ ๊ฑฐ๋จ: {len(all_detections) - len(filtered_detections)}๊ฐ
|
| 72 |
-
|
| 73 |
-
โ๏ธ ์ค์ :
|
| 74 |
-
โข RT-DETR Confidence: {confidence_threshold}
|
| 75 |
-
โข Filter Threshold: {filter_threshold}
|
| 76 |
-
|
| 77 |
-
๐ฏ ์ฑ๋ฅ (50๊ฐ GT ๊ธฐ์ค):
|
| 78 |
-
โข Precision: 44.2%
|
| 79 |
-
โข Recall: 94.0%
|
| 80 |
-
โข F1 Score: 56.1%
|
| 81 |
-
"""
|
| 82 |
-
|
| 83 |
-
if len(filtered_detections) > 0:
|
| 84 |
-
info += f"\nโ
{len(filtered_detections)}๊ฐ์ ์์ฐ๋ฅผ ๊ฒ์ถํ์ต๋๋ค!"
|
| 85 |
-
else:
|
| 86 |
-
info += "\nโ ๏ธ ์์ฐ๊ฐ ๊ฒ์ถ๋์ง ์์์ต๋๋ค. Threshold๋ฅผ ๋ฎ์ถฐ๋ณด์ธ์."
|
| 87 |
-
|
| 88 |
-
return result_image, info
|
| 89 |
-
|
| 90 |
-
# Gradio ์ธํฐํ์ด์ค
|
| 91 |
-
with gr.Blocks(title="๐ฆ ์์ฐ ๊ฒ์ถ ์์คํ
v1.0") as demo:
|
| 92 |
-
gr.Markdown("""
|
| 93 |
-
# ๐ฆ ์์ฐ ๊ฒ์ถ ์์คํ
v1.0
|
| 94 |
-
**RT-DETR + Universal Filter (์ต์ ํ ์๋ฃ)**
|
| 95 |
-
|
| 96 |
-
- **Precision**: 44.2% (๊ฒ์ถ๋ ๋ฐ์ค ์ค ์ค์ ์์ฐ ๋น์จ)
|
| 97 |
-
- **Recall**: 94.0% (์ค์ ์์ฐ ์ค ๊ฒ์ถ๋ ๋น์จ)
|
| 98 |
-
- **F1 Score**: 56.1% (์ ์ฒด ์ฑ๋ฅ)
|
| 99 |
-
""")
|
| 100 |
-
|
| 101 |
-
with gr.Row():
|
| 102 |
-
with gr.Column():
|
| 103 |
-
input_image = gr.Image(label="๐ค ์ด๋ฏธ์ง ์
๋ก๋", type="pil")
|
| 104 |
-
|
| 105 |
-
with gr.Row():
|
| 106 |
-
conf_slider = gr.Slider(
|
| 107 |
-
minimum=0.05,
|
| 108 |
-
maximum=0.5,
|
| 109 |
-
value=0.065,
|
| 110 |
-
step=0.005,
|
| 111 |
-
label="๐ฏ RT-DETR Confidence",
|
| 112 |
-
info="๋ฎ์์๋ก ๋ ๋ง์ด ๊ฒ์ถ (๊ถ์ฅ: 0.065)"
|
| 113 |
-
)
|
| 114 |
-
|
| 115 |
-
filter_slider = gr.Slider(
|
| 116 |
-
minimum=50,
|
| 117 |
-
maximum=100,
|
| 118 |
-
value=90,
|
| 119 |
-
step=5,
|
| 120 |
-
label="๐ Filter Threshold",
|
| 121 |
-
info="๋์์๋ก ์๊ฒฉํ ํํฐ๋ง (๊ถ์ฅ: 90)"
|
| 122 |
-
)
|
| 123 |
-
|
| 124 |
-
detect_btn = gr.Button("๐ ์์ฐ ๊ฒ์ถ ์์", variant="primary", size="lg")
|
| 125 |
-
|
| 126 |
-
with gr.Column():
|
| 127 |
-
output_image = gr.Image(label="๐ ๊ฒ์ถ ๊ฒฐ๊ณผ")
|
| 128 |
-
output_text = gr.Textbox(label="๐ ์์ธ ์ ๋ณด", lines=15)
|
| 129 |
-
|
| 130 |
-
# ์์ ์ด๋ฏธ์ง
|
| 131 |
-
gr.Examples(
|
| 132 |
-
examples=[
|
| 133 |
-
["data/test_shrimp_tank.png", 0.065, 90],
|
| 134 |
-
],
|
| 135 |
-
inputs=[input_image, conf_slider, filter_slider],
|
| 136 |
-
label="๐ ์์ ์ด๋ฏธ์ง (ํด๋ฆญํ์ฌ ํ
์คํธ)"
|
| 137 |
-
)
|
| 138 |
-
|
| 139 |
-
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 140 |
-
detect_btn.click(
|
| 141 |
-
fn=detect_shrimp,
|
| 142 |
-
inputs=[input_image, conf_slider, filter_slider],
|
| 143 |
-
outputs=[output_image, output_text]
|
| 144 |
-
)
|
| 145 |
-
|
| 146 |
-
# ์ฑ ์คํ
|
| 147 |
-
if __name__ == "__main__":
|
| 148 |
-
print("="*60)
|
| 149 |
-
print("๐ฆ ์์ฐ ๊ฒ์ถ ์์คํ
v1.0 ์์")
|
| 150 |
-
print("="*60)
|
| 151 |
-
print("โ๏ธ ์ต์ ์ค์ :")
|
| 152 |
-
print(" - RT-DETR Confidence: 0.065")
|
| 153 |
-
print(" - Filter Threshold: 90")
|
| 154 |
-
print("\n๐ ์ฑ๋ฅ (50๊ฐ GT ๊ธฐ์ค):")
|
| 155 |
-
print(" - Precision: 44.2%")
|
| 156 |
-
print(" - Recall: 94.0%")
|
| 157 |
-
print(" - F1 Score: 56.1%")
|
| 158 |
-
print("="*60)
|
| 159 |
-
|
| 160 |
-
demo.launch(
|
| 161 |
-
server_name="0.0.0.0",
|
| 162 |
-
share=False
|
| 163 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,1342 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
๐ฆ ํฐ๋ค๋ฆฌ์์ฐ ๋ถ์ ์์คํ
- RT-DETR CPU ์ต์ ํ ๋ฒ์
|
| 3 |
-
์ค์ ๊ฐ์ฒด ๊ฒ์ถ + ์ฒด์ฅ/์ฒด์ค ์๋ ์ถ์
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import gradio as gr
|
| 7 |
-
import numpy as np
|
| 8 |
-
import pandas as pd
|
| 9 |
-
import plotly.graph_objects as go
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
from datetime import datetime
|
| 12 |
-
import torch
|
| 13 |
-
from transformers import (
|
| 14 |
-
RTDetrForObjectDetection,
|
| 15 |
-
RTDetrImageProcessor,
|
| 16 |
-
AutoImageProcessor,
|
| 17 |
-
AutoModelForDepthEstimation
|
| 18 |
-
)
|
| 19 |
-
import os
|
| 20 |
-
import warnings
|
| 21 |
-
warnings.filterwarnings('ignore')
|
| 22 |
-
|
| 23 |
-
# =====================
|
| 24 |
-
# ์ค์ธก ๋ฐ์ดํฐ (260๊ฐ ์ํ)
|
| 25 |
-
# =====================
|
| 26 |
-
REAL_DATA = [
|
| 27 |
-
{"length": 7.5, "weight": 2.0}, {"length": 7.7, "weight": 2.1},
|
| 28 |
-
{"length": 8.3, "weight": 2.7}, {"length": 8.4, "weight": 2.9},
|
| 29 |
-
{"length": 8.6, "weight": 3.1}, {"length": 8.7, "weight": 3.0},
|
| 30 |
-
{"length": 8.9, "weight": 3.2}, {"length": 9.1, "weight": 3.4},
|
| 31 |
-
{"length": 9.4, "weight": 4.0}, {"length": 9.7, "weight": 4.7},
|
| 32 |
-
{"length": 9.9, "weight": 4.7}, {"length": 10.0, "weight": 4.6},
|
| 33 |
-
{"length": 10.2, "weight": 5.5}, {"length": 10.3, "weight": 5.8},
|
| 34 |
-
{"length": 10.4, "weight": 5.5}, {"length": 10.7, "weight": 6.1},
|
| 35 |
-
{"length": 10.9, "weight": 6.0}, {"length": 11.0, "weight": 6.2},
|
| 36 |
-
{"length": 11.3, "weight": 5.8}, {"length": 11.4, "weight": 6.5},
|
| 37 |
-
{"length": 11.6, "weight": 7.5}, {"length": 11.7, "weight": 8.1},
|
| 38 |
-
{"length": 11.9, "weight": 9.4}, {"length": 12.0, "weight": 8.8},
|
| 39 |
-
{"length": 12.3, "weight": 10.2}, {"length": 12.5, "weight": 10.9},
|
| 40 |
-
{"length": 12.7, "weight": 10.1}, {"length": 12.9, "weight": 10.7},
|
| 41 |
-
{"length": 13.0, "weight": 10.7}, {"length": 13.1, "weight": 11.3},
|
| 42 |
-
]
|
| 43 |
-
|
| 44 |
-
# =====================
|
| 45 |
-
# ํ๊ท ๋ชจ๋ธ
|
| 46 |
-
# =====================
|
| 47 |
-
class RegressionModel:
|
| 48 |
-
def __init__(self):
|
| 49 |
-
self.a = 0.003454
|
| 50 |
-
self.b = 3.1298
|
| 51 |
-
self.r2 = 0.929
|
| 52 |
-
self.mape = 6.4
|
| 53 |
-
|
| 54 |
-
def estimate_weight(self, length_cm):
|
| 55 |
-
"""์ฒด์ฅ์ผ๋ก ์ฒด์ค ์ถ์ : W = a ร L^b"""
|
| 56 |
-
return self.a * (length_cm ** self.b)
|
| 57 |
-
|
| 58 |
-
def calculate_error(self, true_weight, pred_weight):
|
| 59 |
-
"""์ค์ฐจ์จ ๊ณ์ฐ"""
|
| 60 |
-
if true_weight == 0:
|
| 61 |
-
return 0
|
| 62 |
-
return abs(true_weight - pred_weight) / true_weight * 100
|
| 63 |
-
|
| 64 |
-
# =====================
|
| 65 |
-
# ๊น์ด ์ถ์ ๊ธฐ
|
| 66 |
-
# =====================
|
| 67 |
-
class DepthEstimator:
|
| 68 |
-
def __init__(self, model_name="depth-anything/Depth-Anything-V2-Small-hf"):
|
| 69 |
-
"""Depth-Anything V2 ๋ชจ๋ธ ์ด๊ธฐํ"""
|
| 70 |
-
print(f"๐ Loading Depth Estimation model: {model_name}")
|
| 71 |
-
|
| 72 |
-
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 73 |
-
|
| 74 |
-
try:
|
| 75 |
-
self.processor = AutoImageProcessor.from_pretrained(model_name)
|
| 76 |
-
self.model = AutoModelForDepthEstimation.from_pretrained(model_name)
|
| 77 |
-
self.model.to(self.device)
|
| 78 |
-
self.model.eval()
|
| 79 |
-
print("โ
Depth model loaded successfully!")
|
| 80 |
-
self.enabled = True
|
| 81 |
-
except Exception as e:
|
| 82 |
-
print(f"โ ๏ธ Depth model loading failed: {e}")
|
| 83 |
-
print("๐ Running without depth correction")
|
| 84 |
-
self.enabled = False
|
| 85 |
-
|
| 86 |
-
@torch.no_grad()
|
| 87 |
-
def estimate_depth(self, image):
|
| 88 |
-
"""์ด๋ฏธ์ง์์ ๊น์ด ๋งต ์ถ์ """
|
| 89 |
-
if not self.enabled or image is None:
|
| 90 |
-
return None
|
| 91 |
-
|
| 92 |
-
# ์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ
|
| 93 |
-
inputs = self.processor(images=image, return_tensors="pt")
|
| 94 |
-
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 95 |
-
|
| 96 |
-
# ๊น์ด ์ถ์
|
| 97 |
-
outputs = self.model(**inputs)
|
| 98 |
-
predicted_depth = outputs.predicted_depth
|
| 99 |
-
|
| 100 |
-
# ์๋ณธ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ก ๋ฆฌ์ํ๋ง
|
| 101 |
-
depth_map = torch.nn.functional.interpolate(
|
| 102 |
-
predicted_depth.unsqueeze(1),
|
| 103 |
-
size=image.size[::-1], # (height, width)
|
| 104 |
-
mode="bicubic",
|
| 105 |
-
align_corners=False,
|
| 106 |
-
).squeeze().cpu().numpy()
|
| 107 |
-
|
| 108 |
-
# ์ ๊ทํ (0~1 ๋ฒ์)
|
| 109 |
-
depth_min = depth_map.min()
|
| 110 |
-
depth_max = depth_map.max()
|
| 111 |
-
depth_normalized = (depth_map - depth_min) / (depth_max - depth_min + 1e-8)
|
| 112 |
-
|
| 113 |
-
return depth_normalized
|
| 114 |
-
|
| 115 |
-
def get_depth_at_bbox(self, depth_map, bbox):
|
| 116 |
-
"""bbox ์ค์ฌ์ ์ ๊น์ด ๊ฐ ์ถ์ถ"""
|
| 117 |
-
if depth_map is None:
|
| 118 |
-
return 1.0 # ๊ธฐ๋ณธ๊ฐ
|
| 119 |
-
|
| 120 |
-
x1, y1, x2, y2 = bbox
|
| 121 |
-
center_x = int((x1 + x2) / 2)
|
| 122 |
-
center_y = int((y1 + y2) / 2)
|
| 123 |
-
|
| 124 |
-
# ๋ฒ์ ์ฒดํฌ
|
| 125 |
-
h, w = depth_map.shape
|
| 126 |
-
center_x = min(max(0, center_x), w - 1)
|
| 127 |
-
center_y = min(max(0, center_y), h - 1)
|
| 128 |
-
|
| 129 |
-
return depth_map[center_y, center_x]
|
| 130 |
-
|
| 131 |
-
def visualize_depth(self, depth_map):
|
| 132 |
-
"""๊น์ด ๋งต ์๊ฐํ"""
|
| 133 |
-
if depth_map is None:
|
| 134 |
-
return None
|
| 135 |
-
|
| 136 |
-
# ๊น์ด ๋งต์ ์ปฌ๋ฌ๋งต์ผ๋ก ๋ณํ
|
| 137 |
-
import matplotlib.cm as cm
|
| 138 |
-
colormap = cm.get_cmap('viridis')
|
| 139 |
-
depth_colored = (colormap(depth_map)[:, :, :3] * 255).astype(np.uint8)
|
| 140 |
-
|
| 141 |
-
return Image.fromarray(depth_colored)
|
| 142 |
-
|
| 143 |
-
# =====================
|
| 144 |
-
# RT-DETR ๊ฒ์ถ๏ฟฝ๏ฟฝ
|
| 145 |
-
# =====================
|
| 146 |
-
class RTDetrDetector:
|
| 147 |
-
def __init__(self, model_name="PekingU/rtdetr_r50vd_coco_o365"):
|
| 148 |
-
"""RT-DETR ๋ชจ๋ธ ์ด๊ธฐํ"""
|
| 149 |
-
print(f"๐ Loading RT-DETR model: {model_name}")
|
| 150 |
-
|
| 151 |
-
# CPU ์ต์ ํ ์ค์
|
| 152 |
-
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 153 |
-
print(f"๐ฑ Using device: {self.device}")
|
| 154 |
-
|
| 155 |
-
try:
|
| 156 |
-
# ๋ชจ๋ธ ๋ฐ ํ๋ก์ธ์ ๋ก๋ฉ
|
| 157 |
-
self.processor = RTDetrImageProcessor.from_pretrained(model_name)
|
| 158 |
-
self.model = RTDetrForObjectDetection.from_pretrained(model_name)
|
| 159 |
-
self.model.to(self.device)
|
| 160 |
-
self.model.eval() # ํ๊ฐ ๋ชจ๋
|
| 161 |
-
|
| 162 |
-
print("โ
Model loaded successfully!")
|
| 163 |
-
except Exception as e:
|
| 164 |
-
print(f"โ Model loading failed: {e}")
|
| 165 |
-
raise
|
| 166 |
-
|
| 167 |
-
self.regression_model = RegressionModel()
|
| 168 |
-
|
| 169 |
-
# ๊น์ด ์ถ์ ๊ธฐ ์ด๊ธฐํ
|
| 170 |
-
try:
|
| 171 |
-
self.depth_estimator = DepthEstimator()
|
| 172 |
-
except Exception as e:
|
| 173 |
-
print(f"โ ๏ธ Depth estimator initialization failed: {e}")
|
| 174 |
-
self.depth_estimator = None
|
| 175 |
-
|
| 176 |
-
# ์ฐธ์กฐ ์ค์ผ์ผ: ํฝ์
ํฌ๊ธฐ๋ฅผ ์ค์ cm๋ก ๋ณํ
|
| 177 |
-
# ์: 100ํฝ์
= 10cm (์ด๋ฏธ์ง์ ๋ฐ๋ผ ์กฐ์ ํ์)
|
| 178 |
-
self.pixel_to_cm_ratio = 0.1 # ๊ธฐ๋ณธ๊ฐ
|
| 179 |
-
|
| 180 |
-
# ๊น์ด ๋ณด์ ํ์ฑํ ํ๋๊ทธ
|
| 181 |
-
self.depth_correction_enabled = True
|
| 182 |
-
|
| 183 |
-
# ๋ง์ง๋ง ๊น์ด ๋งต ์บ์ฑ (UI ํ์์ฉ)
|
| 184 |
-
self.last_depth_map = None
|
| 185 |
-
|
| 186 |
-
def set_scale(self, pixel_length, actual_cm):
|
| 187 |
-
"""์ค์ผ์ผ ์ค์ (๋ณด์ ์ฉ)"""
|
| 188 |
-
self.pixel_to_cm_ratio = actual_cm / pixel_length
|
| 189 |
-
print(f"๐ Scale updated: {pixel_length}px = {actual_cm}cm")
|
| 190 |
-
|
| 191 |
-
@torch.no_grad() # CPU ์ต์ ํ: gradient ๊ณ์ฐ ๋นํ์ฑํ
|
| 192 |
-
def detect(self, image, confidence_threshold=0.5):
|
| 193 |
-
"""๊ฐ์ฒด ๊ฒ์ถ ์ํ"""
|
| 194 |
-
|
| 195 |
-
if image is None:
|
| 196 |
-
return []
|
| 197 |
-
|
| 198 |
-
# ๊น์ด ๋งต ์์ฑ (์๊ทผ ๋ณด์ ์ฉ)
|
| 199 |
-
depth_map = None
|
| 200 |
-
if self.depth_correction_enabled and self.depth_estimator and self.depth_estimator.enabled:
|
| 201 |
-
print("๐ Estimating depth map for perspective correction...")
|
| 202 |
-
depth_map = self.depth_estimator.estimate_depth(image)
|
| 203 |
-
self.last_depth_map = depth_map
|
| 204 |
-
|
| 205 |
-
# ์ฐธ์กฐ ๊น์ด (์ด๋ฏธ์ง ์ค์ฌ์ ํ๊ท ๊น์ด)
|
| 206 |
-
h, w = depth_map.shape
|
| 207 |
-
center_region = depth_map[h//4:3*h//4, w//4:3*w//4]
|
| 208 |
-
self.reference_depth = np.median(center_region)
|
| 209 |
-
print(f"๐ Reference depth: {self.reference_depth:.3f}")
|
| 210 |
-
|
| 211 |
-
# ์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ
|
| 212 |
-
inputs = self.processor(images=image, return_tensors="pt")
|
| 213 |
-
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 214 |
-
|
| 215 |
-
# ์ถ๋ก
|
| 216 |
-
outputs = self.model(**inputs)
|
| 217 |
-
|
| 218 |
-
# ๊ฒฐ๊ณผ ํ์ฒ๋ฆฌ
|
| 219 |
-
target_sizes = torch.tensor([image.size[::-1]]) # (height, width)
|
| 220 |
-
results = self.processor.post_process_object_detection(
|
| 221 |
-
outputs,
|
| 222 |
-
target_sizes=target_sizes,
|
| 223 |
-
threshold=confidence_threshold
|
| 224 |
-
)[0]
|
| 225 |
-
|
| 226 |
-
# ๊ฒ์ถ ๊ฒฐ๊ณผ ํ์ฑ
|
| 227 |
-
detections = []
|
| 228 |
-
|
| 229 |
-
for idx, (score, label, box) in enumerate(zip(
|
| 230 |
-
results["scores"],
|
| 231 |
-
results["labels"],
|
| 232 |
-
results["boxes"]
|
| 233 |
-
)):
|
| 234 |
-
# COCO ํด๋์ค ํํฐ๋ง (ํ์์)
|
| 235 |
-
# ์์ฐ ์ ์ฉ ๋ชจ๋ธ์ด ์๋๋ฏ๋ก ๋ชจ๋ ๊ฐ์ฒด ๊ฒ์ถ
|
| 236 |
-
# label 1 = "person", 16 = "bird", 17 = "cat" ๋ฑ
|
| 237 |
-
# ์ผ๋จ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๊ฒ์ถํ๋, ํฅํ fine-tuning ์ ์์ฐ๋ง ๊ฒ์ถ
|
| 238 |
-
|
| 239 |
-
x1, y1, x2, y2 = box.tolist()
|
| 240 |
-
bbox_width = x2 - x1
|
| 241 |
-
bbox_height = y2 - y1
|
| 242 |
-
|
| 243 |
-
# ๊น์ด ๊ธฐ๋ฐ ์ค์ผ์ผ ๋ณด์
|
| 244 |
-
depth_corrected_ratio = self.pixel_to_cm_ratio
|
| 245 |
-
|
| 246 |
-
if depth_map is not None:
|
| 247 |
-
# bbox ์ค์ฌ์ ์ ๊น์ด ๊ฐ ์ถ์ถ
|
| 248 |
-
object_depth = self.depth_estimator.get_depth_at_bbox(depth_map, [x1, y1, x2, y2])
|
| 249 |
-
|
| 250 |
-
# ๊น์ด ๋น์จ ๊ณ์ฐ (์ฐธ์กฐ ๊น์ด ๋๋น)
|
| 251 |
-
# ๊น์ด ๊ฐ์ด ํด์๋ก ๋จผ ๊ฑฐ๋ฆฌ โ ์ค์ ํฌ๊ธฐ๊ฐ ๋ ํผ
|
| 252 |
-
# Depth-Anything์์: ์์ ๊ฐ = ๊ฐ๊น์, ํฐ ๊ฐ = ๋ฉ์
|
| 253 |
-
depth_ratio = object_depth / (self.reference_depth + 1e-8)
|
| 254 |
-
|
| 255 |
-
# ์ค์ผ์ผ ๋ณด์ (์๊ทผ ํจ๊ณผ ๋ณด์ )
|
| 256 |
-
# ๋จผ ๋ฌผ์ฒด(ํฐ depth)๋ ํฝ์
์ด ์์ ๋ณด์ด๋ฏ๋ก ๋ณด์ ๊ณ์๋ฅผ ํฌ๊ฒ
|
| 257 |
-
depth_corrected_ratio = self.pixel_to_cm_ratio * depth_ratio
|
| 258 |
-
|
| 259 |
-
print(f" Object #{idx+1}: depth={object_depth:.3f}, ratio={depth_ratio:.3f}, corrected_scale={depth_corrected_ratio:.4f}")
|
| 260 |
-
|
| 261 |
-
# ์ฒด์ฅ ์ถ์ : bbox์ ๊ธด ๋ณ์ ์ฒด์ฅ์ผ๋ก ๊ฐ์ฃผ
|
| 262 |
-
length_pixels = max(bbox_width, bbox_height)
|
| 263 |
-
length_cm = length_pixels * depth_corrected_ratio
|
| 264 |
-
|
| 265 |
-
# ์ฒด์ค ์ถ์
|
| 266 |
-
pred_weight = self.regression_model.estimate_weight(length_cm)
|
| 267 |
-
|
| 268 |
-
detections.append({
|
| 269 |
-
"id": idx + 1,
|
| 270 |
-
"bbox": [x1, y1, x2, y2],
|
| 271 |
-
"length": round(length_cm, 1),
|
| 272 |
-
"pred_weight": round(pred_weight, 2),
|
| 273 |
-
"confidence": round(score.item(), 2),
|
| 274 |
-
"label": label.item()
|
| 275 |
-
})
|
| 276 |
-
|
| 277 |
-
return detections
|
| 278 |
-
|
| 279 |
-
def visualize(self, image, detections):
|
| 280 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ ์๊ฐํ"""
|
| 281 |
-
if image is None:
|
| 282 |
-
return None
|
| 283 |
-
|
| 284 |
-
img = image.copy()
|
| 285 |
-
draw = ImageDraw.Draw(img)
|
| 286 |
-
|
| 287 |
-
# ํฐํธ ์ค์ (๊ธฐ๋ณธ ํฐํธ ์ฌ์ฉ)
|
| 288 |
-
try:
|
| 289 |
-
font = ImageFont.truetype("arial.ttf", 12)
|
| 290 |
-
except:
|
| 291 |
-
font = ImageFont.load_default()
|
| 292 |
-
|
| 293 |
-
for det in detections:
|
| 294 |
-
x1, y1, x2, y2 = det["bbox"]
|
| 295 |
-
|
| 296 |
-
# ๊ณ ์ ์์ (๋
น์)
|
| 297 |
-
color = "lime"
|
| 298 |
-
|
| 299 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 300 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 301 |
-
|
| 302 |
-
# ๋ผ๋ฒจ
|
| 303 |
-
label = f"#{det['id']} {det['length']}cm {det['pred_weight']}g ({det['confidence']:.0%})"
|
| 304 |
-
|
| 305 |
-
# ๋ฐฐ๊ฒฝ ๋ฐ์ค
|
| 306 |
-
bbox = draw.textbbox((x1, y1 - 20), label, font=font)
|
| 307 |
-
draw.rectangle(bbox, fill=color)
|
| 308 |
-
draw.text((x1, y1 - 20), label, fill="white", font=font)
|
| 309 |
-
|
| 310 |
-
return img
|
| 311 |
-
|
| 312 |
-
def visualize_with_groundtruth(self, image, detection, true_length, true_weight, sample_id):
|
| 313 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ์ ์ค์ธก๊ฐ์ ํจ๊ป ์๊ฐํ"""
|
| 314 |
-
if image is None:
|
| 315 |
-
return None
|
| 316 |
-
|
| 317 |
-
img = image.copy()
|
| 318 |
-
draw = ImageDraw.Draw(img)
|
| 319 |
-
|
| 320 |
-
# ํฐํธ ์ค์
|
| 321 |
-
try:
|
| 322 |
-
font_large = ImageFont.truetype("arial.ttf", 16)
|
| 323 |
-
font_small = ImageFont.truetype("arial.ttf", 12)
|
| 324 |
-
except:
|
| 325 |
-
font_large = ImageFont.load_default()
|
| 326 |
-
font_small = ImageFont.load_default()
|
| 327 |
-
|
| 328 |
-
# Bounding box ๊ทธ๋ฆฌ๊ธฐ
|
| 329 |
-
x1, y1, x2, y2 = detection["bbox"]
|
| 330 |
-
|
| 331 |
-
# ์ค์ฐจ์จ๋ก ์์ ๊ฒฐ์
|
| 332 |
-
error_weight = abs(detection["pred_weight"] - true_weight) / true_weight * 100
|
| 333 |
-
if error_weight < 10:
|
| 334 |
-
color = "lime" # ๋
น์: ์ฐ์
|
| 335 |
-
elif error_weight < 25:
|
| 336 |
-
color = "orange" # ์ฃผํฉ: ์ํธ
|
| 337 |
-
else:
|
| 338 |
-
color = "red" # ๋นจ๊ฐ: ๊ฐ์ ํ์
|
| 339 |
-
|
| 340 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 341 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=4)
|
| 342 |
-
|
| 343 |
-
# ์์ธก๊ฐ ๋ผ๋ฒจ (bbox ์)
|
| 344 |
-
pred_label = f"์์ธก: {detection['length']:.1f}cm / {detection['pred_weight']:.1f}g"
|
| 345 |
-
bbox_pred = draw.textbbox((x1, y1 - 40), pred_label, font=font_small)
|
| 346 |
-
draw.rectangle(bbox_pred, fill=color)
|
| 347 |
-
draw.text((x1, y1 - 40), pred_label, fill="white", font=font_small)
|
| 348 |
-
|
| 349 |
-
# ์ค์ธก๊ฐ ๋ผ๋ฒจ (bbox ์, ์์ธก๊ฐ ์๋)
|
| 350 |
-
true_label = f"์ค์ : {true_length:.1f}cm / {true_weight:.1f}g"
|
| 351 |
-
bbox_true = draw.textbbox((x1, y1 - 20), true_label, font=font_small)
|
| 352 |
-
draw.rectangle(bbox_true, fill="blue")
|
| 353 |
-
draw.text((x1, y1 - 20), true_label, fill="white", font=font_small)
|
| 354 |
-
|
| 355 |
-
# ์ด๋ฏธ์ง ์๋จ์ ์ํ ID์ ์ค์ฐจ์จ ํ์
|
| 356 |
-
header = f"์ํ #{sample_id} | ์ฒด์ฅ ์ค์ฐจ: {abs(detection['length']-true_length)/true_length*100:.1f}% | ์ฒด์ค ์ค์ฐจ: {error_weight:.1f}%"
|
| 357 |
-
header_bbox = draw.textbbox((10, 10), header, font=font_large)
|
| 358 |
-
draw.rectangle([5, 5, header_bbox[2]+5, header_bbox[3]+5], fill="black", outline=color, width=3)
|
| 359 |
-
draw.text((10, 10), header, fill=color, font=font_large)
|
| 360 |
-
|
| 361 |
-
return img
|
| 362 |
-
|
| 363 |
-
# =====================
|
| 364 |
-
# Roboflow ๊ฒ์ถ๊ธฐ
|
| 365 |
-
# =====================
|
| 366 |
-
class RoboflowDetector:
|
| 367 |
-
"""Roboflow ์์ฐ ์ ์ฉ ๊ฒ์ถ๊ธฐ"""
|
| 368 |
-
|
| 369 |
-
def __init__(self, api_key="azcIL8KDJVJMYrsERzI7", model_id="shrimp-konvey/2"):
|
| 370 |
-
"""Roboflow ๋ชจ๋ธ ์ด๊ธฐํ"""
|
| 371 |
-
print(f"๐ Loading Roboflow model: {model_id}")
|
| 372 |
-
|
| 373 |
-
try:
|
| 374 |
-
from inference_sdk import InferenceHTTPClient, InferenceConfiguration
|
| 375 |
-
|
| 376 |
-
self.client = InferenceHTTPClient(
|
| 377 |
-
api_url="https://serverless.roboflow.com",
|
| 378 |
-
api_key=api_key
|
| 379 |
-
)
|
| 380 |
-
self.model_id = model_id
|
| 381 |
-
self.regression_model = RegressionModel()
|
| 382 |
-
|
| 383 |
-
# ์ค์ผ์ผ ์ค์
|
| 384 |
-
self.pixel_to_cm_ratio = 0.01 # ๊ธฐ๋ณธ๊ฐ (์ถํ ์กฐ์ )
|
| 385 |
-
|
| 386 |
-
# ๊ธฐ๋ณธ ์ค์ ๊ฐ
|
| 387 |
-
self.iou_threshold = 0.5
|
| 388 |
-
|
| 389 |
-
print("โ
Roboflow model loaded successfully!")
|
| 390 |
-
except Exception as e:
|
| 391 |
-
print(f"โ Roboflow model loading failed: {e}")
|
| 392 |
-
raise
|
| 393 |
-
|
| 394 |
-
def set_scale(self, pixel_length, actual_cm):
|
| 395 |
-
"""์ค์ผ์ผ ์ค์ """
|
| 396 |
-
self.pixel_to_cm_ratio = actual_cm / pixel_length
|
| 397 |
-
print(f"๐ Roboflow scale updated: {pixel_length}px = {actual_cm}cm")
|
| 398 |
-
|
| 399 |
-
def set_iou_threshold(self, iou_threshold):
|
| 400 |
-
"""IoU ์๊ณ๊ฐ ์ค์ """
|
| 401 |
-
self.iou_threshold = iou_threshold
|
| 402 |
-
print(f"๐ฏ Roboflow IoU threshold updated: {iou_threshold:.2f}")
|
| 403 |
-
|
| 404 |
-
def detect(self, image, confidence_threshold=0.5):
|
| 405 |
-
"""
|
| 406 |
-
์์ฐ ๊ฒ์ถ
|
| 407 |
-
|
| 408 |
-
Args:
|
| 409 |
-
image: PIL Image
|
| 410 |
-
confidence_threshold: ์ ๋ขฐ๋ ์๊ณ๊ฐ
|
| 411 |
-
|
| 412 |
-
Returns:
|
| 413 |
-
List[Dict]: ๊ฒ์ถ ๊ฒฐ๊ณผ
|
| 414 |
-
"""
|
| 415 |
-
import tempfile
|
| 416 |
-
import os as os_module
|
| 417 |
-
from inference_sdk import InferenceConfiguration
|
| 418 |
-
|
| 419 |
-
# ์์ ํ์ผ๋ก ์ ์ฅ (API๊ฐ ํ์ผ ๊ฒฝ๋ก ํ์)
|
| 420 |
-
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
|
| 421 |
-
# RGB๋ก ๋ณํ
|
| 422 |
-
if image.mode != 'RGB':
|
| 423 |
-
image = image.convert('RGB')
|
| 424 |
-
image.save(tmp.name, quality=95)
|
| 425 |
-
tmp_path = tmp.name
|
| 426 |
-
|
| 427 |
-
try:
|
| 428 |
-
# API ํธ์ถ with configuration
|
| 429 |
-
custom_config = InferenceConfiguration(
|
| 430 |
-
confidence_threshold=confidence_threshold,
|
| 431 |
-
iou_threshold=self.iou_threshold
|
| 432 |
-
)
|
| 433 |
-
|
| 434 |
-
with self.client.use_configuration(custom_config):
|
| 435 |
-
result = self.client.infer(tmp_path, model_id=self.model_id)
|
| 436 |
-
|
| 437 |
-
# ๊ฒฐ๊ณผ ๋ณํ (API๊ฐ ์ด๋ฏธ confidence/iou ํํฐ๋ง ์๋ฃ)
|
| 438 |
-
detections = []
|
| 439 |
-
for idx, pred in enumerate(result["predictions"], 1):
|
| 440 |
-
x = pred["x"]
|
| 441 |
-
y = pred["y"]
|
| 442 |
-
w = pred["width"]
|
| 443 |
-
h = pred["height"]
|
| 444 |
-
|
| 445 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค (x1, y1, x2, y2)
|
| 446 |
-
bbox = [x - w/2, y - h/2, x + w/2, y + h/2]
|
| 447 |
-
|
| 448 |
-
# ์ฒด์ฅ ์ถ์ (ํฝ์
)
|
| 449 |
-
length_pixels = max(w, h)
|
| 450 |
-
length_cm = length_pixels * self.pixel_to_cm_ratio
|
| 451 |
-
|
| 452 |
-
# ์ฒด์ค ์ถ์
|
| 453 |
-
pred_weight = self.regression_model.predict(length_cm)
|
| 454 |
-
|
| 455 |
-
detections.append({
|
| 456 |
-
"id": idx,
|
| 457 |
-
"bbox": bbox,
|
| 458 |
-
"confidence": pred["confidence"],
|
| 459 |
-
"length": length_cm,
|
| 460 |
-
"pred_weight": pred_weight,
|
| 461 |
-
"source": "roboflow"
|
| 462 |
-
})
|
| 463 |
-
|
| 464 |
-
return detections
|
| 465 |
-
|
| 466 |
-
finally:
|
| 467 |
-
# ์์ ํ์ผ ์ญ์
|
| 468 |
-
if os_module.path.exists(tmp_path):
|
| 469 |
-
os_module.unlink(tmp_path)
|
| 470 |
-
|
| 471 |
-
def visualize(self, image, detections):
|
| 472 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ ์๊ฐํ"""
|
| 473 |
-
if image is None:
|
| 474 |
-
return None
|
| 475 |
-
|
| 476 |
-
img = image.copy()
|
| 477 |
-
draw = ImageDraw.Draw(img)
|
| 478 |
-
|
| 479 |
-
# ํฐํธ ์ค์
|
| 480 |
-
try:
|
| 481 |
-
font = ImageFont.truetype("arial.ttf", 12)
|
| 482 |
-
except:
|
| 483 |
-
font = ImageFont.load_default()
|
| 484 |
-
|
| 485 |
-
for det in detections:
|
| 486 |
-
x1, y1, x2, y2 = det["bbox"]
|
| 487 |
-
|
| 488 |
-
# ์ ๋ขฐ๋์ ๋ฐ๋ผ ์์ ์ ํ
|
| 489 |
-
confidence = det["confidence"]
|
| 490 |
-
if confidence > 0.8:
|
| 491 |
-
color = "lime"
|
| 492 |
-
elif confidence > 0.6:
|
| 493 |
-
color = "orange"
|
| 494 |
-
else:
|
| 495 |
-
color = "yellow"
|
| 496 |
-
|
| 497 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 498 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 499 |
-
|
| 500 |
-
# ๋ผ๋ฒจ
|
| 501 |
-
label = f"#{det['id']} {det['length']:.1f}cm {det['pred_weight']:.1f}g ({det['confidence']:.0%})"
|
| 502 |
-
|
| 503 |
-
# ๋ฐฐ๊ฒฝ ๋ฐ์ค
|
| 504 |
-
bbox = draw.textbbox((x1, y1 - 20), label, font=font)
|
| 505 |
-
draw.rectangle(bbox, fill=color)
|
| 506 |
-
draw.text((x1, y1 - 20), label, fill="black", font=font)
|
| 507 |
-
|
| 508 |
-
return img
|
| 509 |
-
|
| 510 |
-
def visualize_with_groundtruth(self, image, detection, true_length, true_weight, sample_id):
|
| 511 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ์ ์ค์ธก๊ฐ์ ํจ๊ป ์๊ฐํ"""
|
| 512 |
-
if image is None:
|
| 513 |
-
return None
|
| 514 |
-
|
| 515 |
-
img = image.copy()
|
| 516 |
-
draw = ImageDraw.Draw(img)
|
| 517 |
-
|
| 518 |
-
# ํฐํธ ์ค์
|
| 519 |
-
try:
|
| 520 |
-
font_large = ImageFont.truetype("arial.ttf", 16)
|
| 521 |
-
font_small = ImageFont.truetype("arial.ttf", 12)
|
| 522 |
-
except:
|
| 523 |
-
font_large = ImageFont.load_default()
|
| 524 |
-
font_small = ImageFont.load_default()
|
| 525 |
-
|
| 526 |
-
# Bounding box ๊ทธ๋ฆฌ๊ธฐ
|
| 527 |
-
x1, y1, x2, y2 = detection["bbox"]
|
| 528 |
-
|
| 529 |
-
# ์ค์ฐจ์จ๋ก ์์ ๊ฒฐ์
|
| 530 |
-
error_weight = abs(detection["pred_weight"] - true_weight) / true_weight * 100
|
| 531 |
-
if error_weight < 10:
|
| 532 |
-
color = "lime"
|
| 533 |
-
elif error_weight < 25:
|
| 534 |
-
color = "orange"
|
| 535 |
-
else:
|
| 536 |
-
color = "red"
|
| 537 |
-
|
| 538 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 539 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=4)
|
| 540 |
-
|
| 541 |
-
# ์์ธก๊ฐ ๋ผ๋ฒจ
|
| 542 |
-
pred_label = f"์์ธก: {detection['length']:.1f}cm / {detection['pred_weight']:.1f}g"
|
| 543 |
-
bbox_pred = draw.textbbox((x1, y1 - 40), pred_label, font=font_small)
|
| 544 |
-
draw.rectangle(bbox_pred, fill=color)
|
| 545 |
-
draw.text((x1, y1 - 40), pred_label, fill="white", font=font_small)
|
| 546 |
-
|
| 547 |
-
# ์ค์ธก๊ฐ ๋ผ๋ฒจ
|
| 548 |
-
true_label = f"์ค์ : {true_length:.1f}cm / {true_weight:.1f}g"
|
| 549 |
-
bbox_true = draw.textbbox((x1, y1 - 20), true_label, font=font_small)
|
| 550 |
-
draw.rectangle(bbox_true, fill="blue")
|
| 551 |
-
draw.text((x1, y1 - 20), true_label, fill="white", font=font_small)
|
| 552 |
-
|
| 553 |
-
# ํค๋
|
| 554 |
-
header = f"์ํ #{sample_id} [Roboflow] | ์ฒด๏ฟฝ๏ฟฝ ์ค์ฐจ: {abs(detection['length']-true_length)/true_length*100:.1f}% | ์ฒด์ค ์ค์ฐจ: {error_weight:.1f}%"
|
| 555 |
-
header_bbox = draw.textbbox((10, 10), header, font=font_large)
|
| 556 |
-
draw.rectangle([5, 5, header_bbox[2]+5, header_bbox[3]+5], fill="black", outline=color, width=3)
|
| 557 |
-
draw.text((10, 10), header, fill=color, font=font_large)
|
| 558 |
-
|
| 559 |
-
return img
|
| 560 |
-
|
| 561 |
-
# =====================
|
| 562 |
-
# ์ ์ญ ์ธ์คํด์ค (๋ชจ๋ธ ์บ์ฑ)
|
| 563 |
-
# =====================
|
| 564 |
-
|
| 565 |
-
# ๋ชจ๋ธ ์ด๊ธฐํ
|
| 566 |
-
print("๐ Initializing models...")
|
| 567 |
-
|
| 568 |
-
# RT-DETR ๊ฒ์ถ๊ธฐ
|
| 569 |
-
try:
|
| 570 |
-
rtdetr_detector = RTDetrDetector()
|
| 571 |
-
RTDETR_LOADED = True
|
| 572 |
-
print("โ
RT-DETR loaded")
|
| 573 |
-
except Exception as e:
|
| 574 |
-
print(f"โ ๏ธ RT-DETR failed: {e}")
|
| 575 |
-
RTDETR_LOADED = False
|
| 576 |
-
rtdetr_detector = None
|
| 577 |
-
|
| 578 |
-
# Roboflow ๊ฒ์ถ๊ธฐ
|
| 579 |
-
try:
|
| 580 |
-
roboflow_detector = RoboflowDetector()
|
| 581 |
-
ROBOFLOW_LOADED = True
|
| 582 |
-
print("โ
Roboflow loaded")
|
| 583 |
-
except Exception as e:
|
| 584 |
-
print(f"โ ๏ธ Roboflow failed: {e}")
|
| 585 |
-
ROBOFLOW_LOADED = False
|
| 586 |
-
roboflow_detector = None
|
| 587 |
-
|
| 588 |
-
# ๊ธฐ๋ณธ ๊ฒ์ถ๊ธฐ ์ค์ (Roboflow ์ฐ์ , ์์ผ๋ฉด RT-DETR)
|
| 589 |
-
if ROBOFLOW_LOADED:
|
| 590 |
-
detector = roboflow_detector
|
| 591 |
-
MODEL_LOADED = True
|
| 592 |
-
print("๐ฏ Default detector: Roboflow")
|
| 593 |
-
elif RTDETR_LOADED:
|
| 594 |
-
detector = rtdetr_detector
|
| 595 |
-
MODEL_LOADED = True
|
| 596 |
-
print("๐ฏ Default detector: RT-DETR")
|
| 597 |
-
else:
|
| 598 |
-
detector = None
|
| 599 |
-
MODEL_LOADED = False
|
| 600 |
-
print("โ No models loaded - simulation mode")
|
| 601 |
-
|
| 602 |
-
regression_model = RegressionModel()
|
| 603 |
-
|
| 604 |
-
# =====================
|
| 605 |
-
# Gradio ์ธํฐํ์ด์ค ํจ์
|
| 606 |
-
# =====================
|
| 607 |
-
|
| 608 |
-
def process_image(image, model_choice, confidence, iou_threshold, pixel_scale, cm_scale, enable_depth):
|
| 609 |
-
"""์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ฐ ๋ถ์"""
|
| 610 |
-
|
| 611 |
-
if not MODEL_LOADED:
|
| 612 |
-
return None, None, "โ ๋ชจ๋ธ ๋ก๋ฉ ์คํจ. requirements.txt๋ฅผ ํ์ธํ์ธ์.", pd.DataFrame()
|
| 613 |
-
|
| 614 |
-
if image is None:
|
| 615 |
-
return None, None, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์.", pd.DataFrame()
|
| 616 |
-
|
| 617 |
-
# ๋ชจ๋ธ ์ ํ
|
| 618 |
-
if model_choice == "roboflow":
|
| 619 |
-
if not ROBOFLOW_LOADED:
|
| 620 |
-
return None, None, "โ Roboflow ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. RT-DETR์ ์ ํํ์ธ์.", pd.DataFrame()
|
| 621 |
-
current_detector = roboflow_detector
|
| 622 |
-
model_name = "Roboflow (์์ฐ ์ ์ฉ)"
|
| 623 |
-
# IoU ์๊ณ๊ฐ ์ค์ (Roboflow๋ง)
|
| 624 |
-
current_detector.set_iou_threshold(iou_threshold)
|
| 625 |
-
else: # rtdetr
|
| 626 |
-
if not RTDETR_LOADED:
|
| 627 |
-
return None, None, "โ RT-DETR ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. Roboflow๋ฅผ ์ ํํ์ธ์.", pd.DataFrame()
|
| 628 |
-
current_detector = rtdetr_detector
|
| 629 |
-
model_name = "RT-DETR (๋ฒ์ฉ)"
|
| 630 |
-
# ๊น์ด ๋ณด์ ํ์ฑํ/๋นํ์ฑํ (RT-DETR๋ง ์ง์)
|
| 631 |
-
current_detector.depth_correction_enabled = enable_depth
|
| 632 |
-
|
| 633 |
-
# ์ค์ผ์ผ ์
๋ฐ์ดํธ
|
| 634 |
-
if pixel_scale > 0 and cm_scale > 0:
|
| 635 |
-
current_detector.set_scale(pixel_scale, cm_scale)
|
| 636 |
-
|
| 637 |
-
# ๊ฒ์ถ ์ํ
|
| 638 |
-
detections = current_detector.detect(image, confidence)
|
| 639 |
-
|
| 640 |
-
if not detections:
|
| 641 |
-
return image, None, f"โ ๏ธ [{model_name}] ๊ฒ์ถ๋ ๊ฐ์ฒด๊ฐ ์์ต๋๋ค. ์ ๋ขฐ๋๋ฅผ ๋ฎ์ถฐ๋ณด์ธ์.", pd.DataFrame()
|
| 642 |
-
|
| 643 |
-
# ์๊ฐํ
|
| 644 |
-
result_image = current_detector.visualize(image, detections)
|
| 645 |
-
|
| 646 |
-
# ๊น์ด ๋งต ์๊ฐํ (RT-DETR๋ง)
|
| 647 |
-
depth_vis = None
|
| 648 |
-
if model_choice == "rtdetr" and enable_depth and hasattr(current_detector, 'depth_estimator') and current_detector.depth_estimator and current_detector.last_depth_map is not None:
|
| 649 |
-
depth_vis = current_detector.depth_estimator.visualize_depth(current_detector.last_depth_map)
|
| 650 |
-
|
| 651 |
-
# ํต๊ณ ๊ณ์ฐ
|
| 652 |
-
avg_length = np.mean([d["length"] for d in detections])
|
| 653 |
-
avg_weight = np.mean([d["pred_weight"] for d in detections])
|
| 654 |
-
total_biomass = sum([d["pred_weight"] for d in detections])
|
| 655 |
-
|
| 656 |
-
# ํต๊ณ ํ
์คํธ
|
| 657 |
-
depth_status = "โ
ํ์ฑํ" if (model_choice == "rtdetr" and enable_depth) else "โ ๏ธ ๋นํ์ฑํ"
|
| 658 |
-
stats_text = f"""
|
| 659 |
-
### ๐ ๊ฒ์ถ ๊ฒฐ๊ณผ
|
| 660 |
-
|
| 661 |
-
- **์ฌ์ฉ ๋ชจ๋ธ**: {model_name}
|
| 662 |
-
- **๊ฒ์ถ ๊ฐ์ฒด ์**: {len(detections)}๋ง๋ฆฌ
|
| 663 |
-
- **ํ๊ท ์ฒด์ฅ**: {avg_length:.1f}cm
|
| 664 |
-
- **ํ๊ท ์์ธก ์ฒด์ค**: {avg_weight:.1f}g
|
| 665 |
-
- **์ด ๋ฐ์ด์ค๋งค์ค**: {total_biomass:.1f}g
|
| 666 |
-
- **๊น์ด ๋ณด์ **: {depth_status}
|
| 667 |
-
|
| 668 |
-
๐ก **ํ**: ์ ํ๋ ๊ฒ์ฆ์ "์ ํ๋ ๊ฒ์ฆ" ํญ์์ ์ค์ ๋ฐ์ดํฐ์ ๋น๊ตํ ์ ์์ต๋๋ค.
|
| 669 |
-
"""
|
| 670 |
-
|
| 671 |
-
# ๊ฒฐ๊ณผ ํ
์ด๋ธ
|
| 672 |
-
df_data = []
|
| 673 |
-
for d in detections:
|
| 674 |
-
df_data.append({
|
| 675 |
-
"ID": f"#{d['id']}",
|
| 676 |
-
"์ฒด์ฅ(cm)": d["length"],
|
| 677 |
-
"์์ธก ์ฒด์ค(g)": d["pred_weight"],
|
| 678 |
-
"์ ๋ขฐ๋": f"{d['confidence']:.0%}"
|
| 679 |
-
})
|
| 680 |
-
|
| 681 |
-
df = pd.DataFrame(df_data)
|
| 682 |
-
|
| 683 |
-
return result_image, depth_vis, stats_text, df
|
| 684 |
-
|
| 685 |
-
def evaluate_model():
|
| 686 |
-
"""๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ"""
|
| 687 |
-
|
| 688 |
-
# ์ค์ธก ๋ฐ์ดํฐ๋ก ํ๊ฐ
|
| 689 |
-
predictions = []
|
| 690 |
-
actuals = []
|
| 691 |
-
|
| 692 |
-
for sample in REAL_DATA:
|
| 693 |
-
pred = regression_model.estimate_weight(sample["length"])
|
| 694 |
-
predictions.append(pred)
|
| 695 |
-
actuals.append(sample["weight"])
|
| 696 |
-
|
| 697 |
-
# ๋ฉ๏ฟฝ๏ฟฝ๏ฟฝ๋ฆญ ๊ณ์ฐ
|
| 698 |
-
errors = [abs(p - a) / a * 100 for p, a in zip(predictions, actuals)]
|
| 699 |
-
mape = np.mean(errors)
|
| 700 |
-
mae = np.mean([abs(p - a) for p, a in zip(predictions, actuals)])
|
| 701 |
-
rmse = np.sqrt(np.mean([(p - a) ** 2 for p, a in zip(predictions, actuals)]))
|
| 702 |
-
|
| 703 |
-
# Rยฒ ๊ณ์ฐ
|
| 704 |
-
mean_actual = np.mean(actuals)
|
| 705 |
-
ss_tot = sum([(a - mean_actual) ** 2 for a in actuals])
|
| 706 |
-
ss_res = sum([(a - p) ** 2 for a, p in zip(actuals, predictions)])
|
| 707 |
-
r2 = 1 - (ss_res / ss_tot)
|
| 708 |
-
|
| 709 |
-
eval_text = f"""
|
| 710 |
-
### ๐ฏ ํ๊ท ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ
|
| 711 |
-
|
| 712 |
-
**๋ฐ์ดํฐ์
**: {len(REAL_DATA)}๊ฐ ์ค์ธก ์ํ
|
| 713 |
-
|
| 714 |
-
**์ฑ๋ฅ ์งํ**:
|
| 715 |
-
- Rยฒ Score: **{r2:.4f}** (92.9% ์ค๋ช
๋ ฅ)
|
| 716 |
-
- MAPE: **{mape:.1f}%** (๋ชฉํ 25% ์ด๋ด โ
)
|
| 717 |
-
- MAE: **{mae:.2f}g**
|
| 718 |
-
- RMSE: **{rmse:.2f}g**
|
| 719 |
-
|
| 720 |
-
**๋ชจ๋ธ ์**: W = {regression_model.a:.6f} ร L^{regression_model.b:.4f}
|
| 721 |
-
|
| 722 |
-
**๊ฒฐ๋ก **: โ
์์ฉํ ๊ฐ๋ฅ ์์ค์ ์ ํ๋
|
| 723 |
-
"""
|
| 724 |
-
|
| 725 |
-
# ์ฐจํธ ์์ฑ
|
| 726 |
-
fig = go.Figure()
|
| 727 |
-
|
| 728 |
-
# ์ค์ธก ๋ฐ์ดํฐ
|
| 729 |
-
fig.add_trace(go.Scatter(
|
| 730 |
-
x=[d["length"] for d in REAL_DATA],
|
| 731 |
-
y=[d["weight"] for d in REAL_DATA],
|
| 732 |
-
mode='markers',
|
| 733 |
-
name='์ค์ธก ๋ฐ์ดํฐ',
|
| 734 |
-
marker=dict(color='blue', size=10, opacity=0.6)
|
| 735 |
-
))
|
| 736 |
-
|
| 737 |
-
# ํ๊ท์
|
| 738 |
-
x_line = np.linspace(7, 14, 100)
|
| 739 |
-
y_line = [regression_model.estimate_weight(x) for x in x_line]
|
| 740 |
-
|
| 741 |
-
fig.add_trace(go.Scatter(
|
| 742 |
-
x=x_line,
|
| 743 |
-
y=y_line,
|
| 744 |
-
mode='lines',
|
| 745 |
-
name=f'ํ๊ท ๋ชจ๋ธ (Rยฒ={r2:.3f})',
|
| 746 |
-
line=dict(color='red', width=3)
|
| 747 |
-
))
|
| 748 |
-
|
| 749 |
-
# ์์ธก๊ฐ
|
| 750 |
-
fig.add_trace(go.Scatter(
|
| 751 |
-
x=[d["length"] for d in REAL_DATA],
|
| 752 |
-
y=predictions,
|
| 753 |
-
mode='markers',
|
| 754 |
-
name='์์ธก๊ฐ',
|
| 755 |
-
marker=dict(color='red', size=8, opacity=0.4, symbol='x')
|
| 756 |
-
))
|
| 757 |
-
|
| 758 |
-
fig.update_layout(
|
| 759 |
-
title="ํฐ๋ค๋ฆฌ์์ฐ ์ฒด์ฅ-์ฒด์ค ํ๊ท ๋ถ์",
|
| 760 |
-
xaxis_title="์ฒด์ฅ (cm)",
|
| 761 |
-
yaxis_title="์ฒด์ค (g)",
|
| 762 |
-
template="plotly_white",
|
| 763 |
-
height=500,
|
| 764 |
-
hovermode='closest'
|
| 765 |
-
)
|
| 766 |
-
|
| 767 |
-
return eval_text, fig
|
| 768 |
-
|
| 769 |
-
def export_data():
|
| 770 |
-
"""๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ"""
|
| 771 |
-
df = pd.DataFrame(REAL_DATA)
|
| 772 |
-
csv_path = f"shrimp_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
|
| 773 |
-
df.to_csv(csv_path, index=False)
|
| 774 |
-
|
| 775 |
-
return csv_path
|
| 776 |
-
|
| 777 |
-
# =====================
|
| 778 |
-
# ์์
๋ฐ์ดํฐ ๋ฐ ๋ฐฐ์น ํ
์คํธ
|
| 779 |
-
# =====================
|
| 780 |
-
|
| 781 |
-
def load_excel_data(excel_path, date_serial=45945):
|
| 782 |
-
"""
|
| 783 |
-
์์
ํ์ผ์์ ํน์ ๋ ์ง์ ๋ฐ์ดํฐ ์ฝ๊ธฐ
|
| 784 |
-
date_serial: 45945 = 2025-10-15 (251015)
|
| 785 |
-
"""
|
| 786 |
-
try:
|
| 787 |
-
# Sheet1 ์ฝ๊ธฐ (ํค๋ ์์ด)
|
| 788 |
-
df = pd.read_excel(excel_path, sheet_name='Sheet1', header=None)
|
| 789 |
-
|
| 790 |
-
# Row 4: ๋ ์ง ํ, Row 5: ํค๋ ํ
|
| 791 |
-
date_row = df.iloc[4]
|
| 792 |
-
header_row = df.iloc[5]
|
| 793 |
-
|
| 794 |
-
# ํด๋น ๋ ์ง ์ปฌ๋ผ ์ฐพ๊ธฐ
|
| 795 |
-
for col_idx in range(len(date_row)):
|
| 796 |
-
if date_row[col_idx] == date_serial:
|
| 797 |
-
# ๋ฐ์ดํฐ ์ถ์ถ
|
| 798 |
-
data_dict = {}
|
| 799 |
-
for row_idx in range(6, len(df)): # ๋ฐ์ดํฐ๋ row 6๋ถํฐ
|
| 800 |
-
no = df.iloc[row_idx, 1] # No. ์ปฌ๋ผ
|
| 801 |
-
length = df.iloc[row_idx, col_idx]
|
| 802 |
-
weight = df.iloc[row_idx, col_idx + 1] if col_idx + 1 < len(df.columns) else None
|
| 803 |
-
|
| 804 |
-
if pd.notna(no) and pd.notna(length):
|
| 805 |
-
data_dict[int(no)] = {
|
| 806 |
-
'length': float(length),
|
| 807 |
-
'weight': float(weight) if pd.notna(weight) else None
|
| 808 |
-
}
|
| 809 |
-
|
| 810 |
-
print(f"โ
Loaded {len(data_dict)} samples from Excel for date {date_serial}")
|
| 811 |
-
return data_dict
|
| 812 |
-
|
| 813 |
-
print(f"โ ๏ธ Date {date_serial} not found in Excel")
|
| 814 |
-
return None
|
| 815 |
-
|
| 816 |
-
except Exception as e:
|
| 817 |
-
print(f"โ Excel loading error: {e}")
|
| 818 |
-
return None
|
| 819 |
-
|
| 820 |
-
def process_test_dataset(data_folder, pixel_scale, cm_scale, enable_depth):
|
| 821 |
-
"""ํ
์คํธ ๋ฐ์ดํฐ์
๋ฐฐ์น ์ฒ๋ฆฌ"""
|
| 822 |
-
|
| 823 |
-
if not MODEL_LOADED:
|
| 824 |
-
return "โ ๋ชจ๋ธ ๋ก๋ฉ ์คํจ", pd.DataFrame(), None, []
|
| 825 |
-
|
| 826 |
-
if not os.path.exists(data_folder):
|
| 827 |
-
return f"โ ํด๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค: {data_folder}", pd.DataFrame(), None, []
|
| 828 |
-
|
| 829 |
-
# ์์
๋ฐ์ดํฐ ๋ก๋
|
| 830 |
-
excel_path = os.path.join(os.path.dirname(data_folder), 'ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ(์งํ).xlsx')
|
| 831 |
-
excel_data = load_excel_data(excel_path, date_serial=45945) # 251015
|
| 832 |
-
|
| 833 |
-
if not excel_data:
|
| 834 |
-
return "โ ์์
๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ ์ ์์ต๋๋ค", pd.DataFrame(), None, []
|
| 835 |
-
|
| 836 |
-
# ์ด๋ฏธ์ง ์ฐพ๊ธฐ
|
| 837 |
-
image_list = []
|
| 838 |
-
for i in range(1, 20): # ์ต๋ 20๊ฐ๊น์ง ํ์ธ
|
| 839 |
-
shrimp_img = os.path.join(data_folder, f"251015_{i:02d}.jpg")
|
| 840 |
-
|
| 841 |
-
if os.path.exists(shrimp_img) and i in excel_data:
|
| 842 |
-
image_list.append((shrimp_img, i))
|
| 843 |
-
|
| 844 |
-
if not image_list:
|
| 845 |
-
return "โ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค", pd.DataFrame(), None, []
|
| 846 |
-
|
| 847 |
-
print(f"\n๐ Processing {len(image_list)} images...")
|
| 848 |
-
|
| 849 |
-
# ์ค์ผ์ผ ์ค์
|
| 850 |
-
if pixel_scale > 0 and cm_scale > 0:
|
| 851 |
-
detector.set_scale(pixel_scale, cm_scale)
|
| 852 |
-
|
| 853 |
-
# ๊น์ด ๋ณด์ ์ค์
|
| 854 |
-
detector.depth_correction_enabled = enable_depth
|
| 855 |
-
|
| 856 |
-
results = []
|
| 857 |
-
visualized_images = [] # ์๊ฐํ๋ ์ด๋ฏธ์ง ์ ์ฅ
|
| 858 |
-
|
| 859 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ ํด๋ ์์ฑ
|
| 860 |
-
results_folder = os.path.join(data_folder, "results")
|
| 861 |
-
os.makedirs(results_folder, exist_ok=True)
|
| 862 |
-
|
| 863 |
-
for shrimp_path, idx in image_list:
|
| 864 |
-
print(f"\n๐ Processing image #{idx}...")
|
| 865 |
-
|
| 866 |
-
# 1. ์์ฐ ์ด๋ฏธ์ง ๊ฒ์ถ
|
| 867 |
-
shrimp_img = Image.open(shrimp_path)
|
| 868 |
-
detections = detector.detect(shrimp_img, confidence_threshold=0.3)
|
| 869 |
-
|
| 870 |
-
if not detections:
|
| 871 |
-
print(f" โ ๏ธ No shrimp detected in image #{idx}")
|
| 872 |
-
continue
|
| 873 |
-
|
| 874 |
-
# ์์ฐ ์ ํ: ์ค์ ์์น + ํฌ๊ธฐ + ํํ ๊ธฐ๋ฐ ์ค์ฝ์ด๋ง
|
| 875 |
-
img_width, img_height = shrimp_img.size
|
| 876 |
-
img_area = img_width * img_height
|
| 877 |
-
img_center_x = img_width / 2
|
| 878 |
-
img_center_y = img_height / 2
|
| 879 |
-
|
| 880 |
-
valid_detections = []
|
| 881 |
-
for det in detections:
|
| 882 |
-
x1, y1, x2, y2 = det["bbox"]
|
| 883 |
-
width = x2 - x1
|
| 884 |
-
height = y2 - y1
|
| 885 |
-
area = width * height
|
| 886 |
-
|
| 887 |
-
# ๊ฐ์ฒด ์ค์ฌ์
|
| 888 |
-
obj_center_x = (x1 + x2) / 2
|
| 889 |
-
obj_center_y = (y1 + y2) / 2
|
| 890 |
-
|
| 891 |
-
# 1. ์ค์ ๊ฑฐ๋ฆฌ ์ ์ (0~1, ์ค์์ ๊ฐ๊น์ธ์๋ก ๋์)
|
| 892 |
-
max_dist = ((img_width/2)**2 + (img_height/2)**2)**0.5
|
| 893 |
-
dist_from_center = ((obj_center_x - img_center_x)**2 + (obj_center_y - img_center_y)**2)**0.5
|
| 894 |
-
center_score = 1 - (dist_from_center / max_dist)
|
| 895 |
-
|
| 896 |
-
# 2. ํฌ๊ธฐ ์ ์ (์ ์ ํ ํฌ๊ธฐ: ์ด๋ฏธ์ง์ 5~25%)
|
| 897 |
-
size_ratio = area / img_area
|
| 898 |
-
if 0.05 < size_ratio < 0.25:
|
| 899 |
-
size_score = 1.0
|
| 900 |
-
elif 0.01 < size_ratio < 0.4:
|
| 901 |
-
size_score = 0.5
|
| 902 |
-
else:
|
| 903 |
-
size_score = 0.0
|
| 904 |
-
|
| 905 |
-
# 3. ํํ ์ ์ (๊ธธ์ญํ ํํ)
|
| 906 |
-
longer_side = max(width, height)
|
| 907 |
-
shorter_side = min(width, height)
|
| 908 |
-
elongation = longer_side / (shorter_side + 1e-8)
|
| 909 |
-
if elongation > 2.5:
|
| 910 |
-
shape_score = 1.0
|
| 911 |
-
elif elongation > 1.5:
|
| 912 |
-
shape_score = 0.7
|
| 913 |
-
else:
|
| 914 |
-
shape_score = 0.3
|
| 915 |
-
|
| 916 |
-
# 4. ์ ๋ขฐ๋ ์ ์
|
| 917 |
-
confidence_score = det["confidence"]
|
| 918 |
-
|
| 919 |
-
# ์ต์ข
์ ์ (๊ฐ์ค ํ๊ท )
|
| 920 |
-
final_score = (
|
| 921 |
-
center_score * 0.4 + # ์ค์ ์์น ๊ฐ์ฅ ์ค์
|
| 922 |
-
size_score * 0.2 + # ํฌ๊ธฐ
|
| 923 |
-
shape_score * 0.2 + # ํํ
|
| 924 |
-
confidence_score * 0.2 # ์ ๋ขฐ๋
|
| 925 |
-
)
|
| 926 |
-
|
| 927 |
-
det["final_score"] = final_score
|
| 928 |
-
det["center_score"] = center_score
|
| 929 |
-
det["size_score"] = size_score
|
| 930 |
-
det["shape_score"] = shape_score
|
| 931 |
-
|
| 932 |
-
# ์ต์ ์ ์ ์๊ณ๊ฐ
|
| 933 |
-
if final_score > 0.3:
|
| 934 |
-
valid_detections.append(det)
|
| 935 |
-
|
| 936 |
-
if not valid_detections:
|
| 937 |
-
print(f" โ ๏ธ No valid shrimp detected (filtered out {len(detections)} detections)")
|
| 938 |
-
continue
|
| 939 |
-
|
| 940 |
-
# ์ต์ข
์ ์๊ฐ ๊ฐ์ฅ ๋์ ๊ฐ์ฒด ์ ํ
|
| 941 |
-
largest_det = max(valid_detections, key=lambda d: d["final_score"])
|
| 942 |
-
pred_length = largest_det["length"]
|
| 943 |
-
pred_weight = largest_det["pred_weight"]
|
| 944 |
-
|
| 945 |
-
print(f" โ Selected detection:")
|
| 946 |
-
print(f" - Final score: {largest_det['final_score']:.3f}")
|
| 947 |
-
print(f" - Center: {largest_det['center_score']:.2f}, Size: {largest_det['size_score']:.2f}, Shape: {largest_det['shape_score']:.2f}, Conf: {largest_det['confidence']:.2f}")
|
| 948 |
-
print(f" - Bbox area: {(largest_det['bbox'][2]-largest_det['bbox'][0])*(largest_det['bbox'][3]-largest_det['bbox'][1]):.0f}px")
|
| 949 |
-
|
| 950 |
-
# 2. ์์
์์ ์ค์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
| 951 |
-
true_data = excel_data[idx]
|
| 952 |
-
true_length = true_data['length']
|
| 953 |
-
true_weight = true_data['weight']
|
| 954 |
-
|
| 955 |
-
if true_weight is None:
|
| 956 |
-
print(f" โ ๏ธ No weight data in Excel for #{idx}")
|
| 957 |
-
continue
|
| 958 |
-
|
| 959 |
-
# 3. ์ค์ฐจ ๊ณ์ฐ
|
| 960 |
-
error_weight = abs(pred_weight - true_weight) / true_weight * 100
|
| 961 |
-
error_length = abs(pred_length - true_length) / true_length * 100
|
| 962 |
-
|
| 963 |
-
# 4. ์ด๋ฏธ์ง ์๊ฐํ (์์ธก + ์ค์ ๊ฐ ํ์)
|
| 964 |
-
vis_img = detector.visualize_with_groundtruth(
|
| 965 |
-
shrimp_img, largest_det, true_length, true_weight, idx
|
| 966 |
-
)
|
| 967 |
-
|
| 968 |
-
# ์ด๋ฏธ์ง ํ์ผ๋ก ์ ์ฅ (ํ์ฅ์ ํฌํจ)
|
| 969 |
-
output_filename = f"sample_{idx:02d}_result.jpg"
|
| 970 |
-
output_path = os.path.join(results_folder, output_filename)
|
| 971 |
-
vis_img.save(output_path, quality=95)
|
| 972 |
-
|
| 973 |
-
visualized_images.append(output_path)
|
| 974 |
-
print(f" ๐พ Saved visualization: {output_path}")
|
| 975 |
-
|
| 976 |
-
results.append({
|
| 977 |
-
"ID": f"#{idx}",
|
| 978 |
-
"์ค์ ์ฒด์ฅ(cm)": round(true_length, 1),
|
| 979 |
-
"์์ธก ์ฒด์ฅ(cm)": round(pred_length, 1),
|
| 980 |
-
"์ฒด์ฅ ์ค์ฐจ(%)": round(error_length, 1),
|
| 981 |
-
"์ค์ ์ฒด์ค(g)": round(true_weight, 2),
|
| 982 |
-
"์์ธก ์ฒด์ค(g)": round(pred_weight, 2),
|
| 983 |
-
"์ฒด์ค ์ค์ฐจ(%)": round(error_weight, 1),
|
| 984 |
-
"์ค์ฐจ(g)": round(abs(pred_weight - true_weight), 2)
|
| 985 |
-
})
|
| 986 |
-
|
| 987 |
-
print(f" โ
Length: {pred_length:.1f}cm (true: {true_length:.1f}cm, error: {error_length:.1f}%)")
|
| 988 |
-
print(f" Weight: {pred_weight:.2f}g (true: {true_weight:.2f}g, error: {error_weight:.1f}%)")
|
| 989 |
-
|
| 990 |
-
if not results:
|
| 991 |
-
return "โ ์ฒ๋ฆฌ๋ ์ํ์ด ์์ต๋๋ค", pd.DataFrame(), None, []
|
| 992 |
-
|
| 993 |
-
# ๊ฒฐ๊ณผ DataFrame
|
| 994 |
-
df = pd.DataFrame(results)
|
| 995 |
-
|
| 996 |
-
# ํต๊ณ ๊ณ์ฐ
|
| 997 |
-
avg_error_length = df["์ฒด์ฅ ์ค์ฐจ(%)"].mean()
|
| 998 |
-
avg_error_weight = df["์ฒด์ค ์ค์ฐจ(%)"].mean()
|
| 999 |
-
avg_error_g = df["์ค์ฐจ(g)"].mean()
|
| 1000 |
-
min_error_weight = df["์ฒด์ค ์ค์ฐจ(%)"].min()
|
| 1001 |
-
max_error_weight = df["์ฒด์ค ์ค์ฐจ(%)"].max()
|
| 1002 |
-
|
| 1003 |
-
# ์ฐจํธ ์์ฑ
|
| 1004 |
-
fig = go.Figure()
|
| 1005 |
-
|
| 1006 |
-
# ์์ธก vs ์ค์ ์ฐ์ ๋
|
| 1007 |
-
fig.add_trace(go.Scatter(
|
| 1008 |
-
x=df["์ค์ ์ฒด์ค(g)"],
|
| 1009 |
-
y=df["์์ธก ์ฒด์ค(g)"],
|
| 1010 |
-
mode='markers+text',
|
| 1011 |
-
text=df["ID"],
|
| 1012 |
-
textposition="top center",
|
| 1013 |
-
marker=dict(size=12, color=df["์ฒด์ค ์ค์ฐจ(%)"], colorscale='RdYlGn_r',
|
| 1014 |
-
showscale=True, colorbar=dict(title="์ฒด์ค ์ค์ฐจ(%)")),
|
| 1015 |
-
name='์์ธก vs ์ค์ '
|
| 1016 |
-
))
|
| 1017 |
-
|
| 1018 |
-
# ์๋ฒฝํ ์์ธก ์ (y=x)
|
| 1019 |
-
min_val = min(df["์ค์ ์ฒด์ค(g)"].min(), df["์์ธก ์ฒด์ค(g)"].min())
|
| 1020 |
-
max_val = max(df["์ค์ ์ฒด์ค(g)"].max(), df["์์ธก ์ฒด์ค(g)"].max())
|
| 1021 |
-
fig.add_trace(go.Scatter(
|
| 1022 |
-
x=[min_val, max_val],
|
| 1023 |
-
y=[min_val, max_val],
|
| 1024 |
-
mode='lines',
|
| 1025 |
-
line=dict(dash='dash', color='red', width=2),
|
| 1026 |
-
name='์๋ฒฝํ ์์ธก (y=x)'
|
| 1027 |
-
))
|
| 1028 |
-
|
| 1029 |
-
fig.update_layout(
|
| 1030 |
-
title=f"์์ธก ์ ํ๋ ๊ฒ์ฆ ({len(results)}๊ฐ ์ํ)",
|
| 1031 |
-
xaxis_title="์ค์ ์ฒด์ค (g)",
|
| 1032 |
-
yaxis_title="์์ธก ์ฒด์ค (g)",
|
| 1033 |
-
template="plotly_white",
|
| 1034 |
-
height=500,
|
| 1035 |
-
hovermode='closest'
|
| 1036 |
-
)
|
| 1037 |
-
|
| 1038 |
-
# ํต๊ณ ํ
์คํธ
|
| 1039 |
-
stats_text = f"""
|
| 1040 |
-
### ๐ ๋ฐฐ์น ํ
์คํธ ๊ฒฐ๊ณผ
|
| 1041 |
-
|
| 1042 |
-
- **์ฒ๋ฆฌ ์ํ ์**: {len(results)}๊ฐ
|
| 1043 |
-
- **์ฒด์ฅ ํ๊ท ์ค์ฐจ**: {avg_error_length:.1f}%
|
| 1044 |
-
- **์ฒด์ค ํ๊ท ์ค์ฐจ(MAPE)**: {avg_error_weight:.1f}%
|
| 1045 |
-
- **์ฒด์ค ์ ๋ ์ค์ฐจ**: {avg_error_g:.2f}g
|
| 1046 |
-
- **์ฒด์ค ์ค์ฐจ ๋ฒ์**: {min_error_weight:.1f}% ~ {max_error_weight:.1f}%
|
| 1047 |
-
- **๊น์ด ๋ณด์ **: {'โ
ํ์ฑํ' if enable_depth else 'โ ๏ธ ๋นํ์ฑํ'}
|
| 1048 |
-
|
| 1049 |
-
๐ฏ **ํ๊ฐ**: {'โ
์ฐ์ (MAPE < 25%)' if avg_error_weight < 25 else 'โ ๏ธ ๊ฐ์ ํ์ (MAPE โฅ 25%)'}
|
| 1050 |
-
|
| 1051 |
-
๐ก **์ฐธ๊ณ **: ์ค์ ์ฒด์ฅ๊ณผ ์ฒด์ค ๋ฐ์ดํฐ๋ ์์
ํ์ผ์์ ๋ก๋๋์์ต๋๋ค.
|
| 1052 |
-
|
| 1053 |
-
๐ธ **์ด๋ฏธ์ง ๊ฒฐ๊ณผ**: ์๋ ๊ฐค๋ฌ๋ฆฌ์์ ๊ฐ ์ํ์ ์์ธก/์ค์ ๊ฐ์ ํ์ธํ ์ ์์ต๋๋ค.
|
| 1054 |
-
|
| 1055 |
-
๐พ **์ ์ฅ ์์น**: `{results_folder}` ํด๋์ {len(visualized_images)}๊ฐ ์ด๋ฏธ์ง ์ ์ฅ๋จ
|
| 1056 |
-
"""
|
| 1057 |
-
|
| 1058 |
-
return stats_text, df, fig, visualized_images
|
| 1059 |
-
|
| 1060 |
-
# =====================
|
| 1061 |
-
# Gradio UI
|
| 1062 |
-
# =====================
|
| 1063 |
-
|
| 1064 |
-
with gr.Blocks(title="๐ฆ RT-DETR ์์ฐ ๋ถ์", theme=gr.themes.Soft()) as demo:
|
| 1065 |
-
|
| 1066 |
-
gr.Markdown("""
|
| 1067 |
-
# ๐ฆ ํฐ๋ค๋ฆฌ์์ฐ AI ๋ถ์ ์์คํ
(RT-DETR)
|
| 1068 |
-
|
| 1069 |
-
### ์ค์๊ฐ ๊ฐ์ฒด ๊ฒ์ถ + ์ฒด์ฅ/์ฒด์ค ์๋ ์ถ์
|
| 1070 |
-
**๋ชจ๋ธ**: RT-DETR (PekingU/rtdetr_r50vd_coco_o365) | **ํ๊ท**: W = 0.0035 ร L^3.13
|
| 1071 |
-
**์ ํ๋**: Rยฒ = 0.929, MAPE = 6.4% | **๋๋ฐ์ด์ค**: """ + ("๐ GPU" if torch.cuda.is_available() else "๐ป CPU") + """
|
| 1072 |
-
|
| 1073 |
-
---
|
| 1074 |
-
""")
|
| 1075 |
-
|
| 1076 |
-
with gr.Tabs():
|
| 1077 |
-
# ๊ฒ์ถ ํญ
|
| 1078 |
-
with gr.TabItem("๐ ๊ฐ์ฒด ๊ฒ์ถ"):
|
| 1079 |
-
with gr.Row():
|
| 1080 |
-
with gr.Column():
|
| 1081 |
-
input_img = gr.Image(
|
| 1082 |
-
label="์
๋ ฅ ์ด๋ฏธ์ง",
|
| 1083 |
-
type="pil"
|
| 1084 |
-
)
|
| 1085 |
-
|
| 1086 |
-
conf_slider = gr.Slider(
|
| 1087 |
-
0.1, 0.9, 0.5,
|
| 1088 |
-
label="๊ฒ์ถ ์ ๋ขฐ๋ ์๊ณ๊ฐ (Confidence)",
|
| 1089 |
-
info="๋ฎ์์๋ก ๋ ๋ง์ ๊ฐ์ฒด ๊ฒ์ถ"
|
| 1090 |
-
)
|
| 1091 |
-
|
| 1092 |
-
iou_slider = gr.Slider(
|
| 1093 |
-
0.1, 0.9, 0.5,
|
| 1094 |
-
label="IoU ์๊ณ๊ฐ (Overlap) - Roboflow ์ ์ฉ",
|
| 1095 |
-
info="๊ฒน์น๋ ๋ฐ์ค ์ ๊ฑฐ ๊ธฐ์ค (NMS, ๋์์๋ก ๋ ๋ง์ด ์ ์ง)"
|
| 1096 |
-
)
|
| 1097 |
-
|
| 1098 |
-
# ๋ชจ๋ธ ์ ํ
|
| 1099 |
-
model_selector = gr.Dropdown(
|
| 1100 |
-
choices=[
|
| 1101 |
-
("Roboflow (์์ฐ ์ ์ฉ - ์์กฐ)", "roboflow"),
|
| 1102 |
-
("RT-DETR (๋ฒ์ฉ - ์ธก์ ์ฉ)", "rtdetr")
|
| 1103 |
-
],
|
| 1104 |
-
value="roboflow" if ROBOFLOW_LOADED else "rtdetr",
|
| 1105 |
-
label="๐ค ๊ฒ์ถ ๋ชจ๋ธ ์ ํ",
|
| 1106 |
-
info="Roboflow: ์ด์์๋ ์์ฐ(์์กฐ) ์ ์ฉ | RT-DETR: ์ธก์ ์ฉ ๋งคํธ ์ ์์ฐ"
|
| 1107 |
-
)
|
| 1108 |
-
|
| 1109 |
-
with gr.Row():
|
| 1110 |
-
pixel_scale = gr.Number(
|
| 1111 |
-
value=92,
|
| 1112 |
-
label="ํฝ์
ํฌ๊ธฐ (px)",
|
| 1113 |
-
info="์ฐธ์กฐ ๊ฐ์ฒด์ ํฝ์
ํฌ๊ธฐ"
|
| 1114 |
-
)
|
| 1115 |
-
cm_scale = gr.Number(
|
| 1116 |
-
value=1,
|
| 1117 |
-
label="์ค์ ํฌ๊ธฐ (cm)",
|
| 1118 |
-
info="์ฐธ์กฐ ๊ฐ์ฒด์ ์ค์ ํฌ๊ธฐ"
|
| 1119 |
-
)
|
| 1120 |
-
|
| 1121 |
-
depth_checkbox = gr.Checkbox(
|
| 1122 |
-
value=False,
|
| 1123 |
-
label="๐ ๊น์ด ๊ธฐ๋ฐ ์๊ทผ ๋ณด์ ํ์ฑํ (RT-DETR๋ง)",
|
| 1124 |
-
info="Depth-Anything V2๋ก ์๋ ์๊ทผ ์๊ณก ๋ณด์ "
|
| 1125 |
-
)
|
| 1126 |
-
|
| 1127 |
-
detect_btn = gr.Button(
|
| 1128 |
-
"๐ ๊ฒ์ถ ์คํ",
|
| 1129 |
-
variant="primary",
|
| 1130 |
-
size="lg"
|
| 1131 |
-
)
|
| 1132 |
-
|
| 1133 |
-
with gr.Column():
|
| 1134 |
-
output_img = gr.Image(
|
| 1135 |
-
label="๊ฒ์ถ ๊ฒฐ๊ณผ"
|
| 1136 |
-
)
|
| 1137 |
-
depth_img = gr.Image(
|
| 1138 |
-
label="๊น์ด ๋งต (ํ๋์=๊ฐ๊น์, ๋
ธ๋์=๋ฉ์)"
|
| 1139 |
-
)
|
| 1140 |
-
stats = gr.Markdown()
|
| 1141 |
-
|
| 1142 |
-
results_df = gr.Dataframe(
|
| 1143 |
-
label="๊ฒ์ถ ์์ธ ์ ๋ณด",
|
| 1144 |
-
wrap=True
|
| 1145 |
-
)
|
| 1146 |
-
|
| 1147 |
-
# ํ๊ฐ ํญ
|
| 1148 |
-
with gr.TabItem("๐ ์ฑ๋ฅ ํ๊ฐ"):
|
| 1149 |
-
gr.Markdown("""
|
| 1150 |
-
### ํ๊ท ๋ชจ๋ธ ์ฑ๋ฅ ํ๊ฐ
|
| 1151 |
-
|
| 1152 |
-
์ค์ธก ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฒด์ฅ-์ฒด์ค ํ๊ท ๋ชจ๋ธ์ ์ ํ๋๋ฅผ ํ๊ฐํฉ๋๋ค.
|
| 1153 |
-
""")
|
| 1154 |
-
|
| 1155 |
-
eval_btn = gr.Button(
|
| 1156 |
-
"๐ ํ๊ฐ ์คํ",
|
| 1157 |
-
variant="primary"
|
| 1158 |
-
)
|
| 1159 |
-
eval_text = gr.Markdown()
|
| 1160 |
-
eval_plot = gr.Plot()
|
| 1161 |
-
|
| 1162 |
-
# ๋ฐ์ดํฐ ํญ
|
| 1163 |
-
with gr.TabItem("๐ ์ค์ธก ๋ฐ์ดํฐ"):
|
| 1164 |
-
gr.Markdown(f"""
|
| 1165 |
-
### ๋ฐ์ดํฐ ์์ฝ
|
| 1166 |
-
|
| 1167 |
-
- **์ํ ์**: {len(REAL_DATA)}๊ฐ
|
| 1168 |
-
- **์ฒด์ฅ ๋ฒ์**: 7.5 - 13.1 cm
|
| 1169 |
-
- **์ฒด์ค ๋ฒ์**: 2.0 - 11.3 g
|
| 1170 |
-
- **๋ฐ์ดํฐ ์ถ์ฒ**: ์ค์ธก ๋ฐ์ดํฐ
|
| 1171 |
-
""")
|
| 1172 |
-
|
| 1173 |
-
data_df = gr.Dataframe(
|
| 1174 |
-
value=pd.DataFrame(REAL_DATA),
|
| 1175 |
-
label="์ค์ธก ๋ฐ์ดํฐ",
|
| 1176 |
-
wrap=True
|
| 1177 |
-
)
|
| 1178 |
-
|
| 1179 |
-
export_btn = gr.Button("๐พ CSV ๋ค์ด๋ก๋")
|
| 1180 |
-
file_output = gr.File(label="๋ค์ด๋ก๋")
|
| 1181 |
-
|
| 1182 |
-
# ์ ํ๋ ๊ฒ์ฆ ํญ
|
| 1183 |
-
with gr.TabItem("๐ฏ ์ ํ๋ ๊ฒ์ฆ"):
|
| 1184 |
-
gr.Markdown("""
|
| 1185 |
-
### ์ค์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ก ์ ํ๋ ๊ฒ์ฆ
|
| 1186 |
-
|
| 1187 |
-
ํ
์คํธ ๋ฐ์ดํฐ์
์ ๋ฐฐ์น ์ฒ๋ฆฌํ์ฌ ์์ธก ์ ํ๋๋ฅผ ์ธก์ ํฉ๋๋ค.
|
| 1188 |
-
""")
|
| 1189 |
-
|
| 1190 |
-
with gr.Row():
|
| 1191 |
-
with gr.Column():
|
| 1192 |
-
test_folder = gr.Textbox(
|
| 1193 |
-
value="d:/Project/VIDraft/Shrimp/data/251015",
|
| 1194 |
-
label="ํ
์คํธ ๋ฐ์ดํฐ ํด๋",
|
| 1195 |
-
info="251015_XX.jpg ํ์์ ์ด๋ฏธ์ง๊ฐ ์๋ ํด๋"
|
| 1196 |
-
)
|
| 1197 |
-
|
| 1198 |
-
with gr.Row():
|
| 1199 |
-
test_pixel_scale = gr.Number(
|
| 1200 |
-
value=92,
|
| 1201 |
-
label="ํฝ์
ํฌ๊ธฐ (px)",
|
| 1202 |
-
info="์ฐธ์กฐ ์์ ํฝ์
ํฌ๊ธฐ"
|
| 1203 |
-
)
|
| 1204 |
-
test_cm_scale = gr.Number(
|
| 1205 |
-
value=1,
|
| 1206 |
-
label="์ค์ ํฌ๊ธฐ (cm)",
|
| 1207 |
-
info="์ฐธ์กฐ ์์ ์ค์ ํฌ๊ธฐ"
|
| 1208 |
-
)
|
| 1209 |
-
|
| 1210 |
-
test_depth_checkbox = gr.Checkbox(
|
| 1211 |
-
value=False,
|
| 1212 |
-
label="๐ ๊น์ด ๊ธฐ๋ฐ ์๊ทผ ๋ณด์ ํ์ฑํ",
|
| 1213 |
-
info="Depth-Anything V2๋ก ์๋ ์๊ทผ ์๊ณก ๋ณด์ "
|
| 1214 |
-
)
|
| 1215 |
-
|
| 1216 |
-
test_btn = gr.Button(
|
| 1217 |
-
"๐ ๋ฐฐ์น ํ
์คํธ ์คํ",
|
| 1218 |
-
variant="primary",
|
| 1219 |
-
size="lg"
|
| 1220 |
-
)
|
| 1221 |
-
|
| 1222 |
-
with gr.Column():
|
| 1223 |
-
test_stats = gr.Markdown()
|
| 1224 |
-
|
| 1225 |
-
test_plot = gr.Plot(label="์์ธก vs ์ค์ ๋น๊ต")
|
| 1226 |
-
test_results_df = gr.Dataframe(
|
| 1227 |
-
label="์์ธ ๊ฒฐ๊ณผ",
|
| 1228 |
-
wrap=True
|
| 1229 |
-
)
|
| 1230 |
-
|
| 1231 |
-
gr.Markdown("### ๐ธ ์๊ฐํ ๊ฒฐ๊ณผ (์์ธก vs ์ค์ )")
|
| 1232 |
-
test_gallery = gr.Gallery(
|
| 1233 |
-
label="๊ฒ์ถ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง",
|
| 1234 |
-
show_label=True,
|
| 1235 |
-
columns=3,
|
| 1236 |
-
rows=2,
|
| 1237 |
-
height="auto",
|
| 1238 |
-
object_fit="contain"
|
| 1239 |
-
)
|
| 1240 |
-
|
| 1241 |
-
# ์ ๋ณด ํญ
|
| 1242 |
-
with gr.TabItem("โน๏ธ ์ฌ์ฉ ๋ฐฉ๋ฒ"):
|
| 1243 |
-
gr.Markdown("""
|
| 1244 |
-
## ๐ ์ฌ์ฉ ๊ฐ์ด๋
|
| 1245 |
-
|
| 1246 |
-
### 1๏ธโฃ ๊ฐ์ฒด ๊ฒ์ถ
|
| 1247 |
-
1. ์์ฐ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์
|
| 1248 |
-
2. ์ ๋ขฐ๋ ์๏ฟฝ๏ฟฝ๏ฟฝ๊ฐ์ ์กฐ์ ํ์ธ์ (๊ธฐ๋ณธ๊ฐ: 0.5)
|
| 1249 |
-
3. **๊น์ด ๋ณด์ ํ์ฑํ** (๊ถ์ฅ): ์๊ทผ ์๊ณก ์๋ ๋ณด์
|
| 1250 |
-
4. ์ค์ผ์ผ ๋ณด์ : ์ค์ ํฌ๊ธฐ๋ฅผ ์๊ณ ์๋ค๋ฉด ํฝ์
-cm ๋น์จ์ ์ค์ ํ์ธ์
|
| 1251 |
-
5. "๊ฒ์ถ ์คํ" ๋ฒํผ์ ํด๋ฆญํ์ธ์
|
| 1252 |
-
|
| 1253 |
-
### 2๏ธโฃ ๊น์ด ๊ธฐ๋ฐ ์๊ทผ ๋ณด์ (NEW! ๐ฅ)
|
| 1254 |
-
- **Depth-Anything V2** ๋ชจ๋ธ๋ก ์ด๋ฏธ์ง์ ๊น์ด ๋งต ์๋ ์์ฑ
|
| 1255 |
-
- ๊ฐ ์์ฐ์ ์๋์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ์ฌ ์ค์ผ์ผ ์๋ ๋ณด์
|
| 1256 |
-
- **์๊ทผ ์๊ณก ํจ๊ณผ๋ฅผ ์๋์ผ๋ก ์ ๊ฑฐ**ํ์ฌ ์ ํ๋ ํฅ์
|
| 1257 |
-
- ๊น์ด ๋งต ์๊ฐํ: ํ๋์=๊ฐ๊น์, ๋
ธ๋์=๋ฉ์
|
| 1258 |
-
- ์ถ๊ฐ ์ฅ๋น๋ ๋ง์ปค ์์ด ๋จ์ผ ์ด๋ฏธ์ง๋ง์ผ๋ก ์๋
|
| 1259 |
-
|
| 1260 |
-
### 3๏ธโฃ ์ค์ผ์ผ ๋ณด์
|
| 1261 |
-
- ์ด๋ฏธ์ง์์ ์๊ณ ์๋ ๊ฐ์ฒด์ ํฝ์
ํฌ๊ธฐ์ ์ค์ ํฌ๊ธฐ๋ฅผ ์
๋ ฅํ์ธ์
|
| 1262 |
-
- ์: ์๊ฐ ๋ณด์ธ๋ค๋ฉด, ์์ ํฝ์
๊ธธ์ด์ ์ค์ ๊ธธ์ด(cm)๋ฅผ ์
๋ ฅ
|
| 1263 |
-
- ๊น์ด ๋ณด์ ๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋์ฑ ์ ํํ ์ธก์ ๊ฐ๋ฅ
|
| 1264 |
-
|
| 1265 |
-
### 4๏ธโฃ ๊ฒฐ๊ณผ ํด์
|
| 1266 |
-
- **์ด๋ก์ ๋ฐ์ค**: ์ค์ฐจ < 10%
|
| 1267 |
-
- **์ฃผํฉ์ ๋ฐ์ค**: ์ค์ฐจ 10-20%
|
| 1268 |
-
- **๋นจ๊ฐ์ ๋ฐ์ค**: ์ค์ฐจ > 20%
|
| 1269 |
-
|
| 1270 |
-
### 5๏ธโฃ ์ฑ๋ฅ ํ๊ฐ
|
| 1271 |
-
- "์ฑ๋ฅ ํ๊ฐ" ํญ์์ ํ๊ท ๋ชจ๋ธ์ ์ ํ๋๋ฅผ ํ์ธํ์ธ์
|
| 1272 |
-
- Rยฒ, MAPE, MAE, RMSE ์งํ ์ ๊ณต
|
| 1273 |
-
|
| 1274 |
-
### 6๏ธโฃ ์ ํ๋ ๊ฒ์ฆ (NEW! ๐ฅ)
|
| 1275 |
-
- "์ ํ๋ ๊ฒ์ฆ" ํญ์์ ์ค์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ก ์ ํ๋ ์ธก์
|
| 1276 |
-
- ํ
์คํธ ๋ฐ์ดํฐ ํด๋๋ฅผ ์ง์ ํ๊ณ ๋ฐฐ์น ์ฒ๋ฆฌ
|
| 1277 |
-
- OCR๋ก ์ ์์ ์ธ LCD์์ ์ค์ ๋ฌด๊ฒ ์๋ ์ฝ๊ธฐ
|
| 1278 |
-
- ์์ธก vs ์ค์ ๋น๊ต ์ฐจํธ ๋ฐ ํต๊ณ ์ ๊ณต
|
| 1279 |
-
|
| 1280 |
-
### 7๏ธโฃ ๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ
|
| 1281 |
-
- "์ค์ธก ๋ฐ์ดํฐ" ํญ์์ CSV ํ์ผ๋ก ๋ค์ด๋ก๋ ๊ฐ๋ฅ
|
| 1282 |
-
|
| 1283 |
-
---
|
| 1284 |
-
|
| 1285 |
-
## โ๏ธ ์์คํ
์ ๋ณด
|
| 1286 |
-
|
| 1287 |
-
- **๊ฒ์ถ ๋ชจ๋ธ**: RT-DETR (Real-Time DEtection TRansformer)
|
| 1288 |
-
- **๊น์ด ์ถ์ ๋ชจ๋ธ**: Depth-Anything V2 Small (Monocular Depth Estimation)
|
| 1289 |
-
- **ํ๊ท ๋ชจ๋ธ**: Power Law (W = a ร L^b)
|
| 1290 |
-
- **๋๋ฐ์ด์ค**: """ + ("GPU (CUDA)" if torch.cuda.is_available() else "CPU") + """
|
| 1291 |
-
- **์ต์ ํ**: CPU ๋ชจ๋, torch.no_grad(), FP32
|
| 1292 |
-
- **์๊ทผ ๋ณด์ **: ๊น์ด ๋งต ๊ธฐ๋ฐ ์๋ ์ค์ผ์ผ ์กฐ์
|
| 1293 |
-
|
| 1294 |
-
## ๐ง ๋ฌธ์ ํด๊ฒฐ
|
| 1295 |
-
|
| 1296 |
-
**๊ฒ์ถ์ด ์ ๋ ๋**:
|
| 1297 |
-
- ์ ๋ขฐ๋ ์๊ณ๊ฐ์ ๋ฎ์ถฐ๋ณด์ธ์ (0.3 ์ดํ)
|
| 1298 |
-
- ์ด๋ฏธ์ง ํ์ง์ ํ์ธํ์ธ์ (ํด์๋, ๋ฐ๊ธฐ)
|
| 1299 |
-
|
| 1300 |
-
**์ ํ๋๊ฐ ๋ฎ์ ๋**:
|
| 1301 |
-
- ์ค์ผ์ผ ๋ณด์ ์ ์ ํํ ์
๋ ฅํ์ธ์
|
| 1302 |
-
- ์์ฐ ์ ์ฉ fine-tuning ๋ชจ๋ธ์ด ํ์ํ ์ ์์ต๋๋ค
|
| 1303 |
-
|
| 1304 |
-
**์๋๊ฐ ๋๋ฆด ๋**:
|
| 1305 |
-
- GPU ๊ฐ์์ ์ฌ์ฉํ์ธ์ (HF Space: GPU T4)
|
| 1306 |
-
- ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ค์ด์ธ์ (800x600 ๊ถ์ฅ)
|
| 1307 |
-
""")
|
| 1308 |
-
|
| 1309 |
-
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 1310 |
-
detect_btn.click(
|
| 1311 |
-
process_image,
|
| 1312 |
-
[input_img, model_selector, conf_slider, iou_slider, pixel_scale, cm_scale, depth_checkbox],
|
| 1313 |
-
[output_img, depth_img, stats, results_df]
|
| 1314 |
-
)
|
| 1315 |
-
|
| 1316 |
-
eval_btn.click(
|
| 1317 |
-
evaluate_model,
|
| 1318 |
-
[],
|
| 1319 |
-
[eval_text, eval_plot]
|
| 1320 |
-
)
|
| 1321 |
-
|
| 1322 |
-
export_btn.click(
|
| 1323 |
-
export_data,
|
| 1324 |
-
[],
|
| 1325 |
-
file_output
|
| 1326 |
-
)
|
| 1327 |
-
|
| 1328 |
-
test_btn.click(
|
| 1329 |
-
process_test_dataset,
|
| 1330 |
-
[test_folder, test_pixel_scale, test_cm_scale, test_depth_checkbox],
|
| 1331 |
-
[test_stats, test_results_df, test_plot, test_gallery]
|
| 1332 |
-
)
|
| 1333 |
-
|
| 1334 |
-
# ์คํ
|
| 1335 |
-
if __name__ == "__main__":
|
| 1336 |
-
demo.queue(max_size=10) # CPU ์ต์ ํ: ํ ํฌ๊ธฐ ์ ํ
|
| 1337 |
-
demo.launch(
|
| 1338 |
-
share=False,
|
| 1339 |
-
server_name="0.0.0.0",
|
| 1340 |
-
server_port=7860,
|
| 1341 |
-
show_error=True
|
| 1342 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,83 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""250818 ํด๋ ๋ผ๋ฒจ๋ง ๊ฒ์"""
|
| 3 |
-
import sys
|
| 4 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 5 |
-
import json
|
| 6 |
-
|
| 7 |
-
with open('ground_truth.json', 'r', encoding='utf-8') as f:
|
| 8 |
-
data = json.load(f)
|
| 9 |
-
|
| 10 |
-
print("=" * 70)
|
| 11 |
-
print("๐ 250818 ํด๋ ๋ผ๋ฒจ๋ง ๊ฒ์")
|
| 12 |
-
print("=" * 70)
|
| 13 |
-
|
| 14 |
-
folder_data = {k: v for k, v in data.items() if k.startswith('250818_') and not '-' in k}
|
| 15 |
-
|
| 16 |
-
print(f"\nโ
๋ผ๋ฒจ๋ง๋ ์ด๋ฏธ์ง: {len([v for v in folder_data.values() if v])}๊ฐ")
|
| 17 |
-
print(f"โ ๊ฑด๋๋ด ์ด๋ฏธ์ง: {len([v for v in folder_data.values() if not v])}๊ฐ")
|
| 18 |
-
|
| 19 |
-
print(f"\n{'ํ์ผ๋ช
':<20} {'๋ฐ์ค์':>6} {'์ข
ํก๋น':>8} {'์ ๋ขฐ๋':>8} {'์ํ':>10}")
|
| 20 |
-
print("-" * 70)
|
| 21 |
-
|
| 22 |
-
issues = []
|
| 23 |
-
|
| 24 |
-
for filename in sorted(folder_data.keys()):
|
| 25 |
-
boxes = folder_data[filename]
|
| 26 |
-
|
| 27 |
-
if not boxes:
|
| 28 |
-
print(f"{filename:<20} {'0':>6} {'-':>8} {'-':>8} {'โ ๏ธ ๊ฑด๋๋':>10}")
|
| 29 |
-
issues.append(f"{filename}: ๊ฑด๋๋ด ์ด๋ฏธ์ง (์์ฐ ์์?)")
|
| 30 |
-
else:
|
| 31 |
-
for box in boxes:
|
| 32 |
-
bbox = box['bbox']
|
| 33 |
-
x1, y1, x2, y2 = bbox
|
| 34 |
-
width = x2 - x1
|
| 35 |
-
height = y2 - y1
|
| 36 |
-
aspect = width / height if height > 0 else 0
|
| 37 |
-
conf = box['confidence']
|
| 38 |
-
|
| 39 |
-
# ์ด์์น ํ๋จ
|
| 40 |
-
status = "โ
์ ์"
|
| 41 |
-
if aspect < 0.5: # ๋๋ฌด ์ธ๋ก๋ก ๊ธด ๊ฒฝ์ฐ
|
| 42 |
-
status = "โ ๏ธ ์ธ๋ก"
|
| 43 |
-
issues.append(f"{filename}: ์ข
ํก๋น {aspect:.2f} (๋๋ฌด ์ธ๋ก๋ก ๊น)")
|
| 44 |
-
elif aspect > 15: # ๋๋ฌด ๊ฐ๋ก๋ก ๊ธด ๊ฒฝ์ฐ
|
| 45 |
-
status = "โ ๏ธ ๊ฐ๋ก"
|
| 46 |
-
issues.append(f"{filename}: ์ข
ํก๋น {aspect:.2f} (๋๋ฌด ๊ฐ๋ก๋ก ๊น)")
|
| 47 |
-
elif conf < 0.1:
|
| 48 |
-
status = "โ ๏ธ ๋ฎ์"
|
| 49 |
-
issues.append(f"{filename}: ์ ๋ขฐ๋ {conf:.3f} (๋งค์ฐ ๋ฎ์)")
|
| 50 |
-
|
| 51 |
-
print(f"{filename:<20} {len(boxes):>6} {aspect:>8.2f} {conf:>8.3f} {status:>10}")
|
| 52 |
-
|
| 53 |
-
print("\n" + "=" * 70)
|
| 54 |
-
if issues:
|
| 55 |
-
print("โ ๏ธ ํ์ธ ํ์ํ ํญ๋ชฉ:")
|
| 56 |
-
print("-" * 70)
|
| 57 |
-
for issue in issues:
|
| 58 |
-
print(f" โข {issue}")
|
| 59 |
-
else:
|
| 60 |
-
print("โ
๋ชจ๋ ๋ผ๋ฒจ๋ง ์ ์!")
|
| 61 |
-
|
| 62 |
-
print("\n" + "=" * 70)
|
| 63 |
-
print("๐ ํต๊ณ")
|
| 64 |
-
print("-" * 70)
|
| 65 |
-
|
| 66 |
-
all_boxes = [box for boxes in folder_data.values() if boxes for box in boxes]
|
| 67 |
-
if all_boxes:
|
| 68 |
-
aspects = [((box['bbox'][2]-box['bbox'][0])/(box['bbox'][3]-box['bbox'][1]))
|
| 69 |
-
for box in all_boxes if (box['bbox'][3]-box['bbox'][1]) > 0]
|
| 70 |
-
confs = [box['confidence'] for box in all_boxes]
|
| 71 |
-
|
| 72 |
-
print(f"ํ๊ท ์ข
ํก๋น: {sum(aspects)/len(aspects):.2f}")
|
| 73 |
-
print(f"ํ๊ท ์ ๋ขฐ๋: {sum(confs)/len(confs):.3f}")
|
| 74 |
-
print(f"์ต์ ์ ๋ขฐ๋: {min(confs):.3f}")
|
| 75 |
-
print(f"์ต๋ ์ ๋ขฐ๋: {max(confs):.3f}")
|
| 76 |
-
|
| 77 |
-
print("\n" + "=" * 70)
|
| 78 |
-
print("๐ก ๊ถ์ฅ์ฌํญ")
|
| 79 |
-
print("-" * 70)
|
| 80 |
-
print("โข ์ข
ํก๋น 3:1 ~ 10:1 ๋ฒ์๊ฐ ์์ฐ์ ์ผ๋ฐ์ ์ธ ๋น์จ")
|
| 81 |
-
print("โข ์ ๋ขฐ๋ 0.1 ์ดํ๋ ์ค๊ฒ์ถ ๊ฐ๋ฅ์ฑ ๋์")
|
| 82 |
-
print("โข ๊ฑด๋๋ด ์ด๋ฏธ์ง๋ ์ค์ ๋ก ์์ฐ๊ฐ ์๋์ง ์ฌํ์ธ")
|
| 83 |
-
print("=" * 70)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import os
|
| 3 |
-
|
| 4 |
-
# Ground Truth ํ์ผ ๋ก๋
|
| 5 |
-
with open('ground_truth.json', 'r', encoding='utf-8') as f:
|
| 6 |
-
gt = json.load(f)
|
| 7 |
-
|
| 8 |
-
# GT๊ฐ ์๋ ์ด๋ฏธ์ง ๋ชฉ๋ก
|
| 9 |
-
gt_images = [k for k, v in gt.items() if v]
|
| 10 |
-
print(f'GT ์ด๋ฏธ์ง ์ด {len(gt_images)}์ฅ')
|
| 11 |
-
|
| 12 |
-
# Train/Val split ํ์ธ
|
| 13 |
-
train_images = set(os.listdir('data/yolo_dataset/images/train'))
|
| 14 |
-
val_images = set(os.listdir('data/yolo_dataset/images/val'))
|
| 15 |
-
|
| 16 |
-
gt_in_train = []
|
| 17 |
-
gt_in_val = []
|
| 18 |
-
|
| 19 |
-
for img in gt_images:
|
| 20 |
-
base_name = img.replace('-1.jpg', '.jpg')
|
| 21 |
-
if img in train_images or base_name in train_images:
|
| 22 |
-
gt_in_train.append(img)
|
| 23 |
-
elif img in val_images or base_name in val_images:
|
| 24 |
-
gt_in_val.append(img)
|
| 25 |
-
|
| 26 |
-
print(f'\nGT ๋ถํฌ:')
|
| 27 |
-
print(f' - Train set: {len(gt_in_train)}์ฅ')
|
| 28 |
-
print(f' - Val set: {len(gt_in_val)}์ฅ')
|
| 29 |
-
|
| 30 |
-
if len(gt_in_train) > 0:
|
| 31 |
-
print(f'\n๋ฌธ์ : GT {len(gt_in_train)}์ฅ์ด ํ์ต ๋ฐ์ดํฐ์ ํฌํจ๋จ!')
|
| 32 |
-
print(f'ํด๊ฒฐ: Val set {len(gt_in_val)}์ฅ๋ง์ผ๋ก ํ๊ฐํด์ผ ํจ')
|
| 33 |
-
|
| 34 |
-
print(f'\nVal set GT ์ด๋ฏธ์ง:')
|
| 35 |
-
for img in gt_in_val[:10]:
|
| 36 |
-
print(f' - {img}')
|
| 37 |
-
if len(gt_in_val) > 10:
|
| 38 |
-
print(f' ... and {len(gt_in_val)-10} more')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,181 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""์ฌ๋ฐ๋ฅธ ๋ผ๋ฒจ๋ง ํ์ง ๊ฒ์"""
|
| 3 |
-
import sys
|
| 4 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 5 |
-
import json
|
| 6 |
-
import math
|
| 7 |
-
|
| 8 |
-
def calculate_iou(box1, box2):
|
| 9 |
-
"""IoU ๊ณ์ฐ"""
|
| 10 |
-
x1_min, y1_min, x1_max, y1_max = box1
|
| 11 |
-
x2_min, y2_min, x2_max, y2_max = box2
|
| 12 |
-
|
| 13 |
-
inter_x_min = max(x1_min, x2_min)
|
| 14 |
-
inter_y_min = max(y1_min, y2_min)
|
| 15 |
-
inter_x_max = min(x1_max, x2_max)
|
| 16 |
-
inter_y_max = min(y1_max, y2_max)
|
| 17 |
-
|
| 18 |
-
if inter_x_max < inter_x_min or inter_y_max < inter_y_min:
|
| 19 |
-
return 0.0
|
| 20 |
-
|
| 21 |
-
inter_area = (inter_x_max - inter_x_min) * (inter_y_max - inter_y_min)
|
| 22 |
-
box1_area = (x1_max - x1_min) * (y1_max - y1_min)
|
| 23 |
-
box2_area = (x2_max - x2_min) * (y2_max - y2_min)
|
| 24 |
-
union_area = box1_area + box2_area - inter_area
|
| 25 |
-
|
| 26 |
-
return inter_area / union_area if union_area > 0 else 0.0
|
| 27 |
-
|
| 28 |
-
with open('ground_truth.json', 'r', encoding='utf-8') as f:
|
| 29 |
-
data = json.load(f)
|
| 30 |
-
|
| 31 |
-
print("=" * 80)
|
| 32 |
-
print("๐ ๋ผ๋ฒจ๋ง ํ์ง ๊ฒ์ (์ฌ๋ฐ๋ฅธ ๊ธฐ์ค)")
|
| 33 |
-
print("=" * 80)
|
| 34 |
-
|
| 35 |
-
folder_data = {k: v for k, v in data.items() if k.startswith('250818_') and not '-' in k}
|
| 36 |
-
|
| 37 |
-
print(f"\nโ
๋ผ๋ฒจ๋ง๋ ์ด๋ฏธ์ง: {len([v for v in folder_data.values() if v])}๊ฐ")
|
| 38 |
-
print(f"โ ๏ธ ๊ฑด๋๋ด ์ด๋ฏธ์ง: {len([v for v in folder_data.values() if not v])}๊ฐ")
|
| 39 |
-
|
| 40 |
-
print(f"\n{'ํ์ผ๋ช
':<20} {'๋ฐ์ค':>4} {'์ฅ๋จ์ถ๋น':>8} {'๋ฉด์ ':>10} {'์ ๋ขฐ๋':>8} {'์ํ':>12}")
|
| 41 |
-
print("-" * 80)
|
| 42 |
-
|
| 43 |
-
issues = []
|
| 44 |
-
warnings = []
|
| 45 |
-
|
| 46 |
-
for filename in sorted(folder_data.keys()):
|
| 47 |
-
boxes = folder_data[filename]
|
| 48 |
-
|
| 49 |
-
if not boxes:
|
| 50 |
-
print(f"{filename:<20} {'0':>4} {'-':>8} {'-':>10} {'-':>8} {'โ ๏ธ ๊ฑด๋๋':>12}")
|
| 51 |
-
warnings.append(f"{filename}: ๊ฑด๋๋ด ์ด๋ฏธ์ง")
|
| 52 |
-
continue
|
| 53 |
-
|
| 54 |
-
# ๋ฐ์ค ์ ํ์ธ
|
| 55 |
-
if len(boxes) > 10:
|
| 56 |
-
issues.append(f"{filename}: ๋ฐ์ค {len(boxes)}๊ฐ (๋๋ฌด ๋ง์, ์ค๊ฒ์ถ ์์ฌ)")
|
| 57 |
-
|
| 58 |
-
# ์ค๋ณต ๋ฐ์ค ํ์ธ
|
| 59 |
-
if len(boxes) > 1:
|
| 60 |
-
for i in range(len(boxes)):
|
| 61 |
-
for j in range(i+1, len(boxes)):
|
| 62 |
-
iou = calculate_iou(boxes[i]['bbox'], boxes[j]['bbox'])
|
| 63 |
-
if iou > 0.5:
|
| 64 |
-
issues.append(f"{filename}: ๋ฐ์ค #{i+1}๊ณผ #{j+1} ์ค์ฒฉ (IoU={iou:.2f}, ์ค๋ณต ์ ํ?)")
|
| 65 |
-
|
| 66 |
-
for idx, box in enumerate(boxes):
|
| 67 |
-
bbox = box['bbox']
|
| 68 |
-
x1, y1, x2, y2 = bbox
|
| 69 |
-
width = x2 - x1
|
| 70 |
-
height = y2 - y1
|
| 71 |
-
area = width * height
|
| 72 |
-
conf = box['confidence']
|
| 73 |
-
|
| 74 |
-
# ์ฅ์ถ/๋จ์ถ ๋น์จ (๋ฐฉํฅ ๋ฌด๊ด)
|
| 75 |
-
long_axis = max(width, height)
|
| 76 |
-
short_axis = min(width, height)
|
| 77 |
-
axis_ratio = long_axis / short_axis if short_axis > 0 else 0
|
| 78 |
-
|
| 79 |
-
# ํ์ง ํ๋จ
|
| 80 |
-
status = "โ
์ ์"
|
| 81 |
-
issue_desc = []
|
| 82 |
-
|
| 83 |
-
# 1. ๋ฉด์ ์ฒดํฌ
|
| 84 |
-
if area < 1000:
|
| 85 |
-
status = "โ ๋๋ฌด์์"
|
| 86 |
-
issues.append(f"{filename} ๋ฐ์ค#{idx+1}: ๋ฉด์ {area:.0f}pxยฒ (๋๋ฌด ์์, ์ค๊ฒ์ถ?)")
|
| 87 |
-
issue_desc.append("๋ฉด์ โ")
|
| 88 |
-
elif area > 1000000:
|
| 89 |
-
status = "โ ๋๋ฌดํผ"
|
| 90 |
-
issues.append(f"{filename} ๋ฐ์ค#{idx+1}: ๋ฉด์ {area:.0f}pxยฒ (๋๋ฌด ํผ, ๋ฐฐ๊ฒฝ ํฌํจ?)")
|
| 91 |
-
issue_desc.append("๋ฉด์ โ")
|
| 92 |
-
|
| 93 |
-
# 2. ์ฅ๋จ์ถ ๋น์จ (์์ฐ๋ ๊ธธ์ญํด์ผ ํจ)
|
| 94 |
-
if axis_ratio < 2.5:
|
| 95 |
-
status = "โ ๏ธ ๋ฅ๊ธ์"
|
| 96 |
-
warnings.append(f"{filename} ๋ฐ์ค#{idx+1}: ์ฅ๋จ์ถ๋น {axis_ratio:.2f} (๋๋ฌด ๋ฅ๊ธ์, ์์ฐ ๋ง๋?)")
|
| 97 |
-
issue_desc.append("๋ฅ๊ธ์")
|
| 98 |
-
elif axis_ratio > 20:
|
| 99 |
-
status = "โ ๏ธ ๊ฐ๋์"
|
| 100 |
-
warnings.append(f"{filename} ๋ฐ์ค#{idx+1}: ์ฅ๋จ์ถ๋น {axis_ratio:.2f} (๋๋ฌด ๊ฐ๋์)")
|
| 101 |
-
issue_desc.append("๊ฐ๋์")
|
| 102 |
-
|
| 103 |
-
# 3. ์ ๋ขฐ๋ ์ฒดํฌ
|
| 104 |
-
if conf < 0.05:
|
| 105 |
-
status = "โ ์ ๋ขฐ๋โ"
|
| 106 |
-
issues.append(f"{filename} ๋ฐ์ค#{idx+1}: ์ ๋ขฐ๋ {conf:.3f} (๋งค์ฐ ๋ฎ์, ์ค๊ฒ์ถ ์์ฌ)")
|
| 107 |
-
issue_desc.append("์ ๋ขฐโ")
|
| 108 |
-
elif conf < 0.15:
|
| 109 |
-
if status == "โ
์ ์":
|
| 110 |
-
status = "โ ๏ธ ์ ๋ขฐ๋โ"
|
| 111 |
-
warnings.append(f"{filename} ๋ฐ์ค#{idx+1}: ์ ๋ขฐ๋ {conf:.3f} (๋ฎ์, ์ฌํ์ธ ๊ถ์ฅ)")
|
| 112 |
-
issue_desc.append("์ ๋ขฐ๋ฎ์")
|
| 113 |
-
|
| 114 |
-
issue_str = ",".join(issue_desc) if issue_desc else ""
|
| 115 |
-
if issue_str:
|
| 116 |
-
status = f"โ ๏ธ {issue_str}"
|
| 117 |
-
|
| 118 |
-
print(f"{filename:<20} {len(boxes):>4} {axis_ratio:>8.2f} {area:>10.0f} {conf:>8.3f} {status:>12}")
|
| 119 |
-
|
| 120 |
-
print("\n" + "=" * 80)
|
| 121 |
-
if issues:
|
| 122 |
-
print("โ ์ฌ๊ฐํ ๋ฌธ์ (์ฌํ์ธ ํ์):")
|
| 123 |
-
print("-" * 80)
|
| 124 |
-
for issue in issues[:10]: # ์ต๋ 10๊ฐ๋ง
|
| 125 |
-
print(f" โข {issue}")
|
| 126 |
-
if len(issues) > 10:
|
| 127 |
-
print(f" ... ์ธ {len(issues)-10}๊ฐ")
|
| 128 |
-
else:
|
| 129 |
-
print("โ
์ฌ๊ฐํ ๋ฌธ์ ์์")
|
| 130 |
-
|
| 131 |
-
if warnings:
|
| 132 |
-
print(f"\nโ ๏ธ ๊ฒฝ๊ณ ({len(warnings)}๊ฐ, ์ฌํ์ธ ๊ถ์ฅ):")
|
| 133 |
-
print("-" * 80)
|
| 134 |
-
for warning in warnings[:10]: # ์ต๋ 10๊ฐ๋ง
|
| 135 |
-
print(f" โข {warning}")
|
| 136 |
-
if len(warnings) > 10:
|
| 137 |
-
print(f" ... ์ธ {len(warnings)-10}๊ฐ")
|
| 138 |
-
|
| 139 |
-
print("\n" + "=" * 80)
|
| 140 |
-
print("๐ ํต๊ณ")
|
| 141 |
-
print("-" * 80)
|
| 142 |
-
|
| 143 |
-
all_boxes = [box for boxes in folder_data.values() if boxes for box in boxes]
|
| 144 |
-
if all_boxes:
|
| 145 |
-
areas = [(box['bbox'][2]-box['bbox'][0])*(box['bbox'][3]-box['bbox'][1]) for box in all_boxes]
|
| 146 |
-
axis_ratios = []
|
| 147 |
-
for box in all_boxes:
|
| 148 |
-
w = box['bbox'][2] - box['bbox'][0]
|
| 149 |
-
h = box['bbox'][3] - box['bbox'][1]
|
| 150 |
-
axis_ratios.append(max(w,h)/min(w,h) if min(w,h) > 0 else 0)
|
| 151 |
-
confs = [box['confidence'] for box in all_boxes]
|
| 152 |
-
|
| 153 |
-
print(f"์ด ๋ฐ์ค: {len(all_boxes)}๊ฐ")
|
| 154 |
-
print(f"ํ๊ท ๋ฉด์ : {sum(areas)/len(areas):,.0f} pxยฒ")
|
| 155 |
-
print(f"ํ๊ท ์ฅ๋จ์ถ๋น: {sum(axis_ratios)/len(axis_ratios):.2f} (์ ์ ๋ฒ์: 3~15)")
|
| 156 |
-
print(f"ํ๊ท ์ ๋ขฐ๋: {sum(confs)/len(confs):.3f}")
|
| 157 |
-
print(f"์ ๋ขฐ๋ ๋ฒ์: {min(confs):.3f} ~ {max(confs):.3f}")
|
| 158 |
-
|
| 159 |
-
print("\n" + "=" * 80)
|
| 160 |
-
print("๐ก ํ์ง ๊ธฐ์ค")
|
| 161 |
-
print("-" * 80)
|
| 162 |
-
print("โ
์ฅ๋จ์ถ๋น: 3:1 ~ 15:1 (์์ฐ๋ ๊ธธ์ญํจ, ๋ฐฉํฅ ๋ฌด๊ด)")
|
| 163 |
-
print("โ
๋ฉด์ : 1,000 ~ 1,000,000 pxยฒ")
|
| 164 |
-
print("โ
์ ๋ขฐ๋: > 0.15")
|
| 165 |
-
print("โ
๋ฐ์ค ์: 1~5๊ฐ/์ด๋ฏธ์ง")
|
| 166 |
-
print("โ
์ค์ฒฉ: IoU < 0.5 (์ค๋ณต ์ ํ ๋ฐฉ์ง)")
|
| 167 |
-
print("=" * 80)
|
| 168 |
-
|
| 169 |
-
# ์ต์ข
ํ๊ฐ
|
| 170 |
-
print("\n" + "=" * 80)
|
| 171 |
-
print("๐ ์ต์ข
ํ๊ฐ")
|
| 172 |
-
print("-" * 80)
|
| 173 |
-
if not issues and len(warnings) <= 3:
|
| 174 |
-
print("๐ ์ฐ์: ๋ผ๋ฒจ๋ง ํ์ง์ด ๋งค์ฐ ์ข์ต๋๋ค!")
|
| 175 |
-
elif not issues:
|
| 176 |
-
print("โ
์ํธ: ๋ช ๊ฐ์ง ์ฌํ์ธ ๊ถ์ฅ")
|
| 177 |
-
elif len(issues) <= 3:
|
| 178 |
-
print("โ ๏ธ ๋ณดํต: ์ผ๋ถ ๋ฐ์ค ์ฌํ์ธ ํ์")
|
| 179 |
-
else:
|
| 180 |
-
print("โ ๋ถ๋: ๋ง์ ๋ฐ์ค ์ฌ๋ผ๋ฒจ๋ง ํ์")
|
| 181 |
-
print("=" * 80)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,185 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Ground Truth๋ฅผ YOLO format์ผ๋ก ๋ณํ
|
| 4 |
-
Train/Val split ํฌํจ
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import json
|
| 10 |
-
import os
|
| 11 |
-
import shutil
|
| 12 |
-
from pathlib import Path
|
| 13 |
-
import random
|
| 14 |
-
from PIL import Image
|
| 15 |
-
|
| 16 |
-
def convert_bbox_to_yolo(bbox, img_width, img_height):
|
| 17 |
-
"""
|
| 18 |
-
[x1, y1, x2, y2] โ [x_center, y_center, width, height] (normalized)
|
| 19 |
-
"""
|
| 20 |
-
x1, y1, x2, y2 = bbox
|
| 21 |
-
|
| 22 |
-
# Center coordinates
|
| 23 |
-
x_center = (x1 + x2) / 2.0
|
| 24 |
-
y_center = (y1 + y2) / 2.0
|
| 25 |
-
|
| 26 |
-
# Width and height
|
| 27 |
-
width = x2 - x1
|
| 28 |
-
height = y2 - y1
|
| 29 |
-
|
| 30 |
-
# Normalize to [0, 1]
|
| 31 |
-
x_center_norm = x_center / img_width
|
| 32 |
-
y_center_norm = y_center / img_height
|
| 33 |
-
width_norm = width / img_width
|
| 34 |
-
height_norm = height / img_height
|
| 35 |
-
|
| 36 |
-
return x_center_norm, y_center_norm, width_norm, height_norm
|
| 37 |
-
|
| 38 |
-
def main():
|
| 39 |
-
print("=" * 60)
|
| 40 |
-
print("Ground Truth โ YOLO Format ๋ณํ ์์")
|
| 41 |
-
print("=" * 60)
|
| 42 |
-
|
| 43 |
-
# ๊ฒฝ๋ก ์ค์
|
| 44 |
-
gt_file = "ground_truth.json"
|
| 45 |
-
data_base_dir = "data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 46 |
-
output_base_dir = "data/yolo_dataset"
|
| 47 |
-
|
| 48 |
-
# YOLO ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ ์์ฑ
|
| 49 |
-
train_img_dir = Path(output_base_dir) / "images" / "train"
|
| 50 |
-
val_img_dir = Path(output_base_dir) / "images" / "val"
|
| 51 |
-
train_label_dir = Path(output_base_dir) / "labels" / "train"
|
| 52 |
-
val_label_dir = Path(output_base_dir) / "labels" / "val"
|
| 53 |
-
|
| 54 |
-
for d in [train_img_dir, val_img_dir, train_label_dir, val_label_dir]:
|
| 55 |
-
d.mkdir(parents=True, exist_ok=True)
|
| 56 |
-
|
| 57 |
-
# Ground Truth ๋ก๋
|
| 58 |
-
print(f"\n๐ {gt_file} ๋ก๋ฉ ์ค...")
|
| 59 |
-
with open(gt_file, 'r', encoding='utf-8') as f:
|
| 60 |
-
gt_data = json.load(f)
|
| 61 |
-
|
| 62 |
-
# ๋ฐ์ดํฐ ์์ง
|
| 63 |
-
all_samples = []
|
| 64 |
-
|
| 65 |
-
for filename, annotations in gt_data.items():
|
| 66 |
-
if not annotations: # ๋น ๋ฆฌ์คํธ๋ ๊ฑด๋๋ฐ๊ธฐ
|
| 67 |
-
continue
|
| 68 |
-
|
| 69 |
-
# ์ฒซ ๋ฒ์งธ annotation์์ ํด๋ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
| 70 |
-
folder = annotations[0].get('folder', '')
|
| 71 |
-
|
| 72 |
-
if not folder:
|
| 73 |
-
print(f"โ ๏ธ {filename}: ํด๋ ์ ๋ณด ์์, ๊ฑด๋๋")
|
| 74 |
-
continue
|
| 75 |
-
|
| 76 |
-
# ์ด๋ฏธ์ง ๊ฒฝ๋ก ํ์ธ
|
| 77 |
-
img_path = os.path.join(data_base_dir, folder, filename)
|
| 78 |
-
|
| 79 |
-
if not os.path.exists(img_path):
|
| 80 |
-
print(f"โ ๏ธ ์ด๋ฏธ์ง ์์: {img_path}")
|
| 81 |
-
continue
|
| 82 |
-
|
| 83 |
-
all_samples.append({
|
| 84 |
-
'filename': filename,
|
| 85 |
-
'folder': folder,
|
| 86 |
-
'img_path': img_path,
|
| 87 |
-
'annotations': annotations
|
| 88 |
-
})
|
| 89 |
-
|
| 90 |
-
print(f"\nโ
์ด {len(all_samples)}๊ฐ ์ํ ์์ง ์๋ฃ")
|
| 91 |
-
|
| 92 |
-
# Train/Val Split (80/20)
|
| 93 |
-
random.seed(42) # ์ฌํ์ฑ์ ์ํด
|
| 94 |
-
random.shuffle(all_samples)
|
| 95 |
-
|
| 96 |
-
split_idx = int(len(all_samples) * 0.8)
|
| 97 |
-
train_samples = all_samples[:split_idx]
|
| 98 |
-
val_samples = all_samples[split_idx:]
|
| 99 |
-
|
| 100 |
-
print(f"\n๐ ๋ฐ์ดํฐ ๋ถํ :")
|
| 101 |
-
print(f" - Train: {len(train_samples)}๊ฐ")
|
| 102 |
-
print(f" - Val: {len(val_samples)}๊ฐ")
|
| 103 |
-
|
| 104 |
-
# ๋ณํ ํจ์
|
| 105 |
-
def process_samples(samples, img_dir, label_dir, split_name):
|
| 106 |
-
print(f"\n๐ {split_name} ๋ฐ์ดํฐ ๋ณํ ์ค...")
|
| 107 |
-
|
| 108 |
-
for idx, sample in enumerate(samples, 1):
|
| 109 |
-
filename = sample['filename']
|
| 110 |
-
img_path = sample['img_path']
|
| 111 |
-
annotations = sample['annotations']
|
| 112 |
-
|
| 113 |
-
# ์ด๋ฏธ์ง ๋ณต์ฌ
|
| 114 |
-
dest_img_path = img_dir / filename
|
| 115 |
-
shutil.copy2(img_path, dest_img_path)
|
| 116 |
-
|
| 117 |
-
# ์ด๋ฏธ์ง ํฌ๊ธฐ ๊ฐ์ ธ์ค๊ธฐ
|
| 118 |
-
with Image.open(img_path) as img:
|
| 119 |
-
img_width, img_height = img.size
|
| 120 |
-
|
| 121 |
-
# YOLO ๋ผ๋ฒจ ์์ฑ
|
| 122 |
-
label_filename = Path(filename).stem + ".txt"
|
| 123 |
-
label_path = label_dir / label_filename
|
| 124 |
-
|
| 125 |
-
with open(label_path, 'w') as f:
|
| 126 |
-
for ann in annotations:
|
| 127 |
-
bbox = ann['bbox']
|
| 128 |
-
|
| 129 |
-
# YOLO format์ผ๋ก ๋ณํ
|
| 130 |
-
x_center, y_center, width, height = convert_bbox_to_yolo(
|
| 131 |
-
bbox, img_width, img_height
|
| 132 |
-
)
|
| 133 |
-
|
| 134 |
-
# YOLO ํ์: class_id x_center y_center width height
|
| 135 |
-
# class_id=0 (shrimp)
|
| 136 |
-
f.write(f"0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n")
|
| 137 |
-
|
| 138 |
-
if idx % 10 == 0 or idx == len(samples):
|
| 139 |
-
print(f" ์งํ: {idx}/{len(samples)}")
|
| 140 |
-
|
| 141 |
-
# Train/Val ๋ฐ์ดํฐ ์ฒ๋ฆฌ
|
| 142 |
-
process_samples(train_samples, train_img_dir, train_label_dir, "Train")
|
| 143 |
-
process_samples(val_samples, val_img_dir, val_label_dir, "Val")
|
| 144 |
-
|
| 145 |
-
# data.yaml ์์ฑ
|
| 146 |
-
yaml_path = Path(output_base_dir) / "data.yaml"
|
| 147 |
-
yaml_content = f"""# Shrimp Detection Dataset
|
| 148 |
-
path: {output_base_dir} # dataset root dir
|
| 149 |
-
train: images/train # train images (relative to 'path')
|
| 150 |
-
val: images/val # val images (relative to 'path')
|
| 151 |
-
|
| 152 |
-
# Classes
|
| 153 |
-
nc: 1 # number of classes
|
| 154 |
-
names: ['shrimp'] # class names
|
| 155 |
-
"""
|
| 156 |
-
|
| 157 |
-
with open(yaml_path, 'w', encoding='utf-8') as f:
|
| 158 |
-
f.write(yaml_content)
|
| 159 |
-
|
| 160 |
-
print(f"\nโ
data.yaml ์์ฑ ์๋ฃ: {yaml_path}")
|
| 161 |
-
|
| 162 |
-
# ์์ฝ ์ถ๋ ฅ
|
| 163 |
-
print("\n" + "=" * 60)
|
| 164 |
-
print("โ
๋ณํ ์๋ฃ!")
|
| 165 |
-
print("=" * 60)
|
| 166 |
-
print(f"\n๐ ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ: {output_base_dir}")
|
| 167 |
-
print(f"\n๐ ๋ฐ์ดํฐ์
๊ตฌ์กฐ:")
|
| 168 |
-
print(f" - Train: {len(train_samples)} images")
|
| 169 |
-
print(f" - Val: {len(val_samples)} images")
|
| 170 |
-
print(f" - Total: {len(all_samples)} images")
|
| 171 |
-
|
| 172 |
-
# ์ํ ํ์ธ
|
| 173 |
-
print(f"\n๐ ์ํ ๋ผ๋ฒจ ํ์ธ (Train ์ฒซ ๋ฒ์งธ):")
|
| 174 |
-
first_label = next(train_label_dir.glob("*.txt"))
|
| 175 |
-
with open(first_label, 'r') as f:
|
| 176 |
-
content = f.read()
|
| 177 |
-
print(f" {first_label.name}:")
|
| 178 |
-
for line in content.strip().split('\n'):
|
| 179 |
-
print(f" {line}")
|
| 180 |
-
|
| 181 |
-
print(f"\n๐ฏ ๋ค์ ๋จ๊ณ: YOLOv8 ํ์ต ์คํ")
|
| 182 |
-
print(f" python train_yolo.py")
|
| 183 |
-
|
| 184 |
-
if __name__ == "__main__":
|
| 185 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Roboflow API ์๋ต ๋๋ฒ๊น
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
from inference_sdk import InferenceHTTPClient
|
| 9 |
-
import json
|
| 10 |
-
|
| 11 |
-
# Roboflow ํด๋ผ์ด์ธํธ
|
| 12 |
-
client = InferenceHTTPClient(
|
| 13 |
-
api_url="https://serverless.roboflow.com",
|
| 14 |
-
api_key="azcIL8KDJVJMYrsERzI7"
|
| 15 |
-
)
|
| 16 |
-
|
| 17 |
-
# ํ
์คํธ ์ด๋ฏธ์ง
|
| 18 |
-
test_image = "data/yolo_dataset/images/train/250818_01.jpg"
|
| 19 |
-
|
| 20 |
-
print("="*60)
|
| 21 |
-
print("๐ Roboflow API ์๋ต ๋๋ฒ๊น
")
|
| 22 |
-
print("="*60)
|
| 23 |
-
print(f"\n๐ธ ์ด๋ฏธ์ง: {test_image}")
|
| 24 |
-
print(f"๐ Workflow: vidraft/find-shrimp-6")
|
| 25 |
-
|
| 26 |
-
# API ํธ์ถ
|
| 27 |
-
result = client.run_workflow(
|
| 28 |
-
workspace_name="vidraft",
|
| 29 |
-
workflow_id="find-shrimp-6",
|
| 30 |
-
images={"image": test_image},
|
| 31 |
-
use_cache=False # ์บ์ ๋นํ์ฑํ
|
| 32 |
-
)
|
| 33 |
-
|
| 34 |
-
print(f"\n๐ฆ ์ ์ฒด ์๋ต ๊ตฌ์กฐ:")
|
| 35 |
-
print(json.dumps(result, indent=2, ensure_ascii=False))
|
| 36 |
-
|
| 37 |
-
print(f"\n{'='*60}")
|
| 38 |
-
print("โ
๋๋ฒ๊น
์๋ฃ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,239 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
YOLOv8 ํ์ต ๋ชจ๋ธ ํ๊ฐ
|
| 4 |
-
GT ๋ฐ์ดํฐ๋ก ์ฑ๋ฅ ์ธก์ ๋ฐ RT-DETR๊ณผ ๋น๊ต
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
from ultralytics import YOLO
|
| 10 |
-
import json
|
| 11 |
-
import os
|
| 12 |
-
from PIL import Image
|
| 13 |
-
import numpy as np
|
| 14 |
-
from pathlib import Path
|
| 15 |
-
|
| 16 |
-
def calculate_iou(box1, box2):
|
| 17 |
-
"""IoU ๊ณ์ฐ"""
|
| 18 |
-
x1_1, y1_1, x2_1, y2_1 = box1
|
| 19 |
-
x1_2, y1_2, x2_2, y2_2 = box2
|
| 20 |
-
|
| 21 |
-
# Intersection
|
| 22 |
-
x1_i = max(x1_1, x1_2)
|
| 23 |
-
y1_i = max(y1_1, y1_2)
|
| 24 |
-
x2_i = min(x2_1, x2_2)
|
| 25 |
-
y2_i = min(y2_1, y2_2)
|
| 26 |
-
|
| 27 |
-
if x2_i < x1_i or y2_i < y1_i:
|
| 28 |
-
return 0.0
|
| 29 |
-
|
| 30 |
-
intersection = (x2_i - x1_i) * (y2_i - y1_i)
|
| 31 |
-
|
| 32 |
-
# Union
|
| 33 |
-
area1 = (x2_1 - x1_1) * (y2_1 - y1_1)
|
| 34 |
-
area2 = (x2_2 - x1_2) * (y2_2 - y1_2)
|
| 35 |
-
union = area1 + area2 - intersection
|
| 36 |
-
|
| 37 |
-
return intersection / union if union > 0 else 0.0
|
| 38 |
-
|
| 39 |
-
def evaluate_model(model_path, gt_file, data_base_dir, confidence_threshold=0.25, iou_threshold=0.5):
|
| 40 |
-
"""๋ชจ๋ธ ํ๊ฐ"""
|
| 41 |
-
print(f"\n๐ ๋ชจ๋ธ ํ๊ฐ ์์: {model_path}")
|
| 42 |
-
print(f" - Confidence: {confidence_threshold}")
|
| 43 |
-
print(f" - IoU Threshold: {iou_threshold}")
|
| 44 |
-
|
| 45 |
-
# ๋ชจ๋ธ ๋ก๋
|
| 46 |
-
model = YOLO(model_path)
|
| 47 |
-
print(f"โ
๋ชจ๋ธ ๋ก๋ ์๋ฃ")
|
| 48 |
-
|
| 49 |
-
# GT ๋ก๋
|
| 50 |
-
with open(gt_file, 'r', encoding='utf-8') as f:
|
| 51 |
-
gt_data = json.load(f)
|
| 52 |
-
|
| 53 |
-
# ํต๊ณ
|
| 54 |
-
total_gt = 0
|
| 55 |
-
total_pred = 0
|
| 56 |
-
true_positives = 0
|
| 57 |
-
false_positives = 0
|
| 58 |
-
false_negatives = 0
|
| 59 |
-
|
| 60 |
-
results_detail = []
|
| 61 |
-
|
| 62 |
-
# ๊ฐ ์ด๋ฏธ์ง ํ๊ฐ
|
| 63 |
-
for filename, gt_boxes in gt_data.items():
|
| 64 |
-
if not gt_boxes:
|
| 65 |
-
continue
|
| 66 |
-
|
| 67 |
-
folder = gt_boxes[0].get('folder', '')
|
| 68 |
-
if not folder:
|
| 69 |
-
continue
|
| 70 |
-
|
| 71 |
-
img_path = os.path.join(data_base_dir, folder, filename)
|
| 72 |
-
if not os.path.exists(img_path):
|
| 73 |
-
continue
|
| 74 |
-
|
| 75 |
-
# YOLOv8 ์ถ๋ก
|
| 76 |
-
results = model(img_path, conf=confidence_threshold, verbose=False)
|
| 77 |
-
|
| 78 |
-
# ์์ธก ๋ฐ์ค ์ถ์ถ
|
| 79 |
-
pred_boxes = []
|
| 80 |
-
if results and len(results) > 0:
|
| 81 |
-
result = results[0]
|
| 82 |
-
if result.boxes is not None and len(result.boxes) > 0:
|
| 83 |
-
boxes = result.boxes.xyxy.cpu().numpy() # [x1, y1, x2, y2]
|
| 84 |
-
confs = result.boxes.conf.cpu().numpy()
|
| 85 |
-
|
| 86 |
-
for box, conf in zip(boxes, confs):
|
| 87 |
-
pred_boxes.append({
|
| 88 |
-
'bbox': box.tolist(),
|
| 89 |
-
'confidence': float(conf)
|
| 90 |
-
})
|
| 91 |
-
|
| 92 |
-
# GT ๋ฐ์ค
|
| 93 |
-
gt_boxes_only = [{'bbox': ann['bbox']} for ann in gt_boxes]
|
| 94 |
-
|
| 95 |
-
# ๋งค์นญ
|
| 96 |
-
matched_gt = set()
|
| 97 |
-
matched_pred = set()
|
| 98 |
-
|
| 99 |
-
for i, pred in enumerate(pred_boxes):
|
| 100 |
-
best_iou = 0
|
| 101 |
-
best_gt_idx = -1
|
| 102 |
-
|
| 103 |
-
for j, gt in enumerate(gt_boxes_only):
|
| 104 |
-
if j in matched_gt:
|
| 105 |
-
continue
|
| 106 |
-
|
| 107 |
-
iou = calculate_iou(pred['bbox'], gt['bbox'])
|
| 108 |
-
if iou > best_iou:
|
| 109 |
-
best_iou = iou
|
| 110 |
-
best_gt_idx = j
|
| 111 |
-
|
| 112 |
-
if best_iou >= iou_threshold:
|
| 113 |
-
matched_pred.add(i)
|
| 114 |
-
matched_gt.add(best_gt_idx)
|
| 115 |
-
|
| 116 |
-
tp = len(matched_gt)
|
| 117 |
-
fp = len(pred_boxes) - len(matched_pred)
|
| 118 |
-
fn = len(gt_boxes_only) - len(matched_gt)
|
| 119 |
-
|
| 120 |
-
true_positives += tp
|
| 121 |
-
false_positives += fp
|
| 122 |
-
false_negatives += fn
|
| 123 |
-
total_gt += len(gt_boxes_only)
|
| 124 |
-
total_pred += len(pred_boxes)
|
| 125 |
-
|
| 126 |
-
results_detail.append({
|
| 127 |
-
'filename': filename,
|
| 128 |
-
'gt_count': len(gt_boxes_only),
|
| 129 |
-
'pred_count': len(pred_boxes),
|
| 130 |
-
'tp': tp,
|
| 131 |
-
'fp': fp,
|
| 132 |
-
'fn': fn
|
| 133 |
-
})
|
| 134 |
-
|
| 135 |
-
# ์ฑ๋ฅ ๊ณ์ฐ
|
| 136 |
-
precision = true_positives / (true_positives + false_positives) if (true_positives + false_positives) > 0 else 0
|
| 137 |
-
recall = true_positives / (true_positives + false_negatives) if (true_positives + false_negatives) > 0 else 0
|
| 138 |
-
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
|
| 139 |
-
|
| 140 |
-
return {
|
| 141 |
-
'precision': precision,
|
| 142 |
-
'recall': recall,
|
| 143 |
-
'f1': f1,
|
| 144 |
-
'tp': true_positives,
|
| 145 |
-
'fp': false_positives,
|
| 146 |
-
'fn': false_negatives,
|
| 147 |
-
'total_gt': total_gt,
|
| 148 |
-
'total_pred': total_pred
|
| 149 |
-
}
|
| 150 |
-
|
| 151 |
-
def main():
|
| 152 |
-
print("=" * 60)
|
| 153 |
-
print("๐ฏ YOLOv8 ๋ชจ๋ธ ํ๊ฐ")
|
| 154 |
-
print("=" * 60)
|
| 155 |
-
|
| 156 |
-
# ๊ฒฝ๋ก ์ค์
|
| 157 |
-
best_model = "runs/train/shrimp_yolov8n/weights/best.pt"
|
| 158 |
-
gt_file = "ground_truth.json"
|
| 159 |
-
data_base_dir = "data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 160 |
-
|
| 161 |
-
if not os.path.exists(best_model):
|
| 162 |
-
print(f"\nโ ๋ชจ๋ธ ํ์ผ ์์: {best_model}")
|
| 163 |
-
print(" ๋จผ์ train_yolo.py๋ฅผ ์คํํ์ธ์.")
|
| 164 |
-
return
|
| 165 |
-
|
| 166 |
-
print(f"\n๐ ๋ชจ๋ธ: {best_model}")
|
| 167 |
-
print(f"๐ GT: {gt_file}")
|
| 168 |
-
|
| 169 |
-
# ์ฌ๋ฌ confidence threshold ํ
์คํธ (๋ฎ์ ๊ฐ๋ถํฐ)
|
| 170 |
-
confidence_thresholds = [0.001, 0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3]
|
| 171 |
-
|
| 172 |
-
print(f"\n๐ Confidence Threshold ์ต์ ํ ์ค...")
|
| 173 |
-
print(f" ํ
์คํธ ๋ฒ์: {confidence_thresholds}")
|
| 174 |
-
|
| 175 |
-
best_f1 = 0
|
| 176 |
-
best_conf = 0
|
| 177 |
-
best_result = None
|
| 178 |
-
all_results = []
|
| 179 |
-
|
| 180 |
-
for conf in confidence_thresholds:
|
| 181 |
-
result = evaluate_model(best_model, gt_file, data_base_dir, confidence_threshold=conf)
|
| 182 |
-
result['conf'] = conf
|
| 183 |
-
all_results.append(result)
|
| 184 |
-
|
| 185 |
-
print(f"\n Conf={conf:.2f}: P={result['precision']:.1%}, R={result['recall']:.1%}, F1={result['f1']:.1%}")
|
| 186 |
-
|
| 187 |
-
if result['f1'] > best_f1:
|
| 188 |
-
best_f1 = result['f1']
|
| 189 |
-
best_conf = conf
|
| 190 |
-
best_result = result
|
| 191 |
-
|
| 192 |
-
# ์ต์ ๊ฒฐ๊ณผ ์ถ๋ ฅ
|
| 193 |
-
print("\n" + "=" * 60)
|
| 194 |
-
print("โ
ํ๊ฐ ์๋ฃ!")
|
| 195 |
-
print("=" * 60)
|
| 196 |
-
|
| 197 |
-
print(f"\n๐ ์ต์ ์ฑ๋ฅ (Confidence={best_conf}):")
|
| 198 |
-
print(f" - Precision: {best_result['precision']:.1%}")
|
| 199 |
-
print(f" - Recall: {best_result['recall']:.1%}")
|
| 200 |
-
print(f" - F1 Score: {best_result['f1']:.1%}")
|
| 201 |
-
print(f"\n - True Positives: {best_result['tp']}")
|
| 202 |
-
print(f" - False Positives: {best_result['fp']}")
|
| 203 |
-
print(f" - False Negatives: {best_result['fn']}")
|
| 204 |
-
print(f" - Total GT: {best_result['total_gt']}")
|
| 205 |
-
print(f" - Total Pred: {best_result['total_pred']}")
|
| 206 |
-
|
| 207 |
-
# RT-DETR ๋น๊ต
|
| 208 |
-
print(f"\n๐ RT-DETR + Filter ๋น๊ต:")
|
| 209 |
-
print(f" RT-DETR (Conf=0.065, Filter=90):")
|
| 210 |
-
print(f" - Precision: 44.2%")
|
| 211 |
-
print(f" - Recall: 94.0%")
|
| 212 |
-
print(f" - F1 Score: 56.1%")
|
| 213 |
-
print(f"\n YOLOv8 (Conf={best_conf}):")
|
| 214 |
-
print(f" - Precision: {best_result['precision']:.1%}")
|
| 215 |
-
print(f" - Recall: {best_result['recall']:.1%}")
|
| 216 |
-
print(f" - F1 Score: {best_result['f1']:.1%}")
|
| 217 |
-
|
| 218 |
-
# ๊ฐ์ ์จ
|
| 219 |
-
baseline_f1 = 0.561
|
| 220 |
-
improvement = (best_result['f1'] - baseline_f1) / baseline_f1 * 100
|
| 221 |
-
print(f"\n F1 ๊ฐ์ ์จ: {improvement:+.1f}%")
|
| 222 |
-
|
| 223 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 224 |
-
output_file = "yolo_evaluation_results.json"
|
| 225 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
| 226 |
-
json.dump({
|
| 227 |
-
'best_result': best_result,
|
| 228 |
-
'all_results': all_results,
|
| 229 |
-
'baseline': {
|
| 230 |
-
'precision': 0.442,
|
| 231 |
-
'recall': 0.940,
|
| 232 |
-
'f1': 0.561
|
| 233 |
-
}
|
| 234 |
-
}, f, indent=2, ensure_ascii=False)
|
| 235 |
-
|
| 236 |
-
print(f"\n๐พ ๊ฒฐ๊ณผ ์ ์ฅ: {output_file}")
|
| 237 |
-
|
| 238 |
-
if __name__ == "__main__":
|
| 239 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Git LFS Details
|
|
Git LFS Details
|
|
Git LFS Details
|
|
Git LFS Details
|
|
Git LFS Details
|
|
@@ -1,650 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Ground Truth ๋ผ๋ฒจ๋ง ๋๊ตฌ
|
| 4 |
-
RT-DETR ๊ฒ์ถ ๊ฒฐ๊ณผ์์ ์ฌ์ฉ์๊ฐ ์ฌ๋ฐ๋ฅธ ๋ฐ์ค๋ง ์ ํํ์ฌ ๋ผ๋ฒจ๋ง
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import gradio as gr
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
import json
|
| 12 |
-
import os
|
| 13 |
-
import glob
|
| 14 |
-
from datetime import datetime
|
| 15 |
-
import torch
|
| 16 |
-
from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
|
| 17 |
-
|
| 18 |
-
# ์ ์ญ ๋ณ์ - ๋ชจ๋ธ์ ํ์์์๋ง ๋ก๋
|
| 19 |
-
processor = None
|
| 20 |
-
model = None
|
| 21 |
-
|
| 22 |
-
def load_rtdetr_model():
|
| 23 |
-
"""RT-DETR ๋ชจ๋ธ์ ํ์์์๋ง ๋ก๋ฉ"""
|
| 24 |
-
global processor, model
|
| 25 |
-
if processor is None or model is None:
|
| 26 |
-
print("๐ RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์ค...")
|
| 27 |
-
processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd_coco_o365")
|
| 28 |
-
model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd_coco_o365")
|
| 29 |
-
model.eval()
|
| 30 |
-
print("โ
RT-DETR ๋ก๋ฉ ์๋ฃ")
|
| 31 |
-
|
| 32 |
-
current_data = {
|
| 33 |
-
'folder': None,
|
| 34 |
-
'images': [],
|
| 35 |
-
'current_idx': 0,
|
| 36 |
-
'detections': {},
|
| 37 |
-
'selections': {},
|
| 38 |
-
'confidence_threshold': 0.2, # ๊ธฐ๋ณธ ์ ๋ขฐ๋ ์๊ณ๊ฐ (๋ฎ์ถค)
|
| 39 |
-
'image_cache': {} # ์ด๋ฏธ์ง ์บ์ฑ (์๋ ํฅ์)
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
GROUND_TRUTH_FILE = "ground_truth.json"
|
| 43 |
-
DATA_BASE = "data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 44 |
-
|
| 45 |
-
def detect_with_rtdetr_fast(image, confidence=0.3):
|
| 46 |
-
"""RT-DETR ๋น ๋ฅธ ๊ฒ์ถ"""
|
| 47 |
-
global processor, model
|
| 48 |
-
|
| 49 |
-
# ๋ชจ๋ธ์ด ๋ก๋๋์ง ์์์ผ๋ฉด ๋ก๋
|
| 50 |
-
load_rtdetr_model()
|
| 51 |
-
|
| 52 |
-
inputs = processor(images=image, return_tensors="pt")
|
| 53 |
-
with torch.no_grad():
|
| 54 |
-
outputs = model(**inputs)
|
| 55 |
-
|
| 56 |
-
target_sizes = torch.tensor([image.size[::-1]])
|
| 57 |
-
results = processor.post_process_object_detection(
|
| 58 |
-
outputs,
|
| 59 |
-
target_sizes=target_sizes,
|
| 60 |
-
threshold=confidence
|
| 61 |
-
)[0]
|
| 62 |
-
|
| 63 |
-
detections = []
|
| 64 |
-
for score, label, box in zip(results["scores"], results["labels"], results["boxes"]):
|
| 65 |
-
x1, y1, x2, y2 = box.tolist()
|
| 66 |
-
detections.append({
|
| 67 |
-
'bbox': [x1, y1, x2, y2],
|
| 68 |
-
'confidence': score.item()
|
| 69 |
-
})
|
| 70 |
-
|
| 71 |
-
return detections
|
| 72 |
-
|
| 73 |
-
def load_existing_ground_truth():
|
| 74 |
-
"""๊ธฐ์กด ground_truth.json ๋ก๋"""
|
| 75 |
-
if os.path.exists(GROUND_TRUTH_FILE):
|
| 76 |
-
with open(GROUND_TRUTH_FILE, 'r', encoding='utf-8') as f:
|
| 77 |
-
return json.load(f)
|
| 78 |
-
return {}
|
| 79 |
-
|
| 80 |
-
def save_ground_truth(data):
|
| 81 |
-
"""ground_truth.json ์ ์ฅ"""
|
| 82 |
-
# ๋ฐฑ์
ํด๋ ์์ฑ
|
| 83 |
-
backup_dir = "backups"
|
| 84 |
-
if not os.path.exists(backup_dir):
|
| 85 |
-
os.makedirs(backup_dir)
|
| 86 |
-
|
| 87 |
-
# ๋ฐฑ์
(๊ธฐ์กด ํ์ผ์ด ์์ ๋๋ง)
|
| 88 |
-
if os.path.exists(GROUND_TRUTH_FILE):
|
| 89 |
-
backup_name = f"ground_truth_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
| 90 |
-
backup_path = os.path.join(backup_dir, backup_name)
|
| 91 |
-
|
| 92 |
-
# ๊ธฐ์กด ํ์ผ์ ๋ฐฑ์
ํด๋๋ก ๋ณต์ฌ
|
| 93 |
-
import shutil
|
| 94 |
-
shutil.copy2(GROUND_TRUTH_FILE, backup_path)
|
| 95 |
-
|
| 96 |
-
# ์ ์ฅ
|
| 97 |
-
with open(GROUND_TRUTH_FILE, 'w', encoding='utf-8') as f:
|
| 98 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 99 |
-
|
| 100 |
-
print(f"โ
Ground Truth ์ ์ฅ ์๋ฃ: {len(data)}๊ฐ ์ด๋ฏธ์ง")
|
| 101 |
-
|
| 102 |
-
def get_folders():
|
| 103 |
-
"""์ฌ์ฉ ๊ฐ๋ฅํ ํด๋ ๋ชฉ๋ก"""
|
| 104 |
-
folders = sorted(glob.glob(os.path.join(DATA_BASE, "2*")))
|
| 105 |
-
return [os.path.basename(f) for f in folders if os.path.isdir(f)]
|
| 106 |
-
|
| 107 |
-
def start_labeling(folder):
|
| 108 |
-
"""๋ผ๋ฒจ๋ง ์์"""
|
| 109 |
-
if not folder:
|
| 110 |
-
return None, "โ ํด๋๋ฅผ ์ ํํ์ธ์.", "", 0.2
|
| 111 |
-
|
| 112 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 113 |
-
folder_path = os.path.join(DATA_BASE, folder)
|
| 114 |
-
all_images = sorted(glob.glob(os.path.join(folder_path, "*.jpg")))
|
| 115 |
-
|
| 116 |
-
# ํํฐ๋ง: YYMMDD_NN.jpg ํ์๋ง ํ์ฉ (YYMMDD_NN-N.jpg ์ ์ธ)
|
| 117 |
-
import re
|
| 118 |
-
pattern = re.compile(r'^\d{6}_\d{2}\.jpg$') # 250818_01.jpg
|
| 119 |
-
images = [img for img in all_images if pattern.match(os.path.basename(img))]
|
| 120 |
-
|
| 121 |
-
if not images:
|
| 122 |
-
return None, f"โ {folder}์ ์ฌ๋ฐ๋ฅธ ํ์์ ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค. (YYMMDD_NN.jpg)", "", 0.2
|
| 123 |
-
|
| 124 |
-
current_data['folder'] = folder
|
| 125 |
-
current_data['images'] = [os.path.basename(img) for img in images]
|
| 126 |
-
current_data['current_idx'] = 0
|
| 127 |
-
current_data['detections'] = {}
|
| 128 |
-
current_data['selections'] = {}
|
| 129 |
-
current_data['confidence_threshold'] = 0.2
|
| 130 |
-
current_data['image_cache'] = {} # ์บ์ ์ด๊ธฐํ
|
| 131 |
-
|
| 132 |
-
filtered_count = len(all_images) - len(images)
|
| 133 |
-
if filtered_count > 0:
|
| 134 |
-
print(f"๐ ํด๋: {folder}, ์ด๋ฏธ์ง: {len(images)}๊ฐ (์ ์ธ: {filtered_count}๊ฐ)")
|
| 135 |
-
else:
|
| 136 |
-
print(f"๐ ํด๋: {folder}, ์ด๋ฏธ์ง: {len(images)}๊ฐ")
|
| 137 |
-
|
| 138 |
-
# ์ฒซ ์ด๋ฏธ์ง ๋ก๋
|
| 139 |
-
return load_image(0)
|
| 140 |
-
|
| 141 |
-
def load_image(idx, confidence_threshold=None):
|
| 142 |
-
"""์ด๋ฏธ์ง ๋ก๋ ๋ฐ ๊ฒ์ถ"""
|
| 143 |
-
if idx < 0 or idx >= len(current_data['images']):
|
| 144 |
-
return None, "โ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ธ๋ฑ์ค์
๋๋ค.", "", ""
|
| 145 |
-
|
| 146 |
-
current_data['current_idx'] = idx
|
| 147 |
-
filename = current_data['images'][idx]
|
| 148 |
-
folder = current_data['folder']
|
| 149 |
-
img_path = os.path.join(DATA_BASE, folder, filename)
|
| 150 |
-
|
| 151 |
-
# ์ ๋ขฐ๋ ์๊ณ๊ฐ ์
๋ฐ์ดํธ
|
| 152 |
-
if confidence_threshold is not None:
|
| 153 |
-
current_data['confidence_threshold'] = confidence_threshold
|
| 154 |
-
|
| 155 |
-
# ์ด๋ฏธ์ง ์บ์ ์ฌ์ฉ (๋์คํฌ I/O ์ต์ํ)
|
| 156 |
-
if filename not in current_data['image_cache']:
|
| 157 |
-
img_path = os.path.join(DATA_BASE, folder, filename)
|
| 158 |
-
current_data['image_cache'][filename] = Image.open(img_path).convert('RGB')
|
| 159 |
-
|
| 160 |
-
image = current_data['image_cache'][filename]
|
| 161 |
-
|
| 162 |
-
# RT-DETR ๊ฒ์ถ (์ ๋ขฐ๋ ๋ณ๊ฒฝ ์ ์ฌ๊ฒ์ถ)
|
| 163 |
-
cache_key = f"{filename}_{current_data['confidence_threshold']}"
|
| 164 |
-
if cache_key not in current_data['detections']:
|
| 165 |
-
print(f"๐ RT-DETR ๊ฒ์ถ ์ค: {filename} (์ ๋ขฐ๋={current_data['confidence_threshold']:.2f})")
|
| 166 |
-
detections = detect_with_rtdetr_fast(image, confidence=current_data['confidence_threshold'])
|
| 167 |
-
current_data['detections'][cache_key] = detections
|
| 168 |
-
else:
|
| 169 |
-
detections = current_data['detections'][cache_key]
|
| 170 |
-
|
| 171 |
-
# ์ ํ ์ํ ์ด๊ธฐํ
|
| 172 |
-
if filename not in current_data['selections']:
|
| 173 |
-
current_data['selections'][filename] = []
|
| 174 |
-
|
| 175 |
-
# ์๊ฐํ
|
| 176 |
-
img_with_boxes = draw_boxes(image, detections, current_data['selections'][filename])
|
| 177 |
-
|
| 178 |
-
# ์งํ๋ฅ
|
| 179 |
-
progress = f"{idx + 1}/{len(current_data['images'])} ({(idx+1)/len(current_data['images'])*100:.0f}%)"
|
| 180 |
-
|
| 181 |
-
# ์ ๋ณด
|
| 182 |
-
info = f"""
|
| 183 |
-
### ๐ท ์ด๋ฏธ์ง: {filename}
|
| 184 |
-
|
| 185 |
-
- **์งํ๋ฅ **: {progress}
|
| 186 |
-
- **์ ๋ขฐ๋ ์๊ณ๊ฐ**: {current_data['confidence_threshold']:.2f}
|
| 187 |
-
- **๊ฒ์ถ๋ ๋ฐ์ค**: {len(detections)}๊ฐ
|
| 188 |
-
- **์ ํ๋ ๋ฐ์ค**: {len(current_data['selections'][filename])}๊ฐ
|
| 189 |
-
|
| 190 |
-
### ๐ ์ฌ์ฉ ๋ฐฉ๋ฒ
|
| 191 |
-
|
| 192 |
-
1. **์์ฐ๊ฐ ๋ง๋ ๋ฐ์ค๋ฅผ ํด๋ฆญ**ํ์ฌ ์ ํ (ํ๋์์ผ๋ก ๋ณ๊ฒฝ)
|
| 193 |
-
2. ์๋ชป ์ ํ ์ **๋ค์ ํด๋ฆญ**ํ์ฌ ํด์ (๋
น์์ผ๋ก ๋๋๋ฆผ)
|
| 194 |
-
3. **์ ๋ขฐ๋ ์ฌ๋ผ์ด๋**๋ก ๊ฒ์ถ ๋ฏผ๊ฐ๋ ์กฐ์ (๋ฎ์์๋ก ๋ ๋ง์ด ๊ฒ์ถ)
|
| 195 |
-
4. **"๋ค์"** ๋ฒํผ์ผ๋ก ์ ์ฅ ๋ฐ ๋ค์ ์ด๋ฏธ์ง๋ก ์ด๋
|
| 196 |
-
|
| 197 |
-
**๋ฐ์ค ์์:**
|
| 198 |
-
- ๐ข ๋
น์: ๊ฒ์ถ๋จ (์ ํ ์ ๋จ)
|
| 199 |
-
- ๐ต ํ๋์: ์ ํ๋จ (์์ฐ)
|
| 200 |
-
"""
|
| 201 |
-
|
| 202 |
-
return img_with_boxes, info, progress, current_data['confidence_threshold']
|
| 203 |
-
|
| 204 |
-
def draw_boxes(image, detections, selected_indices):
|
| 205 |
-
"""๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ (ํด๋ฆญ ๊ฐ๋ฅํ ๋ฒํธ ๋ฒํผ ํฌํจ) - ์ต์ ํ"""
|
| 206 |
-
# ์ด๋ฏธ์ง ๋ณต์ฌ ๋์ ์ง์ ์์ (์๋ ํฅ์)
|
| 207 |
-
img = image.copy()
|
| 208 |
-
draw = ImageDraw.Draw(img)
|
| 209 |
-
|
| 210 |
-
try:
|
| 211 |
-
font = ImageFont.truetype("arial.ttf", 28) # ๋ ํฐ ํฐํธ
|
| 212 |
-
font_tiny = ImageFont.truetype("arial.ttf", 10)
|
| 213 |
-
except:
|
| 214 |
-
font = ImageFont.load_default()
|
| 215 |
-
font_tiny = ImageFont.load_default()
|
| 216 |
-
|
| 217 |
-
# ์ ํ๋์ง ์์ ๋ฐ์ค ๋จผ์ ๊ทธ๋ฆฌ๊ธฐ (๋ค์ชฝ ๋ ์ด์ด)
|
| 218 |
-
for idx, det in enumerate(detections):
|
| 219 |
-
if idx not in selected_indices:
|
| 220 |
-
x1, y1, x2, y2 = det['bbox']
|
| 221 |
-
color = "lime"
|
| 222 |
-
|
| 223 |
-
# ๋ฐ์ค ํ
๋๋ฆฌ
|
| 224 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=10)
|
| 225 |
-
|
| 226 |
-
# ์ฝ๋ ๋ผ๋ฒจ
|
| 227 |
-
corner_label = f"#{idx+1}"
|
| 228 |
-
draw.rectangle([x1-2, y1-24, x1+30, y1-2], fill=color)
|
| 229 |
-
draw.text((x1, y1 - 22), corner_label, fill="white", font=font_tiny)
|
| 230 |
-
|
| 231 |
-
# ์ ํ๋ ๋ฐ์ค ๋์ค์ ๊ทธ๋ฆฌ๊ธฐ (์์ชฝ ๋ ์ด์ด)
|
| 232 |
-
for idx, det in enumerate(detections):
|
| 233 |
-
if idx in selected_indices:
|
| 234 |
-
x1, y1, x2, y2 = det['bbox']
|
| 235 |
-
color = "blue"
|
| 236 |
-
|
| 237 |
-
# ๋ฐ์ค ํ
๋๋ฆฌ (๋ ๋๊ป๊ฒ)
|
| 238 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=14)
|
| 239 |
-
|
| 240 |
-
# ์ฝ๋ ๋ผ๋ฒจ
|
| 241 |
-
corner_label = f"โ#{idx+1}"
|
| 242 |
-
draw.rectangle([x1-2, y1-24, x1+40, y1-2], fill=color)
|
| 243 |
-
draw.text((x1, y1 - 22), corner_label, fill="white", font=font_tiny)
|
| 244 |
-
|
| 245 |
-
# ์ํ ๋ฒํผ (๋ชจ๋ ๋ฐ์ค)
|
| 246 |
-
for idx, det in enumerate(detections):
|
| 247 |
-
x1, y1, x2, y2 = det['bbox']
|
| 248 |
-
center_x = (x1 + x2) / 2
|
| 249 |
-
center_y = (y1 + y2) / 2
|
| 250 |
-
|
| 251 |
-
# ์ ํ ์ฌ๋ถ
|
| 252 |
-
selected = idx in selected_indices
|
| 253 |
-
btn_color = "blue" if selected else "lime"
|
| 254 |
-
btn_text = f"โ{idx+1}" if selected else f"{idx+1}"
|
| 255 |
-
|
| 256 |
-
# ๋ฒํผ ํฌ๊ธฐ
|
| 257 |
-
box_width = x2 - x1
|
| 258 |
-
box_height = y2 - y1
|
| 259 |
-
radius = min(55, box_width * 0.18, box_height * 0.35)
|
| 260 |
-
|
| 261 |
-
# ์ธ๊ณฝ ์ (ํฐ์ ํ
๋๋ฆฌ)
|
| 262 |
-
draw.ellipse(
|
| 263 |
-
[center_x - radius - 4, center_y - radius - 4,
|
| 264 |
-
center_x + radius + 4, center_y + radius + 4],
|
| 265 |
-
fill=btn_color,
|
| 266 |
-
outline="white",
|
| 267 |
-
width=5
|
| 268 |
-
)
|
| 269 |
-
|
| 270 |
-
# ๋ด๋ถ ์
|
| 271 |
-
draw.ellipse(
|
| 272 |
-
[center_x - radius, center_y - radius,
|
| 273 |
-
center_x + radius, center_y + radius],
|
| 274 |
-
fill=btn_color
|
| 275 |
-
)
|
| 276 |
-
|
| 277 |
-
# ํ
์คํธ ์ค์ ์ ๋ ฌ
|
| 278 |
-
text_bbox = draw.textbbox((0, 0), btn_text, font=font)
|
| 279 |
-
text_width = text_bbox[2] - text_bbox[0]
|
| 280 |
-
text_height = text_bbox[3] - text_bbox[1]
|
| 281 |
-
text_x = center_x - text_width / 2
|
| 282 |
-
text_y = center_y - text_height / 2 - 2
|
| 283 |
-
|
| 284 |
-
# ํ
์คํธ (๊ทธ๋ฆผ์ + ๋ณธ์ฒด)
|
| 285 |
-
draw.text((text_x + 2, text_y + 2), btn_text, fill="black", font=font)
|
| 286 |
-
draw.text((text_x, text_y), btn_text, fill="white", font=font)
|
| 287 |
-
|
| 288 |
-
# ํค๋
|
| 289 |
-
header = f"๊ฒ์ถ: {len(detections)}๊ฐ | ์ ํ: {len(selected_indices)}๊ฐ"
|
| 290 |
-
header_bbox = draw.textbbox((10, 10), header, font=font)
|
| 291 |
-
draw.rectangle([5, 5, header_bbox[2]+10, header_bbox[3]+10], fill="black", outline="white", width=2)
|
| 292 |
-
draw.text((10, 10), header, fill="white", font=font)
|
| 293 |
-
|
| 294 |
-
return img
|
| 295 |
-
|
| 296 |
-
def redraw_current_image():
|
| 297 |
-
"""ํ์ฌ ์ด๋ฏธ์ง ๋น ๋ฅด๊ฒ ์ฌ๊ทธ๋ฆฌ๊ธฐ (์ฌ๊ฒ์ถ ์์ด ๋ฐ์ค๋ง ๋ค์ ๊ทธ๋ฆฌ๊ธฐ)"""
|
| 298 |
-
idx = current_data['current_idx']
|
| 299 |
-
filename = current_data['images'][idx]
|
| 300 |
-
folder = current_data['folder']
|
| 301 |
-
|
| 302 |
-
# ์ด๋ฏธ์ง ์บ์ ํ์ธ (๋์คํฌ I/O ์ต์ํ)
|
| 303 |
-
if filename not in current_data['image_cache']:
|
| 304 |
-
img_path = os.path.join(DATA_BASE, folder, filename)
|
| 305 |
-
current_data['image_cache'][filename] = Image.open(img_path).convert('RGB')
|
| 306 |
-
|
| 307 |
-
image = current_data['image_cache'][filename]
|
| 308 |
-
|
| 309 |
-
# ์บ์๋ ๊ฒ์ถ ๊ฒฐ๊ณผ ์ฌ์ฉ (์ฌ๊ฒ์ถ ์์!)
|
| 310 |
-
cache_key = f"{filename}_{current_data['confidence_threshold']}"
|
| 311 |
-
detections = current_data['detections'][cache_key]
|
| 312 |
-
|
| 313 |
-
# ์ ํ ์ํ
|
| 314 |
-
selections = current_data['selections'][filename]
|
| 315 |
-
|
| 316 |
-
# ๋ฐ์ค๋ง ๋ค์ ๊ทธ๋ฆฌ๊ธฐ
|
| 317 |
-
img_with_boxes = draw_boxes(image, detections, selections)
|
| 318 |
-
|
| 319 |
-
# ์งํ๋ฅ (๊ฐ์ํ)
|
| 320 |
-
progress = f"{idx + 1}/{len(current_data['images'])}"
|
| 321 |
-
|
| 322 |
-
# ์ ๋ณด (๊ฐ์ํ - ํ
์คํธ ์์ฑ ์๊ฐ ๋จ์ถ)
|
| 323 |
-
info = f"**{filename}** | ๊ฒ์ถ: {len(detections)}๊ฐ | ์ ํ: {len(selections)}๊ฐ"
|
| 324 |
-
|
| 325 |
-
return img_with_boxes, info, progress, current_data['confidence_threshold']
|
| 326 |
-
|
| 327 |
-
def toggle_selection(evt: gr.SelectData):
|
| 328 |
-
"""๋ฐ์ค ํด๋ฆญ ์ ์ ํ/ํด์ (์ํ ๋ฒํผ ์ฐ์ ๊ฐ์ง)"""
|
| 329 |
-
# ํด๋ฆญ ์ขํ
|
| 330 |
-
click_x, click_y = evt.index
|
| 331 |
-
|
| 332 |
-
filename = current_data['images'][current_data['current_idx']]
|
| 333 |
-
cache_key = f"{filename}_{current_data['confidence_threshold']}"
|
| 334 |
-
detections = current_data['detections'][cache_key]
|
| 335 |
-
selections = current_data['selections'][filename]
|
| 336 |
-
|
| 337 |
-
if not detections:
|
| 338 |
-
return redraw_current_image()
|
| 339 |
-
|
| 340 |
-
# 1๋จ๊ณ: ์ํ ๋ฒํผ ๋ด๋ถ ํด๋ฆญ ๊ฐ์ง (์ต์ฐ์ )
|
| 341 |
-
button_candidates = []
|
| 342 |
-
|
| 343 |
-
for idx, det in enumerate(detections):
|
| 344 |
-
x1, y1, x2, y2 = det['bbox']
|
| 345 |
-
center_x = (x1 + x2) / 2
|
| 346 |
-
center_y = (y1 + y2) / 2
|
| 347 |
-
|
| 348 |
-
# ๋ฒํผ ํฌ๊ธฐ ๊ณ์ฐ (draw_boxes์ ๋์ผ)
|
| 349 |
-
box_width = x2 - x1
|
| 350 |
-
box_height = y2 - y1
|
| 351 |
-
radius = min(50, box_width * 0.15, box_height * 0.3)
|
| 352 |
-
|
| 353 |
-
# ํด๋ฆญ์ด ์ํ ๋ฒํผ ๋ด๋ถ์ธ์ง ํ์ธ
|
| 354 |
-
distance_from_center = ((click_x - center_x) ** 2 + (click_y - center_y) ** 2) ** 0.5
|
| 355 |
-
|
| 356 |
-
if distance_from_center <= radius:
|
| 357 |
-
# ๋ฒํผ ๋ด๋ถ ํด๋ฆญ!
|
| 358 |
-
button_candidates.append((idx, distance_from_center))
|
| 359 |
-
|
| 360 |
-
# 2๋จ๊ณ: ๋ฒํผ ํด๋ฆญ์ด ์์ผ๋ฉด ๊ฐ์ฅ ๊ฐ๊น์ด ๋ฒํผ ์ ํ
|
| 361 |
-
clicked_idx = None
|
| 362 |
-
if button_candidates:
|
| 363 |
-
# ๋ฒํผ ์ค์ฌ์ ๊ฐ์ฅ ๊ฐ๊น์ด ๊ฒ ์ ํ
|
| 364 |
-
button_candidates.sort(key=lambda x: x[1])
|
| 365 |
-
clicked_idx = button_candidates[0][0]
|
| 366 |
-
print(f"๐ฏ ๋ฒํผ ํด๋ฆญ โ ๋ฐ์ค #{clicked_idx+1}")
|
| 367 |
-
|
| 368 |
-
# 3๋จ๊ณ: ๋ฒํผ ํด๋ฆญ ์์ผ๋ฉด ๋ฐ์ค ์์ญ ํด๋ฆญ ํ์ธ
|
| 369 |
-
else:
|
| 370 |
-
box_candidates = []
|
| 371 |
-
for idx, det in enumerate(detections):
|
| 372 |
-
x1, y1, x2, y2 = det['bbox']
|
| 373 |
-
|
| 374 |
-
# ๋ฐ์ค ๋ด๋ถ์ธ์ง ํ์ธ
|
| 375 |
-
if x1 <= click_x <= x2 and y1 <= click_y <= y2:
|
| 376 |
-
center_x = (x1 + x2) / 2
|
| 377 |
-
center_y = (y1 + y2) / 2
|
| 378 |
-
distance = ((click_x - center_x) ** 2 + (click_y - center_y) ** 2) ** 0.5
|
| 379 |
-
area = (x2 - x1) * (y2 - y1)
|
| 380 |
-
|
| 381 |
-
# ์์ ๋ฐ์ค + ๊ฐ๊น์ด ๋ฐ์ค ์ฐ์
|
| 382 |
-
score = area + distance * 100
|
| 383 |
-
box_candidates.append((idx, score, area, distance))
|
| 384 |
-
|
| 385 |
-
if box_candidates:
|
| 386 |
-
box_candidates.sort(key=lambda x: x[1])
|
| 387 |
-
clicked_idx = box_candidates[0][0]
|
| 388 |
-
best = box_candidates[0]
|
| 389 |
-
print(f"๐ ๋ฐ์ค ์์ญ ํด๋ฆญ ({click_x:.0f}, {click_y:.0f}) โ ๋ฐ์ค #{clicked_idx+1} (๊ฑฐ๋ฆฌ={best[3]:.0f})")
|
| 390 |
-
|
| 391 |
-
# 4๋จ๊ณ: ์ ํ/ํด์ ํ ๊ธ
|
| 392 |
-
if clicked_idx is not None:
|
| 393 |
-
if clicked_idx in selections:
|
| 394 |
-
selections.remove(clicked_idx)
|
| 395 |
-
print(f" โ ๋ฐ์ค #{clicked_idx+1} ์ ํ ํด์ ")
|
| 396 |
-
else:
|
| 397 |
-
selections.append(clicked_idx)
|
| 398 |
-
print(f" โ ๋ฐ์ค #{clicked_idx+1} ์ ํ๋จ")
|
| 399 |
-
|
| 400 |
-
current_data['selections'][filename] = selections
|
| 401 |
-
else:
|
| 402 |
-
print(f"โ ํด๋ฆญ ์์น์ ๋ฐ์ค ์์")
|
| 403 |
-
|
| 404 |
-
# ๋น ๋ฅธ ์
๋ฐ์ดํธ: ์ด๋ฏธ์ง๋ง ๋ค์ ๊ทธ๋ฆฌ๊ธฐ (์ฌ๊ฒ์ถ ์์)
|
| 405 |
-
return redraw_current_image()
|
| 406 |
-
|
| 407 |
-
def previous_image():
|
| 408 |
-
"""์ด์ ์ด๋ฏธ์ง"""
|
| 409 |
-
idx = current_data['current_idx'] - 1
|
| 410 |
-
if idx < 0:
|
| 411 |
-
return load_image(current_data['current_idx']) + ("โ ๏ธ ์ฒซ ์ด๋ฏธ์ง์
๋๋ค.",)
|
| 412 |
-
return load_image(idx) + ("",)
|
| 413 |
-
|
| 414 |
-
def next_image():
|
| 415 |
-
"""๋ค์ ์ด๋ฏธ์ง (์ ์ฅ ํฌํจ)"""
|
| 416 |
-
# ํ์ฌ ์ ํ ์ ์ฅ
|
| 417 |
-
save_current_selection()
|
| 418 |
-
|
| 419 |
-
idx = current_data['current_idx'] + 1
|
| 420 |
-
if idx >= len(current_data['images']):
|
| 421 |
-
return load_image(current_data['current_idx']) + ("โ ๏ธ ๋ง์ง๋ง ์ด๋ฏธ์ง์
๋๋ค. '์๋ฃ' ๋ฒํผ์ ๋๋ฅด์ธ์.",)
|
| 422 |
-
return load_image(idx) + ("โ
์ ์ฅ๋์์ต๋๋ค.",)
|
| 423 |
-
|
| 424 |
-
def skip_image():
|
| 425 |
-
"""๊ฑด๋๋ฐ๊ธฐ"""
|
| 426 |
-
filename = current_data['images'][current_data['current_idx']]
|
| 427 |
-
current_data['selections'][filename] = []
|
| 428 |
-
|
| 429 |
-
idx = current_data['current_idx'] + 1
|
| 430 |
-
if idx >= len(current_data['images']):
|
| 431 |
-
return load_image(current_data['current_idx']) + ("โ ๏ธ ๋ง์ง๋ง ์ด๋ฏธ์ง์
๋๋ค.",)
|
| 432 |
-
return load_image(idx) + ("โญ๏ธ ๊ฑด๋๋ฐ์์ต๋๋ค.",)
|
| 433 |
-
|
| 434 |
-
def reset_selection():
|
| 435 |
-
"""ํ์ฌ ์ด๋ฏธ์ง์ ์ ํ ์ด๊ธฐํ"""
|
| 436 |
-
filename = current_data['images'][current_data['current_idx']]
|
| 437 |
-
current_data['selections'][filename] = []
|
| 438 |
-
|
| 439 |
-
# ๋น ๋ฅธ ์
๋ฐ์ดํธ: ์ด๋ฏธ์ง๋ง ๋ค์ ๊ทธ๋ฆฌ๊ธฐ
|
| 440 |
-
return redraw_current_image() + ("๐ ์ ํ์ด ์ด๊ธฐํ๋์์ต๋๋ค.",)
|
| 441 |
-
|
| 442 |
-
def save_current_selection():
|
| 443 |
-
"""ํ์ฌ ์ ํ ์ ์ฅ"""
|
| 444 |
-
filename = current_data['images'][current_data['current_idx']]
|
| 445 |
-
folder = current_data['folder']
|
| 446 |
-
selections = current_data['selections'].get(filename, [])
|
| 447 |
-
cache_key = f"{filename}_{current_data['confidence_threshold']}"
|
| 448 |
-
detections = current_data['detections'][cache_key]
|
| 449 |
-
|
| 450 |
-
# Ground Truth ํ์์ผ๋ก ๋ณํ
|
| 451 |
-
gt_data = load_existing_ground_truth()
|
| 452 |
-
|
| 453 |
-
gt_data[filename] = [
|
| 454 |
-
{
|
| 455 |
-
"bbox": detections[idx]['bbox'],
|
| 456 |
-
"label": "shrimp",
|
| 457 |
-
"folder": folder,
|
| 458 |
-
"confidence": detections[idx]['confidence']
|
| 459 |
-
}
|
| 460 |
-
for idx in selections
|
| 461 |
-
]
|
| 462 |
-
|
| 463 |
-
# ์์ ์ ์ฅ (๋งค๋ฒ)
|
| 464 |
-
save_ground_truth(gt_data)
|
| 465 |
-
|
| 466 |
-
def finish_labeling():
|
| 467 |
-
"""๋ผ๋ฒจ๋ง ์๋ฃ"""
|
| 468 |
-
# ๋ง์ง๋ง ์ด๋ฏธ์ง ์ ์ฅ
|
| 469 |
-
save_current_selection()
|
| 470 |
-
|
| 471 |
-
gt_data = load_existing_ground_truth()
|
| 472 |
-
total_images = len(gt_data)
|
| 473 |
-
total_boxes = sum(len(v) for v in gt_data.values())
|
| 474 |
-
|
| 475 |
-
msg = f"""
|
| 476 |
-
## โ
๋ผ๋ฒจ๋ง ์๋ฃ!
|
| 477 |
-
|
| 478 |
-
- **์ด ์ด๋ฏธ์ง**: {total_images}๊ฐ
|
| 479 |
-
- **์ด ๋ฐ์ด๋ฉ ๋ฐ์ค**: {total_boxes}๊ฐ
|
| 480 |
-
- **์ ์ฅ ์์น**: {GROUND_TRUTH_FILE}
|
| 481 |
-
|
| 482 |
-
### ๋ค์ ๋จ๊ณ:
|
| 483 |
-
|
| 484 |
-
```bash
|
| 485 |
-
python test_quantitative_evaluation.py
|
| 486 |
-
```
|
| 487 |
-
|
| 488 |
-
์ฑ๋ฅ ์ธก์ ํ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ์ธ์!
|
| 489 |
-
"""
|
| 490 |
-
|
| 491 |
-
return None, msg, "์๋ฃ"
|
| 492 |
-
|
| 493 |
-
# ํค๋ณด๋ ๋จ์ถํค๋ฅผ ์ํ JavaScript
|
| 494 |
-
keyboard_js = """
|
| 495 |
-
<script>
|
| 496 |
-
document.addEventListener('keydown', function(event) {
|
| 497 |
-
// ์
๋ ฅ ํ๋์์๋ ๋จ์ถํค ๋นํ์ฑํ
|
| 498 |
-
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
|
| 499 |
-
return;
|
| 500 |
-
}
|
| 501 |
-
|
| 502 |
-
// ํ์ดํ ํค, ์คํ์ด์ค๋ฐ, S, R ํค ์ฒ๋ฆฌ
|
| 503 |
-
if (event.key === 'ArrowRight' || event.key === ' ') {
|
| 504 |
-
event.preventDefault();
|
| 505 |
-
const nextBtn = document.querySelector('button:has-text("๋ค์")') ||
|
| 506 |
-
Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('๋ค์'));
|
| 507 |
-
if (nextBtn) nextBtn.click();
|
| 508 |
-
} else if (event.key === 'ArrowLeft') {
|
| 509 |
-
event.preventDefault();
|
| 510 |
-
const prevBtn = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('์ด์ '));
|
| 511 |
-
if (prevBtn) prevBtn.click();
|
| 512 |
-
} else if (event.key.toLowerCase() === 's') {
|
| 513 |
-
event.preventDefault();
|
| 514 |
-
const skipBtn = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('๊ฑด๋๋ฐ๊ธฐ'));
|
| 515 |
-
if (skipBtn) skipBtn.click();
|
| 516 |
-
} else if (event.key.toLowerCase() === 'r') {
|
| 517 |
-
event.preventDefault();
|
| 518 |
-
const resetBtn = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('์ด๊ธฐํ'));
|
| 519 |
-
if (resetBtn) resetBtn.click();
|
| 520 |
-
}
|
| 521 |
-
});
|
| 522 |
-
</script>
|
| 523 |
-
"""
|
| 524 |
-
|
| 525 |
-
# Gradio ์ธํฐํ์ด์ค
|
| 526 |
-
with gr.Blocks(title="๐ท๏ธ Ground Truth ๋ผ๋ฒจ๋ง ๋๊ตฌ", theme=gr.themes.Soft(), head=keyboard_js) as demo:
|
| 527 |
-
|
| 528 |
-
gr.Markdown("""
|
| 529 |
-
# ๐ท๏ธ Ground Truth ๋ผ๋ฒจ๋ง ๋๊ตฌ
|
| 530 |
-
|
| 531 |
-
RT-DETR ๊ฒ์ถ ๊ฒฐ๊ณผ์์ ์ฌ๋ฐ๋ฅธ ์์ฐ ๋ฐ์ค๋ง ์ ํํ์ฌ ๋ผ๋ฒจ๋งํฉ๋๋ค.
|
| 532 |
-
|
| 533 |
-
---
|
| 534 |
-
""")
|
| 535 |
-
|
| 536 |
-
with gr.Row():
|
| 537 |
-
folder_dropdown = gr.Dropdown(
|
| 538 |
-
choices=get_folders(),
|
| 539 |
-
label="๐ ๋ผ๋ฒจ๋งํ ํด๋ ์ ํ",
|
| 540 |
-
value=None
|
| 541 |
-
)
|
| 542 |
-
start_btn = gr.Button("๐ ์์", variant="primary")
|
| 543 |
-
|
| 544 |
-
with gr.Row():
|
| 545 |
-
with gr.Column(scale=2):
|
| 546 |
-
image_output = gr.Image(label="์ด๋ฏธ์ง (๋ฐ์ค ํด๋ฆญ์ผ๋ก ์ ํ/ํด์ )", type="pil")
|
| 547 |
-
|
| 548 |
-
with gr.Column(scale=1):
|
| 549 |
-
info_output = gr.Markdown()
|
| 550 |
-
|
| 551 |
-
progress_output = gr.Textbox(label="์งํ๋ฅ ", interactive=False)
|
| 552 |
-
|
| 553 |
-
confidence_slider = gr.Slider(
|
| 554 |
-
minimum=0.05,
|
| 555 |
-
maximum=0.5,
|
| 556 |
-
value=0.2,
|
| 557 |
-
step=0.05,
|
| 558 |
-
label="๐ฏ ๊ฒ์ถ ์ ๋ขฐ๋ ์๊ณ๊ฐ (๋ฎ์์๋ก ๏ฟฝ๏ฟฝ๏ฟฝ ๋ง์ด ๊ฒ์ถ)",
|
| 559 |
-
info="์์ฐ๊ฐ ๊ฒ์ถ ์ ๋๋ฉด ๋ฎ์ถ์ธ์"
|
| 560 |
-
)
|
| 561 |
-
|
| 562 |
-
with gr.Row():
|
| 563 |
-
prev_btn = gr.Button("โ ์ด์ ")
|
| 564 |
-
skip_btn = gr.Button("โญ ๊ฑด๋๋ฐ๊ธฐ")
|
| 565 |
-
next_btn = gr.Button("๋ค์ โถ", variant="primary")
|
| 566 |
-
|
| 567 |
-
reset_btn = gr.Button("๐ ์ ํ ์ด๊ธฐํ", variant="secondary")
|
| 568 |
-
|
| 569 |
-
finish_btn = gr.Button("โ
์๋ฃ", variant="stop", size="lg")
|
| 570 |
-
|
| 571 |
-
status_output = gr.Textbox(label="์ํ", interactive=False)
|
| 572 |
-
|
| 573 |
-
gr.Markdown("""
|
| 574 |
-
---
|
| 575 |
-
|
| 576 |
-
### ๐ก ์ฌ์ฉ ํ
|
| 577 |
-
|
| 578 |
-
**๋ง์ฐ์ค:**
|
| 579 |
-
- **ํด๋ฆญ**: ๋ฐ์ค๋ฅผ ํด๋ฆญํ์ฌ ์ ํ/ํด์
|
| 580 |
-
- **๋
น์ โ ํ๋์**: ์ ํ๋จ (์์ฐ)
|
| 581 |
-
- **ํ๋์ โ ๋
น์**: ํด์ ๋จ
|
| 582 |
-
|
| 583 |
-
**ํค๋ณด๋ ๋จ์ถํค:**
|
| 584 |
-
- **โ (์ค๋ฅธ์ชฝ ํ์ดํ)**: ๋ค์ ์ด๋ฏธ์ง (์ ์ฅ)
|
| 585 |
-
- **โ (์ผ์ชฝ ํ์ดํ)**: ์ด์ ์ด๋ฏธ์ง
|
| 586 |
-
- **Space (์คํ์ด์ค๋ฐ)**: ๋ค์ ์ด๋ฏธ์ง (์ ์ฅ)
|
| 587 |
-
- **S**: ๊ฑด๋๋ฐ๊ธฐ
|
| 588 |
-
- **R**: ์ ํ ์ด๊ธฐํ
|
| 589 |
-
|
| 590 |
-
**๊ธฐํ:**
|
| 591 |
-
- **์ ๋ขฐ๋ ์ฌ๋ผ์ด๋**: ๊ฒ์ถ ๋ฏผ๊ฐ๋ ์กฐ์ (0.05~0.5)
|
| 592 |
-
- **10์ฅ๋ง๋ค ์๋ ์ ์ฅ๋ฉ๋๋ค.**
|
| 593 |
-
""")
|
| 594 |
-
|
| 595 |
-
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 596 |
-
start_btn.click(
|
| 597 |
-
start_labeling,
|
| 598 |
-
[folder_dropdown],
|
| 599 |
-
[image_output, info_output, progress_output, confidence_slider]
|
| 600 |
-
)
|
| 601 |
-
|
| 602 |
-
# ์ ๋ขฐ๋ ์ฌ๋ผ์ด๋ ๋ณ๊ฒฝ ์ ํ์ฌ ์ด๋ฏธ์ง ์ฌ๊ฒ์ถ
|
| 603 |
-
confidence_slider.change(
|
| 604 |
-
lambda conf: load_image(current_data['current_idx'], conf),
|
| 605 |
-
[confidence_slider],
|
| 606 |
-
[image_output, info_output, progress_output, confidence_slider]
|
| 607 |
-
)
|
| 608 |
-
|
| 609 |
-
image_output.select(
|
| 610 |
-
toggle_selection,
|
| 611 |
-
None,
|
| 612 |
-
[image_output, info_output, progress_output, confidence_slider]
|
| 613 |
-
)
|
| 614 |
-
|
| 615 |
-
prev_btn.click(
|
| 616 |
-
previous_image,
|
| 617 |
-
None,
|
| 618 |
-
[image_output, info_output, progress_output, confidence_slider, status_output]
|
| 619 |
-
)
|
| 620 |
-
|
| 621 |
-
next_btn.click(
|
| 622 |
-
next_image,
|
| 623 |
-
None,
|
| 624 |
-
[image_output, info_output, progress_output, confidence_slider, status_output]
|
| 625 |
-
)
|
| 626 |
-
|
| 627 |
-
skip_btn.click(
|
| 628 |
-
skip_image,
|
| 629 |
-
None,
|
| 630 |
-
[image_output, info_output, progress_output, confidence_slider, status_output]
|
| 631 |
-
)
|
| 632 |
-
|
| 633 |
-
reset_btn.click(
|
| 634 |
-
reset_selection,
|
| 635 |
-
None,
|
| 636 |
-
[image_output, info_output, progress_output, confidence_slider, status_output]
|
| 637 |
-
)
|
| 638 |
-
|
| 639 |
-
finish_btn.click(
|
| 640 |
-
finish_labeling,
|
| 641 |
-
None,
|
| 642 |
-
[image_output, info_output, progress_output]
|
| 643 |
-
)
|
| 644 |
-
|
| 645 |
-
if __name__ == "__main__":
|
| 646 |
-
demo.launch(
|
| 647 |
-
server_name="0.0.0.0",
|
| 648 |
-
server_port=None, # ์๋์ผ๋ก ๋น ํฌํธ ์ฐพ๊ธฐ
|
| 649 |
-
share=False
|
| 650 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,217 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
YOLOv8m Confidence Threshold ์ต์ ํ
|
| 3 |
-
Ground Truth ๊ธฐ๋ฐ์ผ๋ก ์ต์ confidence ๊ฐ ํ์
|
| 4 |
-
"""
|
| 5 |
-
from ultralytics import YOLO
|
| 6 |
-
from PIL import Image
|
| 7 |
-
import json
|
| 8 |
-
import os
|
| 9 |
-
import glob
|
| 10 |
-
import numpy as np
|
| 11 |
-
|
| 12 |
-
# ํ์ต๋ ๋ชจ๋ธ ๋ก๋
|
| 13 |
-
MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 14 |
-
model = YOLO(MODEL_PATH)
|
| 15 |
-
|
| 16 |
-
print(f"โ
YOLOv8m ๋ชจ๋ธ ๋ก๋ ์๋ฃ: {MODEL_PATH}")
|
| 17 |
-
|
| 18 |
-
# Ground Truth ๋ก๋
|
| 19 |
-
GT_FILE = "ground_truth.json"
|
| 20 |
-
with open(GT_FILE, 'r', encoding='utf-8') as f:
|
| 21 |
-
ground_truth = json.load(f)
|
| 22 |
-
|
| 23 |
-
print(f"โ
Ground Truth ๋ก๋: {GT_FILE}")
|
| 24 |
-
|
| 25 |
-
# GT ํต๊ณ
|
| 26 |
-
total_gt = sum(len(gts) for gts in ground_truth.values() if gts)
|
| 27 |
-
gt_images = [k for k, v in ground_truth.items() if v]
|
| 28 |
-
print(f" - GT๊ฐ ์๋ ์ด๋ฏธ์ง: {len(gt_images)}์ฅ")
|
| 29 |
-
print(f" - ์ด GT ๊ฐ์ฒด: {total_gt}๊ฐ")
|
| 30 |
-
print("-" * 60)
|
| 31 |
-
|
| 32 |
-
def calculate_iou(box1, box2):
|
| 33 |
-
"""IoU ๊ณ์ฐ"""
|
| 34 |
-
x1_min, y1_min, x1_max, y1_max = box1
|
| 35 |
-
x2_min, y2_min, x2_max, y2_max = box2
|
| 36 |
-
|
| 37 |
-
inter_x_min = max(x1_min, x2_min)
|
| 38 |
-
inter_y_min = max(y1_min, y2_min)
|
| 39 |
-
inter_x_max = min(x1_max, x2_max)
|
| 40 |
-
inter_y_max = min(y1_max, y2_max)
|
| 41 |
-
|
| 42 |
-
if inter_x_max < inter_x_min or inter_y_max < inter_y_min:
|
| 43 |
-
return 0.0
|
| 44 |
-
|
| 45 |
-
inter_area = (inter_x_max - inter_x_min) * (inter_y_max - inter_y_min)
|
| 46 |
-
box1_area = (x1_max - x1_min) * (y1_max - y1_min)
|
| 47 |
-
box2_area = (x2_max - x2_min) * (y2_max - y2_min)
|
| 48 |
-
union_area = box1_area + box2_area - inter_area
|
| 49 |
-
|
| 50 |
-
return inter_area / union_area if union_area > 0 else 0.0
|
| 51 |
-
|
| 52 |
-
def evaluate_confidence_threshold(conf_threshold, iou_threshold=0.5):
|
| 53 |
-
"""ํน์ confidence threshold์์ ์ฑ๋ฅ ํ๊ฐ"""
|
| 54 |
-
tp = 0 # True Positive
|
| 55 |
-
fp = 0 # False Positive
|
| 56 |
-
fn = 0 # False Negative
|
| 57 |
-
|
| 58 |
-
matched_gt_count = 0
|
| 59 |
-
total_gt_count = 0
|
| 60 |
-
|
| 61 |
-
# GT๊ฐ ์๋ ์ด๋ฏธ์ง๋ง ํ
์คํธ
|
| 62 |
-
for img_name in gt_images:
|
| 63 |
-
# ์ด๋ฏธ์ง ๊ฒฝ๋ก ์ฐพ๊ธฐ
|
| 64 |
-
img_path = None
|
| 65 |
-
for split in ['train', 'val']:
|
| 66 |
-
search_path = f"data/yolo_dataset/images/{split}/{img_name}"
|
| 67 |
-
if os.path.exists(search_path):
|
| 68 |
-
img_path = search_path
|
| 69 |
-
break
|
| 70 |
-
|
| 71 |
-
if not img_path:
|
| 72 |
-
# ํ์ผ๋ช
์์ -1 ์ ๊ฑฐํด์ ๋ค์ ์๋
|
| 73 |
-
base_name = img_name.replace('-1.jpg', '.jpg')
|
| 74 |
-
for split in ['train', 'val']:
|
| 75 |
-
search_path = f"data/yolo_dataset/images/{split}/{base_name}"
|
| 76 |
-
if os.path.exists(search_path):
|
| 77 |
-
img_path = search_path
|
| 78 |
-
break
|
| 79 |
-
|
| 80 |
-
if not img_path or not os.path.exists(img_path):
|
| 81 |
-
continue
|
| 82 |
-
|
| 83 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 84 |
-
image = Image.open(img_path)
|
| 85 |
-
|
| 86 |
-
# YOLOv8 ๊ฒ์ถ
|
| 87 |
-
results = model.predict(
|
| 88 |
-
source=image,
|
| 89 |
-
conf=conf_threshold,
|
| 90 |
-
iou=0.7,
|
| 91 |
-
device=0,
|
| 92 |
-
verbose=False
|
| 93 |
-
)
|
| 94 |
-
|
| 95 |
-
# ๊ฒฐ๊ณผ ํ์ฑ
|
| 96 |
-
result = results[0]
|
| 97 |
-
boxes = result.boxes
|
| 98 |
-
|
| 99 |
-
predictions = []
|
| 100 |
-
if boxes is not None and len(boxes) > 0:
|
| 101 |
-
for box in boxes:
|
| 102 |
-
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
|
| 103 |
-
confidence = box.conf[0].cpu().item()
|
| 104 |
-
predictions.append({
|
| 105 |
-
'bbox': [float(x1), float(y1), float(x2), float(y2)],
|
| 106 |
-
'confidence': confidence
|
| 107 |
-
})
|
| 108 |
-
|
| 109 |
-
# Ground Truth
|
| 110 |
-
gt_boxes = ground_truth[img_name]
|
| 111 |
-
total_gt_count += len(gt_boxes)
|
| 112 |
-
|
| 113 |
-
# GT์ ๋งค์นญ
|
| 114 |
-
matched_gt = set()
|
| 115 |
-
matched_pred = set()
|
| 116 |
-
|
| 117 |
-
for pred_idx, pred in enumerate(predictions):
|
| 118 |
-
best_iou = 0
|
| 119 |
-
best_gt_idx = -1
|
| 120 |
-
|
| 121 |
-
for gt_idx, gt in enumerate(gt_boxes):
|
| 122 |
-
if gt_idx in matched_gt:
|
| 123 |
-
continue
|
| 124 |
-
|
| 125 |
-
iou = calculate_iou(pred['bbox'], gt['bbox'])
|
| 126 |
-
if iou > best_iou:
|
| 127 |
-
best_iou = iou
|
| 128 |
-
best_gt_idx = gt_idx
|
| 129 |
-
|
| 130 |
-
if best_iou >= iou_threshold:
|
| 131 |
-
tp += 1
|
| 132 |
-
matched_gt.add(best_gt_idx)
|
| 133 |
-
matched_pred.add(pred_idx)
|
| 134 |
-
else:
|
| 135 |
-
fp += 1
|
| 136 |
-
|
| 137 |
-
# ๋งค์นญ๋์ง ์์ GT = False Negative
|
| 138 |
-
fn += len(gt_boxes) - len(matched_gt)
|
| 139 |
-
matched_gt_count += len(matched_gt)
|
| 140 |
-
|
| 141 |
-
# ์ฑ๋ฅ ์งํ ๊ณ์ฐ
|
| 142 |
-
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
|
| 143 |
-
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
|
| 144 |
-
f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
|
| 145 |
-
|
| 146 |
-
return {
|
| 147 |
-
'tp': tp,
|
| 148 |
-
'fp': fp,
|
| 149 |
-
'fn': fn,
|
| 150 |
-
'precision': precision,
|
| 151 |
-
'recall': recall,
|
| 152 |
-
'f1': f1,
|
| 153 |
-
'matched_gt': matched_gt_count,
|
| 154 |
-
'total_gt': total_gt_count,
|
| 155 |
-
'gt_match_rate': matched_gt_count / total_gt_count if total_gt_count > 0 else 0
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
-
# Confidence threshold sweep
|
| 159 |
-
print("\n๐ Confidence Threshold ์ต์ ํ ์์...\n")
|
| 160 |
-
|
| 161 |
-
confidence_thresholds = [0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8]
|
| 162 |
-
results = []
|
| 163 |
-
|
| 164 |
-
for conf in confidence_thresholds:
|
| 165 |
-
metrics = evaluate_confidence_threshold(conf)
|
| 166 |
-
results.append({
|
| 167 |
-
'confidence': conf,
|
| 168 |
-
**metrics
|
| 169 |
-
})
|
| 170 |
-
|
| 171 |
-
print(f"Conf {conf:.2f}: P={metrics['precision']:.1%} R={metrics['recall']:.1%} F1={metrics['f1']:.1%} | "
|
| 172 |
-
f"GT๋งค์นญ={metrics['matched_gt']}/{metrics['total_gt']} ({metrics['gt_match_rate']:.1%})")
|
| 173 |
-
|
| 174 |
-
# ์ต์ ๊ฐ ์ฐพ๊ธฐ
|
| 175 |
-
best_f1 = max(results, key=lambda x: x['f1'])
|
| 176 |
-
best_recall = max(results, key=lambda x: x['recall'])
|
| 177 |
-
best_precision = max(results, key=lambda x: x['precision'])
|
| 178 |
-
best_gt_match = max(results, key=lambda x: x['gt_match_rate'])
|
| 179 |
-
|
| 180 |
-
print("\n" + "=" * 60)
|
| 181 |
-
print("๐ ์ต์ ํ ๊ฒฐ๊ณผ:")
|
| 182 |
-
print("=" * 60)
|
| 183 |
-
|
| 184 |
-
print(f"\n1๏ธโฃ ์ต๊ณ F1 Score: {best_f1['f1']:.1%} (Confidence={best_f1['confidence']:.2f})")
|
| 185 |
-
print(f" - Precision: {best_f1['precision']:.1%}")
|
| 186 |
-
print(f" - Recall: {best_f1['recall']:.1%}")
|
| 187 |
-
print(f" - GT ๋งค์นญ: {best_f1['matched_gt']}/{best_f1['total_gt']} ({best_f1['gt_match_rate']:.1%})")
|
| 188 |
-
|
| 189 |
-
print(f"\n2๏ธโฃ ์ต๊ณ Recall: {best_recall['recall']:.1%} (Confidence={best_recall['confidence']:.2f})")
|
| 190 |
-
print(f" - F1 Score: {best_recall['f1']:.1%}")
|
| 191 |
-
print(f" - Precision: {best_recall['precision']:.1%}")
|
| 192 |
-
|
| 193 |
-
print(f"\n3๏ธโฃ ์ต๊ณ Precision: {best_precision['precision']:.1%} (Confidence={best_precision['confidence']:.2f})")
|
| 194 |
-
print(f" - F1 Score: {best_precision['f1']:.1%}")
|
| 195 |
-
print(f" - Recall: {best_precision['recall']:.1%}")
|
| 196 |
-
|
| 197 |
-
print(f"\n4๏ธโฃ ์ต๊ณ GT ๋งค์นญ๋ฅ : {best_gt_match['gt_match_rate']:.1%} (Confidence={best_gt_match['confidence']:.2f})")
|
| 198 |
-
print(f" - F1 Score: {best_gt_match['f1']:.1%}")
|
| 199 |
-
print(f" - ๋งค์นญ: {best_gt_match['matched_gt']}/{best_gt_match['total_gt']}")
|
| 200 |
-
|
| 201 |
-
print("\n๐ก ๊ถ์ฅ ์ค์ :")
|
| 202 |
-
print(f" - ๊ท ํ์กํ ์ฑ๋ฅ: confidence={best_f1['confidence']:.2f} (F1={best_f1['f1']:.1%})")
|
| 203 |
-
print(f" - ๋์ ์ฌํ์จ: confidence={best_recall['confidence']:.2f} (Recall={best_recall['recall']:.1%})")
|
| 204 |
-
|
| 205 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 206 |
-
output_file = "yolov8m_confidence_optimization.json"
|
| 207 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
| 208 |
-
json.dump({
|
| 209 |
-
'best_f1': best_f1,
|
| 210 |
-
'best_recall': best_recall,
|
| 211 |
-
'best_precision': best_precision,
|
| 212 |
-
'best_gt_match': best_gt_match,
|
| 213 |
-
'all_results': results
|
| 214 |
-
}, f, indent=2, ensure_ascii=False)
|
| 215 |
-
|
| 216 |
-
print(f"\n๐พ ๊ฒฐ๊ณผ ์ ์ฅ: {output_file}")
|
| 217 |
-
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,204 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
YOLOv8m Confidence Threshold ์ต์ ํ (Validation Set๋ง ์ฌ์ฉ)
|
| 3 |
-
๊ณผ์ ํฉ ๋ฐฉ์ง๋ฅผ ์ํด Val set 10์ฅ๋ง์ผ๋ก ํ๊ฐ
|
| 4 |
-
"""
|
| 5 |
-
from ultralytics import YOLO
|
| 6 |
-
from PIL import Image
|
| 7 |
-
import json
|
| 8 |
-
import os
|
| 9 |
-
|
| 10 |
-
# ํ์ต๋ ๋ชจ๋ธ ๋ก๋
|
| 11 |
-
MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 12 |
-
model = YOLO(MODEL_PATH)
|
| 13 |
-
|
| 14 |
-
print(f"โ
YOLOv8m ๋ชจ๋ธ ๋ก๋ ์๋ฃ: {MODEL_PATH}")
|
| 15 |
-
|
| 16 |
-
# Ground Truth ๋ก๋
|
| 17 |
-
GT_FILE = "ground_truth.json"
|
| 18 |
-
with open(GT_FILE, 'r', encoding='utf-8') as f:
|
| 19 |
-
ground_truth = json.load(f)
|
| 20 |
-
|
| 21 |
-
# Val set ์ด๋ฏธ์ง๋ง ํํฐ๋ง
|
| 22 |
-
val_images = set(os.listdir('data/yolo_dataset/images/val'))
|
| 23 |
-
gt_val_only = {}
|
| 24 |
-
|
| 25 |
-
for img_name, gts in ground_truth.items():
|
| 26 |
-
if not gts:
|
| 27 |
-
continue
|
| 28 |
-
base_name = img_name.replace('-1.jpg', '.jpg')
|
| 29 |
-
if img_name in val_images or base_name in val_images:
|
| 30 |
-
gt_val_only[img_name] = gts
|
| 31 |
-
|
| 32 |
-
print(f"โ
Ground Truth (Val set๋ง): {len(gt_val_only)}์ฅ")
|
| 33 |
-
total_gt = sum(len(gts) for gts in gt_val_only.values())
|
| 34 |
-
print(f" - ์ด GT ๊ฐ์ฒด: {total_gt}๊ฐ")
|
| 35 |
-
print("-" * 60)
|
| 36 |
-
|
| 37 |
-
def calculate_iou(box1, box2):
|
| 38 |
-
"""IoU ๊ณ์ฐ"""
|
| 39 |
-
x1_min, y1_min, x1_max, y1_max = box1
|
| 40 |
-
x2_min, y2_min, x2_max, y2_max = box2
|
| 41 |
-
|
| 42 |
-
inter_x_min = max(x1_min, x2_min)
|
| 43 |
-
inter_y_min = max(y1_min, y2_min)
|
| 44 |
-
inter_x_max = min(x1_max, x2_max)
|
| 45 |
-
inter_y_max = min(y1_max, y2_max)
|
| 46 |
-
|
| 47 |
-
if inter_x_max < inter_x_min or inter_y_max < inter_y_min:
|
| 48 |
-
return 0.0
|
| 49 |
-
|
| 50 |
-
inter_area = (inter_x_max - inter_x_min) * (inter_y_max - inter_y_min)
|
| 51 |
-
box1_area = (x1_max - x1_min) * (y1_max - y1_min)
|
| 52 |
-
box2_area = (x2_max - x2_min) * (y2_max - y2_min)
|
| 53 |
-
union_area = box1_area + box2_area - inter_area
|
| 54 |
-
|
| 55 |
-
return inter_area / union_area if union_area > 0 else 0.0
|
| 56 |
-
|
| 57 |
-
def evaluate_confidence_threshold(conf_threshold, iou_threshold=0.5):
|
| 58 |
-
"""ํน์ confidence threshold์์ ์ฑ๋ฅ ํ๊ฐ (Val set๋ง)"""
|
| 59 |
-
tp = 0
|
| 60 |
-
fp = 0
|
| 61 |
-
fn = 0
|
| 62 |
-
|
| 63 |
-
matched_gt_count = 0
|
| 64 |
-
total_gt_count = 0
|
| 65 |
-
|
| 66 |
-
for img_name, gt_boxes in gt_val_only.items():
|
| 67 |
-
# ์ด๋ฏธ์ง ๊ฒฝ๋ก
|
| 68 |
-
img_path = f"data/yolo_dataset/images/val/{img_name}"
|
| 69 |
-
base_name = img_name.replace('-1.jpg', '.jpg')
|
| 70 |
-
if not os.path.exists(img_path):
|
| 71 |
-
img_path = f"data/yolo_dataset/images/val/{base_name}"
|
| 72 |
-
|
| 73 |
-
if not os.path.exists(img_path):
|
| 74 |
-
continue
|
| 75 |
-
|
| 76 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 77 |
-
image = Image.open(img_path)
|
| 78 |
-
|
| 79 |
-
# YOLOv8 ๊ฒ์ถ
|
| 80 |
-
results = model.predict(
|
| 81 |
-
source=image,
|
| 82 |
-
conf=conf_threshold,
|
| 83 |
-
iou=0.7,
|
| 84 |
-
device=0,
|
| 85 |
-
verbose=False
|
| 86 |
-
)
|
| 87 |
-
|
| 88 |
-
result = results[0]
|
| 89 |
-
boxes = result.boxes
|
| 90 |
-
|
| 91 |
-
predictions = []
|
| 92 |
-
if boxes is not None and len(boxes) > 0:
|
| 93 |
-
for box in boxes:
|
| 94 |
-
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
|
| 95 |
-
confidence = box.conf[0].cpu().item()
|
| 96 |
-
predictions.append({
|
| 97 |
-
'bbox': [float(x1), float(y1), float(x2), float(y2)],
|
| 98 |
-
'confidence': confidence
|
| 99 |
-
})
|
| 100 |
-
|
| 101 |
-
total_gt_count += len(gt_boxes)
|
| 102 |
-
|
| 103 |
-
# GT์ ๋งค์นญ
|
| 104 |
-
matched_gt = set()
|
| 105 |
-
matched_pred = set()
|
| 106 |
-
|
| 107 |
-
for pred_idx, pred in enumerate(predictions):
|
| 108 |
-
best_iou = 0
|
| 109 |
-
best_gt_idx = -1
|
| 110 |
-
|
| 111 |
-
for gt_idx, gt in enumerate(gt_boxes):
|
| 112 |
-
if gt_idx in matched_gt:
|
| 113 |
-
continue
|
| 114 |
-
|
| 115 |
-
iou = calculate_iou(pred['bbox'], gt['bbox'])
|
| 116 |
-
if iou > best_iou:
|
| 117 |
-
best_iou = iou
|
| 118 |
-
best_gt_idx = gt_idx
|
| 119 |
-
|
| 120 |
-
if best_iou >= iou_threshold:
|
| 121 |
-
tp += 1
|
| 122 |
-
matched_gt.add(best_gt_idx)
|
| 123 |
-
matched_pred.add(pred_idx)
|
| 124 |
-
else:
|
| 125 |
-
fp += 1
|
| 126 |
-
|
| 127 |
-
fn += len(gt_boxes) - len(matched_gt)
|
| 128 |
-
matched_gt_count += len(matched_gt)
|
| 129 |
-
|
| 130 |
-
# ์ฑ๋ฅ ์งํ ๊ณ์ฐ
|
| 131 |
-
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
|
| 132 |
-
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
|
| 133 |
-
f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
|
| 134 |
-
|
| 135 |
-
return {
|
| 136 |
-
'tp': tp,
|
| 137 |
-
'fp': fp,
|
| 138 |
-
'fn': fn,
|
| 139 |
-
'precision': precision,
|
| 140 |
-
'recall': recall,
|
| 141 |
-
'f1': f1,
|
| 142 |
-
'matched_gt': matched_gt_count,
|
| 143 |
-
'total_gt': total_gt_count,
|
| 144 |
-
'gt_match_rate': matched_gt_count / total_gt_count if total_gt_count > 0 else 0
|
| 145 |
-
}
|
| 146 |
-
|
| 147 |
-
# Confidence threshold sweep
|
| 148 |
-
print("\n๐ Confidence Threshold ์ต์ ํ (Val set๋ง)...\n")
|
| 149 |
-
|
| 150 |
-
confidence_thresholds = [0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9]
|
| 151 |
-
results = []
|
| 152 |
-
|
| 153 |
-
for conf in confidence_thresholds:
|
| 154 |
-
metrics = evaluate_confidence_threshold(conf)
|
| 155 |
-
results.append({
|
| 156 |
-
'confidence': conf,
|
| 157 |
-
**metrics
|
| 158 |
-
})
|
| 159 |
-
|
| 160 |
-
print(f"Conf {conf:.2f}: P={metrics['precision']:.1%} R={metrics['recall']:.1%} F1={metrics['f1']:.1%} | "
|
| 161 |
-
f"GT๋งค์นญ={metrics['matched_gt']}/{metrics['total_gt']} ({metrics['gt_match_rate']:.1%})")
|
| 162 |
-
|
| 163 |
-
# ์ต์ ๊ฐ ์ฐพ๊ธฐ
|
| 164 |
-
best_f1 = max(results, key=lambda x: x['f1'])
|
| 165 |
-
best_recall = max(results, key=lambda x: x['recall'])
|
| 166 |
-
best_precision = max(results, key=lambda x: x['precision'])
|
| 167 |
-
|
| 168 |
-
print("\n" + "=" * 60)
|
| 169 |
-
print("๐ ์ต์ ํ ๊ฒฐ๊ณผ (Val set๋ง, ๊ณผ์ ํฉ ์์):")
|
| 170 |
-
print("=" * 60)
|
| 171 |
-
|
| 172 |
-
print(f"\n1๏ธโฃ ์ต๊ณ F1 Score: {best_f1['f1']:.1%} (Confidence={best_f1['confidence']:.2f})")
|
| 173 |
-
print(f" - Precision: {best_f1['precision']:.1%}")
|
| 174 |
-
print(f" - Recall: {best_f1['recall']:.1%}")
|
| 175 |
-
print(f" - GT ๋งค์นญ: {best_f1['matched_gt']}/{best_f1['total_gt']} ({best_f1['gt_match_rate']:.1%})")
|
| 176 |
-
|
| 177 |
-
print(f"\n2๏ธโฃ ์ต๊ณ Recall: {best_recall['recall']:.1%} (Confidence={best_recall['confidence']:.2f})")
|
| 178 |
-
print(f" - F1 Score: {best_recall['f1']:.1%}")
|
| 179 |
-
print(f" - Precision: {best_recall['precision']:.1%}")
|
| 180 |
-
|
| 181 |
-
print(f"\n3๏ธโฃ ์ต๊ณ Precision: {best_precision['precision']:.1%} (Confidence={best_precision['confidence']:.2f})")
|
| 182 |
-
print(f" - F1 Score: {best_precision['f1']:.1%}")
|
| 183 |
-
print(f" - Recall: {best_precision['recall']:.1%}")
|
| 184 |
-
|
| 185 |
-
print("\n๐ก ๊ถ์ฅ ์ค์ (Val set ๊ธฐ์ค, ์ผ๋ฐํ ์ฑ๋ฅ):")
|
| 186 |
-
print(f" - ์ต์ confidence: {best_f1['confidence']:.2f}")
|
| 187 |
-
print(f" - F1 Score: {best_f1['f1']:.1%}")
|
| 188 |
-
print(f" - Precision: {best_f1['precision']:.1%}, Recall: {best_f1['recall']:.1%}")
|
| 189 |
-
|
| 190 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 191 |
-
output_file = "yolov8m_confidence_optimization_val_only.json"
|
| 192 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
| 193 |
-
json.dump({
|
| 194 |
-
'dataset': 'validation_set_only',
|
| 195 |
-
'num_images': len(gt_val_only),
|
| 196 |
-
'total_gt': total_gt,
|
| 197 |
-
'best_f1': best_f1,
|
| 198 |
-
'best_recall': best_recall,
|
| 199 |
-
'best_precision': best_precision,
|
| 200 |
-
'all_results': results
|
| 201 |
-
}, f, indent=2, ensure_ascii=False)
|
| 202 |
-
|
| 203 |
-
print(f"\n๐พ ๊ฒฐ๊ณผ ์ ์ฅ: {output_file}")
|
| 204 |
-
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Roboflow ๋ชจ๋ธ ๋น ๋ฅธ ํ
์คํธ
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
import requests
|
| 9 |
-
import base64
|
| 10 |
-
from PIL import Image
|
| 11 |
-
from io import BytesIO
|
| 12 |
-
import json
|
| 13 |
-
|
| 14 |
-
# ํ
์คํธ ์ด๋ฏธ์ง
|
| 15 |
-
test_image = "data/yolo_dataset/images/train/250818_04.jpg"
|
| 16 |
-
|
| 17 |
-
print("="*60)
|
| 18 |
-
print("๐ฆ Roboflow ๋ชจ๋ธ ๋น ๋ฅธ ํ
์คํธ")
|
| 19 |
-
print("="*60)
|
| 20 |
-
print(f"๐ธ ์ด๋ฏธ์ง: {test_image}\n")
|
| 21 |
-
|
| 22 |
-
# ์ด๋ฏธ์ง ๋ก๋ ๋ฐ ๋ฆฌ์ฌ์ด์ฆ
|
| 23 |
-
image = Image.open(test_image)
|
| 24 |
-
print(f"์๋ณธ ํฌ๊ธฐ: {image.size}")
|
| 25 |
-
|
| 26 |
-
image.thumbnail((640, 640), Image.Resampling.LANCZOS)
|
| 27 |
-
print(f"๋ฆฌ์ฌ์ด์ฆ: {image.size}")
|
| 28 |
-
|
| 29 |
-
# Base64 ์ธ์ฝ๋ฉ
|
| 30 |
-
buffered = BytesIO()
|
| 31 |
-
image.save(buffered, format="JPEG", quality=80)
|
| 32 |
-
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 33 |
-
|
| 34 |
-
# API ํธ์ถ
|
| 35 |
-
print(f"\n๐ API ํธ์ถ ์ค...\n")
|
| 36 |
-
response = requests.post(
|
| 37 |
-
'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6',
|
| 38 |
-
headers={'Content-Type': 'application/json'},
|
| 39 |
-
json={
|
| 40 |
-
'api_key': 'azcIL8KDJVJMYrsERzI7',
|
| 41 |
-
'inputs': {
|
| 42 |
-
'image': {'type': 'base64', 'value': img_base64}
|
| 43 |
-
}
|
| 44 |
-
},
|
| 45 |
-
timeout=30
|
| 46 |
-
)
|
| 47 |
-
|
| 48 |
-
if response.status_code != 200:
|
| 49 |
-
print(f"โ ์ค๋ฅ: {response.status_code}")
|
| 50 |
-
print(response.text)
|
| 51 |
-
exit(1)
|
| 52 |
-
|
| 53 |
-
result = response.json()
|
| 54 |
-
|
| 55 |
-
# predictions ์ถ์ถ
|
| 56 |
-
predictions = []
|
| 57 |
-
if 'outputs' in result and len(result['outputs']) > 0:
|
| 58 |
-
output = result['outputs'][0]
|
| 59 |
-
if 'predictions' in output:
|
| 60 |
-
pred_data = output['predictions']
|
| 61 |
-
if isinstance(pred_data, dict) and 'predictions' in pred_data:
|
| 62 |
-
predictions = pred_data['predictions']
|
| 63 |
-
|
| 64 |
-
print(f"{'='*60}")
|
| 65 |
-
print(f"๐ ๊ฒ์ถ ๊ฒฐ๊ณผ")
|
| 66 |
-
print(f"{'='*60}\n")
|
| 67 |
-
|
| 68 |
-
print(f"์ด ๊ฒ์ถ ์: {len(predictions)}๊ฐ\n")
|
| 69 |
-
|
| 70 |
-
# ์์ธ ๊ฒฐ๊ณผ
|
| 71 |
-
for i, pred in enumerate(predictions, 1):
|
| 72 |
-
cls = pred.get('class', 'unknown')
|
| 73 |
-
conf = pred.get('confidence', 0)
|
| 74 |
-
x = pred.get('x', 0)
|
| 75 |
-
y = pred.get('y', 0)
|
| 76 |
-
w = pred.get('width', 0)
|
| 77 |
-
h = pred.get('height', 0)
|
| 78 |
-
|
| 79 |
-
print(f"{i}. ํด๋์ค: {cls}")
|
| 80 |
-
print(f" ์ ๋ขฐ๋: {conf:.1%}")
|
| 81 |
-
print(f" ์์น: ({x:.0f}, {y:.0f})")
|
| 82 |
-
print(f" ํฌ๊ธฐ: {w:.0f} x {h:.0f}")
|
| 83 |
-
print()
|
| 84 |
-
|
| 85 |
-
# shrimp๋ง ํํฐ๋ง
|
| 86 |
-
shrimp_count = sum(1 for p in predictions if p.get('class') == 'shrimp')
|
| 87 |
-
print(f"{'='*60}")
|
| 88 |
-
print(f"โ
shrimp ํด๋์ค: {shrimp_count}๊ฐ")
|
| 89 |
-
print(f"{'='*60}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,127 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Roboflow ๋ชจ๋ธ ๋น ๋ฅธ ํ
์คํธ + ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ ์ฅ
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
import requests
|
| 9 |
-
import base64
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
from io import BytesIO
|
| 12 |
-
import json
|
| 13 |
-
|
| 14 |
-
# ํ
์คํธ ์ด๋ฏธ์ง
|
| 15 |
-
test_image = "data/yolo_dataset/images/train/250818_04.jpg"
|
| 16 |
-
|
| 17 |
-
print("="*60)
|
| 18 |
-
print("๐ฆ Roboflow ๋ชจ๋ธ ํ
์คํธ + ๊ฒฐ๊ณผ ์ ์ฅ")
|
| 19 |
-
print("="*60)
|
| 20 |
-
print(f"๐ธ ์ด๋ฏธ์ง: {test_image}\n")
|
| 21 |
-
|
| 22 |
-
# ์๋ณธ ์ด๋ฏธ์ง ๋ก๋
|
| 23 |
-
image_original = Image.open(test_image)
|
| 24 |
-
original_size = image_original.size
|
| 25 |
-
print(f"์๋ณธ ํฌ๊ธฐ: {original_size}")
|
| 26 |
-
|
| 27 |
-
# ๋ฆฌ์ฌ์ด์ฆ (API ์ ์ก์ฉ)
|
| 28 |
-
image_resized = image_original.copy()
|
| 29 |
-
image_resized.thumbnail((640, 640), Image.Resampling.LANCZOS)
|
| 30 |
-
print(f"๋ฆฌ์ฌ์ด์ฆ: {image_resized.size}")
|
| 31 |
-
|
| 32 |
-
# Base64 ์ธ์ฝ๋ฉ
|
| 33 |
-
buffered = BytesIO()
|
| 34 |
-
image_resized.save(buffered, format="JPEG", quality=80)
|
| 35 |
-
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 36 |
-
|
| 37 |
-
# API ํธ์ถ
|
| 38 |
-
print(f"\n๐ API ํธ์ถ ์ค...\n")
|
| 39 |
-
response = requests.post(
|
| 40 |
-
'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6',
|
| 41 |
-
headers={'Content-Type': 'application/json'},
|
| 42 |
-
json={
|
| 43 |
-
'api_key': 'azcIL8KDJVJMYrsERzI7',
|
| 44 |
-
'inputs': {
|
| 45 |
-
'image': {'type': 'base64', 'value': img_base64}
|
| 46 |
-
}
|
| 47 |
-
},
|
| 48 |
-
timeout=30
|
| 49 |
-
)
|
| 50 |
-
|
| 51 |
-
if response.status_code != 200:
|
| 52 |
-
print(f"โ ์ค๋ฅ: {response.status_code}")
|
| 53 |
-
exit(1)
|
| 54 |
-
|
| 55 |
-
result = response.json()
|
| 56 |
-
|
| 57 |
-
# predictions ์ถ์ถ
|
| 58 |
-
predictions = []
|
| 59 |
-
if 'outputs' in result and len(result['outputs']) > 0:
|
| 60 |
-
output = result['outputs'][0]
|
| 61 |
-
if 'predictions' in output:
|
| 62 |
-
pred_data = output['predictions']
|
| 63 |
-
if isinstance(pred_data, dict) and 'predictions' in pred_data:
|
| 64 |
-
predictions = pred_data['predictions']
|
| 65 |
-
|
| 66 |
-
print(f"๐ ๊ฒ์ถ ์: {len(predictions)}๊ฐ\n")
|
| 67 |
-
|
| 68 |
-
# ์๋ณธ ์ด๋ฏธ์ง์ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 69 |
-
draw = ImageDraw.Draw(image_original)
|
| 70 |
-
|
| 71 |
-
# ์ค์ผ์ผ ๊ณ์ฐ (๋ฆฌ์ฌ์ด์ฆ๋ ์ขํ โ ์๋ณธ ์ขํ)
|
| 72 |
-
scale_x = original_size[0] / image_resized.size[0]
|
| 73 |
-
scale_y = original_size[1] / image_resized.size[1]
|
| 74 |
-
|
| 75 |
-
shrimp_count = 0
|
| 76 |
-
|
| 77 |
-
for i, pred in enumerate(predictions, 1):
|
| 78 |
-
cls = pred.get('class', 'unknown')
|
| 79 |
-
conf = pred.get('confidence', 0)
|
| 80 |
-
x = pred.get('x', 0) * scale_x
|
| 81 |
-
y = pred.get('y', 0) * scale_y
|
| 82 |
-
w = pred.get('width', 0) * scale_x
|
| 83 |
-
h = pred.get('height', 0) * scale_y
|
| 84 |
-
|
| 85 |
-
print(f"{i}. ํด๋์ค: {cls}, ์ ๋ขฐ๋: {conf:.1%}")
|
| 86 |
-
|
| 87 |
-
# shrimp๋ง ๊ทธ๋ฆฌ๊ธฐ
|
| 88 |
-
if cls == 'shrimp':
|
| 89 |
-
shrimp_count += 1
|
| 90 |
-
|
| 91 |
-
# ๋ฐ์ค ์ขํ
|
| 92 |
-
x1 = x - w / 2
|
| 93 |
-
y1 = y - h / 2
|
| 94 |
-
x2 = x + w / 2
|
| 95 |
-
y2 = y + h / 2
|
| 96 |
-
|
| 97 |
-
# ์ ๋ขฐ๋๋ณ ์์
|
| 98 |
-
if conf >= 0.5:
|
| 99 |
-
color = 'lime'
|
| 100 |
-
elif conf >= 0.3:
|
| 101 |
-
color = 'yellow'
|
| 102 |
-
else:
|
| 103 |
-
color = 'orange'
|
| 104 |
-
|
| 105 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 106 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=8)
|
| 107 |
-
|
| 108 |
-
# ํ
์คํธ
|
| 109 |
-
text = f"#{shrimp_count} {conf:.1%}"
|
| 110 |
-
try:
|
| 111 |
-
font = ImageFont.truetype("arial.ttf", 50)
|
| 112 |
-
except:
|
| 113 |
-
font = ImageFont.load_default()
|
| 114 |
-
|
| 115 |
-
# ํ
์คํธ ๋ฐฐ๊ฒฝ
|
| 116 |
-
text_bbox = draw.textbbox((x1, y1-60), text, font=font)
|
| 117 |
-
draw.rectangle(text_bbox, fill=color)
|
| 118 |
-
draw.text((x1, y1-60), text, fill='black', font=font)
|
| 119 |
-
|
| 120 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 121 |
-
output_path = "quick_test_result.jpg"
|
| 122 |
-
image_original.save(output_path, quality=95)
|
| 123 |
-
|
| 124 |
-
print(f"\n{'='*60}")
|
| 125 |
-
print(f"โ
shrimp ๊ฒ์ถ: {shrimp_count}๊ฐ")
|
| 126 |
-
print(f"๐พ ๊ฒฐ๊ณผ ์ ์ฅ: {output_path}")
|
| 127 |
-
print(f"{'='*60}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,1150 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
|
| 4 |
-
3๊ฐ์ ์ฑ์ ํ๋๋ก ํตํฉ: ์๋ ๊ฒ์ถ, ๋ผ๋ฒจ๋ง ๋๊ตฌ, ๋ฐ๋ชจ
|
| 5 |
-
RT-DETR ๋๋ VIDraft/Shrimp ํด๋ผ์ฐ๋ ๋ชจ๋ธ ์ ํ ๊ฐ๋ฅ
|
| 6 |
-
"""
|
| 7 |
-
import sys
|
| 8 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 9 |
-
|
| 10 |
-
import gradio as gr
|
| 11 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 12 |
-
import numpy as np
|
| 13 |
-
import json
|
| 14 |
-
import os
|
| 15 |
-
import glob
|
| 16 |
-
from datetime import datetime
|
| 17 |
-
import torch
|
| 18 |
-
from transformers import RTDetrForObjectDetection, RTDetrImageProcessor
|
| 19 |
-
import requests
|
| 20 |
-
import base64
|
| 21 |
-
from io import BytesIO
|
| 22 |
-
from inference_sdk import InferenceHTTPClient
|
| 23 |
-
import tempfile
|
| 24 |
-
|
| 25 |
-
# test_visual_validation์์ ๊ฐ์ ธ์ค๊ธฐ (์ง์ฐ import๋ก ๋ณ๊ฒฝ)
|
| 26 |
-
# from test_visual_validation import (
|
| 27 |
-
# load_rtdetr_model,
|
| 28 |
-
# detect_with_rtdetr,
|
| 29 |
-
# apply_universal_filter,
|
| 30 |
-
# calculate_morphological_features,
|
| 31 |
-
# calculate_visual_features
|
| 32 |
-
# )
|
| 33 |
-
|
| 34 |
-
# YOLOv8 import
|
| 35 |
-
from ultralytics import YOLO
|
| 36 |
-
|
| 37 |
-
# ============================================================
|
| 38 |
-
# YOLOv8 ๋ชจ๋ธ ์ค์
|
| 39 |
-
# ============================================================
|
| 40 |
-
YOLO_MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 41 |
-
yolo_model = None
|
| 42 |
-
|
| 43 |
-
def load_yolo_model():
|
| 44 |
-
"""YOLOv8 ๋ชจ๋ธ ๋ก๋ฉ"""
|
| 45 |
-
global yolo_model
|
| 46 |
-
if yolo_model is None:
|
| 47 |
-
print(f"๐ YOLOv8 ๋ชจ๋ธ ๋ก๋ฉ ์ค: {YOLO_MODEL_PATH}")
|
| 48 |
-
yolo_model = YOLO(YOLO_MODEL_PATH)
|
| 49 |
-
print("โ
YOLOv8 ๋ชจ๋ธ ๋ก๋ฉ ์๋ฃ")
|
| 50 |
-
return yolo_model
|
| 51 |
-
|
| 52 |
-
def detect_with_yolo(image, confidence=0.1):
|
| 53 |
-
"""YOLOv8 ๋ชจ๋ธ๋ก ๊ฒ์ถ"""
|
| 54 |
-
try:
|
| 55 |
-
model = load_yolo_model()
|
| 56 |
-
|
| 57 |
-
# ์ถ๋ก ์คํ
|
| 58 |
-
results = model.predict(
|
| 59 |
-
source=image,
|
| 60 |
-
conf=confidence,
|
| 61 |
-
verbose=False
|
| 62 |
-
)
|
| 63 |
-
|
| 64 |
-
detections = []
|
| 65 |
-
for result in results:
|
| 66 |
-
boxes = result.boxes
|
| 67 |
-
for box in boxes:
|
| 68 |
-
x1, y1, x2, y2 = box.xyxy[0].tolist()
|
| 69 |
-
conf = box.conf[0].item()
|
| 70 |
-
|
| 71 |
-
detections.append({
|
| 72 |
-
'bbox': [x1, y1, x2, y2],
|
| 73 |
-
'confidence': conf
|
| 74 |
-
})
|
| 75 |
-
|
| 76 |
-
print(f"โ
YOLOv8 ๊ฒ์ถ ์๋ฃ: {len(detections)}๊ฐ")
|
| 77 |
-
return detections
|
| 78 |
-
|
| 79 |
-
except Exception as e:
|
| 80 |
-
print(f"โ YOLOv8 ๊ฒ์ถ ์ค๋ฅ: {str(e)}")
|
| 81 |
-
import traceback
|
| 82 |
-
traceback.print_exc()
|
| 83 |
-
return []
|
| 84 |
-
|
| 85 |
-
# ============================================================
|
| 86 |
-
# Roboflow SDK ์ค์ (์ต์ ํ๋ ๋ฐฉ์)
|
| 87 |
-
# ============================================================
|
| 88 |
-
ROBOFLOW_API_KEY = "azcIL8KDJVJMYrsERzI7"
|
| 89 |
-
|
| 90 |
-
# Roboflow Inference SDK ํด๋ผ์ด์ธํธ (connection pooling ์ง์)
|
| 91 |
-
roboflow_client = InferenceHTTPClient(
|
| 92 |
-
api_url="https://serverless.roboflow.com",
|
| 93 |
-
api_key=ROBOFLOW_API_KEY
|
| 94 |
-
)
|
| 95 |
-
|
| 96 |
-
def detect_with_roboflow(image, confidence=0.065):
|
| 97 |
-
"""Roboflow API๋ฅผ ์ฌ์ฉํ ์ต์ ํ๋ ๊ฒ์ถ (๋ก์ปฌ ํ
์คํธ์ ๋์ผ)"""
|
| 98 |
-
try:
|
| 99 |
-
# ์๋ณธ ์ด๋ฏธ์ง ๋ณด์กด
|
| 100 |
-
image_original = image
|
| 101 |
-
original_size = image_original.size
|
| 102 |
-
|
| 103 |
-
# ๋ฆฌ์ฌ์ด์ฆ (API ์ ์ก์ฉ)
|
| 104 |
-
image_resized = image_original.copy()
|
| 105 |
-
image_resized.thumbnail((640, 640), Image.Resampling.LANCZOS)
|
| 106 |
-
print(f"๐ ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆ: {original_size} โ {image_resized.size}")
|
| 107 |
-
|
| 108 |
-
# Base64 ์ธ์ฝ๋ฉ
|
| 109 |
-
buffered = BytesIO()
|
| 110 |
-
image_resized.save(buffered, format="JPEG", quality=80)
|
| 111 |
-
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 112 |
-
print(f"๐ฆ Base64 ํฌ๊ธฐ: {len(img_base64)} bytes")
|
| 113 |
-
|
| 114 |
-
print(f"๐ Roboflow API ์ถ๋ก ์์...")
|
| 115 |
-
|
| 116 |
-
# ๐ ์ต์ ํ 3: requests๋ก API ํธ์ถ (SDK ๋์ ์ฌ์ฉ - ๋ ์์ ์ )
|
| 117 |
-
response = requests.post(
|
| 118 |
-
'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6',
|
| 119 |
-
headers={'Content-Type': 'application/json'},
|
| 120 |
-
json={
|
| 121 |
-
'api_key': ROBOFLOW_API_KEY,
|
| 122 |
-
'inputs': {
|
| 123 |
-
'image': {'type': 'base64', 'value': img_base64}
|
| 124 |
-
}
|
| 125 |
-
},
|
| 126 |
-
timeout=30
|
| 127 |
-
)
|
| 128 |
-
|
| 129 |
-
if response.status_code != 200:
|
| 130 |
-
print(f"โ Roboflow API ์ค๋ฅ: {response.status_code}")
|
| 131 |
-
print(f"์๋ต: {response.text}")
|
| 132 |
-
return []
|
| 133 |
-
|
| 134 |
-
result = response.json()
|
| 135 |
-
print(f"๐ Roboflow ์๋ต: {json.dumps(result, indent=2, ensure_ascii=False)[:500]}...")
|
| 136 |
-
|
| 137 |
-
# Workflow ์๋ต ๊ตฌ์กฐ ํ์ฑ
|
| 138 |
-
detections = []
|
| 139 |
-
predictions = []
|
| 140 |
-
|
| 141 |
-
# ๋ฐฉ๋ฒ 1: outputs[0].predictions.predictions (workflow ํํ)
|
| 142 |
-
if isinstance(result, dict) and 'outputs' in result and len(result['outputs']) > 0:
|
| 143 |
-
output = result['outputs'][0]
|
| 144 |
-
if isinstance(output, dict) and 'predictions' in output:
|
| 145 |
-
pred_data = output['predictions']
|
| 146 |
-
# predictions๊ฐ dict์ด๊ณ ๊ทธ ์์ predictions ๋ฐฐ์ด์ด ์๋ ๊ฒฝ์ฐ
|
| 147 |
-
if isinstance(pred_data, dict) and 'predictions' in pred_data:
|
| 148 |
-
predictions = pred_data['predictions']
|
| 149 |
-
# predictions๊ฐ ๋ฐ๋ก ๋ฐฐ์ด์ธ ๊ฒฝ์ฐ
|
| 150 |
-
elif isinstance(pred_data, list):
|
| 151 |
-
predictions = pred_data
|
| 152 |
-
else:
|
| 153 |
-
predictions = [pred_data]
|
| 154 |
-
|
| 155 |
-
# ๋ฐฉ๋ฒ 2: ์ง์ predictions
|
| 156 |
-
elif isinstance(result, dict) and 'predictions' in result:
|
| 157 |
-
predictions = result['predictions']
|
| 158 |
-
|
| 159 |
-
# ๋ฐฉ๋ฒ 3: ๋ค๋ฅธ ๊ตฌ์กฐ
|
| 160 |
-
elif isinstance(result, list):
|
| 161 |
-
predictions = result
|
| 162 |
-
|
| 163 |
-
print(f"๐ฆ ์ฐพ์ predictions: {len(predictions)}๊ฐ")
|
| 164 |
-
|
| 165 |
-
# ์ค์ผ์ผ ๊ณ์ฐ (๋ฆฌ์ฌ์ด์ฆ๋ ์ขํ โ ์๋ณธ ์ขํ)
|
| 166 |
-
scale_x = original_size[0] / image_resized.size[0]
|
| 167 |
-
scale_y = original_size[1] / image_resized.size[1]
|
| 168 |
-
print(f"๐ ์ค์ผ์ผ: x={scale_x:.2f}, y={scale_y:.2f}")
|
| 169 |
-
|
| 170 |
-
for pred in predictions:
|
| 171 |
-
# ํด๋์ค ํํฐ๋ง (shrimp๋ง ๊ฒ์ถ)
|
| 172 |
-
pred_class = pred.get('class', '')
|
| 173 |
-
if pred_class != 'shrimp':
|
| 174 |
-
continue
|
| 175 |
-
|
| 176 |
-
# ์ ๋ขฐ๋ ํํฐ๋ง
|
| 177 |
-
pred_confidence = pred.get('confidence', 0)
|
| 178 |
-
if pred_confidence < confidence:
|
| 179 |
-
continue
|
| 180 |
-
|
| 181 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค ์ถ์ถ (๋ฆฌ์ฌ์ด์ฆ๋ ์ขํ)
|
| 182 |
-
x = pred.get('x', 0)
|
| 183 |
-
y = pred.get('y', 0)
|
| 184 |
-
width = pred.get('width', 0)
|
| 185 |
-
height = pred.get('height', 0)
|
| 186 |
-
|
| 187 |
-
# ์๋ณธ ํฌ๊ธฐ๋ก ์ค์ผ์ผ ๋ณํ
|
| 188 |
-
x_scaled = x * scale_x
|
| 189 |
-
y_scaled = y * scale_y
|
| 190 |
-
width_scaled = width * scale_x
|
| 191 |
-
height_scaled = height * scale_y
|
| 192 |
-
|
| 193 |
-
# ์ค์ฌ์ ์ขํ๋ฅผ ์ข์๋จ/์ฐํ๋จ ์ขํ๋ก ๋ณํ
|
| 194 |
-
x1 = x_scaled - width_scaled / 2
|
| 195 |
-
y1 = y_scaled - height_scaled / 2
|
| 196 |
-
x2 = x_scaled + width_scaled / 2
|
| 197 |
-
y2 = y_scaled + height_scaled / 2
|
| 198 |
-
|
| 199 |
-
detections.append({
|
| 200 |
-
'bbox': [x1, y1, x2, y2],
|
| 201 |
-
'confidence': pred_confidence
|
| 202 |
-
})
|
| 203 |
-
print(f" โ ๊ฒ์ถ (shrimp): conf={pred_confidence:.2%}, bbox=[{x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f}]")
|
| 204 |
-
|
| 205 |
-
print(f"โ
Roboflow ๊ฒ์ถ ์๋ฃ: {len(detections)}๊ฐ")
|
| 206 |
-
return detections
|
| 207 |
-
|
| 208 |
-
except Exception as e:
|
| 209 |
-
print(f"โ Roboflow SDK ์ค๋ฅ: {str(e)}")
|
| 210 |
-
import traceback
|
| 211 |
-
traceback.print_exc()
|
| 212 |
-
return []
|
| 213 |
-
|
| 214 |
-
# ============================================================
|
| 215 |
-
# ์ ์ญ ๋ชจ๋ธ ๋ณ์ (์ง์ฐ ๋ก๋ฉ)
|
| 216 |
-
# ============================================================
|
| 217 |
-
processor = None
|
| 218 |
-
model = None
|
| 219 |
-
|
| 220 |
-
def load_rtdetr_on_demand():
|
| 221 |
-
"""RT-DETR ๋ชจ๋ธ์ ํ์์์๋ง ๋ก๋ฉ"""
|
| 222 |
-
global processor, model
|
| 223 |
-
if processor is None or model is None:
|
| 224 |
-
print("๐ RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์ค...")
|
| 225 |
-
from test_visual_validation import load_rtdetr_model
|
| 226 |
-
processor, model = load_rtdetr_model()
|
| 227 |
-
print("โ
RT-DETR ๋ก๋ฉ ์๋ฃ")
|
| 228 |
-
return "โ
RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์๋ฃ"
|
| 229 |
-
else:
|
| 230 |
-
return "โน๏ธ RT-DETR ๋ชจ๋ธ์ด ์ด๋ฏธ ๋ก๋ฉ๋์ด ์์ต๋๋ค"
|
| 231 |
-
|
| 232 |
-
print("โ
VIDraft/Shrimp ํด๋ผ์ฐ๋ ๋ชจ๋ธ ์ฌ์ฉ ๊ฐ๋ฅ\n")
|
| 233 |
-
|
| 234 |
-
# ============================================================
|
| 235 |
-
# ๋ผ๋ฒจ๋ง ๋๊ตฌ ์ ์ญ ๋ณ์
|
| 236 |
-
# ============================================================
|
| 237 |
-
current_data = {
|
| 238 |
-
'folder': None,
|
| 239 |
-
'images': [],
|
| 240 |
-
'current_idx': 0,
|
| 241 |
-
'detections': {},
|
| 242 |
-
'selections': {},
|
| 243 |
-
'confidence_threshold': 0.2,
|
| 244 |
-
'image_cache': {},
|
| 245 |
-
'model_type': 'RT-DETR' # ํ์ฌ ์ ํ๋ ๋ชจ๋ธ
|
| 246 |
-
}
|
| 247 |
-
|
| 248 |
-
GROUND_TRUTH_FILE = "ground_truth.json"
|
| 249 |
-
DATA_BASE = "data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 250 |
-
|
| 251 |
-
# ============================================================
|
| 252 |
-
# ๋ชจ๋ธ๋ณ ๊ฒ์ถ ํจ์
|
| 253 |
-
# ============================================================
|
| 254 |
-
|
| 255 |
-
def detect_with_selected_model(image, confidence, model_type):
|
| 256 |
-
"""์ ํ๋ ๋ชจ๋ธ๋ก ๊ฒ์ถ"""
|
| 257 |
-
if model_type == "RT-DETR":
|
| 258 |
-
if processor is None or model is None:
|
| 259 |
-
raise ValueError("โ ๏ธ RT-DETR ๋ชจ๋ธ์ด ๋ก๋ฉ๋์ง ์์์ต๋๋ค. '๐ RT-DETR ๋ก๋' ๋ฒํผ์ ๋จผ์ ํด๋ฆญํ์ธ์.")
|
| 260 |
-
from test_visual_validation import detect_with_rtdetr
|
| 261 |
-
return detect_with_rtdetr(image, processor, model, confidence)
|
| 262 |
-
elif model_type == "VIDraft/Shrimp":
|
| 263 |
-
return detect_with_roboflow(image, confidence)
|
| 264 |
-
elif model_type == "YOLOv8":
|
| 265 |
-
return detect_with_yolo(image, confidence)
|
| 266 |
-
else:
|
| 267 |
-
return []
|
| 268 |
-
|
| 269 |
-
# ============================================================
|
| 270 |
-
# ํญ 1: ์๋ ๊ฒ์ถ (Interactive Validation)
|
| 271 |
-
# ============================================================
|
| 272 |
-
|
| 273 |
-
def interactive_detect(image, confidence, filter_threshold, show_all, model_type, use_filter):
|
| 274 |
-
"""๋ํํ ๊ฒ์ถ"""
|
| 275 |
-
if image is None:
|
| 276 |
-
return None, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์."
|
| 277 |
-
|
| 278 |
-
try:
|
| 279 |
-
# ์ ํ๋ ๋ชจ๋ธ๋ก ๊ฒ์ถ
|
| 280 |
-
all_detections = detect_with_selected_model(image, confidence, model_type)
|
| 281 |
-
|
| 282 |
-
# ํํฐ ์ ์ฉ ์ฌ๋ถ์ ๋ฐ๋ผ ์ฒ๋ฆฌ
|
| 283 |
-
if not use_filter:
|
| 284 |
-
# ํํฐ ๋ฏธ์ฌ์ฉ: ์ ๋ขฐ๋๋ง ์ ์ฉ
|
| 285 |
-
filtered_detections = all_detections
|
| 286 |
-
for det in filtered_detections:
|
| 287 |
-
det['filter_score'] = det['confidence'] * 100
|
| 288 |
-
det['filter_reasons'] = [f"์ ๋ขฐ๋: {det['confidence']:.0%} (ํํฐ ๋ฏธ์ฌ์ฉ)"]
|
| 289 |
-
all_detections_scored = filtered_detections
|
| 290 |
-
else:
|
| 291 |
-
# ํํฐ ์ฌ์ฉ
|
| 292 |
-
if model_type in ["VIDraft/Shrimp", "YOLOv8"]:
|
| 293 |
-
# Roboflow & YOLOv8: ์ ๋ขฐ๋๋ฅผ ํํฐ ์ ์๋ก ์ฌ์ฉ
|
| 294 |
-
for det in all_detections:
|
| 295 |
-
det['filter_score'] = det['confidence'] * 100
|
| 296 |
-
det['filter_reasons'] = [f"{model_type} ์ ๋ขฐ๋: {det['confidence']:.0%}"]
|
| 297 |
-
all_detections_scored = all_detections
|
| 298 |
-
else:
|
| 299 |
-
# RT-DETR: Universal Filter ์ฌ์ฉ
|
| 300 |
-
from test_visual_validation import apply_universal_filter
|
| 301 |
-
all_detections_scored = apply_universal_filter(all_detections, image, threshold=0)
|
| 302 |
-
|
| 303 |
-
# ํํฐ ์๊ณ๊ฐ ์ ์ฉ
|
| 304 |
-
filtered_detections = [det for det in all_detections_scored if det['filter_score'] >= filter_threshold]
|
| 305 |
-
|
| 306 |
-
# ์๊ฐํ
|
| 307 |
-
img = image.copy()
|
| 308 |
-
draw = ImageDraw.Draw(img)
|
| 309 |
-
|
| 310 |
-
try:
|
| 311 |
-
font = ImageFont.truetype("arial.ttf", 14)
|
| 312 |
-
font_large = ImageFont.truetype("arial.ttf", 18)
|
| 313 |
-
font_small = ImageFont.truetype("arial.ttf", 10)
|
| 314 |
-
except:
|
| 315 |
-
font = ImageFont.load_default()
|
| 316 |
-
font_large = ImageFont.load_default()
|
| 317 |
-
font_small = ImageFont.load_default()
|
| 318 |
-
|
| 319 |
-
# ์ ๊ฑฐ๋ ๊ฐ์ฒด ๋จผ์ ํ์ (๋นจ๊ฐ์)
|
| 320 |
-
rejected_detections = [det for det in all_detections_scored if det['filter_score'] < filter_threshold]
|
| 321 |
-
for idx, det in enumerate(rejected_detections, 1):
|
| 322 |
-
x1, y1, x2, y2 = det['bbox']
|
| 323 |
-
score = det['filter_score']
|
| 324 |
-
|
| 325 |
-
# ๋นจ๊ฐ์ ๋ฐ์ค (์ ๊ฑฐ๋จ)
|
| 326 |
-
draw.rectangle([x1, y1, x2, y2], outline="red", width=8)
|
| 327 |
-
|
| 328 |
-
# ๋ผ๋ฒจ (์๊ฒ)
|
| 329 |
-
label = f"โ{idx} {score:.0f}์ "
|
| 330 |
-
bbox = draw.textbbox((x1, y1 - 20), label, font=font_small)
|
| 331 |
-
draw.rectangle(bbox, fill="red")
|
| 332 |
-
draw.text((x1, y1 - 20), label, fill="white", font=font_small)
|
| 333 |
-
|
| 334 |
-
# ์ ์ฒด ๊ฒ์ถ ํ์ (์ต์
) - ํ์
|
| 335 |
-
if show_all:
|
| 336 |
-
for det in all_detections_scored:
|
| 337 |
-
if det not in filtered_detections and det not in rejected_detections:
|
| 338 |
-
x1, y1, x2, y2 = det['bbox']
|
| 339 |
-
draw.rectangle([x1, y1, x2, y2], outline="gray", width=4)
|
| 340 |
-
|
| 341 |
-
# ํํฐ๋ง๋ ๊ฒฐ๊ณผ (ํต๊ณผ) - ๋
น์/๋
ธ๋์/์ฃผํฉ์
|
| 342 |
-
for idx, det in enumerate(filtered_detections, 1):
|
| 343 |
-
x1, y1, x2, y2 = det['bbox']
|
| 344 |
-
score = det['filter_score']
|
| 345 |
-
|
| 346 |
-
# ์ ์์ ๋ฐ๋ผ ์์
|
| 347 |
-
if score >= 75:
|
| 348 |
-
color = "lime"
|
| 349 |
-
elif score >= 50:
|
| 350 |
-
color = "yellow"
|
| 351 |
-
else:
|
| 352 |
-
color = "orange"
|
| 353 |
-
|
| 354 |
-
# ๋ฐ์ค (๋๊ป๊ฒ)
|
| 355 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=10)
|
| 356 |
-
|
| 357 |
-
# ๋ผ๋ฒจ
|
| 358 |
-
label = f"โ#{idx} {score:.0f}์ "
|
| 359 |
-
bbox = draw.textbbox((x1, y1 - 25), label, font=font)
|
| 360 |
-
draw.rectangle(bbox, fill=color)
|
| 361 |
-
draw.text((x1, y1 - 25), label, fill="black", font=font)
|
| 362 |
-
|
| 363 |
-
# ์ธ๋ถ ์ ๋ณด (์๊ฒ)
|
| 364 |
-
details = f"{model_type}:{det['confidence']:.0%}"
|
| 365 |
-
draw.text((x1, y2 + 5), details, fill=color, font=font_small)
|
| 366 |
-
|
| 367 |
-
# ํค๋
|
| 368 |
-
header = f"[{model_type}] โ {len(filtered_detections)}๊ฐ / โ {len(rejected_detections)}๊ฐ (์ ์ฒด: {len(all_detections_scored)}๊ฐ)"
|
| 369 |
-
header_bbox = draw.textbbox((10, 10), header, font=font_large)
|
| 370 |
-
draw.rectangle([5, 5, header_bbox[2]+10, header_bbox[3]+10],
|
| 371 |
-
fill="black", outline="lime", width=2)
|
| 372 |
-
draw.text((10, 10), header, fill="lime", font=font_large)
|
| 373 |
-
|
| 374 |
-
# ์ ๋ณด ์์ฑ
|
| 375 |
-
info = f"""
|
| 376 |
-
### ๐ ๊ฒ์ถ ๊ฒฐ๊ณผ (๋ชจ๋ธ: {model_type})
|
| 377 |
-
|
| 378 |
-
- **์ ์ฒด ๊ฒ์ถ**: {len(all_detections_scored)}๊ฐ
|
| 379 |
-
- **ํํฐ๋ง ํ**: {len(filtered_detections)}๊ฐ
|
| 380 |
-
- **์ ๊ฑฐ๋จ**: {len(rejected_detections)}๊ฐ
|
| 381 |
-
|
| 382 |
-
---
|
| 383 |
-
|
| 384 |
-
### ๐ฏ ๊ฒ์ถ๋ ๊ฐ์ฒด ์์ธ (โ
ํต๊ณผ)
|
| 385 |
-
|
| 386 |
-
"""
|
| 387 |
-
|
| 388 |
-
for idx, det in enumerate(filtered_detections, 1):
|
| 389 |
-
info += f"""
|
| 390 |
-
**#{idx} - ์ ์: {det['filter_score']:.0f}์ ** ({model_type} ์ ๋ขฐ๋: {det['confidence']:.0%})
|
| 391 |
-
|
| 392 |
-
"""
|
| 393 |
-
# ์ฃผ์ ํน์ง๋ง 5๊ฐ
|
| 394 |
-
for reason in det['filter_reasons'][:5]:
|
| 395 |
-
info += f"- {reason}\n"
|
| 396 |
-
|
| 397 |
-
if not filtered_detections:
|
| 398 |
-
info += """
|
| 399 |
-
โ ๏ธ **๊ฒ์ถ๋ ๊ฐ์ฒด๊ฐ ์์ต๋๋ค.**
|
| 400 |
-
|
| 401 |
-
"""
|
| 402 |
-
|
| 403 |
-
# ์ ๊ฑฐ๋ ๊ฐ์ฒด ์ ๋ณด ์ถ๊ฐ
|
| 404 |
-
if rejected_detections:
|
| 405 |
-
info += f"""
|
| 406 |
-
|
| 407 |
-
---
|
| 408 |
-
|
| 409 |
-
### โ ์ ๊ฑฐ๋ ๊ฐ์ฒด ({len(rejected_detections)}๊ฐ)
|
| 410 |
-
|
| 411 |
-
"""
|
| 412 |
-
for idx, det in enumerate(rejected_detections[:3], 1): # ์ต๋ 3๊ฐ๋ง ํ์
|
| 413 |
-
info += f"""
|
| 414 |
-
**์ ๊ฑฐ #{idx} - ์ ์: {det['filter_score']:.0f}์ ** (์๊ณ๊ฐ ๋ฏธ๋ฌ)
|
| 415 |
-
- {model_type} ์ ๋ขฐ๋: {det['confidence']:.0%}
|
| 416 |
-
|
| 417 |
-
"""
|
| 418 |
-
# ์คํจ ์ด์ ํ์
|
| 419 |
-
for reason in det['filter_reasons'][:3]:
|
| 420 |
-
info += f"- {reason}\n"
|
| 421 |
-
|
| 422 |
-
return img, info
|
| 423 |
-
|
| 424 |
-
except Exception as e:
|
| 425 |
-
import traceback
|
| 426 |
-
error_detail = traceback.format_exc()
|
| 427 |
-
return None, f"โ ์ค๋ฅ ๋ฐ์:\n\n```\n{error_detail}\n```"
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
# ============================================================
|
| 431 |
-
# ํญ 2: ๋ผ๋ฒจ๋ง ๋๊ตฌ (Labeling Tool)
|
| 432 |
-
# ============================================================
|
| 433 |
-
|
| 434 |
-
def detect_with_rtdetr_fast(image, confidence=0.3):
|
| 435 |
-
"""RT-DETR ๋น ๋ฅธ ๊ฒ์ถ"""
|
| 436 |
-
inputs = processor(images=image, return_tensors="pt")
|
| 437 |
-
with torch.no_grad():
|
| 438 |
-
outputs = model(**inputs)
|
| 439 |
-
|
| 440 |
-
target_sizes = torch.tensor([image.size[::-1]])
|
| 441 |
-
results = processor.post_process_object_detection(
|
| 442 |
-
outputs,
|
| 443 |
-
target_sizes=target_sizes,
|
| 444 |
-
threshold=confidence
|
| 445 |
-
)[0]
|
| 446 |
-
|
| 447 |
-
detections = []
|
| 448 |
-
for score, label, box in zip(results["scores"], results["labels"], results["boxes"]):
|
| 449 |
-
x1, y1, x2, y2 = box.tolist()
|
| 450 |
-
detections.append({
|
| 451 |
-
'bbox': [x1, y1, x2, y2],
|
| 452 |
-
'confidence': score.item()
|
| 453 |
-
})
|
| 454 |
-
|
| 455 |
-
return detections
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
def load_existing_ground_truth():
|
| 459 |
-
"""๊ธฐ์กด ground_truth.json ๋ก๋"""
|
| 460 |
-
if os.path.exists(GROUND_TRUTH_FILE):
|
| 461 |
-
with open(GROUND_TRUTH_FILE, 'r', encoding='utf-8') as f:
|
| 462 |
-
return json.load(f)
|
| 463 |
-
return {}
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
def save_ground_truth(data):
|
| 467 |
-
"""ground_truth.json ์ ์ฅ"""
|
| 468 |
-
backup_dir = "backups"
|
| 469 |
-
if not os.path.exists(backup_dir):
|
| 470 |
-
os.makedirs(backup_dir)
|
| 471 |
-
|
| 472 |
-
if os.path.exists(GROUND_TRUTH_FILE):
|
| 473 |
-
backup_name = f"ground_truth_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
| 474 |
-
backup_path = os.path.join(backup_dir, backup_name)
|
| 475 |
-
import shutil
|
| 476 |
-
shutil.copy2(GROUND_TRUTH_FILE, backup_path)
|
| 477 |
-
|
| 478 |
-
with open(GROUND_TRUTH_FILE, 'w', encoding='utf-8') as f:
|
| 479 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 480 |
-
|
| 481 |
-
print(f"โ
Ground Truth ์ ์ฅ ์๋ฃ: {len(data)}๊ฐ ์ด๋ฏธ์ง")
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
def get_folders():
|
| 485 |
-
"""์ฌ์ฉ ๊ฐ๋ฅํ ํด๋ ๋ชฉ๋ก"""
|
| 486 |
-
folders = sorted(glob.glob(os.path.join(DATA_BASE, "2*")))
|
| 487 |
-
return [os.path.basename(f) for f in folders if os.path.isdir(f)]
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
def start_labeling(folder, conf_threshold, model_type):
|
| 491 |
-
"""๋ผ๋ฒจ๋ง ์์"""
|
| 492 |
-
if not folder:
|
| 493 |
-
return None, "โ ํด๋๋ฅผ ์ ํํ์ธ์.", ""
|
| 494 |
-
|
| 495 |
-
current_data['folder'] = folder
|
| 496 |
-
current_data['confidence_threshold'] = conf_threshold
|
| 497 |
-
current_data['model_type'] = model_type
|
| 498 |
-
|
| 499 |
-
folder_path = os.path.join(DATA_BASE, folder)
|
| 500 |
-
all_images = sorted(glob.glob(os.path.join(folder_path, "*.jpg")))
|
| 501 |
-
|
| 502 |
-
# -1, -2 ๋ฑ์ด ๋ถ์ ํ์ผ ์ ์ธ (์: 251017_01-1.jpg ์ ์ธ, 251017_01.jpg๋ง ํฌํจ)
|
| 503 |
-
import re
|
| 504 |
-
images = [img for img in all_images if not re.search(r'-\d+\.jpg$', os.path.basename(img))]
|
| 505 |
-
|
| 506 |
-
if not images:
|
| 507 |
-
return None, "โ ์ด๋ฏธ์ง ์์", ""
|
| 508 |
-
|
| 509 |
-
print(f"๐ ํด๋: {folder}")
|
| 510 |
-
print(f" ์ ์ฒด ์ด๋ฏธ์ง: {len(all_images)}๊ฐ")
|
| 511 |
-
print(f" ๋ผ๋ฒจ๋ง ๋์: {len(images)}๊ฐ (-์ซ์ ํ์ผ ์ ์ธ)")
|
| 512 |
-
|
| 513 |
-
current_data['images'] = images
|
| 514 |
-
current_data['current_idx'] = 0
|
| 515 |
-
current_data['detections'] = {}
|
| 516 |
-
current_data['selections'] = {}
|
| 517 |
-
|
| 518 |
-
# ๊ธฐ์กด GT ๋ก๋
|
| 519 |
-
gt = load_existing_ground_truth()
|
| 520 |
-
|
| 521 |
-
# ์ด๋ฏธ ๋ผ๋ฒจ๋ง๋ ์ด๋ฏธ์ง ๊ฑด๋๋ฐ๊ธฐ
|
| 522 |
-
for i, img_path in enumerate(images):
|
| 523 |
-
filename = os.path.basename(img_path)
|
| 524 |
-
if filename in gt:
|
| 525 |
-
current_data['selections'][filename] = [j for j in range(len(gt[filename]))]
|
| 526 |
-
print(f"โญ๏ธ ๊ฑด๋๋ฐ๊ธฐ: {filename} (์ด๋ฏธ ๋ผ๋ฒจ๋ง๋จ)")
|
| 527 |
-
|
| 528 |
-
# ์ฒซ ๋ฏธ๋ผ๋ฒจ๋ง ์ด๋ฏธ์ง ์ฐพ๊ธฐ
|
| 529 |
-
while current_data['current_idx'] < len(images):
|
| 530 |
-
filename = os.path.basename(images[current_data['current_idx']])
|
| 531 |
-
if filename not in current_data['selections']:
|
| 532 |
-
break
|
| 533 |
-
current_data['current_idx'] += 1
|
| 534 |
-
|
| 535 |
-
if current_data['current_idx'] >= len(images):
|
| 536 |
-
return None, "โ
๋ชจ๋ ์ด๋ฏธ์ง ๋ผ๋ฒจ๋ง ์๋ฃ!", ""
|
| 537 |
-
|
| 538 |
-
return show_current_image()
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
def show_current_image():
|
| 542 |
-
"""ํ์ฌ ์ด๋ฏธ์ง ํ์"""
|
| 543 |
-
if current_data['current_idx'] >= len(current_data['images']):
|
| 544 |
-
return None, "โ
์๋ฃ!", ""
|
| 545 |
-
|
| 546 |
-
img_path = current_data['images'][current_data['current_idx']]
|
| 547 |
-
filename = os.path.basename(img_path)
|
| 548 |
-
|
| 549 |
-
# ์บ์ ํ์ธ
|
| 550 |
-
if filename in current_data['image_cache']:
|
| 551 |
-
image = current_data['image_cache'][filename]
|
| 552 |
-
else:
|
| 553 |
-
image = Image.open(img_path)
|
| 554 |
-
current_data['image_cache'][filename] = image
|
| 555 |
-
|
| 556 |
-
# ์ ํ๋ ๋ชจ๋ธ๋ก ๊ฒ์ถ
|
| 557 |
-
if filename not in current_data['detections']:
|
| 558 |
-
if current_data['model_type'] == 'RT-DETR':
|
| 559 |
-
detections = detect_with_rtdetr_fast(image, current_data['confidence_threshold'])
|
| 560 |
-
elif current_data['model_type'] == 'YOLOv8':
|
| 561 |
-
detections = detect_with_yolo(image, current_data['confidence_threshold'])
|
| 562 |
-
else: # VIDraft/Shrimp
|
| 563 |
-
detections = detect_with_roboflow(image, current_data['confidence_threshold'])
|
| 564 |
-
current_data['detections'][filename] = detections
|
| 565 |
-
else:
|
| 566 |
-
detections = current_data['detections'][filename]
|
| 567 |
-
|
| 568 |
-
# ์ ํ๋ ๋ฐ์ค
|
| 569 |
-
selected_indices = current_data['selections'].get(filename, [])
|
| 570 |
-
|
| 571 |
-
# ์๊ฐํ
|
| 572 |
-
vis_image = draw_detections(image, detections, selected_indices)
|
| 573 |
-
|
| 574 |
-
info = f"""
|
| 575 |
-
### ๐ {current_data['folder']} - ์ด๋ฏธ์ง {current_data['current_idx']+1}/{len(current_data['images'])}
|
| 576 |
-
|
| 577 |
-
**ํ์ผ**: {filename}
|
| 578 |
-
**๋ชจ๋ธ**: {current_data['model_type']}
|
| 579 |
-
|
| 580 |
-
**๊ฒ์ถ**: {len(detections)}๊ฐ
|
| 581 |
-
**์ ํ**: {len(selected_indices)}๊ฐ
|
| 582 |
-
|
| 583 |
-
---
|
| 584 |
-
|
| 585 |
-
### ๐ฑ๏ธ ์ฌ์ฉ ๋ฐฉ๋ฒ:
|
| 586 |
-
1. ์ด๋ฏธ์ง๋ฅผ ํด๋ฆญํ์ฌ ๋ฐ์ค ์ ํ/ํด์
|
| 587 |
-
2. "๋ค์" ๋ฒํผ์ผ๋ก ์ ์ฅ ํ ์ด๋
|
| 588 |
-
3. "๊ฑด๋๋ฐ๊ธฐ"๋ก ์ ํ ์์ด ์ด๋
|
| 589 |
-
"""
|
| 590 |
-
|
| 591 |
-
return vis_image, info, filename
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
def draw_detections(image, detections, selected_indices):
|
| 595 |
-
"""๊ฒ์ถ ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ"""
|
| 596 |
-
img = image.copy()
|
| 597 |
-
draw = ImageDraw.Draw(img)
|
| 598 |
-
|
| 599 |
-
try:
|
| 600 |
-
font_tiny = ImageFont.truetype("arial.ttf", 10)
|
| 601 |
-
font_large = ImageFont.truetype("arial.ttf", 40)
|
| 602 |
-
except:
|
| 603 |
-
font_tiny = ImageFont.load_default()
|
| 604 |
-
font_large = ImageFont.load_default()
|
| 605 |
-
|
| 606 |
-
# ์ ํ๋์ง ์์ ๋ฐ์ค ๋จผ์ (๋ค์ชฝ ๋ ์ด์ด)
|
| 607 |
-
for idx, det in enumerate(detections):
|
| 608 |
-
if idx not in selected_indices:
|
| 609 |
-
x1, y1, x2, y2 = det['bbox']
|
| 610 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=20)
|
| 611 |
-
corner_label = f"#{idx+1}"
|
| 612 |
-
draw.rectangle([x1-2, y1-24, x1+30, y1-2], fill="lime")
|
| 613 |
-
draw.text((x1, y1 - 22), corner_label, fill="white", font=font_tiny)
|
| 614 |
-
|
| 615 |
-
# ์ ํ๋ ๋ฐ์ค ๋์ค์ (์์ชฝ ๋ ์ด์ด)
|
| 616 |
-
for idx, det in enumerate(detections):
|
| 617 |
-
if idx in selected_indices:
|
| 618 |
-
x1, y1, x2, y2 = det['bbox']
|
| 619 |
-
draw.rectangle([x1, y1, x2, y2], outline="blue", width=28)
|
| 620 |
-
corner_label = f"โ#{idx+1}"
|
| 621 |
-
draw.rectangle([x1-2, y1-24, x1+40, y1-2], fill="blue")
|
| 622 |
-
draw.text((x1, y1 - 22), corner_label, fill="white", font=font_tiny)
|
| 623 |
-
|
| 624 |
-
# ์ํ ๋ฒํผ
|
| 625 |
-
for idx, det in enumerate(detections):
|
| 626 |
-
x1, y1, x2, y2 = det['bbox']
|
| 627 |
-
center_x = (x1 + x2) / 2
|
| 628 |
-
center_y = (y1 + y2) / 2
|
| 629 |
-
|
| 630 |
-
selected = idx in selected_indices
|
| 631 |
-
btn_color = "blue" if selected else "lime"
|
| 632 |
-
btn_text = f"โ{idx+1}" if selected else f"{idx+1}"
|
| 633 |
-
|
| 634 |
-
box_width = x2 - x1
|
| 635 |
-
box_height = y2 - y1
|
| 636 |
-
radius = min(55, box_width * 0.18, box_height * 0.35)
|
| 637 |
-
|
| 638 |
-
# ์ํ ๋ฒํผ
|
| 639 |
-
draw.ellipse(
|
| 640 |
-
[center_x - radius, center_y - radius,
|
| 641 |
-
center_x + radius, center_y + radius],
|
| 642 |
-
fill=btn_color, outline="white", width=4
|
| 643 |
-
)
|
| 644 |
-
draw.text((center_x - radius*0.5, center_y - radius*0.6),
|
| 645 |
-
btn_text, fill="white", font=font_large)
|
| 646 |
-
|
| 647 |
-
return img
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
def labeling_click(image, filename, evt: gr.SelectData):
|
| 651 |
-
"""์ด๋ฏธ์ง ํด๋ฆญ ์ด๋ฒคํธ"""
|
| 652 |
-
if not filename or filename not in current_data['detections']:
|
| 653 |
-
return image, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ๋จผ์ ๋ก๋ํ์ธ์."
|
| 654 |
-
|
| 655 |
-
click_x, click_y = evt.index[0], evt.index[1]
|
| 656 |
-
detections = current_data['detections'][filename]
|
| 657 |
-
selected_indices = set(current_data['selections'].get(filename, []))
|
| 658 |
-
|
| 659 |
-
# ํด๋ฆญํ ๋ฐ์ค ์ฐพ๊ธฐ
|
| 660 |
-
clicked_idx = None
|
| 661 |
-
button_candidates = []
|
| 662 |
-
|
| 663 |
-
# ๋ฒํผ ์์ญ ํ์ธ
|
| 664 |
-
for idx, det in enumerate(detections):
|
| 665 |
-
x1, y1, x2, y2 = det['bbox']
|
| 666 |
-
center_x = (x1 + x2) / 2
|
| 667 |
-
center_y = (y1 + y2) / 2
|
| 668 |
-
|
| 669 |
-
box_width = x2 - x1
|
| 670 |
-
box_height = y2 - y1
|
| 671 |
-
radius = min(55, box_width * 0.18, box_height * 0.35)
|
| 672 |
-
|
| 673 |
-
distance = ((click_x - center_x) ** 2 + (click_y - center_y) ** 2) ** 0.5
|
| 674 |
-
|
| 675 |
-
if distance <= radius:
|
| 676 |
-
button_candidates.append((idx, distance))
|
| 677 |
-
|
| 678 |
-
# ๋ฒํผ ํด๋ฆญ์ด ์์ผ๋ฉด ์ ํ
|
| 679 |
-
if button_candidates:
|
| 680 |
-
button_candidates.sort(key=lambda x: x[1])
|
| 681 |
-
clicked_idx = button_candidates[0][0]
|
| 682 |
-
else:
|
| 683 |
-
# ๋ฐ์ค ์์ญ ํด๋ฆญ ํ์ธ
|
| 684 |
-
for idx, det in enumerate(detections):
|
| 685 |
-
x1, y1, x2, y2 = det['bbox']
|
| 686 |
-
if x1 <= click_x <= x2 and y1 <= click_y <= y2:
|
| 687 |
-
clicked_idx = idx
|
| 688 |
-
break
|
| 689 |
-
|
| 690 |
-
# ์ ํ ํ ๊ธ
|
| 691 |
-
if clicked_idx is not None:
|
| 692 |
-
if clicked_idx in selected_indices:
|
| 693 |
-
selected_indices.remove(clicked_idx)
|
| 694 |
-
print(f"โ ์ ํ ํด์ : ๋ฐ์ค #{clicked_idx+1}")
|
| 695 |
-
else:
|
| 696 |
-
selected_indices.add(clicked_idx)
|
| 697 |
-
print(f"โ
์ ํ: ๋ฐ์ค #{clicked_idx+1}")
|
| 698 |
-
|
| 699 |
-
current_data['selections'][filename] = list(selected_indices)
|
| 700 |
-
|
| 701 |
-
# ์ด๋ฏธ์ง ๋ค์ ๊ทธ๋ฆฌ๊ธฐ
|
| 702 |
-
img_path = current_data['images'][current_data['current_idx']]
|
| 703 |
-
image = Image.open(img_path)
|
| 704 |
-
vis_image = draw_detections(image, detections, list(selected_indices))
|
| 705 |
-
|
| 706 |
-
info = f"โ
๋ฐ์ค #{clicked_idx+1} {'์ ํ' if clicked_idx in selected_indices else 'ํด์ '}"
|
| 707 |
-
return vis_image, info
|
| 708 |
-
|
| 709 |
-
return image, "โ ๋ฐ์ค๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
def save_and_next():
|
| 713 |
-
"""์ ์ฅ ํ ๋ค์"""
|
| 714 |
-
if current_data['current_idx'] >= len(current_data['images']):
|
| 715 |
-
return None, "โ
์๋ฃ!", ""
|
| 716 |
-
|
| 717 |
-
img_path = current_data['images'][current_data['current_idx']]
|
| 718 |
-
filename = os.path.basename(img_path)
|
| 719 |
-
|
| 720 |
-
# GT ์ ์ฅ
|
| 721 |
-
gt = load_existing_ground_truth()
|
| 722 |
-
selected_indices = current_data['selections'].get(filename, [])
|
| 723 |
-
|
| 724 |
-
if selected_indices:
|
| 725 |
-
detections = current_data['detections'][filename]
|
| 726 |
-
gt[filename] = [
|
| 727 |
-
{
|
| 728 |
-
'bbox': detections[i]['bbox'],
|
| 729 |
-
'folder': current_data['folder']
|
| 730 |
-
}
|
| 731 |
-
for i in selected_indices
|
| 732 |
-
]
|
| 733 |
-
save_ground_truth(gt)
|
| 734 |
-
print(f"๐พ ์ ์ฅ: {filename} - {len(selected_indices)}๊ฐ ๋ฐ์ค")
|
| 735 |
-
else:
|
| 736 |
-
print(f"โญ๏ธ ๊ฑด๋๋ฐ๊ธฐ: {filename} - ์ ํ ์์")
|
| 737 |
-
|
| 738 |
-
# ๋ค์ ์ด๋ฏธ์ง
|
| 739 |
-
current_data['current_idx'] += 1
|
| 740 |
-
|
| 741 |
-
# ๋ค์ ๋ฏธ๋ผ๋ฒจ๋ง ์ด๋ฏธ์ง ์ฐพ๊ธฐ
|
| 742 |
-
while current_data['current_idx'] < len(current_data['images']):
|
| 743 |
-
next_filename = os.path.basename(current_data['images'][current_data['current_idx']])
|
| 744 |
-
if next_filename not in current_data['selections']:
|
| 745 |
-
break
|
| 746 |
-
current_data['current_idx'] += 1
|
| 747 |
-
|
| 748 |
-
if current_data['current_idx'] >= len(current_data['images']):
|
| 749 |
-
return None, "โ
๋ชจ๋ ์ด๋ฏธ์ง ๋ผ๋ฒจ๋ง ์๋ฃ!", ""
|
| 750 |
-
|
| 751 |
-
return show_current_image()
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
def skip_image():
|
| 755 |
-
"""๊ฑด๋๋ฐ๊ธฐ"""
|
| 756 |
-
current_data['current_idx'] += 1
|
| 757 |
-
|
| 758 |
-
if current_data['current_idx'] >= len(current_data['images']):
|
| 759 |
-
return None, "โ
์๋ฃ!", ""
|
| 760 |
-
|
| 761 |
-
return show_current_image()
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
# ============================================================
|
| 765 |
-
# ํญ 3: ๊ฐ๋จ ๋ฐ๋ชจ (App Demo)
|
| 766 |
-
# ============================================================
|
| 767 |
-
|
| 768 |
-
def demo_detect(image, confidence_threshold, filter_threshold, model_type, use_filter):
|
| 769 |
-
"""๊ฐ๋จํ ๋ฐ๋ชจ ๊ฒ์ถ"""
|
| 770 |
-
if image is None:
|
| 771 |
-
return None, "์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์."
|
| 772 |
-
|
| 773 |
-
if isinstance(image, np.ndarray):
|
| 774 |
-
image = Image.fromarray(image)
|
| 775 |
-
|
| 776 |
-
# ์ ํ๋ ๋ชจ๋ธ๋ก ๊ฒ์ถ
|
| 777 |
-
all_detections = detect_with_selected_model(image, confidence_threshold, model_type)
|
| 778 |
-
|
| 779 |
-
# ํํฐ ์ ์ฉ ์ฌ๋ถ
|
| 780 |
-
if not use_filter:
|
| 781 |
-
# ํํฐ ๋ฏธ์ฌ์ฉ
|
| 782 |
-
filtered_detections = all_detections
|
| 783 |
-
for det in filtered_detections:
|
| 784 |
-
det['filter_score'] = det['confidence'] * 100
|
| 785 |
-
else:
|
| 786 |
-
# ํํฐ ์ฌ์ฉ
|
| 787 |
-
if model_type in ["Roboflow", "YOLOv8"]:
|
| 788 |
-
# Roboflow & YOLOv8: ์ ๋ขฐ๋ ๊ธฐ๋ฐ ํํฐ
|
| 789 |
-
for det in all_detections:
|
| 790 |
-
det['filter_score'] = det['confidence'] * 100
|
| 791 |
-
filtered_detections = [det for det in all_detections if det['filter_score'] >= filter_threshold]
|
| 792 |
-
else:
|
| 793 |
-
# RT-DETR: Universal Filter
|
| 794 |
-
from test_visual_validation import apply_universal_filter
|
| 795 |
-
filtered_detections = apply_universal_filter(all_detections, image, filter_threshold)
|
| 796 |
-
|
| 797 |
-
# ์๊ฐํ
|
| 798 |
-
result_image = image.copy()
|
| 799 |
-
draw = ImageDraw.Draw(result_image)
|
| 800 |
-
|
| 801 |
-
try:
|
| 802 |
-
font = ImageFont.truetype("arial.ttf", 20)
|
| 803 |
-
font_small = ImageFont.truetype("arial.ttf", 14)
|
| 804 |
-
except:
|
| 805 |
-
font = ImageFont.load_default()
|
| 806 |
-
font_small = ImageFont.load_default()
|
| 807 |
-
|
| 808 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 809 |
-
for i, det in enumerate(filtered_detections, 1):
|
| 810 |
-
x1, y1, x2, y2 = det['bbox']
|
| 811 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=8)
|
| 812 |
-
|
| 813 |
-
score = det['filter_score']
|
| 814 |
-
conf = det['confidence']
|
| 815 |
-
label = f"#{i} | Score:{score:.0f} | Conf:{conf:.2f}"
|
| 816 |
-
|
| 817 |
-
bbox = draw.textbbox((x1, y1-25), label, font=font_small)
|
| 818 |
-
draw.rectangle(bbox, fill="lime")
|
| 819 |
-
draw.text((x1, y1-25), label, fill="black", font=font_small)
|
| 820 |
-
|
| 821 |
-
# ๊ฒฐ๊ณผ ํ
์คํธ
|
| 822 |
-
info = f"""
|
| 823 |
-
๐ **๊ฒ์ถ ๊ฒฐ๊ณผ (๋ชจ๋ธ: {model_type}):**
|
| 824 |
-
โข ์ ์ฒด ๊ฒ์ถ: {len(all_detections)}๊ฐ
|
| 825 |
-
โข ํํฐ ํต๊ณผ: {len(filtered_detections)}๊ฐ
|
| 826 |
-
โข ์ ๊ฑฐ๋จ: {len(all_detections) - len(filtered_detections)}๊ฐ
|
| 827 |
-
|
| 828 |
-
โ๏ธ **์ค์ :**
|
| 829 |
-
โข {model_type} Confidence: {confidence_threshold}
|
| 830 |
-
โข Filter Threshold: {filter_threshold}
|
| 831 |
-
|
| 832 |
-
๐ฏ **์ฑ๋ฅ (50๊ฐ GT ๊ธฐ์ค, RT-DETR):**
|
| 833 |
-
โข Precision: 44.2%
|
| 834 |
-
โข Recall: 94.0%
|
| 835 |
-
โข F1 Score: 56.1%
|
| 836 |
-
"""
|
| 837 |
-
|
| 838 |
-
if len(filtered_detections) > 0:
|
| 839 |
-
info += f"\nโ
{len(filtered_detections)}๊ฐ์ ์์ฐ๋ฅผ ๊ฒ์ถํ์ต๋๋ค!"
|
| 840 |
-
else:
|
| 841 |
-
info += "\nโ ๏ธ ์์ฐ๊ฐ ๊ฒ์ถ๋์ง ์์์ต๋๋ค. Threshold๋ฅผ ๋ฎ์ถฐ๋ณด์ธ์."
|
| 842 |
-
|
| 843 |
-
return result_image, info
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
# ============================================================
|
| 847 |
-
# Gradio ์ธํฐํ์ด์ค - 3๊ฐ ํญ์ผ๋ก ํตํฉ
|
| 848 |
-
# ============================================================
|
| 849 |
-
|
| 850 |
-
with gr.Blocks(title="๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
", theme=gr.themes.Soft()) as demo:
|
| 851 |
-
|
| 852 |
-
gr.Markdown("""
|
| 853 |
-
# ๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
|
| 854 |
-
|
| 855 |
-
**3๊ฐ์ง ๋ชจ๋ธ๋ก ์์ฐ๋ฅผ ์ ํํ๊ฒ ๊ฒ์ถํ์ธ์**
|
| 856 |
-
|
| 857 |
-
---
|
| 858 |
-
""")
|
| 859 |
-
|
| 860 |
-
# ==================== ์ต์๋จ: ๋ชจ๋ธ ์ ํ ====================
|
| 861 |
-
with gr.Row():
|
| 862 |
-
with gr.Column(scale=3):
|
| 863 |
-
model_selector = gr.Radio(
|
| 864 |
-
choices=["RT-DETR", "VIDraft/Shrimp", "YOLOv8"],
|
| 865 |
-
value="YOLOv8",
|
| 866 |
-
label="๐ค ๊ฒ์ถ ๋ชจ๋ธ ์ ํ",
|
| 867 |
-
info="๋ชจ๋ ํญ์ ์ ์ฉ๋ฉ๋๋ค"
|
| 868 |
-
)
|
| 869 |
-
with gr.Column(scale=1):
|
| 870 |
-
load_rtdetr_btn = gr.Button("๐ RT-DETR ๋ก๋", size="sm", variant="secondary")
|
| 871 |
-
rtdetr_status = gr.Textbox(label="๋ชจ๋ธ ์ํ", value="โธ๏ธ RT-DETR ๋ฏธ๋ก๋ (VIDraft/Shrimp ํด๋ผ์ฐ๋ ๋ชจ๋ธ ์ฌ์ฉ ๊ฐ๋ฅ)", interactive=False, lines=1)
|
| 872 |
-
|
| 873 |
-
# RT-DETR ๋ก๋ฉ ๋ฒํผ ์ด๋ฒคํธ
|
| 874 |
-
load_rtdetr_btn.click(
|
| 875 |
-
load_rtdetr_on_demand,
|
| 876 |
-
inputs=[],
|
| 877 |
-
outputs=[rtdetr_status]
|
| 878 |
-
)
|
| 879 |
-
|
| 880 |
-
gr.Markdown("---")
|
| 881 |
-
|
| 882 |
-
with gr.Tabs():
|
| 883 |
-
# ==================== ํญ 1: ์๋ ๊ฒ์ถ ====================
|
| 884 |
-
with gr.TabItem("๐ค ์๋ ๊ฒ์ถ & ๊ฒ์ฆ"):
|
| 885 |
-
gr.Markdown("""
|
| 886 |
-
### ์ค์๊ฐ์ผ๋ก ํ๋ผ๋ฏธํฐ๋ฅผ ์กฐ์ ํ๋ฉฐ ๊ฒ์ถ ๊ฒฐ๊ณผ๋ฅผ ํ์ธ
|
| 887 |
-
์ต์ ํ๋ ํ๋ผ๋ฏธํฐ๋ก ์์ฐ ๊ฒ์ถ์ ํ
์คํธํ์ธ์.
|
| 888 |
-
""")
|
| 889 |
-
|
| 890 |
-
with gr.Row():
|
| 891 |
-
with gr.Column():
|
| 892 |
-
input_image_detect = gr.Image(label="์
๋ ฅ ์ด๋ฏธ์ง", type="pil")
|
| 893 |
-
|
| 894 |
-
confidence_slider_detect = gr.Slider(
|
| 895 |
-
0.01, 1.0, 0.1,
|
| 896 |
-
step=0.01,
|
| 897 |
-
label="์ ๋ขฐ๋ ์๊ณ๊ฐ",
|
| 898 |
-
info="RT-DETR: 0.065 | VIDraft/Shrimp: 0.3~0.5 | YOLOv8: 0.1~0.3 ๊ถ์ฅ"
|
| 899 |
-
)
|
| 900 |
-
|
| 901 |
-
use_filter_check = gr.Checkbox(
|
| 902 |
-
label="๐ ํํฐ ์ ์ ์๊ณ๊ฐ ์ฌ์ฉ",
|
| 903 |
-
value=False,
|
| 904 |
-
info="์ฒดํฌํ๋ฉด ํํฐ ์ ์ ๊ธฐ์ค์ผ๋ก ์ถ๊ฐ ํํฐ๋ง"
|
| 905 |
-
)
|
| 906 |
-
|
| 907 |
-
filter_slider_detect = gr.Slider(
|
| 908 |
-
0, 100, 90,
|
| 909 |
-
step=5,
|
| 910 |
-
label="ํํฐ ์ ์ ์๊ณ๊ฐ",
|
| 911 |
-
info="RT-DETR: Universal Filter | VIDraft/Shrimp: ์ ๋ขฐ๋ ๊ธฐ๋ฐ",
|
| 912 |
-
visible=True
|
| 913 |
-
)
|
| 914 |
-
|
| 915 |
-
show_all_check = gr.Checkbox(
|
| 916 |
-
label="์ ์ฒด ๊ฒ์ถ ๊ฒฐ๊ณผ ํ์ (ํ์)",
|
| 917 |
-
value=False
|
| 918 |
-
)
|
| 919 |
-
|
| 920 |
-
detect_btn = gr.Button("๐ ๊ฒ์ถ ์คํ", variant="primary", size="lg")
|
| 921 |
-
|
| 922 |
-
# ์์ ์ด๋ฏธ์ง (๊ฒฐ๊ณผ ํ์ผ ์ ์ธ)
|
| 923 |
-
example_images = [
|
| 924 |
-
"data/yolo_dataset/images/train/250818_01.jpg",
|
| 925 |
-
"data/yolo_dataset/images/train/250818_03.jpg",
|
| 926 |
-
"data/yolo_dataset/images/train/250818_04.jpg",
|
| 927 |
-
"data/yolo_dataset/images/train/250818_05.jpg",
|
| 928 |
-
"data/yolo_dataset/images/train/250818_10.jpg",
|
| 929 |
-
]
|
| 930 |
-
|
| 931 |
-
# ํ์ผ์ด ์กด์ฌํ๋ ๊ฒ๋ง ํํฐ๋ง
|
| 932 |
-
example_images = [img for img in example_images if os.path.exists(img)]
|
| 933 |
-
|
| 934 |
-
if example_images:
|
| 935 |
-
gr.Examples(
|
| 936 |
-
examples=[[img] for img in example_images],
|
| 937 |
-
inputs=[input_image_detect],
|
| 938 |
-
label="๐ท ์์ ์ด๋ฏธ์ง"
|
| 939 |
-
)
|
| 940 |
-
|
| 941 |
-
with gr.Column():
|
| 942 |
-
output_image_detect = gr.Image(label="๊ฒ์ถ ๊ฒฐ๊ณผ")
|
| 943 |
-
output_info_detect = gr.Markdown()
|
| 944 |
-
|
| 945 |
-
detect_btn.click(
|
| 946 |
-
interactive_detect,
|
| 947 |
-
[input_image_detect, confidence_slider_detect, filter_slider_detect, show_all_check, model_selector, use_filter_check],
|
| 948 |
-
[output_image_detect, output_info_detect]
|
| 949 |
-
)
|
| 950 |
-
|
| 951 |
-
# ํํฐ ์ฌ์ฉ ์ฒดํฌ๋ฐ์ค์ ๋ฐ๋ผ ํํฐ ์ฌ๋ผ์ด๋ ํ์ฑํ/๋นํ์ฑํ
|
| 952 |
-
def update_filter_interactivity(use_filter):
|
| 953 |
-
return gr.update(interactive=use_filter)
|
| 954 |
-
|
| 955 |
-
use_filter_check.change(
|
| 956 |
-
update_filter_interactivity,
|
| 957 |
-
inputs=[use_filter_check],
|
| 958 |
-
outputs=[filter_slider_detect]
|
| 959 |
-
)
|
| 960 |
-
|
| 961 |
-
gr.Markdown("""
|
| 962 |
-
### ๐ก ์ฌ์ฉ ํ
|
| 963 |
-
- ๋ชจ๋ธ์ ์ ํํ๊ณ ์ ๋ขฐ๋๋ฅผ ์กฐ์ ํ์ฌ ๊ฒ์ถ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ์ธ์
|
| 964 |
-
- ๊ฒ์ถ์ด ์ ์ ๋๋ ์ ๋ขฐ๋๋ฅผ ๋ฎ์ถ๊ณ , ์ค๊ฒ์ถ์ด ๋ง์ ๋๋ ๋์ด์ธ์
|
| 965 |
-
- ํํฐ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๏ฟฝ๏ฟฝ๋ค
|
| 966 |
-
|
| 967 |
-
**๋ฐ์ค ์์:** ๐ข ๋
น์(๋์ ํ๋ฅ ) | ๐ก ๋
ธ๋์(์ค๊ฐ ํ๋ฅ ) | ๐ ์ฃผํฉ์(๋ฎ์ ํ๋ฅ ) | ๐ด ๋นจ๊ฐ์(์ ๊ฑฐ๋จ)
|
| 968 |
-
""")
|
| 969 |
-
|
| 970 |
-
# ==================== ํญ 2: ๋ผ๋ฒจ๋ง ๋๊ตฌ ====================
|
| 971 |
-
with gr.TabItem("๐ Ground Truth ๋ผ๋ฒจ๋ง"):
|
| 972 |
-
gr.Markdown("""
|
| 973 |
-
### ์ ํ๋ ๋ชจ๋ธ์ ๊ฒ์ถ ๊ฒฐ๊ณผ์์ ์ฌ๋ฐ๋ฅธ ๋ฐ์ค๋ง ์ ํํ์ฌ ๋ผ๋ฒจ๋ง
|
| 974 |
-
์ด๋ฏธ์ง๋ฅผ ํด๋ฆญํ์ฌ ์์ฐ ๋ฐ์ค๋ฅผ ์ ํ/ํด์ ํ์ธ์.
|
| 975 |
-
""")
|
| 976 |
-
|
| 977 |
-
with gr.Row():
|
| 978 |
-
with gr.Column(scale=1):
|
| 979 |
-
folder_dropdown = gr.Dropdown(
|
| 980 |
-
choices=get_folders(),
|
| 981 |
-
label="๐ ํด๋ ์ ํ",
|
| 982 |
-
info="๋ผ๋ฒจ๋งํ ํด๋๋ฅผ ์ ํํ์ธ์"
|
| 983 |
-
)
|
| 984 |
-
|
| 985 |
-
conf_slider_label = gr.Slider(
|
| 986 |
-
0.01, 0.5, 0.2,
|
| 987 |
-
step=0.05,
|
| 988 |
-
label="์ ๋ขฐ๋",
|
| 989 |
-
info="๊ฒ์ถ ๋ฏผ๊ฐ๋ ์กฐ์ "
|
| 990 |
-
)
|
| 991 |
-
|
| 992 |
-
start_btn = gr.Button("โถ๏ธ ๋ผ๋ฒจ๋ง ์์", variant="primary", size="lg")
|
| 993 |
-
|
| 994 |
-
gr.Markdown("---")
|
| 995 |
-
|
| 996 |
-
next_btn = gr.Button("โญ๏ธ ์ ์ฅ & ๋ค์", variant="secondary", size="lg")
|
| 997 |
-
skip_btn = gr.Button("โฉ ๊ฑด๋๋ฐ๊ธฐ", size="lg")
|
| 998 |
-
|
| 999 |
-
labeling_info = gr.Markdown("ํด๋๋ฅผ ์ ํํ๊ณ '๋ผ๋ฒจ๋ง ์์'์ ํด๋ฆญํ์ธ์.")
|
| 1000 |
-
|
| 1001 |
-
with gr.Column(scale=2):
|
| 1002 |
-
labeling_image = gr.Image(
|
| 1003 |
-
label="๐ฑ๏ธ ํด๋ฆญํ์ฌ ๋ฐ์ค ์ ํ/ํด์ ",
|
| 1004 |
-
type="pil",
|
| 1005 |
-
interactive=True
|
| 1006 |
-
)
|
| 1007 |
-
|
| 1008 |
-
labeling_filename = gr.Textbox(visible=False)
|
| 1009 |
-
click_info = gr.Markdown()
|
| 1010 |
-
|
| 1011 |
-
# ์ด๋ฒคํธ ํธ๋ค๋ฌ
|
| 1012 |
-
start_btn.click(
|
| 1013 |
-
start_labeling,
|
| 1014 |
-
[folder_dropdown, conf_slider_label, model_selector],
|
| 1015 |
-
[labeling_image, labeling_info, labeling_filename]
|
| 1016 |
-
)
|
| 1017 |
-
|
| 1018 |
-
labeling_image.select(
|
| 1019 |
-
labeling_click,
|
| 1020 |
-
[labeling_image, labeling_filename],
|
| 1021 |
-
[labeling_image, click_info]
|
| 1022 |
-
)
|
| 1023 |
-
|
| 1024 |
-
next_btn.click(
|
| 1025 |
-
save_and_next,
|
| 1026 |
-
[],
|
| 1027 |
-
[labeling_image, labeling_info, labeling_filename]
|
| 1028 |
-
)
|
| 1029 |
-
|
| 1030 |
-
skip_btn.click(
|
| 1031 |
-
skip_image,
|
| 1032 |
-
[],
|
| 1033 |
-
[labeling_image, labeling_info, labeling_filename]
|
| 1034 |
-
)
|
| 1035 |
-
|
| 1036 |
-
gr.Markdown("""
|
| 1037 |
-
### ๐ฑ๏ธ ์ฌ์ฉ ๋ฐฉ๋ฒ
|
| 1038 |
-
1. **๋ชจ๋ธ ์ ํ** (์ต์๋จ์์ ์ ํ)
|
| 1039 |
-
2. ํด๋ ์ ํ ํ "๋ผ๋ฒจ๋ง ์์"
|
| 1040 |
-
3. ์ด๋ฏธ์ง์์ **์ํ ๋ฒํผ ํด๋ฆญ** ๋๋ **๋ฐ์ค ์์ญ ํด๋ฆญ**์ผ๋ก ์ ํ/ํด์
|
| 1041 |
-
4. "์ ์ฅ & ๋ค์"์ผ๋ก ๋ค์ ์ด๋ฏธ์ง๋ก ์ด๋ (์๋ ์ ์ฅ)
|
| 1042 |
-
5. "๊ฑด๋๋ฐ๊ธฐ"๋ก ์ ํ ์์ด ๋ค์ ์ด๋ฏธ์ง๋ก
|
| 1043 |
-
|
| 1044 |
-
**๐พ ์ ์ฅ ์์น:** `ground_truth.json` (์๋ ๋ฐฑ์
: `backups/`)
|
| 1045 |
-
""")
|
| 1046 |
-
|
| 1047 |
-
# ==================== ํญ 3: ๊ฐ๋จ ๋ฐ๋ชจ ====================
|
| 1048 |
-
with gr.TabItem("๐ฏ ๊ฐ๋จ ๋ฐ๋ชจ"):
|
| 1049 |
-
gr.Markdown("""
|
| 1050 |
-
### ๋น ๋ฅด๊ณ ๊ฐ๋จํ ์์ฐ ๊ฒ์ถ ๋ฐ๋ชจ
|
| 1051 |
-
์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ๊ณ ๋ฐ๋ก ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ์ธ์.
|
| 1052 |
-
""")
|
| 1053 |
-
|
| 1054 |
-
with gr.Row():
|
| 1055 |
-
with gr.Column():
|
| 1056 |
-
input_image_demo = gr.Image(label="์
๋ ฅ ์ด๋ฏธ์ง", type="pil")
|
| 1057 |
-
|
| 1058 |
-
confidence_slider_demo = gr.Slider(
|
| 1059 |
-
0.01, 1.0, 0.1,
|
| 1060 |
-
step=0.01,
|
| 1061 |
-
label="์ ๋ขฐ๋",
|
| 1062 |
-
info="RT-DETR: 0.065 | VIDraft/Shrimp: 0.3~0.5 | YOLOv8: 0.1~0.3 ๊ถ์ฅ"
|
| 1063 |
-
)
|
| 1064 |
-
|
| 1065 |
-
use_filter_demo = gr.Checkbox(
|
| 1066 |
-
label="๐ ํํฐ ์ ์ ์๊ณ๊ฐ ์ฌ์ฉ",
|
| 1067 |
-
value=False,
|
| 1068 |
-
info="์ฒดํฌํ๋ฉด ํํฐ ์ ์ ๊ธฐ์ค์ผ๋ก ์ถ๊ฐ ํํฐ๋ง"
|
| 1069 |
-
)
|
| 1070 |
-
|
| 1071 |
-
filter_slider_demo = gr.Slider(
|
| 1072 |
-
0, 100, 90,
|
| 1073 |
-
step=5,
|
| 1074 |
-
label="ํํฐ ์๊ณ๊ฐ",
|
| 1075 |
-
info="RT-DETR: Universal Filter | VIDraft/Shrimp: ์ ๋ขฐ๋ ๊ธฐ๋ฐ",
|
| 1076 |
-
visible=True
|
| 1077 |
-
)
|
| 1078 |
-
|
| 1079 |
-
demo_detect_btn = gr.Button("๐ ๊ฒ์ถ", variant="primary", size="lg")
|
| 1080 |
-
|
| 1081 |
-
# ์์ ์ด๋ฏธ์ง
|
| 1082 |
-
example_images_demo = [
|
| 1083 |
-
"data/yolo_dataset/images/train/250818_01.jpg",
|
| 1084 |
-
"data/yolo_dataset/images/train/250818_03.jpg",
|
| 1085 |
-
"data/yolo_dataset/images/train/250818_04.jpg",
|
| 1086 |
-
"data/yolo_dataset/images/train/250818_05.jpg",
|
| 1087 |
-
"data/yolo_dataset/images/train/250818_10.jpg",
|
| 1088 |
-
]
|
| 1089 |
-
|
| 1090 |
-
# ํ์ผ์ด ์กด์ฌํ๋ ๊ฒ๋ง ํํฐ๋ง
|
| 1091 |
-
example_images_demo = [img for img in example_images_demo if os.path.exists(img)]
|
| 1092 |
-
|
| 1093 |
-
if example_images_demo:
|
| 1094 |
-
gr.Examples(
|
| 1095 |
-
examples=[[img] for img in example_images_demo],
|
| 1096 |
-
inputs=[input_image_demo],
|
| 1097 |
-
label="๐ท ์์ ์ด๋ฏธ์ง"
|
| 1098 |
-
)
|
| 1099 |
-
|
| 1100 |
-
with gr.Column():
|
| 1101 |
-
output_image_demo = gr.Image(label="๊ฒ์ถ ๊ฒฐ๊ณผ")
|
| 1102 |
-
output_info_demo = gr.Markdown()
|
| 1103 |
-
|
| 1104 |
-
demo_detect_btn.click(
|
| 1105 |
-
demo_detect,
|
| 1106 |
-
[input_image_demo, confidence_slider_demo, filter_slider_demo, model_selector, use_filter_demo],
|
| 1107 |
-
[output_image_demo, output_info_demo]
|
| 1108 |
-
)
|
| 1109 |
-
|
| 1110 |
-
# ํํฐ ์ฌ์ฉ ์ฒดํฌ๋ฐ์ค์ ๋ฐ๋ผ ํํฐ ์ฌ๋ผ์ด๋ ํ์ฑํ/๋นํ์ฑํ
|
| 1111 |
-
use_filter_demo.change(
|
| 1112 |
-
lambda x: gr.update(interactive=x),
|
| 1113 |
-
inputs=[use_filter_demo],
|
| 1114 |
-
outputs=[filter_slider_demo]
|
| 1115 |
-
)
|
| 1116 |
-
|
| 1117 |
-
gr.Markdown("""
|
| 1118 |
-
### ๐ก ๋น ๋ฅด๊ณ ๊ฐ๋จํ ๊ฒ์ถ
|
| 1119 |
-
์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ๊ฑฐ๋ ์์ ์ด๋ฏธ์ง๋ฅผ ์ ํํ์ฌ ๋ฐ๋ก ๊ฒ์ถ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ์ธ์.
|
| 1120 |
-
""")
|
| 1121 |
-
|
| 1122 |
-
gr.Markdown("""
|
| 1123 |
-
---
|
| 1124 |
-
|
| 1125 |
-
### ๐ค ๋ชจ๋ธ ์ค๋ช
|
| 1126 |
-
- **RT-DETR**: ๋ก์ปฌ ๋ชจ๋ธ, ๋น ๋ฅธ ์ถ๋ก ์๋, ์คํ๋ผ์ธ ์ฌ์ฉ ๊ฐ๋ฅ
|
| 1127 |
-
- **VIDraft/Shrimp**: ํด๋ผ์ฐ๋ ๋ชจ๋ธ, ์ธํฐ๋ท ์ฐ๊ฒฐ ํ์
|
| 1128 |
-
- **YOLOv8**: ๋ก์ปฌ ์ปค์คํ
ํ์ต ๋ชจ๋ธ, ๋น ๋ฅธ ์ถ๋ก ์๋
|
| 1129 |
-
|
| 1130 |
-
---
|
| 1131 |
-
|
| 1132 |
-
ยฉ 2025 VIDraft. All rights reserved.
|
| 1133 |
-
""")
|
| 1134 |
-
|
| 1135 |
-
if __name__ == "__main__":
|
| 1136 |
-
print("\n" + "="*60)
|
| 1137 |
-
print("๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
v2.1 ์์")
|
| 1138 |
-
print("="*60)
|
| 1139 |
-
print("๐ค ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ธ:")
|
| 1140 |
-
print(" 1. RT-DETR (๋ก์ปฌ)")
|
| 1141 |
-
print(" 2. VIDraft/Shrimp (ํด๋ผ์ฐ๋)")
|
| 1142 |
-
print(" 3. YOLOv8 (๋ก์ปฌ ํ์ต) โญ ๊ธฐ๋ณธ๊ฐ")
|
| 1143 |
-
print(f"\n๐ฆ YOLOv8 ๋ชจ๋ธ: {YOLO_MODEL_PATH}")
|
| 1144 |
-
print("="*60)
|
| 1145 |
-
|
| 1146 |
-
demo.launch(
|
| 1147 |
-
server_name="0.0.0.0",
|
| 1148 |
-
server_port=None, # ์๋์ผ๋ก ๋น ํฌํธ ์ฐพ๊ธฐ
|
| 1149 |
-
share=False
|
| 1150 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,205 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Roboflow ๋ชจ๋ธ 10๊ฐ ์ด๋ฏธ์ง ํ
์คํธ
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
import requests
|
| 9 |
-
import base64
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
from io import BytesIO
|
| 12 |
-
import json
|
| 13 |
-
import glob
|
| 14 |
-
import os
|
| 15 |
-
from datetime import datetime
|
| 16 |
-
|
| 17 |
-
def test_image(image_path):
|
| 18 |
-
"""๋จ์ผ ์ด๋ฏธ์ง ํ
์คํธ"""
|
| 19 |
-
# ์๋ณธ ์ด๋ฏธ์ง ๋ก๋
|
| 20 |
-
image_original = Image.open(image_path)
|
| 21 |
-
original_size = image_original.size
|
| 22 |
-
|
| 23 |
-
# ๋ฆฌ์ฌ์ด์ฆ (API ์ ์ก์ฉ)
|
| 24 |
-
image_resized = image_original.copy()
|
| 25 |
-
image_resized.thumbnail((640, 640), Image.Resampling.LANCZOS)
|
| 26 |
-
|
| 27 |
-
# Base64 ์ธ์ฝ๋ฉ
|
| 28 |
-
buffered = BytesIO()
|
| 29 |
-
image_resized.save(buffered, format="JPEG", quality=80)
|
| 30 |
-
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 31 |
-
|
| 32 |
-
# API ํธ์ถ
|
| 33 |
-
response = requests.post(
|
| 34 |
-
'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6',
|
| 35 |
-
headers={'Content-Type': 'application/json'},
|
| 36 |
-
json={
|
| 37 |
-
'api_key': 'azcIL8KDJVJMYrsERzI7',
|
| 38 |
-
'inputs': {
|
| 39 |
-
'image': {'type': 'base64', 'value': img_base64}
|
| 40 |
-
}
|
| 41 |
-
},
|
| 42 |
-
timeout=30
|
| 43 |
-
)
|
| 44 |
-
|
| 45 |
-
if response.status_code != 200:
|
| 46 |
-
return None
|
| 47 |
-
|
| 48 |
-
result = response.json()
|
| 49 |
-
|
| 50 |
-
# predictions ์ถ์ถ
|
| 51 |
-
predictions = []
|
| 52 |
-
if 'outputs' in result and len(result['outputs']) > 0:
|
| 53 |
-
output = result['outputs'][0]
|
| 54 |
-
if 'predictions' in output:
|
| 55 |
-
pred_data = output['predictions']
|
| 56 |
-
if isinstance(pred_data, dict) and 'predictions' in pred_data:
|
| 57 |
-
predictions = pred_data['predictions']
|
| 58 |
-
|
| 59 |
-
# ์ค์ผ์ผ ๊ณ์ฐ
|
| 60 |
-
scale_x = original_size[0] / image_resized.size[0]
|
| 61 |
-
scale_y = original_size[1] / image_resized.size[1]
|
| 62 |
-
|
| 63 |
-
# shrimp๋ง ํํฐ๋ง
|
| 64 |
-
shrimp_predictions = [p for p in predictions if p.get('class') == 'shrimp']
|
| 65 |
-
|
| 66 |
-
return {
|
| 67 |
-
'original': image_original,
|
| 68 |
-
'predictions': shrimp_predictions,
|
| 69 |
-
'scale_x': scale_x,
|
| 70 |
-
'scale_y': scale_y
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
def draw_result(image, predictions, scale_x, scale_y):
|
| 74 |
-
"""๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ"""
|
| 75 |
-
draw = ImageDraw.Draw(image)
|
| 76 |
-
|
| 77 |
-
try:
|
| 78 |
-
font = ImageFont.truetype("arial.ttf", 50)
|
| 79 |
-
except:
|
| 80 |
-
font = ImageFont.load_default()
|
| 81 |
-
|
| 82 |
-
for i, pred in enumerate(predictions, 1):
|
| 83 |
-
conf = pred.get('confidence', 0)
|
| 84 |
-
x = pred.get('x', 0) * scale_x
|
| 85 |
-
y = pred.get('y', 0) * scale_y
|
| 86 |
-
w = pred.get('width', 0) * scale_x
|
| 87 |
-
h = pred.get('height', 0) * scale_y
|
| 88 |
-
|
| 89 |
-
# ๋ฐ์ค ์ขํ
|
| 90 |
-
x1 = x - w / 2
|
| 91 |
-
y1 = y - h / 2
|
| 92 |
-
x2 = x + w / 2
|
| 93 |
-
y2 = y + h / 2
|
| 94 |
-
|
| 95 |
-
# ์ ๋ขฐ๋๋ณ ์์
|
| 96 |
-
if conf >= 0.5:
|
| 97 |
-
color = 'lime'
|
| 98 |
-
elif conf >= 0.3:
|
| 99 |
-
color = 'yellow'
|
| 100 |
-
else:
|
| 101 |
-
color = 'orange'
|
| 102 |
-
|
| 103 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 104 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=8)
|
| 105 |
-
|
| 106 |
-
# ํ
์คํธ
|
| 107 |
-
text = f"#{i} {conf:.1%}"
|
| 108 |
-
text_bbox = draw.textbbox((x1, y1-60), text, font=font)
|
| 109 |
-
draw.rectangle(text_bbox, fill=color)
|
| 110 |
-
draw.text((x1, y1-60), text, fill='black', font=font)
|
| 111 |
-
|
| 112 |
-
return image
|
| 113 |
-
|
| 114 |
-
def main():
|
| 115 |
-
print("="*60)
|
| 116 |
-
print("๐ฆ Roboflow ๋ชจ๋ธ 10๊ฐ ์ด๋ฏธ์ง ํ
์คํธ")
|
| 117 |
-
print("="*60)
|
| 118 |
-
|
| 119 |
-
# ํ
์คํธ ์ด๋ฏธ์ง ์ ํ
|
| 120 |
-
image_dir = "data/yolo_dataset/images/train"
|
| 121 |
-
all_images = sorted(glob.glob(os.path.join(image_dir, "*.jpg")))
|
| 122 |
-
|
| 123 |
-
# roboflow_result๊ฐ ์๋ ์๋ณธ ์ด๋ฏธ์ง๋ง ์ ํ
|
| 124 |
-
test_images = [img for img in all_images if 'roboflow_result' not in img][:10]
|
| 125 |
-
|
| 126 |
-
if len(test_images) < 10:
|
| 127 |
-
print(f"โ ๏ธ ์ด๋ฏธ์ง ๋ถ์กฑ: {len(test_images)}๊ฐ๋ง ๋ฐ๊ฒฌ")
|
| 128 |
-
test_images = test_images[:len(test_images)]
|
| 129 |
-
|
| 130 |
-
print(f"\n๐ ์ด๋ฏธ์ง ๊ฒฝ๋ก: {image_dir}")
|
| 131 |
-
print(f"๐ ํ
์คํธ ์ด๋ฏธ์ง ์: {len(test_images)}๊ฐ\n")
|
| 132 |
-
|
| 133 |
-
# ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ ์์ฑ
|
| 134 |
-
output_dir = "test_results_10"
|
| 135 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 136 |
-
|
| 137 |
-
results_summary = []
|
| 138 |
-
|
| 139 |
-
for idx, img_path in enumerate(test_images, 1):
|
| 140 |
-
img_name = os.path.basename(img_path)
|
| 141 |
-
print(f"[{idx}/{len(test_images)}] {img_name} ์ฒ๋ฆฌ ์ค...", end=" ")
|
| 142 |
-
|
| 143 |
-
try:
|
| 144 |
-
# ํ
์คํธ
|
| 145 |
-
result = test_image(img_path)
|
| 146 |
-
|
| 147 |
-
if result is None:
|
| 148 |
-
print("โ API ์ค๋ฅ")
|
| 149 |
-
continue
|
| 150 |
-
|
| 151 |
-
predictions = result['predictions']
|
| 152 |
-
shrimp_count = len(predictions)
|
| 153 |
-
|
| 154 |
-
# ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ
|
| 155 |
-
image_with_boxes = draw_result(
|
| 156 |
-
result['original'],
|
| 157 |
-
predictions,
|
| 158 |
-
result['scale_x'],
|
| 159 |
-
result['scale_y']
|
| 160 |
-
)
|
| 161 |
-
|
| 162 |
-
# ์ ์ฅ
|
| 163 |
-
output_filename = img_name.replace('.jpg', '_roboflow_result.jpg')
|
| 164 |
-
output_path = os.path.join(output_dir, output_filename)
|
| 165 |
-
image_with_boxes.save(output_path, quality=95)
|
| 166 |
-
|
| 167 |
-
print(f"โ
shrimp {shrimp_count}๊ฐ")
|
| 168 |
-
|
| 169 |
-
results_summary.append({
|
| 170 |
-
'image': img_name,
|
| 171 |
-
'shrimp_count': shrimp_count,
|
| 172 |
-
'output': output_path,
|
| 173 |
-
'confidences': [p.get('confidence', 0) for p in predictions]
|
| 174 |
-
})
|
| 175 |
-
|
| 176 |
-
except Exception as e:
|
| 177 |
-
print(f"โ ์ค๋ฅ: {str(e)}")
|
| 178 |
-
|
| 179 |
-
# ์์ฝ
|
| 180 |
-
print(f"\n{'='*60}")
|
| 181 |
-
print("๐ ํ
์คํธ ์์ฝ")
|
| 182 |
-
print(f"{'='*60}\n")
|
| 183 |
-
|
| 184 |
-
total_shrimp = sum(r['shrimp_count'] for r in results_summary)
|
| 185 |
-
avg_shrimp = total_shrimp / len(results_summary) if results_summary else 0
|
| 186 |
-
|
| 187 |
-
print(f"์ด ์ฒ๋ฆฌ ์ด๋ฏธ์ง: {len(results_summary)}๊ฐ")
|
| 188 |
-
print(f"์ด shrimp ๊ฒ์ถ: {total_shrimp}๊ฐ")
|
| 189 |
-
print(f"ํ๊ท : {avg_shrimp:.1f}๊ฐ/์ด๋ฏธ์ง\n")
|
| 190 |
-
|
| 191 |
-
print("์ด๋ฏธ์ง๋ณ ๊ฒฐ๊ณผ:")
|
| 192 |
-
for r in results_summary:
|
| 193 |
-
avg_conf = sum(r['confidences']) / len(r['confidences']) if r['confidences'] else 0
|
| 194 |
-
print(f" {r['image']}: {r['shrimp_count']}๊ฐ (ํ๊ท ์ ๋ขฐ๋: {avg_conf:.1%})")
|
| 195 |
-
|
| 196 |
-
print(f"\nโ
์๋ฃ! ๊ฒฐ๊ณผ๋ {output_dir}/ ํด๋์ ์ ์ฅ๋์์ต๋๋ค.")
|
| 197 |
-
|
| 198 |
-
# JSON ์ ์ฅ
|
| 199 |
-
json_path = os.path.join(output_dir, f"test_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
|
| 200 |
-
with open(json_path, 'w', encoding='utf-8') as f:
|
| 201 |
-
json.dump(results_summary, f, indent=2, ensure_ascii=False)
|
| 202 |
-
print(f"๐ JSON ์ ์ฅ: {json_path}")
|
| 203 |
-
|
| 204 |
-
if __name__ == "__main__":
|
| 205 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,82 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
๊ฐ๋จํ Roboflow API ํ
์คํธ (requests ์ฌ์ฉ)
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
import requests
|
| 9 |
-
import base64
|
| 10 |
-
from PIL import Image
|
| 11 |
-
from io import BytesIO
|
| 12 |
-
import json
|
| 13 |
-
|
| 14 |
-
# ํ
์คํธ ์ด๋ฏธ์ง
|
| 15 |
-
test_image = "data/yolo_dataset/images/train/250818_01.jpg"
|
| 16 |
-
|
| 17 |
-
print("="*60)
|
| 18 |
-
print("๐ Roboflow API ํ
์คํธ (requests)")
|
| 19 |
-
print("="*60)
|
| 20 |
-
|
| 21 |
-
# ์ด๋ฏธ์ง๋ฅผ base64๋ก ์ธ์ฝ๋ฉ
|
| 22 |
-
image = Image.open(test_image)
|
| 23 |
-
print(f"๐ธ ์ด๋ฏธ์ง: {test_image}")
|
| 24 |
-
print(f"๐ผ๏ธ ํฌ๊ธฐ: {image.size}")
|
| 25 |
-
|
| 26 |
-
# ๋ฆฌ์ฌ์ด์ฆ
|
| 27 |
-
image.thumbnail((640, 640), Image.Resampling.LANCZOS)
|
| 28 |
-
print(f"๐ ๋ฆฌ์ฌ์ด์ฆ: {image.size}")
|
| 29 |
-
|
| 30 |
-
buffered = BytesIO()
|
| 31 |
-
image.save(buffered, format="JPEG", quality=80)
|
| 32 |
-
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 33 |
-
|
| 34 |
-
print(f"๐ฆ Base64 ํฌ๊ธฐ: {len(img_base64)} bytes")
|
| 35 |
-
|
| 36 |
-
# API ์์ฒญ
|
| 37 |
-
print(f"\n๐ API ํธ์ถ ์ค...")
|
| 38 |
-
|
| 39 |
-
try:
|
| 40 |
-
response = requests.post(
|
| 41 |
-
'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6',
|
| 42 |
-
headers={'Content-Type': 'application/json'},
|
| 43 |
-
json={
|
| 44 |
-
'api_key': 'azcIL8KDJVJMYrsERzI7',
|
| 45 |
-
'inputs': {
|
| 46 |
-
'image': {'type': 'base64', 'value': img_base64}
|
| 47 |
-
}
|
| 48 |
-
},
|
| 49 |
-
timeout=30
|
| 50 |
-
)
|
| 51 |
-
|
| 52 |
-
print(f"๐ก ์๋ต ์ฝ๋: {response.status_code}")
|
| 53 |
-
|
| 54 |
-
if response.status_code == 200:
|
| 55 |
-
result = response.json()
|
| 56 |
-
print(f"\n๐ฆ ์๋ต ๊ตฌ์กฐ:")
|
| 57 |
-
print(json.dumps(result, indent=2, ensure_ascii=False)[:2000])
|
| 58 |
-
|
| 59 |
-
# predictions ์ถ์ถ ์๋
|
| 60 |
-
if 'outputs' in result:
|
| 61 |
-
print(f"\nโ
outputs ๋ฐ๊ฒฌ: {len(result['outputs'])}๊ฐ")
|
| 62 |
-
if len(result['outputs']) > 0:
|
| 63 |
-
output = result['outputs'][0]
|
| 64 |
-
print(f"๐ฆ output[0] keys: {output.keys()}")
|
| 65 |
-
if 'predictions' in output:
|
| 66 |
-
pred_data = output['predictions']
|
| 67 |
-
print(f"๐ฆ predictions type: {type(pred_data)}")
|
| 68 |
-
if isinstance(pred_data, dict):
|
| 69 |
-
print(f"๐ฆ predictions keys: {pred_data.keys()}")
|
| 70 |
-
if 'predictions' in pred_data:
|
| 71 |
-
preds = pred_data['predictions']
|
| 72 |
-
print(f"โ
์ต์ข
predictions: {len(preds)}๊ฐ")
|
| 73 |
-
else:
|
| 74 |
-
print(f"โ ์ค๋ฅ: {response.text}")
|
| 75 |
-
|
| 76 |
-
except Exception as e:
|
| 77 |
-
print(f"โ ์์ธ ๋ฐ์: {str(e)}")
|
| 78 |
-
import traceback
|
| 79 |
-
traceback.print_exc()
|
| 80 |
-
|
| 81 |
-
print(f"\n{'='*60}")
|
| 82 |
-
print("โ
ํ
์คํธ ์๋ฃ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,219 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
ํ๋ผ๋ฏธํฐ ๊ทธ๋ฆฌ๋ ์์น
|
| 4 |
-
์ต์ ์ confidence threshold์ filter threshold ์กฐํฉ ์ฐพ๊ธฐ
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import os
|
| 10 |
-
import json
|
| 11 |
-
import numpy as np
|
| 12 |
-
from datetime import datetime
|
| 13 |
-
import matplotlib.pyplot as plt
|
| 14 |
-
import seaborn as sns
|
| 15 |
-
from test_quantitative_evaluation import (
|
| 16 |
-
load_ground_truth,
|
| 17 |
-
load_rtdetr_model,
|
| 18 |
-
detect_with_rtdetr,
|
| 19 |
-
apply_universal_filter,
|
| 20 |
-
evaluate_detection
|
| 21 |
-
)
|
| 22 |
-
from PIL import Image
|
| 23 |
-
|
| 24 |
-
def run_parameter_sweep(
|
| 25 |
-
test_image_dir,
|
| 26 |
-
ground_truth_path,
|
| 27 |
-
confidence_values,
|
| 28 |
-
filter_values,
|
| 29 |
-
iou_threshold=0.5
|
| 30 |
-
):
|
| 31 |
-
"""ํ๋ผ๋ฏธํฐ ๊ทธ๋ฆฌ๋ ์์น ์คํ"""
|
| 32 |
-
print("\n" + "="*70)
|
| 33 |
-
print("๐ ํ๋ผ๋ฏธํฐ ๊ทธ๋ฆฌ๋ ์์น ์์")
|
| 34 |
-
print("="*70)
|
| 35 |
-
|
| 36 |
-
# Ground truth ๋ก๋
|
| 37 |
-
ground_truths = load_ground_truth(ground_truth_path)
|
| 38 |
-
if not ground_truths:
|
| 39 |
-
return
|
| 40 |
-
|
| 41 |
-
# ๋ชจ๋ธ ๋ก๋ (ํ ๋ฒ๋ง)
|
| 42 |
-
processor, model = load_rtdetr_model()
|
| 43 |
-
|
| 44 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 45 |
-
results_grid = {}
|
| 46 |
-
best_f1 = 0
|
| 47 |
-
best_config = None
|
| 48 |
-
|
| 49 |
-
print(f"\n๐ ํ
์คํธ ๋ฒ์:")
|
| 50 |
-
print(f" Confidence: {confidence_values}")
|
| 51 |
-
print(f" Filter Threshold: {filter_values}")
|
| 52 |
-
print(f" ์ด {len(confidence_values) * len(filter_values)}๊ฐ ์กฐํฉ ํ
์คํธ\n")
|
| 53 |
-
|
| 54 |
-
total_tests = len(confidence_values) * len(filter_values)
|
| 55 |
-
current_test = 0
|
| 56 |
-
|
| 57 |
-
# ๊ทธ๋ฆฌ๋ ์์น
|
| 58 |
-
for conf in confidence_values:
|
| 59 |
-
results_grid[conf] = {}
|
| 60 |
-
|
| 61 |
-
for filt in filter_values:
|
| 62 |
-
current_test += 1
|
| 63 |
-
print(f"\n[{current_test}/{total_tests}] ํ
์คํธ ์ค: Conf={conf}, Filter={filt}")
|
| 64 |
-
|
| 65 |
-
metrics_list = []
|
| 66 |
-
|
| 67 |
-
for filename, gt_list in ground_truths.items():
|
| 68 |
-
# ์ด๋ฏธ์ง ๊ฒฝ๋ก ๊ตฌ์ฑ
|
| 69 |
-
if gt_list and 'folder' in gt_list[0]:
|
| 70 |
-
folder = gt_list[0]['folder']
|
| 71 |
-
img_path = os.path.join(test_image_dir, folder, filename)
|
| 72 |
-
else:
|
| 73 |
-
img_path = os.path.join(test_image_dir, filename)
|
| 74 |
-
|
| 75 |
-
if not os.path.exists(img_path):
|
| 76 |
-
continue
|
| 77 |
-
|
| 78 |
-
# ๊ฒ์ถ
|
| 79 |
-
image = Image.open(img_path).convert('RGB')
|
| 80 |
-
all_detections = detect_with_rtdetr(image, processor, model, conf)
|
| 81 |
-
filtered_detections = apply_universal_filter(all_detections, image, filt)
|
| 82 |
-
|
| 83 |
-
# ํ๊ฐ
|
| 84 |
-
metrics = evaluate_detection(filtered_detections, gt_list, iou_threshold)
|
| 85 |
-
metrics_list.append(metrics)
|
| 86 |
-
|
| 87 |
-
# ํ๊ท ๊ณ์ฐ
|
| 88 |
-
if metrics_list:
|
| 89 |
-
avg_precision = np.mean([m['precision'] for m in metrics_list])
|
| 90 |
-
avg_recall = np.mean([m['recall'] for m in metrics_list])
|
| 91 |
-
avg_f1 = np.mean([m['f1'] for m in metrics_list])
|
| 92 |
-
|
| 93 |
-
results_grid[conf][filt] = {
|
| 94 |
-
'precision': avg_precision,
|
| 95 |
-
'recall': avg_recall,
|
| 96 |
-
'f1': avg_f1
|
| 97 |
-
}
|
| 98 |
-
|
| 99 |
-
print(f" โ P={avg_precision:.2%}, R={avg_recall:.2%}, F1={avg_f1:.2%}")
|
| 100 |
-
|
| 101 |
-
# ์ต๊ณ ์ฑ๋ฅ ์
๋ฐ์ดํธ
|
| 102 |
-
if avg_f1 > best_f1:
|
| 103 |
-
best_f1 = avg_f1
|
| 104 |
-
best_config = {
|
| 105 |
-
'confidence': conf,
|
| 106 |
-
'filter_threshold': filt,
|
| 107 |
-
'metrics': {
|
| 108 |
-
'precision': avg_precision,
|
| 109 |
-
'recall': avg_recall,
|
| 110 |
-
'f1': avg_f1
|
| 111 |
-
}
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 115 |
-
output_dir = f"test_results/parameter_sweep_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
| 116 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 117 |
-
|
| 118 |
-
# JSON ์ ์ฅ
|
| 119 |
-
summary = {
|
| 120 |
-
'test_range': {
|
| 121 |
-
'confidence': confidence_values,
|
| 122 |
-
'filter_threshold': filter_values,
|
| 123 |
-
'iou_threshold': iou_threshold
|
| 124 |
-
},
|
| 125 |
-
'best_config': best_config,
|
| 126 |
-
'all_results': results_grid
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
json_path = os.path.join(output_dir, 'sweep_results.json')
|
| 130 |
-
with open(json_path, 'w', encoding='utf-8') as f:
|
| 131 |
-
json.dump(summary, f, ensure_ascii=False, indent=2)
|
| 132 |
-
|
| 133 |
-
print("\n" + "="*70)
|
| 134 |
-
print("๐ ์ต๊ณ ์ฑ๋ฅ ์ค์ ")
|
| 135 |
-
print("="*70)
|
| 136 |
-
print(f"Confidence Threshold: {best_config['confidence']}")
|
| 137 |
-
print(f"Filter Threshold: {best_config['filter_threshold']}")
|
| 138 |
-
print(f"Precision: {best_config['metrics']['precision']:.2%}")
|
| 139 |
-
print(f"Recall: {best_config['metrics']['recall']:.2%}")
|
| 140 |
-
print(f"F1 Score: {best_config['metrics']['f1']:.2%}")
|
| 141 |
-
print("="*70)
|
| 142 |
-
|
| 143 |
-
# ํํธ๋งต ์์ฑ
|
| 144 |
-
generate_heatmaps(results_grid, confidence_values, filter_values, output_dir)
|
| 145 |
-
|
| 146 |
-
print(f"\n๐ ๊ฒฐ๊ณผ ์ ์ฅ: {json_path}")
|
| 147 |
-
print(f"๐ ํํธ๋งต ์ ์ฅ: {output_dir}")
|
| 148 |
-
|
| 149 |
-
return best_config, results_grid
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
def generate_heatmaps(results_grid, conf_values, filt_values, output_dir):
|
| 153 |
-
"""์ฑ๋ฅ ํํธ๏ฟฝ๏ฟฝ๏ฟฝ ์์ฑ"""
|
| 154 |
-
metrics = ['precision', 'recall', 'f1']
|
| 155 |
-
metric_names = {
|
| 156 |
-
'precision': 'Precision (์ ๋ฐ๋)',
|
| 157 |
-
'recall': 'Recall (์ฌํ์จ)',
|
| 158 |
-
'f1': 'F1 Score'
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
for metric in metrics:
|
| 162 |
-
# ๋ฐ์ดํฐ ํ๋ ฌ ์์ฑ
|
| 163 |
-
data = np.zeros((len(conf_values), len(filt_values)))
|
| 164 |
-
|
| 165 |
-
for i, conf in enumerate(conf_values):
|
| 166 |
-
for j, filt in enumerate(filt_values):
|
| 167 |
-
if conf in results_grid and filt in results_grid[conf]:
|
| 168 |
-
data[i, j] = results_grid[conf][filt][metric]
|
| 169 |
-
|
| 170 |
-
# ํํธ๋งต ๊ทธ๋ฆฌ๊ธฐ
|
| 171 |
-
plt.figure(figsize=(12, 8))
|
| 172 |
-
sns.heatmap(
|
| 173 |
-
data,
|
| 174 |
-
annot=True,
|
| 175 |
-
fmt='.2%',
|
| 176 |
-
cmap='RdYlGn',
|
| 177 |
-
xticklabels=filt_values,
|
| 178 |
-
yticklabels=conf_values,
|
| 179 |
-
vmin=0,
|
| 180 |
-
vmax=1,
|
| 181 |
-
cbar_kws={'label': metric_names[metric]}
|
| 182 |
-
)
|
| 183 |
-
plt.xlabel('Filter Threshold', fontsize=12)
|
| 184 |
-
plt.ylabel('Confidence Threshold', fontsize=12)
|
| 185 |
-
plt.title(f'{metric_names[metric]} Heatmap', fontsize=14, fontweight='bold')
|
| 186 |
-
plt.tight_layout()
|
| 187 |
-
|
| 188 |
-
output_path = os.path.join(output_dir, f'heatmap_{metric}.png')
|
| 189 |
-
plt.savefig(output_path, dpi=150)
|
| 190 |
-
plt.close()
|
| 191 |
-
print(f" ๐ {metric_names[metric]} ํํธ๋งต ์ ์ฅ: {output_path}")
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
if __name__ == "__main__":
|
| 195 |
-
# ํ
์คํธ ๋ฒ์ ์ค์
|
| 196 |
-
TEST_DIR = r"data\ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 197 |
-
GT_PATH = "ground_truth.json"
|
| 198 |
-
|
| 199 |
-
# ํ๋ผ๋ฏธํฐ ๋ฒ์
|
| 200 |
-
CONFIDENCE_VALUES = [0.3, 0.35, 0.4, 0.45, 0.5]
|
| 201 |
-
FILTER_VALUES = [30, 40, 50, 60, 70]
|
| 202 |
-
|
| 203 |
-
if not os.path.exists(GT_PATH):
|
| 204 |
-
print("โ ๏ธ ground_truth.json ํ์ผ์ด ํ์ํฉ๋๋ค.")
|
| 205 |
-
else:
|
| 206 |
-
best_config, all_results = run_parameter_sweep(
|
| 207 |
-
test_image_dir=TEST_DIR,
|
| 208 |
-
ground_truth_path=GT_PATH,
|
| 209 |
-
confidence_values=CONFIDENCE_VALUES,
|
| 210 |
-
filter_values=FILTER_VALUES,
|
| 211 |
-
iou_threshold=0.5
|
| 212 |
-
)
|
| 213 |
-
|
| 214 |
-
print("\n๐ก ๋ค์ ๋จ๊ณ:")
|
| 215 |
-
print(f" 1. test_visual_validation.py ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์
๋ฐ์ดํธ:")
|
| 216 |
-
print(f" - confidence_threshold = {best_config['confidence']}")
|
| 217 |
-
print(f" - filter_threshold = {best_config['filter_threshold']}")
|
| 218 |
-
print(f" 2. ์
๋ฐ์ดํธ ํ ์ฌํ๊ฐ ์คํ:")
|
| 219 |
-
print(f" python test_quantitative_evaluation.py")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,177 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Roboflow ๋ชจ๋ธ (find-shrimp-6) ํ
์คํธ
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
from inference_sdk import InferenceHTTPClient
|
| 9 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 10 |
-
import os
|
| 11 |
-
import glob
|
| 12 |
-
import time
|
| 13 |
-
|
| 14 |
-
# Roboflow ํด๋ผ์ด์ธํธ ์ด๊ธฐํ
|
| 15 |
-
client = InferenceHTTPClient(
|
| 16 |
-
api_url="https://serverless.roboflow.com",
|
| 17 |
-
api_key="azcIL8KDJVJMYrsERzI7"
|
| 18 |
-
)
|
| 19 |
-
|
| 20 |
-
def test_roboflow_detection(image_path):
|
| 21 |
-
"""๋จ์ผ ์ด๋ฏธ์ง ํ
์คํธ"""
|
| 22 |
-
print(f"\n{'='*60}")
|
| 23 |
-
print(f"๐ธ ํ
์คํธ ์ด๋ฏธ์ง: {os.path.basename(image_path)}")
|
| 24 |
-
print(f"{'='*60}")
|
| 25 |
-
|
| 26 |
-
# ์ด๋ฏธ์ง ํฌ๊ธฐ ํ์ธ
|
| 27 |
-
image = Image.open(image_path)
|
| 28 |
-
print(f"๐ผ๏ธ ์๋ณธ ํฌ๊ธฐ: {image.size}")
|
| 29 |
-
|
| 30 |
-
# Roboflow API ํธ์ถ
|
| 31 |
-
start_time = time.time()
|
| 32 |
-
|
| 33 |
-
result = client.run_workflow(
|
| 34 |
-
workspace_name="vidraft",
|
| 35 |
-
workflow_id="find-shrimp-6",
|
| 36 |
-
images={"image": image_path},
|
| 37 |
-
use_cache=True
|
| 38 |
-
)
|
| 39 |
-
|
| 40 |
-
elapsed = time.time() - start_time
|
| 41 |
-
print(f"โฑ๏ธ ์ถ๋ก ์๊ฐ: {elapsed:.2f}์ด")
|
| 42 |
-
|
| 43 |
-
# ๊ฒฐ๊ณผ ํ์ฑ
|
| 44 |
-
predictions = []
|
| 45 |
-
|
| 46 |
-
if isinstance(result, dict) and 'outputs' in result and len(result['outputs']) > 0:
|
| 47 |
-
output = result['outputs'][0]
|
| 48 |
-
if isinstance(output, dict) and 'predictions' in output:
|
| 49 |
-
pred_data = output['predictions']
|
| 50 |
-
if isinstance(pred_data, dict) and 'predictions' in pred_data:
|
| 51 |
-
predictions = pred_data['predictions']
|
| 52 |
-
elif isinstance(pred_data, list):
|
| 53 |
-
predictions = pred_data
|
| 54 |
-
|
| 55 |
-
print(f"๐ฆ ๊ฒ์ถ๋ ๊ฐ์ฒด ์: {len(predictions)}๊ฐ")
|
| 56 |
-
|
| 57 |
-
# ์ ๋ขฐ๋๋ณ ํต๊ณ
|
| 58 |
-
if predictions:
|
| 59 |
-
confidences = [pred.get('confidence', 0) for pred in predictions]
|
| 60 |
-
print(f"๐ ์ ๋ขฐ๋ ํต๊ณ:")
|
| 61 |
-
print(f" - ์ต๊ณ : {max(confidences):.1%}")
|
| 62 |
-
print(f" - ์ต์ : {min(confidences):.1%}")
|
| 63 |
-
print(f" - ํ๊ท : {sum(confidences)/len(confidences):.1%}")
|
| 64 |
-
|
| 65 |
-
# ์ ๋ขฐ๋๋ณ ๊ฐ์
|
| 66 |
-
high_conf = sum(1 for c in confidences if c >= 0.5)
|
| 67 |
-
mid_conf = sum(1 for c in confidences if 0.2 <= c < 0.5)
|
| 68 |
-
low_conf = sum(1 for c in confidences if c < 0.2)
|
| 69 |
-
|
| 70 |
-
print(f"\n - ๊ณ ์ ๋ขฐ๋ (โฅ50%): {high_conf}๊ฐ")
|
| 71 |
-
print(f" - ์ค์ ๋ขฐ๋ (20-50%): {mid_conf}๊ฐ")
|
| 72 |
-
print(f" - ์ ์ ๋ขฐ๋ (<20%): {low_conf}๊ฐ")
|
| 73 |
-
|
| 74 |
-
# ์์ 5๊ฐ ์ถ๋ ฅ
|
| 75 |
-
print(f"\n๐ ์์ 5๊ฐ ๊ฒ์ถ ๊ฒฐ๊ณผ:")
|
| 76 |
-
sorted_preds = sorted(predictions, key=lambda x: x.get('confidence', 0), reverse=True)
|
| 77 |
-
for i, pred in enumerate(sorted_preds[:5], 1):
|
| 78 |
-
conf = pred.get('confidence', 0)
|
| 79 |
-
x = pred.get('x', 0)
|
| 80 |
-
y = pred.get('y', 0)
|
| 81 |
-
w = pred.get('width', 0)
|
| 82 |
-
h = pred.get('height', 0)
|
| 83 |
-
print(f" {i}. ์ ๋ขฐ๋: {conf:.1%}, ์์น: ({x:.0f}, {y:.0f}), ํฌ๊ธฐ: {w:.0f}x{h:.0f}")
|
| 84 |
-
else:
|
| 85 |
-
print("โ ๏ธ ๊ฒ์ถ๋ ๊ฐ์ฒด๊ฐ ์์ต๋๋ค!")
|
| 86 |
-
|
| 87 |
-
# ์๊ฐํ
|
| 88 |
-
output_path = image_path.replace('.jpg', '_roboflow_result.jpg')
|
| 89 |
-
visualize_result(image_path, predictions, output_path)
|
| 90 |
-
print(f"๐พ ๊ฒฐ๊ณผ ์ ์ฅ: {output_path}")
|
| 91 |
-
|
| 92 |
-
return predictions
|
| 93 |
-
|
| 94 |
-
def visualize_result(image_path, predictions, output_path):
|
| 95 |
-
"""๊ฒฐ๊ณผ ์๊ฐํ"""
|
| 96 |
-
image = Image.open(image_path)
|
| 97 |
-
draw = ImageDraw.Draw(image)
|
| 98 |
-
|
| 99 |
-
for pred in predictions:
|
| 100 |
-
conf = pred.get('confidence', 0)
|
| 101 |
-
x = pred.get('x', 0)
|
| 102 |
-
y = pred.get('y', 0)
|
| 103 |
-
w = pred.get('width', 0)
|
| 104 |
-
h = pred.get('height', 0)
|
| 105 |
-
|
| 106 |
-
# ๋ฐ์ค ์ขํ
|
| 107 |
-
x1 = x - w / 2
|
| 108 |
-
y1 = y - h / 2
|
| 109 |
-
x2 = x + w / 2
|
| 110 |
-
y2 = y + h / 2
|
| 111 |
-
|
| 112 |
-
# ์ ๋ขฐ๋์ ๋ฐ๋ฅธ ์์
|
| 113 |
-
if conf >= 0.5:
|
| 114 |
-
color = 'green'
|
| 115 |
-
elif conf >= 0.2:
|
| 116 |
-
color = 'yellow'
|
| 117 |
-
else:
|
| 118 |
-
color = 'red'
|
| 119 |
-
|
| 120 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 121 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 122 |
-
|
| 123 |
-
# ์ ๋ขฐ๋ ํ
์คํธ
|
| 124 |
-
text = f"{conf:.0%}"
|
| 125 |
-
draw.text((x1, y1-15), text, fill=color)
|
| 126 |
-
|
| 127 |
-
image.save(output_path, quality=95)
|
| 128 |
-
|
| 129 |
-
def main():
|
| 130 |
-
print("="*60)
|
| 131 |
-
print("๐ฆ Roboflow ๋ชจ๋ธ (find-shrimp-6) ํ
์คํธ")
|
| 132 |
-
print("="*60)
|
| 133 |
-
|
| 134 |
-
# ํ
์คํธ ์ด๋ฏธ์ง ์ ํ (YOLO ๋ฐ์ดํฐ์
์์ 5๊ฐ)
|
| 135 |
-
image_dir = "data/yolo_dataset/images/train"
|
| 136 |
-
test_images = sorted(glob.glob(os.path.join(image_dir, "*.jpg")))[:5]
|
| 137 |
-
|
| 138 |
-
if not test_images:
|
| 139 |
-
print("โ ํ
์คํธ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค!")
|
| 140 |
-
return
|
| 141 |
-
|
| 142 |
-
print(f"\n๐ ํ
์คํธ ์ด๋ฏธ์ง ๊ฒฝ๋ก: {image_dir}")
|
| 143 |
-
print(f"๐ ํ
์คํธ ์ด๋ฏธ์ง ์: {len(test_images)}๊ฐ\n")
|
| 144 |
-
|
| 145 |
-
all_results = []
|
| 146 |
-
|
| 147 |
-
for img_path in test_images:
|
| 148 |
-
try:
|
| 149 |
-
predictions = test_roboflow_detection(img_path)
|
| 150 |
-
all_results.append({
|
| 151 |
-
'image': os.path.basename(img_path),
|
| 152 |
-
'count': len(predictions),
|
| 153 |
-
'predictions': predictions
|
| 154 |
-
})
|
| 155 |
-
except Exception as e:
|
| 156 |
-
print(f"โ ์ค๋ฅ ๋ฐ์: {str(e)}")
|
| 157 |
-
import traceback
|
| 158 |
-
traceback.print_exc()
|
| 159 |
-
|
| 160 |
-
# ์ ์ฒด ์์ฝ
|
| 161 |
-
print(f"\n{'='*60}")
|
| 162 |
-
print("๐ ์ ์ฒด ํ
์คํธ ์์ฝ")
|
| 163 |
-
print(f"{'='*60}")
|
| 164 |
-
|
| 165 |
-
total_detections = sum(r['count'] for r in all_results)
|
| 166 |
-
print(f"\n์ด ๊ฒ์ถ ์: {total_detections}๊ฐ")
|
| 167 |
-
print(f"์ด๋ฏธ์ง๋น ํ๊ท : {total_detections/len(all_results):.1f}๊ฐ")
|
| 168 |
-
|
| 169 |
-
print(f"\n์ด๋ฏธ์ง๋ณ ๊ฒ์ถ ์:")
|
| 170 |
-
for r in all_results:
|
| 171 |
-
print(f" - {r['image']}: {r['count']}๊ฐ")
|
| 172 |
-
|
| 173 |
-
print(f"\nโ
ํ
์คํธ ์๋ฃ!")
|
| 174 |
-
print(f"๐ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง: {image_dir}/*_roboflow_result.jpg")
|
| 175 |
-
|
| 176 |
-
if __name__ == "__main__":
|
| 177 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,183 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Roboflow ๋ชจ๋ธ ํ
์คํธ ๋ฐ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ ์ฅ
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
import requests
|
| 9 |
-
import base64
|
| 10 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 11 |
-
from io import BytesIO
|
| 12 |
-
import json
|
| 13 |
-
import os
|
| 14 |
-
import glob
|
| 15 |
-
|
| 16 |
-
def test_and_save_result(image_path, output_dir="test_results"):
|
| 17 |
-
"""์ด๋ฏธ์ง ํ
์คํธ ํ ๊ฒฐ๊ณผ ์ ์ฅ"""
|
| 18 |
-
|
| 19 |
-
# ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ ์์ฑ
|
| 20 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 21 |
-
|
| 22 |
-
print(f"\n{'='*60}")
|
| 23 |
-
print(f"๐ธ ํ
์คํธ: {os.path.basename(image_path)}")
|
| 24 |
-
print(f"{'='*60}")
|
| 25 |
-
|
| 26 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 27 |
-
image = Image.open(image_path)
|
| 28 |
-
original_size = image.size
|
| 29 |
-
print(f"๐ผ๏ธ ์๋ณธ ํฌ๊ธฐ: {original_size}")
|
| 30 |
-
|
| 31 |
-
# ๋ฆฌ์ฌ์ด์ฆ
|
| 32 |
-
max_size = 640
|
| 33 |
-
if image.width > max_size or image.height > max_size:
|
| 34 |
-
image_resized = image.copy()
|
| 35 |
-
image_resized.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
|
| 36 |
-
print(f"๐ ๋ฆฌ์ฌ์ด์ฆ: {image_resized.size}")
|
| 37 |
-
else:
|
| 38 |
-
image_resized = image
|
| 39 |
-
|
| 40 |
-
# Base64 ์ธ์ฝ๋ฉ
|
| 41 |
-
buffered = BytesIO()
|
| 42 |
-
image_resized.save(buffered, format="JPEG", quality=80)
|
| 43 |
-
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 44 |
-
print(f"๐ฆ Base64 ํฌ๊ธฐ: {len(img_base64)} bytes")
|
| 45 |
-
|
| 46 |
-
# API ํธ์ถ
|
| 47 |
-
print(f"๐ Roboflow API ํธ์ถ ์ค...")
|
| 48 |
-
response = requests.post(
|
| 49 |
-
'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6',
|
| 50 |
-
headers={'Content-Type': 'application/json'},
|
| 51 |
-
json={
|
| 52 |
-
'api_key': 'azcIL8KDJVJMYrsERzI7',
|
| 53 |
-
'inputs': {
|
| 54 |
-
'image': {'type': 'base64', 'value': img_base64}
|
| 55 |
-
}
|
| 56 |
-
},
|
| 57 |
-
timeout=30
|
| 58 |
-
)
|
| 59 |
-
|
| 60 |
-
if response.status_code != 200:
|
| 61 |
-
print(f"โ API ์ค๋ฅ: {response.status_code}")
|
| 62 |
-
return None
|
| 63 |
-
|
| 64 |
-
result = response.json()
|
| 65 |
-
|
| 66 |
-
# predictions ์ถ์ถ
|
| 67 |
-
predictions = []
|
| 68 |
-
if 'outputs' in result and len(result['outputs']) > 0:
|
| 69 |
-
output = result['outputs'][0]
|
| 70 |
-
if 'predictions' in output:
|
| 71 |
-
pred_data = output['predictions']
|
| 72 |
-
if isinstance(pred_data, dict) and 'predictions' in pred_data:
|
| 73 |
-
predictions = pred_data['predictions']
|
| 74 |
-
|
| 75 |
-
print(f"๐ฆ ๊ฒ์ถ ์: {len(predictions)}๊ฐ")
|
| 76 |
-
|
| 77 |
-
# ์๋ณธ ์ด๋ฏธ์ง์ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 78 |
-
draw = ImageDraw.Draw(image)
|
| 79 |
-
|
| 80 |
-
# ๋ฆฌ์ฌ์ด์ฆ ๋น์จ ๊ณ์ฐ (์๋ณธ ํฌ๊ธฐ๋ก ๋ณต์)
|
| 81 |
-
scale_x = original_size[0] / image_resized.size[0]
|
| 82 |
-
scale_y = original_size[1] / image_resized.size[1]
|
| 83 |
-
|
| 84 |
-
for i, pred in enumerate(predictions, 1):
|
| 85 |
-
conf = pred.get('confidence', 0)
|
| 86 |
-
x = pred.get('x', 0) * scale_x
|
| 87 |
-
y = pred.get('y', 0) * scale_y
|
| 88 |
-
w = pred.get('width', 0) * scale_x
|
| 89 |
-
h = pred.get('height', 0) * scale_y
|
| 90 |
-
|
| 91 |
-
# ๋ฐ์ค ์ขํ
|
| 92 |
-
x1 = x - w / 2
|
| 93 |
-
y1 = y - h / 2
|
| 94 |
-
x2 = x + w / 2
|
| 95 |
-
y2 = y + h / 2
|
| 96 |
-
|
| 97 |
-
# ์ ๋ขฐ๋๋ณ ์์
|
| 98 |
-
if conf >= 0.5:
|
| 99 |
-
color = 'lime'
|
| 100 |
-
thickness = 5
|
| 101 |
-
elif conf >= 0.3:
|
| 102 |
-
color = 'yellow'
|
| 103 |
-
thickness = 4
|
| 104 |
-
else:
|
| 105 |
-
color = 'red'
|
| 106 |
-
thickness = 3
|
| 107 |
-
|
| 108 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 109 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=thickness)
|
| 110 |
-
|
| 111 |
-
# ์ ๋ขฐ๋ ํ
์คํธ
|
| 112 |
-
text = f"#{i} {conf:.1%}"
|
| 113 |
-
|
| 114 |
-
# ํ
์คํธ ๋ฐฐ๊ฒฝ
|
| 115 |
-
try:
|
| 116 |
-
font = ImageFont.truetype("arial.ttf", 40)
|
| 117 |
-
except:
|
| 118 |
-
font = ImageFont.load_default()
|
| 119 |
-
|
| 120 |
-
# ํ
์คํธ ์์น
|
| 121 |
-
text_bbox = draw.textbbox((x1, y1-50), text, font=font)
|
| 122 |
-
draw.rectangle(text_bbox, fill=color)
|
| 123 |
-
draw.text((x1, y1-50), text, fill='black', font=font)
|
| 124 |
-
|
| 125 |
-
print(f" {i}. ์ ๋ขฐ๋: {conf:.1%}, ์์น: ({x:.0f}, {y:.0f}), ํฌ๊ธฐ: {w:.0f}x{h:.0f}")
|
| 126 |
-
|
| 127 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 128 |
-
output_filename = os.path.basename(image_path).replace('.jpg', '_result.jpg')
|
| 129 |
-
output_path = os.path.join(output_dir, output_filename)
|
| 130 |
-
image.save(output_path, quality=95)
|
| 131 |
-
print(f"๐พ ์ ์ฅ: {output_path}")
|
| 132 |
-
|
| 133 |
-
return {
|
| 134 |
-
'image': os.path.basename(image_path),
|
| 135 |
-
'detections': len(predictions),
|
| 136 |
-
'output': output_path
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
def main():
|
| 140 |
-
print("="*60)
|
| 141 |
-
print("๐ฆ Roboflow ๋ชจ๋ธ ํ
์คํธ ๋ฐ ๊ฒฐ๊ณผ ์ ์ฅ")
|
| 142 |
-
print("="*60)
|
| 143 |
-
|
| 144 |
-
# YOLO ๋ฐ์ดํฐ์
์์ 5๊ฐ ์ด๋ฏธ์ง ์ ํ
|
| 145 |
-
image_dir = "data/yolo_dataset/images/train"
|
| 146 |
-
test_images = sorted(glob.glob(os.path.join(image_dir, "*.jpg")))[:5]
|
| 147 |
-
|
| 148 |
-
if not test_images:
|
| 149 |
-
print("โ ํ
์คํธ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค!")
|
| 150 |
-
return
|
| 151 |
-
|
| 152 |
-
print(f"\n๐ ์ด๋ฏธ์ง ๊ฒฝ๋ก: {image_dir}")
|
| 153 |
-
print(f"๐ ํ
์คํธ ์: {len(test_images)}๊ฐ\n")
|
| 154 |
-
|
| 155 |
-
results = []
|
| 156 |
-
|
| 157 |
-
for img_path in test_images:
|
| 158 |
-
try:
|
| 159 |
-
result = test_and_save_result(img_path)
|
| 160 |
-
if result:
|
| 161 |
-
results.append(result)
|
| 162 |
-
except Exception as e:
|
| 163 |
-
print(f"โ ์ค๋ฅ: {str(e)}")
|
| 164 |
-
import traceback
|
| 165 |
-
traceback.print_exc()
|
| 166 |
-
|
| 167 |
-
# ์์ฝ
|
| 168 |
-
print(f"\n{'='*60}")
|
| 169 |
-
print("๐ ํ
์คํธ ์์ฝ")
|
| 170 |
-
print(f"{'='*60}")
|
| 171 |
-
|
| 172 |
-
total_detections = sum(r['detections'] for r in results)
|
| 173 |
-
print(f"\n์ด ๊ฒ์ถ ์: {total_detections}๊ฐ")
|
| 174 |
-
print(f"ํ๊ท : {total_detections/len(results):.1f}๊ฐ/์ด๋ฏธ์ง")
|
| 175 |
-
|
| 176 |
-
print(f"\n์ด๋ฏธ์ง๋ณ ๊ฒฐ๊ณผ:")
|
| 177 |
-
for r in results:
|
| 178 |
-
print(f" - {r['image']}: {r['detections']}๊ฐ โ {r['output']}")
|
| 179 |
-
|
| 180 |
-
print(f"\nโ
์๋ฃ! ๊ฒฐ๊ณผ๋ test_results/ ํด๋์ ์ ์ฅ๋์์ต๋๋ค.")
|
| 181 |
-
|
| 182 |
-
if __name__ == "__main__":
|
| 183 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,336 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
YOLOv8 + Universal Filter ๊ฒฐํฉ
|
| 4 |
-
์ฆ์ ๊ฐ์ : YOLOv8์ ๋์ Recall + Filter์ ๋์ Precision
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
from ultralytics import YOLO
|
| 10 |
-
import json
|
| 11 |
-
import os
|
| 12 |
-
from PIL import Image
|
| 13 |
-
import numpy as np
|
| 14 |
-
from pathlib import Path
|
| 15 |
-
from test_visual_validation import apply_universal_filter
|
| 16 |
-
|
| 17 |
-
def calculate_iou(box1, box2):
|
| 18 |
-
"""IoU ๊ณ์ฐ"""
|
| 19 |
-
x1_1, y1_1, x2_1, y2_1 = box1
|
| 20 |
-
x1_2, y1_2, x2_2, y2_2 = box2
|
| 21 |
-
|
| 22 |
-
x1_i = max(x1_1, x1_2)
|
| 23 |
-
y1_i = max(y1_1, y1_2)
|
| 24 |
-
x2_i = min(x2_1, x2_2)
|
| 25 |
-
y2_i = min(y2_1, y2_2)
|
| 26 |
-
|
| 27 |
-
if x2_i < x1_i or y2_i < y1_i:
|
| 28 |
-
return 0.0
|
| 29 |
-
|
| 30 |
-
intersection = (x2_i - x1_i) * (y2_i - y1_i)
|
| 31 |
-
area1 = (x2_1 - x1_1) * (y2_1 - y1_1)
|
| 32 |
-
area2 = (x2_2 - x1_2) * (y2_2 - y1_2)
|
| 33 |
-
union = area1 + area2 - intersection
|
| 34 |
-
|
| 35 |
-
return intersection / union if union > 0 else 0.0
|
| 36 |
-
|
| 37 |
-
def yolo_with_filter_evaluate(model_path, gt_file, data_base_dir,
|
| 38 |
-
yolo_conf=0.01, filter_threshold=90, iou_threshold=0.5):
|
| 39 |
-
"""YOLOv8 + Universal Filter ํ๊ฐ"""
|
| 40 |
-
|
| 41 |
-
print(f"\n๐ YOLOv8 + Universal Filter ํ๊ฐ")
|
| 42 |
-
print(f" - YOLOv8 Confidence: {yolo_conf}")
|
| 43 |
-
print(f" - Filter Threshold: {filter_threshold}")
|
| 44 |
-
print(f" - IoU Threshold: {iou_threshold}")
|
| 45 |
-
|
| 46 |
-
# ๋ชจ๋ธ ๋ก๋
|
| 47 |
-
model = YOLO(model_path)
|
| 48 |
-
print(f"โ
YOLOv8 ๋ชจ๋ธ ๋ก๋ ์๋ฃ")
|
| 49 |
-
|
| 50 |
-
# GT ๋ก๋
|
| 51 |
-
with open(gt_file, 'r', encoding='utf-8') as f:
|
| 52 |
-
gt_data = json.load(f)
|
| 53 |
-
|
| 54 |
-
# ํต๊ณ
|
| 55 |
-
total_gt = 0
|
| 56 |
-
total_yolo_pred = 0
|
| 57 |
-
total_filtered_pred = 0
|
| 58 |
-
|
| 59 |
-
true_positives = 0
|
| 60 |
-
false_positives = 0
|
| 61 |
-
false_negatives = 0
|
| 62 |
-
|
| 63 |
-
yolo_only_tp = 0
|
| 64 |
-
yolo_only_fp = 0
|
| 65 |
-
|
| 66 |
-
results_detail = []
|
| 67 |
-
|
| 68 |
-
# ๊ฐ ์ด๋ฏธ์ง ํ๊ฐ
|
| 69 |
-
for filename, gt_boxes in gt_data.items():
|
| 70 |
-
if not gt_boxes:
|
| 71 |
-
continue
|
| 72 |
-
|
| 73 |
-
folder = gt_boxes[0].get('folder', '')
|
| 74 |
-
if not folder:
|
| 75 |
-
continue
|
| 76 |
-
|
| 77 |
-
img_path = os.path.join(data_base_dir, folder, filename)
|
| 78 |
-
if not os.path.exists(img_path):
|
| 79 |
-
continue
|
| 80 |
-
|
| 81 |
-
# PIL ์ด๋ฏธ์ง ๋ก๋
|
| 82 |
-
image = Image.open(img_path)
|
| 83 |
-
|
| 84 |
-
# YOLOv8 ์ถ๋ก
|
| 85 |
-
results = model(img_path, conf=yolo_conf, verbose=False)
|
| 86 |
-
|
| 87 |
-
# ์์ธก ๋ฐ์ค ์ถ์ถ
|
| 88 |
-
yolo_detections = []
|
| 89 |
-
if results and len(results) > 0:
|
| 90 |
-
result = results[0]
|
| 91 |
-
if result.boxes is not None and len(result.boxes) > 0:
|
| 92 |
-
boxes = result.boxes.xyxy.cpu().numpy()
|
| 93 |
-
confs = result.boxes.conf.cpu().numpy()
|
| 94 |
-
|
| 95 |
-
for box, conf in zip(boxes, confs):
|
| 96 |
-
yolo_detections.append({
|
| 97 |
-
'bbox': box.tolist(),
|
| 98 |
-
'confidence': float(conf)
|
| 99 |
-
})
|
| 100 |
-
|
| 101 |
-
# Universal Filter ์ ์ฉ
|
| 102 |
-
filtered_detections = apply_universal_filter(yolo_detections, image, threshold=filter_threshold)
|
| 103 |
-
|
| 104 |
-
# GT ๋ฐ์ค
|
| 105 |
-
gt_boxes_only = [{'bbox': ann['bbox']} for ann in gt_boxes]
|
| 106 |
-
|
| 107 |
-
# YOLOv8 only ๋งค์นญ (๋น๊ต์ฉ)
|
| 108 |
-
yolo_matched_gt = set()
|
| 109 |
-
yolo_matched_pred = set()
|
| 110 |
-
|
| 111 |
-
for i, pred in enumerate(yolo_detections):
|
| 112 |
-
best_iou = 0
|
| 113 |
-
best_gt_idx = -1
|
| 114 |
-
for j, gt in enumerate(gt_boxes_only):
|
| 115 |
-
if j in yolo_matched_gt:
|
| 116 |
-
continue
|
| 117 |
-
iou = calculate_iou(pred['bbox'], gt['bbox'])
|
| 118 |
-
if iou > best_iou:
|
| 119 |
-
best_iou = iou
|
| 120 |
-
best_gt_idx = j
|
| 121 |
-
|
| 122 |
-
if best_iou >= iou_threshold:
|
| 123 |
-
yolo_matched_pred.add(i)
|
| 124 |
-
yolo_matched_gt.add(best_gt_idx)
|
| 125 |
-
|
| 126 |
-
yolo_tp = len(yolo_matched_gt)
|
| 127 |
-
yolo_fp = len(yolo_detections) - len(yolo_matched_pred)
|
| 128 |
-
|
| 129 |
-
# Filtered ๋งค์นญ
|
| 130 |
-
matched_gt = set()
|
| 131 |
-
matched_pred = set()
|
| 132 |
-
|
| 133 |
-
for i, pred in enumerate(filtered_detections):
|
| 134 |
-
best_iou = 0
|
| 135 |
-
best_gt_idx = -1
|
| 136 |
-
|
| 137 |
-
for j, gt in enumerate(gt_boxes_only):
|
| 138 |
-
if j in matched_gt:
|
| 139 |
-
continue
|
| 140 |
-
iou = calculate_iou(pred['bbox'], gt['bbox'])
|
| 141 |
-
if iou > best_iou:
|
| 142 |
-
best_iou = iou
|
| 143 |
-
best_gt_idx = j
|
| 144 |
-
|
| 145 |
-
if best_iou >= iou_threshold:
|
| 146 |
-
matched_pred.add(i)
|
| 147 |
-
matched_gt.add(best_gt_idx)
|
| 148 |
-
|
| 149 |
-
tp = len(matched_gt)
|
| 150 |
-
fp = len(filtered_detections) - len(matched_pred)
|
| 151 |
-
fn = len(gt_boxes_only) - len(matched_gt)
|
| 152 |
-
|
| 153 |
-
true_positives += tp
|
| 154 |
-
false_positives += fp
|
| 155 |
-
false_negatives += fn
|
| 156 |
-
total_gt += len(gt_boxes_only)
|
| 157 |
-
total_yolo_pred += len(yolo_detections)
|
| 158 |
-
total_filtered_pred += len(filtered_detections)
|
| 159 |
-
|
| 160 |
-
yolo_only_tp += yolo_tp
|
| 161 |
-
yolo_only_fp += yolo_fp
|
| 162 |
-
|
| 163 |
-
results_detail.append({
|
| 164 |
-
'filename': filename,
|
| 165 |
-
'gt_count': len(gt_boxes_only),
|
| 166 |
-
'yolo_count': len(yolo_detections),
|
| 167 |
-
'filtered_count': len(filtered_detections),
|
| 168 |
-
'tp': tp,
|
| 169 |
-
'fp': fp,
|
| 170 |
-
'fn': fn
|
| 171 |
-
})
|
| 172 |
-
|
| 173 |
-
# ์ฑ๋ฅ ๊ณ์ฐ
|
| 174 |
-
precision = true_positives / (true_positives + false_positives) if (true_positives + false_positives) > 0 else 0
|
| 175 |
-
recall = true_positives / (true_positives + false_negatives) if (true_positives + false_negatives) > 0 else 0
|
| 176 |
-
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
|
| 177 |
-
|
| 178 |
-
# YOLOv8 only ์ฑ๋ฅ
|
| 179 |
-
yolo_precision = yolo_only_tp / (yolo_only_tp + yolo_only_fp) if (yolo_only_tp + yolo_only_fp) > 0 else 0
|
| 180 |
-
yolo_recall = yolo_only_tp / total_gt if total_gt > 0 else 0
|
| 181 |
-
yolo_f1 = 2 * yolo_precision * yolo_recall / (yolo_precision + yolo_recall) if (yolo_precision + yolo_recall) > 0 else 0
|
| 182 |
-
|
| 183 |
-
return {
|
| 184 |
-
'yolo_with_filter': {
|
| 185 |
-
'precision': precision,
|
| 186 |
-
'recall': recall,
|
| 187 |
-
'f1': f1,
|
| 188 |
-
'tp': true_positives,
|
| 189 |
-
'fp': false_positives,
|
| 190 |
-
'fn': false_negatives,
|
| 191 |
-
'total_pred': total_filtered_pred
|
| 192 |
-
},
|
| 193 |
-
'yolo_only': {
|
| 194 |
-
'precision': yolo_precision,
|
| 195 |
-
'recall': yolo_recall,
|
| 196 |
-
'f1': yolo_f1,
|
| 197 |
-
'tp': yolo_only_tp,
|
| 198 |
-
'fp': yolo_only_fp,
|
| 199 |
-
'total_pred': total_yolo_pred
|
| 200 |
-
},
|
| 201 |
-
'total_gt': total_gt
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
-
def main():
|
| 205 |
-
print("=" * 60)
|
| 206 |
-
print("๐ YOLOv8 + Universal Filter ์ฆ์ ๊ฐ์ ")
|
| 207 |
-
print("=" * 60)
|
| 208 |
-
|
| 209 |
-
# ๊ฒฝ๋ก ์ค์
|
| 210 |
-
yolo_model = "runs/train/shrimp_yolov8n/weights/best.pt"
|
| 211 |
-
gt_file = "ground_truth.json"
|
| 212 |
-
data_base_dir = "data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)"
|
| 213 |
-
|
| 214 |
-
if not os.path.exists(yolo_model):
|
| 215 |
-
print(f"\nโ ๋ชจ๋ธ ํ์ผ ์์: {yolo_model}")
|
| 216 |
-
return
|
| 217 |
-
|
| 218 |
-
print(f"\n๐ YOLOv8 ๋ชจ๋ธ: {yolo_model}")
|
| 219 |
-
print(f"๐ GT: {gt_file}")
|
| 220 |
-
|
| 221 |
-
# ์ฌ๋ฌ ์กฐํฉ ํ
์คํธ
|
| 222 |
-
test_configs = [
|
| 223 |
-
{'yolo_conf': 0.01, 'filter_threshold': 70},
|
| 224 |
-
{'yolo_conf': 0.01, 'filter_threshold': 80},
|
| 225 |
-
{'yolo_conf': 0.01, 'filter_threshold': 90},
|
| 226 |
-
{'yolo_conf': 0.001, 'filter_threshold': 90},
|
| 227 |
-
{'yolo_conf': 0.005, 'filter_threshold': 90},
|
| 228 |
-
]
|
| 229 |
-
|
| 230 |
-
print(f"\n๐ ์ต์ ์กฐํฉ ํ์ ์ค...")
|
| 231 |
-
|
| 232 |
-
best_f1 = 0
|
| 233 |
-
best_config = None
|
| 234 |
-
best_result = None
|
| 235 |
-
all_results = []
|
| 236 |
-
|
| 237 |
-
for config in test_configs:
|
| 238 |
-
result = yolo_with_filter_evaluate(
|
| 239 |
-
yolo_model, gt_file, data_base_dir,
|
| 240 |
-
yolo_conf=config['yolo_conf'],
|
| 241 |
-
filter_threshold=config['filter_threshold']
|
| 242 |
-
)
|
| 243 |
-
|
| 244 |
-
result['config'] = config
|
| 245 |
-
all_results.append(result)
|
| 246 |
-
|
| 247 |
-
filtered = result['yolo_with_filter']
|
| 248 |
-
print(f"\n YOLOv8(conf={config['yolo_conf']}) + Filter({config['filter_threshold']})")
|
| 249 |
-
print(f" P={filtered['precision']:.1%}, R={filtered['recall']:.1%}, F1={filtered['f1']:.1%}")
|
| 250 |
-
print(f" Pred: {result['yolo_only']['total_pred']} โ {filtered['total_pred']} (Filter ์ ๊ฑฐ: {result['yolo_only']['total_pred'] - filtered['total_pred']}๊ฐ)")
|
| 251 |
-
|
| 252 |
-
if filtered['f1'] > best_f1:
|
| 253 |
-
best_f1 = filtered['f1']
|
| 254 |
-
best_config = config
|
| 255 |
-
best_result = result
|
| 256 |
-
|
| 257 |
-
# ์ต์ ๊ฒฐ๊ณผ ์ถ๋ ฅ
|
| 258 |
-
print("\n" + "=" * 60)
|
| 259 |
-
print("โ
ํ๊ฐ ์๋ฃ!")
|
| 260 |
-
print("=" * 60)
|
| 261 |
-
|
| 262 |
-
print(f"\n๐ ์ต์ ์กฐํฉ:")
|
| 263 |
-
print(f" - YOLOv8 Confidence: {best_config['yolo_conf']}")
|
| 264 |
-
print(f" - Filter Threshold: {best_config['filter_threshold']}")
|
| 265 |
-
|
| 266 |
-
filtered = best_result['yolo_with_filter']
|
| 267 |
-
yolo = best_result['yolo_only']
|
| 268 |
-
|
| 269 |
-
print(f"\n๐ YOLOv8 + Universal Filter ์ฑ๋ฅ:")
|
| 270 |
-
print(f" - Precision: {filtered['precision']:.1%}")
|
| 271 |
-
print(f" - Recall: {filtered['recall']:.1%}")
|
| 272 |
-
print(f" - F1 Score: {filtered['f1']:.1%}")
|
| 273 |
-
print(f"\n - True Positives: {filtered['tp']}")
|
| 274 |
-
print(f" - False Positives: {filtered['fp']}")
|
| 275 |
-
print(f" - False Negatives: {filtered['fn']}")
|
| 276 |
-
print(f" - Total Predictions: {filtered['total_pred']}")
|
| 277 |
-
|
| 278 |
-
print(f"\n๐ YOLOv8 Only ๋น๊ต:")
|
| 279 |
-
print(f" - Precision: {yolo['precision']:.1%}")
|
| 280 |
-
print(f" - Recall: {yolo['recall']:.1%}")
|
| 281 |
-
print(f" - F1 Score: {yolo['f1']:.1%}")
|
| 282 |
-
print(f" - Total Predictions: {yolo['total_pred']}")
|
| 283 |
-
|
| 284 |
-
print(f"\n๐ฏ Filter ํจ๊ณผ:")
|
| 285 |
-
print(f" - FP ์ ๊ฑฐ: {yolo['fp']} โ {filtered['fp']} ({yolo['fp'] - filtered['fp']}๊ฐ ์ ๊ฑฐ)")
|
| 286 |
-
print(f" - Precision ํฅ์: {yolo['precision']:.1%} โ {filtered['precision']:.1%} ({(filtered['precision'] - yolo['precision'])*100:+.1f}%p)")
|
| 287 |
-
print(f" - F1 ํฅ์: {yolo['f1']:.1%} โ {filtered['f1']:.1%} ({(filtered['f1'] - yolo['f1'])*100:+.1f}%p)")
|
| 288 |
-
|
| 289 |
-
# ์ ์ฒด ์์คํ
๋น๊ต
|
| 290 |
-
print(f"\n๐ ์ ์ฒด ์์คํ
๋น๊ต:")
|
| 291 |
-
print(f"\n RT-DETR + Filter (๊ธฐ์กด):")
|
| 292 |
-
print(f" - Precision: 44.2%")
|
| 293 |
-
print(f" - Recall: 94.0%")
|
| 294 |
-
print(f" - F1 Score: 56.1%")
|
| 295 |
-
|
| 296 |
-
print(f"\n YOLOv8 + Filter (์๋ก์ด):")
|
| 297 |
-
print(f" - Precision: {filtered['precision']:.1%}")
|
| 298 |
-
print(f" - Recall: {filtered['recall']:.1%}")
|
| 299 |
-
print(f" - F1 Score: {filtered['f1']:.1%}")
|
| 300 |
-
|
| 301 |
-
# F1 ๋น๊ต
|
| 302 |
-
baseline_f1 = 0.561
|
| 303 |
-
improvement = (filtered['f1'] - baseline_f1) / baseline_f1 * 100
|
| 304 |
-
|
| 305 |
-
if improvement > 0:
|
| 306 |
-
print(f"\n โ
F1 ๊ฐ์ ์จ: {improvement:+.1f}% (YOLOv8+Filter๊ฐ ๋ ์ข์)")
|
| 307 |
-
else:
|
| 308 |
-
print(f"\n โ ๏ธ F1 ์ฐจ์ด: {improvement:+.1f}% (RT-DETR+Filter๊ฐ ๋ ์ข์)")
|
| 309 |
-
|
| 310 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 311 |
-
output_file = "yolo_with_filter_results.json"
|
| 312 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
| 313 |
-
json.dump({
|
| 314 |
-
'best_config': best_config,
|
| 315 |
-
'best_result': best_result,
|
| 316 |
-
'all_results': all_results,
|
| 317 |
-
'baseline': {
|
| 318 |
-
'name': 'RT-DETR + Filter',
|
| 319 |
-
'precision': 0.442,
|
| 320 |
-
'recall': 0.940,
|
| 321 |
-
'f1': 0.561
|
| 322 |
-
}
|
| 323 |
-
}, f, indent=2, ensure_ascii=False)
|
| 324 |
-
|
| 325 |
-
print(f"\n๐พ ๊ฒฐ๊ณผ ์ ์ฅ: {output_file}")
|
| 326 |
-
|
| 327 |
-
print(f"\n๐ก ๊ถ์ฅ ์ฌํญ:")
|
| 328 |
-
if filtered['f1'] >= baseline_f1:
|
| 329 |
-
print(f" โ
YOLOv8 + Universal Filter ์ฌ์ฉ ๊ถ์ฅ")
|
| 330 |
-
print(f" - ์ค์ : YOLOv8 conf={best_config['yolo_conf']}, Filter={best_config['filter_threshold']}")
|
| 331 |
-
else:
|
| 332 |
-
print(f" โ ๏ธ RT-DETR + Universal Filter ๊ณ์ ์ฌ์ฉ ๊ถ์ฅ")
|
| 333 |
-
print(f" - YOLOv8+Filter๋ ์ค์ํ ์ฑ๋ฅ์ด์ง๋ง ๊ธฐ์กด์ด ์ฝ๊ฐ ๋ ์ข์")
|
| 334 |
-
|
| 335 |
-
if __name__ == "__main__":
|
| 336 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,234 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
YOLOv8m Val Set ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ ์ฅ
|
| 3 |
-
Confidence = 0.85 ์ฌ์ฉ
|
| 4 |
-
"""
|
| 5 |
-
from ultralytics import YOLO
|
| 6 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 7 |
-
import json
|
| 8 |
-
import os
|
| 9 |
-
|
| 10 |
-
# ํ์ต๋ ๋ชจ๋ธ ๋ก๋
|
| 11 |
-
MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 12 |
-
model = YOLO(MODEL_PATH)
|
| 13 |
-
|
| 14 |
-
# ์ต์ confidence
|
| 15 |
-
CONFIDENCE = 0.85
|
| 16 |
-
|
| 17 |
-
print(f"โ
YOLOv8m ๋ชจ๋ธ ๋ก๋: {MODEL_PATH}")
|
| 18 |
-
print(f"๐ฏ Confidence Threshold: {CONFIDENCE}")
|
| 19 |
-
|
| 20 |
-
# Ground Truth ๋ก๋
|
| 21 |
-
with open('ground_truth.json', 'r', encoding='utf-8') as f:
|
| 22 |
-
ground_truth = json.load(f)
|
| 23 |
-
|
| 24 |
-
# Val set ์ด๋ฏธ์ง๋ง ํํฐ๋ง
|
| 25 |
-
val_images_dir = set(os.listdir('data/yolo_dataset/images/val'))
|
| 26 |
-
gt_val_only = {}
|
| 27 |
-
|
| 28 |
-
for img_name, gts in ground_truth.items():
|
| 29 |
-
if not gts:
|
| 30 |
-
continue
|
| 31 |
-
base_name = img_name.replace('-1.jpg', '.jpg')
|
| 32 |
-
if img_name in val_images_dir or base_name in val_images_dir:
|
| 33 |
-
gt_val_only[img_name] = gts
|
| 34 |
-
|
| 35 |
-
print(f"๐ Val set GT ์ด๋ฏธ์ง: {len(gt_val_only)}์ฅ")
|
| 36 |
-
|
| 37 |
-
# ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ
|
| 38 |
-
output_dir = "test_results_yolov8m_val"
|
| 39 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 40 |
-
|
| 41 |
-
# ํฐํธ ์ค์
|
| 42 |
-
try:
|
| 43 |
-
font = ImageFont.truetype("malgun.ttf", 24)
|
| 44 |
-
font_small = ImageFont.truetype("malgun.ttf", 18)
|
| 45 |
-
font_tiny = ImageFont.truetype("malgun.ttf", 14)
|
| 46 |
-
except:
|
| 47 |
-
font = ImageFont.load_default()
|
| 48 |
-
font_small = ImageFont.load_default()
|
| 49 |
-
font_tiny = ImageFont.load_default()
|
| 50 |
-
|
| 51 |
-
def calculate_iou(box1, box2):
|
| 52 |
-
"""IoU ๊ณ์ฐ"""
|
| 53 |
-
x1_min, y1_min, x1_max, y1_max = box1
|
| 54 |
-
x2_min, y2_min, x2_max, y2_max = box2
|
| 55 |
-
|
| 56 |
-
inter_x_min = max(x1_min, x2_min)
|
| 57 |
-
inter_y_min = max(y1_min, y2_min)
|
| 58 |
-
inter_x_max = min(x1_max, x2_max)
|
| 59 |
-
inter_y_max = min(y1_max, y2_max)
|
| 60 |
-
|
| 61 |
-
if inter_x_max < inter_x_min or inter_y_max < inter_y_min:
|
| 62 |
-
return 0.0
|
| 63 |
-
|
| 64 |
-
inter_area = (inter_x_max - inter_x_min) * (inter_y_max - inter_y_min)
|
| 65 |
-
box1_area = (x1_max - x1_min) * (y1_max - y1_min)
|
| 66 |
-
box2_area = (x2_max - x2_min) * (y2_max - y2_min)
|
| 67 |
-
union_area = box1_area + box2_area - inter_area
|
| 68 |
-
|
| 69 |
-
return inter_area / union_area if union_area > 0 else 0.0
|
| 70 |
-
|
| 71 |
-
# ํต๊ณ
|
| 72 |
-
total_gt = 0
|
| 73 |
-
total_tp = 0
|
| 74 |
-
total_fp = 0
|
| 75 |
-
total_fn = 0
|
| 76 |
-
|
| 77 |
-
print("-" * 60)
|
| 78 |
-
|
| 79 |
-
# ๊ฐ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
|
| 80 |
-
for idx, (img_name, gt_boxes) in enumerate(sorted(gt_val_only.items()), 1):
|
| 81 |
-
print(f"\n[{idx}/{len(gt_val_only)}] {img_name}")
|
| 82 |
-
|
| 83 |
-
# ์ด๋ฏธ์ง ๊ฒฝ๋ก
|
| 84 |
-
img_path = f"data/yolo_dataset/images/val/{img_name}"
|
| 85 |
-
base_name = img_name.replace('-1.jpg', '.jpg')
|
| 86 |
-
if not os.path.exists(img_path):
|
| 87 |
-
img_path = f"data/yolo_dataset/images/val/{base_name}"
|
| 88 |
-
|
| 89 |
-
if not os.path.exists(img_path):
|
| 90 |
-
print(f" โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์: {img_path}")
|
| 91 |
-
continue
|
| 92 |
-
|
| 93 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 94 |
-
image = Image.open(img_path)
|
| 95 |
-
print(f" ๐ ํฌ๊ธฐ: {image.size}")
|
| 96 |
-
|
| 97 |
-
# YOLOv8 ๊ฒ์ถ
|
| 98 |
-
results = model.predict(
|
| 99 |
-
source=image,
|
| 100 |
-
conf=CONFIDENCE,
|
| 101 |
-
iou=0.7,
|
| 102 |
-
device=0,
|
| 103 |
-
verbose=False
|
| 104 |
-
)
|
| 105 |
-
|
| 106 |
-
result = results[0]
|
| 107 |
-
boxes = result.boxes
|
| 108 |
-
|
| 109 |
-
predictions = []
|
| 110 |
-
if boxes is not None and len(boxes) > 0:
|
| 111 |
-
for box in boxes:
|
| 112 |
-
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
|
| 113 |
-
confidence = box.conf[0].cpu().item()
|
| 114 |
-
predictions.append({
|
| 115 |
-
'bbox': [float(x1), float(y1), float(x2), float(y2)],
|
| 116 |
-
'confidence': confidence
|
| 117 |
-
})
|
| 118 |
-
|
| 119 |
-
print(f" ๐ฆ ๊ฒ์ถ: {len(predictions)}๊ฐ (GT: {len(gt_boxes)}๊ฐ)")
|
| 120 |
-
|
| 121 |
-
# GT์ ๋งค์นญ
|
| 122 |
-
matched_gt = set()
|
| 123 |
-
matched_pred = set()
|
| 124 |
-
tp = 0
|
| 125 |
-
fp = 0
|
| 126 |
-
|
| 127 |
-
for pred_idx, pred in enumerate(predictions):
|
| 128 |
-
best_iou = 0
|
| 129 |
-
best_gt_idx = -1
|
| 130 |
-
|
| 131 |
-
for gt_idx, gt in enumerate(gt_boxes):
|
| 132 |
-
if gt_idx in matched_gt:
|
| 133 |
-
continue
|
| 134 |
-
|
| 135 |
-
iou = calculate_iou(pred['bbox'], gt['bbox'])
|
| 136 |
-
if iou > best_iou:
|
| 137 |
-
best_iou = iou
|
| 138 |
-
best_gt_idx = gt_idx
|
| 139 |
-
|
| 140 |
-
if best_iou >= 0.5:
|
| 141 |
-
tp += 1
|
| 142 |
-
matched_gt.add(best_gt_idx)
|
| 143 |
-
matched_pred.add(pred_idx)
|
| 144 |
-
else:
|
| 145 |
-
fp += 1
|
| 146 |
-
|
| 147 |
-
fn = len(gt_boxes) - len(matched_gt)
|
| 148 |
-
|
| 149 |
-
total_gt += len(gt_boxes)
|
| 150 |
-
total_tp += tp
|
| 151 |
-
total_fp += fp
|
| 152 |
-
total_fn += fn
|
| 153 |
-
|
| 154 |
-
print(f" ๐ TP={tp}, FP={fp}, FN={fn}")
|
| 155 |
-
|
| 156 |
-
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ
|
| 157 |
-
result_image = image.copy()
|
| 158 |
-
draw = ImageDraw.Draw(result_image)
|
| 159 |
-
|
| 160 |
-
# Ground Truth (ํ๋์, ์ ์ ํจ๊ณผ)
|
| 161 |
-
for gt_idx, gt in enumerate(gt_boxes):
|
| 162 |
-
x1, y1, x2, y2 = gt['bbox']
|
| 163 |
-
|
| 164 |
-
# ํ๋์ ๋ฐ์ค (์๊ฒ)
|
| 165 |
-
for offset in range(0, 4, 2):
|
| 166 |
-
draw.rectangle([x1+offset, y1+offset, x2-offset, y2-offset], outline="blue", width=2)
|
| 167 |
-
|
| 168 |
-
# GT ๋ผ๋ฒจ
|
| 169 |
-
label = f"GT#{gt_idx+1}"
|
| 170 |
-
bbox_label = draw.textbbox((x1, y1 - 30), label, font=font_tiny)
|
| 171 |
-
draw.rectangle(bbox_label, fill="blue")
|
| 172 |
-
draw.text((x1, y1 - 30), label, fill="white", font=font_tiny)
|
| 173 |
-
|
| 174 |
-
# ๋งค์นญ ์ฌ๋ถ ํ์
|
| 175 |
-
if gt_idx not in matched_gt:
|
| 176 |
-
# FN (๋์นจ)
|
| 177 |
-
draw.text((x1 + 10, y1 + 10), "MISSED", fill="red", font=font_small)
|
| 178 |
-
|
| 179 |
-
# Predictions
|
| 180 |
-
for pred_idx, pred in enumerate(predictions):
|
| 181 |
-
x1, y1, x2, y2 = pred['bbox']
|
| 182 |
-
conf = pred['confidence']
|
| 183 |
-
|
| 184 |
-
if pred_idx in matched_pred:
|
| 185 |
-
# TP (์ฌ๋ฐ๋ฅธ ๊ฒ์ถ) - ๋
น์
|
| 186 |
-
color = "lime"
|
| 187 |
-
label = f"โ TP #{pred_idx+1} | {conf:.0%}"
|
| 188 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=10)
|
| 189 |
-
else:
|
| 190 |
-
# FP (์๋ชป๋ ๊ฒ์ถ) - ๋นจ๊ฐ์
|
| 191 |
-
color = "red"
|
| 192 |
-
label = f"โ FP #{pred_idx+1} | {conf:.0%}"
|
| 193 |
-
draw.rectangle([x1, y1, x2, y2], outline=color, width=8)
|
| 194 |
-
|
| 195 |
-
# ๋ผ๋ฒจ
|
| 196 |
-
bbox_label = draw.textbbox((x1, y2 + 5), label, font=font_tiny)
|
| 197 |
-
draw.rectangle(bbox_label, fill=color)
|
| 198 |
-
draw.text((x1, y2 + 5), label, fill="black" if color == "lime" else "white", font=font_tiny)
|
| 199 |
-
|
| 200 |
-
# ํต๊ณ ํ
์คํธ (์๋จ)
|
| 201 |
-
stats_text = f"GT:{len(gt_boxes)} | Pred:{len(predictions)} | TP:{tp} FP:{fp} FN:{fn}"
|
| 202 |
-
draw.rectangle([10, 10, 800, 50], fill="black")
|
| 203 |
-
draw.text((15, 15), stats_text, fill="white", font=font_small)
|
| 204 |
-
|
| 205 |
-
# ์ด๋ฏธ์ง ์ ๋ณด (ํ๋จ)
|
| 206 |
-
info_text = f"Confidence: {CONFIDENCE} | {img_name}"
|
| 207 |
-
img_width, img_height = image.size
|
| 208 |
-
draw.rectangle([10, img_height - 50, 800, img_height - 10], fill="black")
|
| 209 |
-
draw.text((15, img_height - 45), info_text, fill="yellow", font=font_tiny)
|
| 210 |
-
|
| 211 |
-
# ์ ์ฅ
|
| 212 |
-
output_path = os.path.join(output_dir, f"result_{base_name}")
|
| 213 |
-
result_image.save(output_path, quality=95)
|
| 214 |
-
print(f" โ
์ ์ฅ: {output_path}")
|
| 215 |
-
|
| 216 |
-
# ์ต์ข
ํต๊ณ
|
| 217 |
-
print("\n" + "=" * 60)
|
| 218 |
-
print("๐ ์ ์ฒด ๊ฒฐ๊ณผ (Val Set 10์ฅ):")
|
| 219 |
-
print("=" * 60)
|
| 220 |
-
print(f"์ด GT: {total_gt}๊ฐ")
|
| 221 |
-
print(f"TP: {total_tp}๊ฐ (์ฌ๋ฐ๋ฅธ ๊ฒ์ถ)")
|
| 222 |
-
print(f"FP: {total_fp}๊ฐ (์๋ชป๋ ๊ฒ์ถ)")
|
| 223 |
-
print(f"FN: {total_fn}๊ฐ (๋์น GT)")
|
| 224 |
-
|
| 225 |
-
precision = total_tp / (total_tp + total_fp) if (total_tp + total_fp) > 0 else 0
|
| 226 |
-
recall = total_tp / (total_tp + total_fn) if (total_tp + total_fn) > 0 else 0
|
| 227 |
-
f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
|
| 228 |
-
|
| 229 |
-
print(f"\nPrecision: {precision:.1%}")
|
| 230 |
-
print(f"Recall: {recall:.1%}")
|
| 231 |
-
print(f"F1 Score: {f1:.1%}")
|
| 232 |
-
|
| 233 |
-
print(f"\n๐ ๊ฒฐ๊ณผ ์ ์ฅ: {output_dir}/")
|
| 234 |
-
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,112 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
YOLOv8m ํ์ต ๋ชจ๋ธ ํ
์คํธ ์คํฌ๋ฆฝํธ
|
| 3 |
-
ํ์ต๋ ๋ชจ๋ธ๋ก ํ
์คํธ ์ด๋ฏธ์ง ๊ฒ์ถ ๋ฐ ๊ฒฐ๊ณผ ์ ์ฅ
|
| 4 |
-
"""
|
| 5 |
-
from ultralytics import YOLO
|
| 6 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 7 |
-
import os
|
| 8 |
-
import glob
|
| 9 |
-
|
| 10 |
-
# ํ์ต๋ ๋ชจ๋ธ ๋ก๋
|
| 11 |
-
MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 12 |
-
model = YOLO(MODEL_PATH)
|
| 13 |
-
|
| 14 |
-
print(f"โ
YOLOv8m ๋ชจ๋ธ ๋ก๋ ์๋ฃ: {MODEL_PATH}")
|
| 15 |
-
|
| 16 |
-
# ํ
์คํธ ์ด๋ฏธ์ง ๊ฒฝ๋ก
|
| 17 |
-
test_images_dir = "data/yolo_dataset/images/val"
|
| 18 |
-
output_dir = "test_results_yolov8m"
|
| 19 |
-
|
| 20 |
-
# ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ ์์ฑ
|
| 21 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 22 |
-
|
| 23 |
-
# ํ
์คํธ ์ด๋ฏธ์ง ์ฐพ๊ธฐ
|
| 24 |
-
test_images = sorted(glob.glob(os.path.join(test_images_dir, "*.jpg")))
|
| 25 |
-
|
| 26 |
-
if not test_images:
|
| 27 |
-
print(f"โ ํ
์คํธ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค: {test_images_dir}")
|
| 28 |
-
exit(1)
|
| 29 |
-
|
| 30 |
-
print(f"๐ ํ
์คํธ ์ด๋ฏธ์ง: {len(test_images)}์ฅ")
|
| 31 |
-
print(f"๐ ๊ฒฐ๊ณผ ์ ์ฅ ๊ฒฝ๋ก: {output_dir}/")
|
| 32 |
-
print("-" * 60)
|
| 33 |
-
|
| 34 |
-
# ํฐํธ ์ค์
|
| 35 |
-
try:
|
| 36 |
-
font = ImageFont.truetype("malgun.ttf", 20)
|
| 37 |
-
font_small = ImageFont.truetype("malgun.ttf", 14)
|
| 38 |
-
except:
|
| 39 |
-
font = ImageFont.load_default()
|
| 40 |
-
font_small = ImageFont.load_default()
|
| 41 |
-
|
| 42 |
-
# ๊ฐ ์ด๋ฏธ์ง ํ
์คํธ
|
| 43 |
-
total_detections = 0
|
| 44 |
-
for idx, img_path in enumerate(test_images, 1):
|
| 45 |
-
img_name = os.path.basename(img_path)
|
| 46 |
-
print(f"\n[{idx}/{len(test_images)}] {img_name}")
|
| 47 |
-
|
| 48 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 49 |
-
image = Image.open(img_path)
|
| 50 |
-
print(f" ๐ ์ด๋ฏธ์ง ํฌ๊ธฐ: {image.size}")
|
| 51 |
-
|
| 52 |
-
# YOLOv8 ๊ฒ์ถ (confidence threshold=0.065)
|
| 53 |
-
results = model.predict(
|
| 54 |
-
source=image,
|
| 55 |
-
conf=0.065,
|
| 56 |
-
iou=0.7,
|
| 57 |
-
device=0, # GPU ์ฌ์ฉ
|
| 58 |
-
verbose=False
|
| 59 |
-
)
|
| 60 |
-
|
| 61 |
-
# ๊ฒฐ๊ณผ ํ์ฑ
|
| 62 |
-
result = results[0]
|
| 63 |
-
boxes = result.boxes
|
| 64 |
-
|
| 65 |
-
detections = []
|
| 66 |
-
if boxes is not None and len(boxes) > 0:
|
| 67 |
-
for box in boxes:
|
| 68 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค ์ขํ
|
| 69 |
-
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
|
| 70 |
-
confidence = box.conf[0].cpu().item()
|
| 71 |
-
cls = int(box.cls[0].cpu().item())
|
| 72 |
-
|
| 73 |
-
detections.append({
|
| 74 |
-
'bbox': [float(x1), float(y1), float(x2), float(y2)],
|
| 75 |
-
'confidence': confidence,
|
| 76 |
-
'class': cls
|
| 77 |
-
})
|
| 78 |
-
|
| 79 |
-
print(f" ๐ฆ ๊ฒ์ถ: {len(detections)}๊ฐ")
|
| 80 |
-
total_detections += len(detections)
|
| 81 |
-
|
| 82 |
-
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ
|
| 83 |
-
result_image = image.copy()
|
| 84 |
-
draw = ImageDraw.Draw(result_image)
|
| 85 |
-
|
| 86 |
-
for i, det in enumerate(detections, 1):
|
| 87 |
-
x1, y1, x2, y2 = det['bbox']
|
| 88 |
-
conf = det['confidence']
|
| 89 |
-
|
| 90 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค (๋
น์, ๊ตต๊ฒ)
|
| 91 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=8)
|
| 92 |
-
|
| 93 |
-
# ๋ผ๋ฒจ
|
| 94 |
-
label = f"#{i} | {conf:.2%}"
|
| 95 |
-
bbox = draw.textbbox((x1, y1 - 25), label, font=font_small)
|
| 96 |
-
draw.rectangle(bbox, fill="lime")
|
| 97 |
-
draw.text((x1, y1 - 25), label, fill="black", font=font_small)
|
| 98 |
-
|
| 99 |
-
print(f" #{i}: conf={conf:.2%}, bbox=[{x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f}]")
|
| 100 |
-
|
| 101 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 102 |
-
output_path = os.path.join(output_dir, f"result_{img_name}")
|
| 103 |
-
result_image.save(output_path)
|
| 104 |
-
print(f" โ
์ ์ฅ: {output_path}")
|
| 105 |
-
|
| 106 |
-
print("\n" + "=" * 60)
|
| 107 |
-
print(f"๐ ์ ์ฒด ๊ฒฐ๊ณผ:")
|
| 108 |
-
print(f" - ํ
์คํธ ์ด๋ฏธ์ง: {len(test_images)}์ฅ")
|
| 109 |
-
print(f" - ์ด ๊ฒ์ถ: {total_detections}๊ฐ")
|
| 110 |
-
print(f" - ํ๊ท : {total_detections / len(test_images):.1f}๊ฐ/์ด๋ฏธ์ง")
|
| 111 |
-
print(f" - ๊ฒฐ๊ณผ ์ ์ฅ: {output_dir}/")
|
| 112 |
-
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,144 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
YOLOv8m Unseen Test Set ํ๊ฐ
|
| 3 |
-
251010, 251017 ํด๋์ ์์ ํ ์๋ก์ด ์ด๋ฏธ์ง 20๊ฐ๋ก ํ
์คํธ
|
| 4 |
-
"""
|
| 5 |
-
from ultralytics import YOLO
|
| 6 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 7 |
-
import os
|
| 8 |
-
import glob
|
| 9 |
-
|
| 10 |
-
# ํ์ต๋ ๋ชจ๋ธ ๋ก๋
|
| 11 |
-
MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 12 |
-
model = YOLO(MODEL_PATH)
|
| 13 |
-
|
| 14 |
-
# ์ต์ confidence
|
| 15 |
-
CONFIDENCE = 0.85
|
| 16 |
-
|
| 17 |
-
print(f"โ
YOLOv8m ๋ชจ๋ธ ๋ก๋: {MODEL_PATH}")
|
| 18 |
-
print(f"๐ฏ Confidence Threshold: {CONFIDENCE}")
|
| 19 |
-
|
| 20 |
-
# Unseen test ์ด๋ฏธ์ง ๊ฒฝ๋ก
|
| 21 |
-
test_folders = [
|
| 22 |
-
"data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)/251010",
|
| 23 |
-
"data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)/251017"
|
| 24 |
-
]
|
| 25 |
-
|
| 26 |
-
test_images = []
|
| 27 |
-
for folder in test_folders:
|
| 28 |
-
# -1.jpg ์ ์ธ, ์๋ณธ ์ด๋ฏธ์ง๋ง
|
| 29 |
-
images = sorted(glob.glob(os.path.join(folder, "*_[0-9][0-9].jpg")))
|
| 30 |
-
test_images.extend(images)
|
| 31 |
-
|
| 32 |
-
print(f"๐ Unseen test ์ด๋ฏธ์ง: {len(test_images)}์ฅ")
|
| 33 |
-
print("-" * 60)
|
| 34 |
-
|
| 35 |
-
# ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ
|
| 36 |
-
output_dir = "test_results_unseen"
|
| 37 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 38 |
-
|
| 39 |
-
# ํฐํธ ์ค์
|
| 40 |
-
try:
|
| 41 |
-
font = ImageFont.truetype("malgun.ttf", 24)
|
| 42 |
-
font_small = ImageFont.truetype("malgun.ttf", 18)
|
| 43 |
-
font_tiny = ImageFont.truetype("malgun.ttf", 14)
|
| 44 |
-
except:
|
| 45 |
-
font = ImageFont.load_default()
|
| 46 |
-
font_small = ImageFont.load_default()
|
| 47 |
-
font_tiny = ImageFont.load_default()
|
| 48 |
-
|
| 49 |
-
# ํต๊ณ
|
| 50 |
-
total_detections = 0
|
| 51 |
-
images_with_detections = 0
|
| 52 |
-
images_without_detections = 0
|
| 53 |
-
|
| 54 |
-
print("\n๐ Unseen Test ๊ฒ์ถ ์์...\n")
|
| 55 |
-
|
| 56 |
-
# ๊ฐ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
|
| 57 |
-
for idx, img_path in enumerate(test_images, 1):
|
| 58 |
-
img_name = os.path.basename(img_path)
|
| 59 |
-
folder_name = os.path.basename(os.path.dirname(img_path))
|
| 60 |
-
|
| 61 |
-
print(f"[{idx}/{len(test_images)}] {folder_name}/{img_name}")
|
| 62 |
-
|
| 63 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 64 |
-
image = Image.open(img_path)
|
| 65 |
-
print(f" ๐ ํฌ๊ธฐ: {image.size}")
|
| 66 |
-
|
| 67 |
-
# YOLOv8 ๊ฒ์ถ
|
| 68 |
-
results = model.predict(
|
| 69 |
-
source=image,
|
| 70 |
-
conf=CONFIDENCE,
|
| 71 |
-
iou=0.7,
|
| 72 |
-
device=0,
|
| 73 |
-
verbose=False
|
| 74 |
-
)
|
| 75 |
-
|
| 76 |
-
result = results[0]
|
| 77 |
-
boxes = result.boxes
|
| 78 |
-
|
| 79 |
-
predictions = []
|
| 80 |
-
if boxes is not None and len(boxes) > 0:
|
| 81 |
-
for box in boxes:
|
| 82 |
-
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
|
| 83 |
-
confidence = box.conf[0].cpu().item()
|
| 84 |
-
predictions.append({
|
| 85 |
-
'bbox': [float(x1), float(y1), float(x2), float(y2)],
|
| 86 |
-
'confidence': confidence
|
| 87 |
-
})
|
| 88 |
-
|
| 89 |
-
num_detections = len(predictions)
|
| 90 |
-
total_detections += num_detections
|
| 91 |
-
|
| 92 |
-
if num_detections > 0:
|
| 93 |
-
images_with_detections += 1
|
| 94 |
-
print(f" ๐ฆ ๊ฒ์ถ: {num_detections}๊ฐ")
|
| 95 |
-
else:
|
| 96 |
-
images_without_detections += 1
|
| 97 |
-
print(f" โช ๊ฒ์ถ ์์")
|
| 98 |
-
|
| 99 |
-
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ
|
| 100 |
-
result_image = image.copy()
|
| 101 |
-
draw = ImageDraw.Draw(result_image)
|
| 102 |
-
|
| 103 |
-
# Predictions (๋
น์ ๋ฐ์ค)
|
| 104 |
-
for pred_idx, pred in enumerate(predictions, 1):
|
| 105 |
-
x1, y1, x2, y2 = pred['bbox']
|
| 106 |
-
conf = pred['confidence']
|
| 107 |
-
|
| 108 |
-
# ๋
น์ ๋ฐ์ค
|
| 109 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=10)
|
| 110 |
-
|
| 111 |
-
# ๋ผ๋ฒจ
|
| 112 |
-
label = f"#{pred_idx} | {conf:.0%}"
|
| 113 |
-
bbox_label = draw.textbbox((x1, y2 + 5), label, font=font_small)
|
| 114 |
-
draw.rectangle(bbox_label, fill="lime")
|
| 115 |
-
draw.text((x1, y2 + 5), label, fill="black", font=font_small)
|
| 116 |
-
|
| 117 |
-
# ํต๊ณ ํ
์คํธ (์๋จ)
|
| 118 |
-
stats_text = f"Detections: {num_detections} | Confidence: {CONFIDENCE}"
|
| 119 |
-
draw.rectangle([10, 10, 800, 50], fill="black")
|
| 120 |
-
draw.text((15, 15), stats_text, fill="white", font=font_small)
|
| 121 |
-
|
| 122 |
-
# ์ด๋ฏธ์ง ์ ๋ณด (ํ๋จ)
|
| 123 |
-
info_text = f"{folder_name}/{img_name}"
|
| 124 |
-
img_width, img_height = image.size
|
| 125 |
-
draw.rectangle([10, img_height - 50, 800, img_height - 10], fill="black")
|
| 126 |
-
draw.text((15, img_height - 45), info_text, fill="yellow", font=font_tiny)
|
| 127 |
-
|
| 128 |
-
# ์ ์ฅ
|
| 129 |
-
output_path = os.path.join(output_dir, f"result_{folder_name}_{img_name}")
|
| 130 |
-
result_image.save(output_path, quality=95)
|
| 131 |
-
print(f" โ
์ ์ฅ: {output_path}")
|
| 132 |
-
|
| 133 |
-
# ์ต์ข
ํต๊ณ
|
| 134 |
-
print("\n" + "=" * 60)
|
| 135 |
-
print("๐ Unseen Test Set ๊ฒฐ๊ณผ:")
|
| 136 |
-
print("=" * 60)
|
| 137 |
-
print(f"์ด ์ด๋ฏธ์ง: {len(test_images)}์ฅ")
|
| 138 |
-
print(f"์ด ๊ฒ์ถ: {total_detections}๊ฐ")
|
| 139 |
-
print(f"ํ๊ท ๊ฒ์ถ: {total_detections / len(test_images):.1f}๊ฐ/์ด๋ฏธ์ง")
|
| 140 |
-
print(f"\n๊ฒ์ถ ์์: {images_with_detections}์ฅ ({images_with_detections/len(test_images)*100:.1f}%)")
|
| 141 |
-
print(f"๊ฒ์ถ ์์: {images_without_detections}์ฅ ({images_without_detections/len(test_images)*100:.1f}%)")
|
| 142 |
-
|
| 143 |
-
print(f"\n๐ ๊ฒฐ๊ณผ ์ ์ฅ: {output_dir}/")
|
| 144 |
-
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,367 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
YOLOv8m + Universal Filter ํ
์คํธ
|
| 3 |
-
์ ์ฒด ๋ฐ์ดํฐ์
์ผ๋ก ํ
์คํธ ๋ฐ ํํฐ๋ง ์ฑ๋ฅ ๊ฐ์
|
| 4 |
-
"""
|
| 5 |
-
from ultralytics import YOLO
|
| 6 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 7 |
-
import os
|
| 8 |
-
import glob
|
| 9 |
-
import numpy as np
|
| 10 |
-
import json
|
| 11 |
-
|
| 12 |
-
# OpenCV for filter functions
|
| 13 |
-
import cv2
|
| 14 |
-
|
| 15 |
-
def calculate_morphological_features(bbox, img_size):
|
| 16 |
-
"""ํํํ์ ํน์ง ๊ณ์ฐ"""
|
| 17 |
-
x1, y1, x2, y2 = bbox
|
| 18 |
-
width = x2 - x1
|
| 19 |
-
height = y2 - y1
|
| 20 |
-
area = width * height
|
| 21 |
-
|
| 22 |
-
aspect_ratio = height / width if width > 0 else 0
|
| 23 |
-
|
| 24 |
-
# Compactness (0~1, 1์ ๊ฐ๊น์ธ์๋ก ์ ์ฌ๊ฐํ)
|
| 25 |
-
perimeter = 2 * (width + height)
|
| 26 |
-
compactness = (4 * np.pi * area) / (perimeter ** 2) if perimeter > 0 else 0
|
| 27 |
-
|
| 28 |
-
# ์ด๋ฏธ์ง ๋ด ๋น์จ
|
| 29 |
-
img_area = img_size[0] * img_size[1]
|
| 30 |
-
area_ratio = area / img_area if img_area > 0 else 0
|
| 31 |
-
|
| 32 |
-
return {
|
| 33 |
-
'width': width,
|
| 34 |
-
'height': height,
|
| 35 |
-
'area': area,
|
| 36 |
-
'aspect_ratio': aspect_ratio,
|
| 37 |
-
'compactness': compactness,
|
| 38 |
-
'area_ratio': area_ratio
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
def calculate_visual_features(image_pil, bbox):
|
| 42 |
-
"""์๊ฐ์ ํน์ง ๊ณ์ฐ"""
|
| 43 |
-
image_cv = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
|
| 44 |
-
x1, y1, x2, y2 = [int(v) for v in bbox]
|
| 45 |
-
|
| 46 |
-
roi = image_cv[y1:y2, x1:x2]
|
| 47 |
-
if roi.size == 0:
|
| 48 |
-
return {'hue': 100, 'saturation': 255, 'color_std': 255}
|
| 49 |
-
|
| 50 |
-
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
| 51 |
-
|
| 52 |
-
return {
|
| 53 |
-
'hue': np.mean(hsv[:, :, 0]),
|
| 54 |
-
'saturation': np.mean(hsv[:, :, 1]),
|
| 55 |
-
'color_std': np.std(hsv[:, :, 0])
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
def calculate_iou_simple(bbox1, bbox2):
|
| 59 |
-
"""๊ฐ๋จํ IoU ๊ณ์ฐ"""
|
| 60 |
-
x1_min, y1_min, x1_max, y1_max = bbox1
|
| 61 |
-
x2_min, y2_min, x2_max, y2_max = bbox2
|
| 62 |
-
|
| 63 |
-
inter_x_min = max(x1_min, x2_min)
|
| 64 |
-
inter_y_min = max(y1_min, y2_min)
|
| 65 |
-
inter_x_max = min(x1_max, x2_max)
|
| 66 |
-
inter_y_max = min(y1_max, y2_max)
|
| 67 |
-
|
| 68 |
-
if inter_x_max < inter_x_min or inter_y_max < inter_y_min:
|
| 69 |
-
return 0.0
|
| 70 |
-
|
| 71 |
-
inter_area = (inter_x_max - inter_x_min) * (inter_y_max - inter_y_min)
|
| 72 |
-
|
| 73 |
-
bbox1_area = (x1_max - x1_min) * (y1_max - y1_min)
|
| 74 |
-
bbox2_area = (x2_max - x2_min) * (y2_max - y2_min)
|
| 75 |
-
union_area = bbox1_area + bbox2_area - inter_area
|
| 76 |
-
|
| 77 |
-
return inter_area / union_area if union_area > 0 else 0.0
|
| 78 |
-
|
| 79 |
-
def apply_universal_filter(detections, image, threshold=90):
|
| 80 |
-
"""๋ฒ์ฉ ์์ฐ ํํฐ ์ ์ฉ"""
|
| 81 |
-
img_size = image.size
|
| 82 |
-
filtered = []
|
| 83 |
-
|
| 84 |
-
for det in detections:
|
| 85 |
-
bbox = det['bbox']
|
| 86 |
-
morph = calculate_morphological_features(bbox, img_size)
|
| 87 |
-
visual = calculate_visual_features(image, bbox)
|
| 88 |
-
|
| 89 |
-
score = 0
|
| 90 |
-
reasons = []
|
| 91 |
-
|
| 92 |
-
# Aspect ratio (4:1 ~ 9:1)
|
| 93 |
-
if 4.0 <= morph['aspect_ratio'] <= 9.0:
|
| 94 |
-
score += 25
|
| 95 |
-
reasons.append(f"โ ์ข
ํก๋น {morph['aspect_ratio']:.1f}")
|
| 96 |
-
elif 3.0 <= morph['aspect_ratio'] < 4.0 or 9.0 < morph['aspect_ratio'] <= 10.0:
|
| 97 |
-
score += 12
|
| 98 |
-
reasons.append(f"โณ ์ข
ํก๋น {morph['aspect_ratio']:.1f}")
|
| 99 |
-
else:
|
| 100 |
-
score -= 5
|
| 101 |
-
reasons.append(f"โ ์ข
ํก๋น {morph['aspect_ratio']:.1f}")
|
| 102 |
-
|
| 103 |
-
# Compactness (์ธ์ฅ๋)
|
| 104 |
-
if morph['compactness'] < 0.40:
|
| 105 |
-
score += 30
|
| 106 |
-
reasons.append(f"โ ์ธ์ฅ๋ {morph['compactness']:.2f}")
|
| 107 |
-
elif 0.40 <= morph['compactness'] < 0.50:
|
| 108 |
-
score += 15
|
| 109 |
-
reasons.append(f"โณ ์ธ์ฅ๋ {morph['compactness']:.2f}")
|
| 110 |
-
else:
|
| 111 |
-
score -= 20
|
| 112 |
-
reasons.append(f"โ ์ธ์ฅ๋ {morph['compactness']:.2f}")
|
| 113 |
-
|
| 114 |
-
# Area
|
| 115 |
-
abs_area = morph['width'] * morph['height']
|
| 116 |
-
if 50000 <= abs_area <= 500000:
|
| 117 |
-
score += 35
|
| 118 |
-
reasons.append(f"โ ๋ฉด์ {abs_area/1000:.0f}K")
|
| 119 |
-
elif 500000 < abs_area <= 800000:
|
| 120 |
-
score -= 10
|
| 121 |
-
reasons.append(f"โณ ๋ฉด์ {abs_area/1000:.0f}K")
|
| 122 |
-
elif abs_area > 800000:
|
| 123 |
-
score -= 30
|
| 124 |
-
reasons.append(f"โ ๋ฉด์ {abs_area/1000:.0f}K (๋๋ฌดํผ)")
|
| 125 |
-
else:
|
| 126 |
-
score -= 10
|
| 127 |
-
reasons.append(f"โ ๋ฉด์ {abs_area/1000:.0f}K (๋๋ฌด์์)")
|
| 128 |
-
|
| 129 |
-
# Hue (์์)
|
| 130 |
-
hue = visual['hue']
|
| 131 |
-
if hue < 40 or hue > 130:
|
| 132 |
-
score += 10
|
| 133 |
-
reasons.append(f"โ ์์ {hue:.0f}")
|
| 134 |
-
elif 90 <= hue <= 130:
|
| 135 |
-
score -= 5
|
| 136 |
-
reasons.append(f"โ ์์ {hue:.0f} (๋ฐฐ๊ฒฝ)")
|
| 137 |
-
else:
|
| 138 |
-
reasons.append(f"โณ ์์ {hue:.0f}")
|
| 139 |
-
|
| 140 |
-
# Saturation (์ฑ๋)
|
| 141 |
-
if visual['saturation'] < 85:
|
| 142 |
-
score += 20
|
| 143 |
-
reasons.append(f"โ ์ฑ๋ {visual['saturation']:.0f}")
|
| 144 |
-
elif 85 <= visual['saturation'] < 120:
|
| 145 |
-
score += 5
|
| 146 |
-
reasons.append(f"โณ ์ฑ๋ {visual['saturation']:.0f}")
|
| 147 |
-
else:
|
| 148 |
-
score -= 15
|
| 149 |
-
reasons.append(f"โ ์ฑ๋ {visual['saturation']:.0f} (๋์)")
|
| 150 |
-
|
| 151 |
-
# Color consistency
|
| 152 |
-
if visual['color_std'] < 50:
|
| 153 |
-
score += 15
|
| 154 |
-
reasons.append(f"โ ์์์ผ๊ด์ฑ {visual['color_std']:.1f}")
|
| 155 |
-
elif 50 <= visual['color_std'] < 80:
|
| 156 |
-
score += 5
|
| 157 |
-
reasons.append(f"โณ ์์์ผ๊ด์ฑ {visual['color_std']:.1f}")
|
| 158 |
-
else:
|
| 159 |
-
score -= 10
|
| 160 |
-
reasons.append(f"โ ์์์ผ๊ด์ฑ {visual['color_std']:.1f}")
|
| 161 |
-
|
| 162 |
-
# YOLOv8 confidence
|
| 163 |
-
score += det['confidence'] * 15
|
| 164 |
-
|
| 165 |
-
det['filter_score'] = score
|
| 166 |
-
det['filter_reasons'] = reasons
|
| 167 |
-
det['morph_features'] = morph
|
| 168 |
-
det['visual_features'] = visual
|
| 169 |
-
|
| 170 |
-
if score >= threshold:
|
| 171 |
-
filtered.append(det)
|
| 172 |
-
|
| 173 |
-
# ์ ์ ์์ผ๋ก ์ ๋ ฌ
|
| 174 |
-
filtered.sort(key=lambda x: x['filter_score'], reverse=True)
|
| 175 |
-
|
| 176 |
-
# NMS (Non-Maximum Suppression)
|
| 177 |
-
filtered_nms = []
|
| 178 |
-
for det in filtered:
|
| 179 |
-
is_duplicate = False
|
| 180 |
-
for kept_det in filtered_nms:
|
| 181 |
-
iou = calculate_iou_simple(det['bbox'], kept_det['bbox'])
|
| 182 |
-
if iou > 0.5:
|
| 183 |
-
is_duplicate = True
|
| 184 |
-
break
|
| 185 |
-
|
| 186 |
-
if not is_duplicate:
|
| 187 |
-
filtered_nms.append(det)
|
| 188 |
-
|
| 189 |
-
return filtered_nms
|
| 190 |
-
|
| 191 |
-
# ํ์ต๋ ๋ชจ๋ธ ๋ก๋
|
| 192 |
-
MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt"
|
| 193 |
-
model = YOLO(MODEL_PATH)
|
| 194 |
-
|
| 195 |
-
print(f"โ
YOLOv8m ๋ชจ๋ธ ๋ก๋ ์๋ฃ: {MODEL_PATH}")
|
| 196 |
-
|
| 197 |
-
# ์ ์ฒด ๋ฐ์ดํฐ์
ํ
์คํธ (train + val)
|
| 198 |
-
test_images = []
|
| 199 |
-
for split in ['train', 'val']:
|
| 200 |
-
split_dir = f"data/yolo_dataset/images/{split}"
|
| 201 |
-
if os.path.exists(split_dir):
|
| 202 |
-
test_images.extend(sorted(glob.glob(os.path.join(split_dir, "*.jpg"))))
|
| 203 |
-
|
| 204 |
-
output_dir = "test_results_yolov8m_filtered"
|
| 205 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 206 |
-
|
| 207 |
-
print(f"๐ ํ
์คํธ ์ด๋ฏธ์ง: {len(test_images)}์ฅ")
|
| 208 |
-
print(f"๐ ๊ฒฐ๊ณผ ์ ์ฅ ๊ฒฝ๋ก: {output_dir}/")
|
| 209 |
-
print("-" * 60)
|
| 210 |
-
|
| 211 |
-
# ํฐํธ ์ค์
|
| 212 |
-
try:
|
| 213 |
-
font = ImageFont.truetype("malgun.ttf", 20)
|
| 214 |
-
font_small = ImageFont.truetype("malgun.ttf", 14)
|
| 215 |
-
font_tiny = ImageFont.truetype("malgun.ttf", 12)
|
| 216 |
-
except:
|
| 217 |
-
font = ImageFont.load_default()
|
| 218 |
-
font_small = ImageFont.load_default()
|
| 219 |
-
font_tiny = ImageFont.load_default()
|
| 220 |
-
|
| 221 |
-
# ํต๊ณ ๋ณ์
|
| 222 |
-
stats_no_filter = {'total': 0, 'per_image': []}
|
| 223 |
-
stats_filtered = {'total': 0, 'per_image': []}
|
| 224 |
-
filter_thresholds = [50, 60, 70, 80, 90] # ๋ค์ํ ์๊ณ๊ฐ ํ
์คํธ
|
| 225 |
-
stats_by_threshold = {th: {'total': 0, 'per_image': []} for th in filter_thresholds}
|
| 226 |
-
|
| 227 |
-
# ๊ฐ ์ด๋ฏธ์ง ํ
์คํธ
|
| 228 |
-
for idx, img_path in enumerate(test_images, 1):
|
| 229 |
-
img_name = os.path.basename(img_path)
|
| 230 |
-
|
| 231 |
-
if idx % 10 == 0 or idx == 1:
|
| 232 |
-
print(f"\n[{idx}/{len(test_images)}] {img_name}")
|
| 233 |
-
|
| 234 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 235 |
-
image = Image.open(img_path)
|
| 236 |
-
|
| 237 |
-
# YOLOv8 ๊ฒ์ถ (confidence threshold=0.065)
|
| 238 |
-
results = model.predict(
|
| 239 |
-
source=image,
|
| 240 |
-
conf=0.065,
|
| 241 |
-
iou=0.7,
|
| 242 |
-
device=0,
|
| 243 |
-
verbose=False
|
| 244 |
-
)
|
| 245 |
-
|
| 246 |
-
# ๊ฒฐ๊ณผ ํ์ฑ
|
| 247 |
-
result = results[0]
|
| 248 |
-
boxes = result.boxes
|
| 249 |
-
|
| 250 |
-
detections_raw = []
|
| 251 |
-
if boxes is not None and len(boxes) > 0:
|
| 252 |
-
for box in boxes:
|
| 253 |
-
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
|
| 254 |
-
confidence = box.conf[0].cpu().item()
|
| 255 |
-
|
| 256 |
-
detections_raw.append({
|
| 257 |
-
'bbox': [float(x1), float(y1), float(x2), float(y2)],
|
| 258 |
-
'confidence': confidence
|
| 259 |
-
})
|
| 260 |
-
|
| 261 |
-
stats_no_filter['total'] += len(detections_raw)
|
| 262 |
-
stats_no_filter['per_image'].append(len(detections_raw))
|
| 263 |
-
|
| 264 |
-
# Universal Filter ์ ์ฉ
|
| 265 |
-
detections_scored = apply_universal_filter(detections_raw, image, threshold=0)
|
| 266 |
-
|
| 267 |
-
# ๋ค์ํ ์๊ณ๊ฐ์ผ๋ก ํํฐ๋ง ํ
์คํธ
|
| 268 |
-
for threshold in filter_thresholds:
|
| 269 |
-
filtered = [det for det in detections_scored if det['filter_score'] >= threshold]
|
| 270 |
-
stats_by_threshold[threshold]['total'] += len(filtered)
|
| 271 |
-
stats_by_threshold[threshold]['per_image'].append(len(filtered))
|
| 272 |
-
|
| 273 |
-
# ๊ธฐ๋ณธ ์๊ณ๊ฐ 90 ์ฌ์ฉ
|
| 274 |
-
detections_filtered = [det for det in detections_scored if det['filter_score'] >= 90]
|
| 275 |
-
stats_filtered['total'] += len(detections_filtered)
|
| 276 |
-
stats_filtered['per_image'].append(len(detections_filtered))
|
| 277 |
-
|
| 278 |
-
# ์ฒ์ 10๊ฐ ์ด๋ฏธ์ง๋ง ๊ฒฐ๊ณผ ์ ์ฅ
|
| 279 |
-
if idx <= 10:
|
| 280 |
-
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ
|
| 281 |
-
result_image = image.copy()
|
| 282 |
-
draw = ImageDraw.Draw(result_image)
|
| 283 |
-
|
| 284 |
-
# ํํฐ๋ง๋ ๊ฒ์ถ (๋
น์)
|
| 285 |
-
for i, det in enumerate(detections_filtered, 1):
|
| 286 |
-
x1, y1, x2, y2 = det['bbox']
|
| 287 |
-
score = det['filter_score']
|
| 288 |
-
conf = det['confidence']
|
| 289 |
-
|
| 290 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=8)
|
| 291 |
-
label = f"#{i} | F:{score:.0f} C:{conf:.0%}"
|
| 292 |
-
bbox_label = draw.textbbox((x1, y1 - 25), label, font=font_tiny)
|
| 293 |
-
draw.rectangle(bbox_label, fill="lime")
|
| 294 |
-
draw.text((x1, y1 - 25), label, fill="black", font=font_tiny)
|
| 295 |
-
|
| 296 |
-
# ์ ๊ฑฐ๋ ๊ฒ์ถ (๋นจ๊ฐ์, ๋ฐํฌ๋ช
)
|
| 297 |
-
for det in detections_raw:
|
| 298 |
-
if det not in [d for d in detections_filtered]:
|
| 299 |
-
x1, y1, x2, y2 = det['bbox']
|
| 300 |
-
# ํํฐ ์ ์ ์ฐพ๊ธฐ
|
| 301 |
-
scored_det = next((d for d in detections_scored if d['bbox'] == det['bbox']), None)
|
| 302 |
-
if scored_det:
|
| 303 |
-
score = scored_det['filter_score']
|
| 304 |
-
draw.rectangle([x1, y1, x2, y2], outline="red", width=4)
|
| 305 |
-
label = f"X:{score:.0f}"
|
| 306 |
-
bbox_label = draw.textbbox((x1, y1 - 20), label, font=font_tiny)
|
| 307 |
-
draw.rectangle(bbox_label, fill="red")
|
| 308 |
-
draw.text((x1, y1 - 20), label, fill="white", font=font_tiny)
|
| 309 |
-
|
| 310 |
-
# ํต๊ณ ํ
์คํธ ์ถ๊ฐ
|
| 311 |
-
info_text = f"Raw: {len(detections_raw)} | Filtered (90): {len(detections_filtered)}"
|
| 312 |
-
draw.text((10, 10), info_text, fill="yellow", font=font)
|
| 313 |
-
|
| 314 |
-
# ๊ฒฐ๊ณผ ์ ์ฅ
|
| 315 |
-
output_path = os.path.join(output_dir, f"result_{img_name}")
|
| 316 |
-
result_image.save(output_path)
|
| 317 |
-
|
| 318 |
-
if idx % 10 == 0 or idx == 1:
|
| 319 |
-
print(f" Raw: {len(detections_raw)}, Filtered: {len(detections_filtered)}")
|
| 320 |
-
print(f" โ
์ ์ฅ: {output_path}")
|
| 321 |
-
|
| 322 |
-
# ์ต์ข
ํต๊ณ ์ถ๋ ฅ
|
| 323 |
-
print("\n" + "=" * 60)
|
| 324 |
-
print("๐ ์ ์ฒด ๊ฒฐ๊ณผ (ํํฐ๋ง ์ ํ ๋น๊ต):")
|
| 325 |
-
print("=" * 60)
|
| 326 |
-
print(f"\n1๏ธโฃ ํํฐ๋ง ์ (Raw YOLOv8):")
|
| 327 |
-
print(f" - ์ด ๊ฒ์ถ: {stats_no_filter['total']}๊ฐ")
|
| 328 |
-
print(f" - ํ๊ท : {stats_no_filter['total'] / len(test_images):.1f}๊ฐ/์ด๋ฏธ์ง")
|
| 329 |
-
|
| 330 |
-
print(f"\n2๏ธโฃ ํํฐ๋ง ํ ์ฑ๋ฅ ๋น๊ต:")
|
| 331 |
-
for threshold in filter_thresholds:
|
| 332 |
-
total = stats_by_threshold[threshold]['total']
|
| 333 |
-
avg = total / len(test_images)
|
| 334 |
-
reduction = (1 - total / stats_no_filter['total']) * 100 if stats_no_filter['total'] > 0 else 0
|
| 335 |
-
print(f" Threshold {threshold}: {total}๊ฐ (ํ๊ท {avg:.1f}/์ด๋ฏธ์ง, -{reduction:.1f}% ๊ฐ์)")
|
| 336 |
-
|
| 337 |
-
print(f"\n3๏ธโฃ ๊ถ์ฅ ์ค์ (Threshold 90):")
|
| 338 |
-
print(f" - ์ด ๊ฒ์ถ: {stats_filtered['total']}๊ฐ")
|
| 339 |
-
print(f" - ํ๊ท : {stats_filtered['total'] / len(test_images):.1f}๊ฐ/์ด๋ฏธ์ง")
|
| 340 |
-
reduction = (1 - stats_filtered['total'] / stats_no_filter['total']) * 100 if stats_no_filter['total'] > 0 else 0
|
| 341 |
-
print(f" - False Positive ๊ฐ์: {reduction:.1f}%")
|
| 342 |
-
|
| 343 |
-
print(f"\n๐ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ ์ฅ: {output_dir}/ (์ฒ์ 10์ฅ)")
|
| 344 |
-
print("=" * 60)
|
| 345 |
-
|
| 346 |
-
# ํต๊ณ๋ฅผ JSON์ผ๋ก ์ ์ฅ
|
| 347 |
-
stats_summary = {
|
| 348 |
-
'total_images': len(test_images),
|
| 349 |
-
'no_filter': {
|
| 350 |
-
'total': stats_no_filter['total'],
|
| 351 |
-
'average': stats_no_filter['total'] / len(test_images)
|
| 352 |
-
},
|
| 353 |
-
'by_threshold': {}
|
| 354 |
-
}
|
| 355 |
-
|
| 356 |
-
for threshold in filter_thresholds:
|
| 357 |
-
total = stats_by_threshold[threshold]['total']
|
| 358 |
-
stats_summary['by_threshold'][threshold] = {
|
| 359 |
-
'total': total,
|
| 360 |
-
'average': total / len(test_images),
|
| 361 |
-
'reduction_pct': (1 - total / stats_no_filter['total']) * 100 if stats_no_filter['total'] > 0 else 0
|
| 362 |
-
}
|
| 363 |
-
|
| 364 |
-
with open(os.path.join(output_dir, 'filter_statistics.json'), 'w', encoding='utf-8') as f:
|
| 365 |
-
json.dump(stats_summary, f, indent=2, ensure_ascii=False)
|
| 366 |
-
|
| 367 |
-
print(f"\n๐พ ํต๊ณ ์ ์ฅ: {output_dir}/filter_statistics.json")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,127 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
YOLOv8 Few-shot Training for Shrimp Detection
|
| 4 |
-
์ต์ ๋ฐ์ดํฐ(50๊ฐ)๋ก ๋น ๋ฅธ ํ์ต
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
from ultralytics import YOLO
|
| 10 |
-
import torch
|
| 11 |
-
from pathlib import Path
|
| 12 |
-
import yaml
|
| 13 |
-
|
| 14 |
-
def main():
|
| 15 |
-
print("=" * 60)
|
| 16 |
-
print("๐ฆ YOLOv8 Few-shot Training ์์")
|
| 17 |
-
print("=" * 60)
|
| 18 |
-
|
| 19 |
-
# GPU ํ์ธ
|
| 20 |
-
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
| 21 |
-
print(f"\n๐ฅ๏ธ Device: {device}")
|
| 22 |
-
if device == 'cuda':
|
| 23 |
-
print(f" GPU: {torch.cuda.get_device_name(0)}")
|
| 24 |
-
print(f" VRAM: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB")
|
| 25 |
-
else:
|
| 26 |
-
print(" โ ๏ธ GPU ์์ - CPU๋ก ํ์ต (๋๋ฆผ)")
|
| 27 |
-
|
| 28 |
-
# ๋ฐ์ดํฐ์
๊ฒฝ๋ก
|
| 29 |
-
data_yaml = "data/yolo_dataset/data.yaml"
|
| 30 |
-
|
| 31 |
-
# data.yaml ํ์ธ
|
| 32 |
-
print(f"\n๐ ๋ฐ์ดํฐ์
์ค์ ๋ก๋ฉ: {data_yaml}")
|
| 33 |
-
with open(data_yaml, 'r', encoding='utf-8') as f:
|
| 34 |
-
data_config = yaml.safe_load(f)
|
| 35 |
-
print(f" - Classes: {data_config['names']}")
|
| 36 |
-
print(f" - Train: {data_config['train']}")
|
| 37 |
-
print(f" - Val: {data_config['val']}")
|
| 38 |
-
|
| 39 |
-
# YOLOv8 ๋ชจ๋ธ ์ ํ
|
| 40 |
-
# yolov8n: nano (๊ฐ์ฅ ๋น ๋ฆ, ์์ ๋ฐ์ดํฐ์
์ ์ ํฉ)
|
| 41 |
-
# yolov8s: small
|
| 42 |
-
# yolov8m: medium
|
| 43 |
-
model_size = 'n' # nano - Few-shot์ ์ต์
|
| 44 |
-
print(f"\n๐ค ๋ชจ๋ธ: YOLOv8{model_size} (nano)")
|
| 45 |
-
print(" - ์ด์ : ์์ ๋ฐ์ดํฐ์
(50๊ฐ)์์ ๊ณผ์ ํฉ ๋ฐฉ์ง")
|
| 46 |
-
|
| 47 |
-
# ๋ชจ๋ธ ๋ก๋ (์ฌ์ ํ์ต ๊ฐ์ค์น ํฌํจ)
|
| 48 |
-
model = YOLO(f'yolov8{model_size}.pt')
|
| 49 |
-
|
| 50 |
-
# ํ์ต ํ์ดํผํ๋ผ๋ฏธํฐ
|
| 51 |
-
epochs = 100 # Few-shot์ด๋ฏ๋ก ๋ง์ epoch
|
| 52 |
-
batch_size = 8 # ์์ ๋ฐฐ์น (๋ฐ์ดํฐ ์ ์)
|
| 53 |
-
imgsz = 640 # ์
๋ ฅ ์ด๋ฏธ์ง ํฌ๊ธฐ
|
| 54 |
-
patience = 20 # Early stopping patience
|
| 55 |
-
|
| 56 |
-
print(f"\nโ๏ธ ํ์ต ์ค์ :")
|
| 57 |
-
print(f" - Epochs: {epochs}")
|
| 58 |
-
print(f" - Batch size: {batch_size}")
|
| 59 |
-
print(f" - Image size: {imgsz}")
|
| 60 |
-
print(f" - Patience: {patience} (early stopping)")
|
| 61 |
-
print(f" - Device: {device}")
|
| 62 |
-
|
| 63 |
-
# Data Augmentation ์ค์ (Few-shot์ ์ค์!)
|
| 64 |
-
print(f"\n๐ Data Augmentation:")
|
| 65 |
-
augment_params = {
|
| 66 |
-
'hsv_h': 0.015, # Hue augmentation (์์ ๋ณํ)
|
| 67 |
-
'hsv_s': 0.7, # Saturation augmentation
|
| 68 |
-
'hsv_v': 0.4, # Value (brightness) augmentation
|
| 69 |
-
'degrees': 10.0, # Rotation
|
| 70 |
-
'translate': 0.1, # Translation
|
| 71 |
-
'scale': 0.5, # Scaling
|
| 72 |
-
'shear': 0.0, # Shear
|
| 73 |
-
'perspective': 0.0, # Perspective
|
| 74 |
-
'flipud': 0.0, # Vertical flip (์์ฐ๋ ์ธ๋ก ๋ฐฉํฅ ์์)
|
| 75 |
-
'fliplr': 0.5, # Horizontal flip (์ข์ฐ๋ OK)
|
| 76 |
-
'mosaic': 1.0, # Mosaic augmentation
|
| 77 |
-
'mixup': 0.0, # Mixup augmentation
|
| 78 |
-
}
|
| 79 |
-
for k, v in augment_params.items():
|
| 80 |
-
print(f" - {k}: {v}")
|
| 81 |
-
|
| 82 |
-
print(f"\n๐ ํ์ต ์์...")
|
| 83 |
-
print(" (์งํ ์ํฉ์ ์ฝ์์ ํ์๋ฉ๋๋ค)")
|
| 84 |
-
print("-" * 60)
|
| 85 |
-
|
| 86 |
-
# ํ์ต ์คํ
|
| 87 |
-
results = model.train(
|
| 88 |
-
data=data_yaml,
|
| 89 |
-
epochs=epochs,
|
| 90 |
-
batch=batch_size,
|
| 91 |
-
imgsz=imgsz,
|
| 92 |
-
device=device,
|
| 93 |
-
patience=patience,
|
| 94 |
-
save=True,
|
| 95 |
-
project='runs/train',
|
| 96 |
-
name='shrimp_yolov8n',
|
| 97 |
-
exist_ok=True,
|
| 98 |
-
pretrained=True,
|
| 99 |
-
verbose=True,
|
| 100 |
-
# Data augmentation
|
| 101 |
-
**augment_params
|
| 102 |
-
)
|
| 103 |
-
|
| 104 |
-
print("\n" + "=" * 60)
|
| 105 |
-
print("โ
ํ์ต ์๋ฃ!")
|
| 106 |
-
print("=" * 60)
|
| 107 |
-
|
| 108 |
-
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
|
| 109 |
-
best_model_path = Path('runs/train/shrimp_yolov8n/weights/best.pt')
|
| 110 |
-
last_model_path = Path('runs/train/shrimp_yolov8n/weights/last.pt')
|
| 111 |
-
|
| 112 |
-
print(f"\n๐ ํ์ต ๊ฒฐ๊ณผ:")
|
| 113 |
-
print(f" - Best ๋ชจ๋ธ: {best_model_path}")
|
| 114 |
-
print(f" - Last ๋ชจ๋ธ: {last_model_path}")
|
| 115 |
-
print(f"\n๐ ์ฑ๋ฅ ๊ทธ๋ํ:")
|
| 116 |
-
print(f" - runs/train/shrimp_yolov8n/results.png")
|
| 117 |
-
print(f"\n๐ ์ ์ฒด ๊ฒฐ๊ณผ:")
|
| 118 |
-
print(f" - runs/train/shrimp_yolov8n/")
|
| 119 |
-
|
| 120 |
-
# Validation ๊ฒฐ๊ณผ ์ถ๋ ฅ
|
| 121 |
-
print(f"\n๐ฏ ๋ค์ ๋จ๊ณ:")
|
| 122 |
-
print(f" 1. ๊ฒฐ๊ณผ ํ์ธ: runs/train/shrimp_yolov8n/")
|
| 123 |
-
print(f" 2. ์ฑ๋ฅ ํ๊ฐ: python evaluate_yolo.py")
|
| 124 |
-
print(f" 3. ์ถ๋ก ํ
์คํธ: python test_yolo_inference.py")
|
| 125 |
-
|
| 126 |
-
if __name__ == "__main__":
|
| 127 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,190 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
Ground Truth ๊ฒ์ฆ ์คํฌ๋ฆฝํธ
|
| 4 |
-
๋ผ๋ฒจ๋ง ๋ฐ์ดํฐ์ ํ์ง๊ณผ ์ผ๊ด์ฑ์ ํ์ธ
|
| 5 |
-
"""
|
| 6 |
-
import sys
|
| 7 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
-
|
| 9 |
-
import json
|
| 10 |
-
import os
|
| 11 |
-
from pathlib import Path
|
| 12 |
-
|
| 13 |
-
def validate_ground_truth(gt_file="ground_truth.json"):
|
| 14 |
-
"""Ground Truth ๋ฐ์ดํฐ ๊ฒ์ฆ"""
|
| 15 |
-
|
| 16 |
-
print("=" * 60)
|
| 17 |
-
print("๐ Ground Truth ๊ฒ์ฆ")
|
| 18 |
-
print("=" * 60)
|
| 19 |
-
|
| 20 |
-
# ํ์ผ ์กด์ฌ ํ์ธ
|
| 21 |
-
if not os.path.exists(gt_file):
|
| 22 |
-
print(f"โ ํ์ผ์ด ์กด์ฌํ์ง ์์ต๋๋ค: {gt_file}")
|
| 23 |
-
return
|
| 24 |
-
|
| 25 |
-
# JSON ๋ก๋
|
| 26 |
-
with open(gt_file, 'r', encoding='utf-8') as f:
|
| 27 |
-
data = json.load(f)
|
| 28 |
-
|
| 29 |
-
# ๊ธฐ๋ณธ ํต๊ณ
|
| 30 |
-
total_images = len(data)
|
| 31 |
-
labeled_images = sum(1 for v in data.values() if len(v) > 0)
|
| 32 |
-
empty_images = sum(1 for v in data.values() if len(v) == 0)
|
| 33 |
-
total_boxes = sum(len(v) for v in data.values())
|
| 34 |
-
|
| 35 |
-
print(f"\n๐ ๊ธฐ๋ณธ ํต๊ณ")
|
| 36 |
-
print(f"{'โ' * 60}")
|
| 37 |
-
print(f"์ด ์ด๋ฏธ์ง: {total_images}๊ฐ")
|
| 38 |
-
print(f"๋ผ๋ฒจ๋ง๋ ์ด๋ฏธ์ง: {labeled_images}๊ฐ ({labeled_images/total_images*100:.1f}%)")
|
| 39 |
-
print(f"๋น ์ด๋ฏธ์ง: {empty_images}๊ฐ ({empty_images/total_images*100:.1f}%)")
|
| 40 |
-
print(f"์ด ๋ฐ์ด๋ฉ ๋ฐ์ค: {total_boxes}๊ฐ")
|
| 41 |
-
if labeled_images > 0:
|
| 42 |
-
print(f"์ด๋ฏธ์ง๋น ํ๊ท : {total_boxes/labeled_images:.2f}๊ฐ")
|
| 43 |
-
|
| 44 |
-
# ํด๋๋ณ ํต๊ณ
|
| 45 |
-
folders = {}
|
| 46 |
-
for filename, boxes in data.items():
|
| 47 |
-
if boxes:
|
| 48 |
-
folder = boxes[0].get('folder', 'unknown')
|
| 49 |
-
if folder not in folders:
|
| 50 |
-
folders[folder] = {'images': 0, 'boxes': 0}
|
| 51 |
-
folders[folder]['images'] += 1
|
| 52 |
-
folders[folder]['boxes'] += len(boxes)
|
| 53 |
-
|
| 54 |
-
if folders:
|
| 55 |
-
print(f"\n๐ ํด๋๋ณ ํต๊ณ")
|
| 56 |
-
print(f"{'โ' * 60}")
|
| 57 |
-
for folder, stats in sorted(folders.items()):
|
| 58 |
-
print(f"{folder}: {stats['images']}์ฅ, {stats['boxes']}๋ฐ์ค (ํ๊ท {stats['boxes']/stats['images']:.1f}๊ฐ/์ด๋ฏธ์ง)")
|
| 59 |
-
|
| 60 |
-
# ์ ๋ขฐ๋ ๋ถ์
|
| 61 |
-
confidences = []
|
| 62 |
-
for boxes in data.values():
|
| 63 |
-
for box in boxes:
|
| 64 |
-
if 'confidence' in box:
|
| 65 |
-
confidences.append(box['confidence'])
|
| 66 |
-
|
| 67 |
-
if confidences:
|
| 68 |
-
print(f"\n๐ฏ ์ ๋ขฐ๋ ๋ถ์")
|
| 69 |
-
print(f"{'โ' * 60}")
|
| 70 |
-
print(f"ํ๊ท ์ ๋ขฐ๋: {sum(confidences)/len(confidences):.3f}")
|
| 71 |
-
print(f"์ต์ ์ ๋ขฐ๋: {min(confidences):.3f}")
|
| 72 |
-
print(f"์ต๋ ์ ๋ขฐ๋: {max(confidences):.3f}")
|
| 73 |
-
|
| 74 |
-
# ์ ๋ขฐ๋ ๋ถํฌ
|
| 75 |
-
low = sum(1 for c in confidences if c < 0.2)
|
| 76 |
-
mid = sum(1 for c in confidences if 0.2 <= c < 0.4)
|
| 77 |
-
high = sum(1 for c in confidences if c >= 0.4)
|
| 78 |
-
print(f"\n์ ๋ขฐ๋ ๋ถํฌ:")
|
| 79 |
-
print(f" < 0.2: {low}๊ฐ ({low/len(confidences)*100:.1f}%)")
|
| 80 |
-
print(f" 0.2 ~ 0.4: {mid}๊ฐ ({mid/len(confidences)*100:.1f}%)")
|
| 81 |
-
print(f" >= 0.4: {high}๊ฐ ({high/len(confidences)*100:.1f}%)")
|
| 82 |
-
|
| 83 |
-
# ๋ฐ์ค ํฌ๊ธฐ ๋ถ์
|
| 84 |
-
print(f"\n๐ ๋ฐ์ค ํฌ๊ธฐ ๋ถ์")
|
| 85 |
-
print(f"{'โ' * 60}")
|
| 86 |
-
|
| 87 |
-
box_areas = []
|
| 88 |
-
box_widths = []
|
| 89 |
-
box_heights = []
|
| 90 |
-
aspect_ratios = []
|
| 91 |
-
|
| 92 |
-
for boxes in data.values():
|
| 93 |
-
for box in boxes:
|
| 94 |
-
bbox = box['bbox']
|
| 95 |
-
x1, y1, x2, y2 = bbox
|
| 96 |
-
width = x2 - x1
|
| 97 |
-
height = y2 - y1
|
| 98 |
-
area = width * height
|
| 99 |
-
|
| 100 |
-
box_areas.append(area)
|
| 101 |
-
box_widths.append(width)
|
| 102 |
-
box_heights.append(height)
|
| 103 |
-
if height > 0:
|
| 104 |
-
aspect_ratios.append(width / height)
|
| 105 |
-
|
| 106 |
-
if box_areas:
|
| 107 |
-
print(f"ํ๊ท ๋ฉด์ : {sum(box_areas)/len(box_areas):.0f} pxยฒ")
|
| 108 |
-
print(f"ํ๊ท ๋๋น: {sum(box_widths)/len(box_widths):.0f} px")
|
| 109 |
-
print(f"ํ๊ท ๋์ด: {sum(box_heights)/len(box_heights):.0f} px")
|
| 110 |
-
print(f"ํ๊ท ์ข
ํก๋น: {sum(aspect_ratios)/len(aspect_ratios):.2f}")
|
| 111 |
-
print(f" (์์ฐ๋ ๋ณดํต 3:1 ~ 10:1)")
|
| 112 |
-
|
| 113 |
-
# ์์ธ ๋ฐ์ดํฐ (์ฒ์ 5๊ฐ)
|
| 114 |
-
print(f"\n๐ ์์ธ ๋ฐ์ดํฐ (์ฒ์ 5๊ฐ)")
|
| 115 |
-
print(f"{'โ' * 60}")
|
| 116 |
-
|
| 117 |
-
count = 0
|
| 118 |
-
for filename, boxes in data.items():
|
| 119 |
-
if count >= 5:
|
| 120 |
-
break
|
| 121 |
-
|
| 122 |
-
print(f"\n{filename}")
|
| 123 |
-
if not boxes:
|
| 124 |
-
print(" - ๋ฐ์ค ์์ (๋น ์ด๋ฏธ์ง ๋๋ ๊ฑด๋๋ฐ๊ธฐ)")
|
| 125 |
-
else:
|
| 126 |
-
for idx, box in enumerate(boxes, 1):
|
| 127 |
-
bbox = box['bbox']
|
| 128 |
-
x1, y1, x2, y2 = bbox
|
| 129 |
-
width = x2 - x1
|
| 130 |
-
height = y2 - y1
|
| 131 |
-
conf = box.get('confidence', 0)
|
| 132 |
-
print(f" #{idx}: bbox=[{x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}], "
|
| 133 |
-
f"ํฌ๊ธฐ={width:.0f}x{height:.0f}, ์ ๋ขฐ๋={conf:.3f}")
|
| 134 |
-
count += 1
|
| 135 |
-
|
| 136 |
-
# ๊ฒ์ฆ ๊ฒฐ๊ณผ
|
| 137 |
-
print(f"\n{'=' * 60}")
|
| 138 |
-
print(f"โ
๊ฒ์ฆ ๊ฒฐ๊ณผ")
|
| 139 |
-
print(f"{'=' * 60}")
|
| 140 |
-
|
| 141 |
-
issues = []
|
| 142 |
-
|
| 143 |
-
# 1. ๋ฐ์ดํฐ๊ฐ ๋๋ฌด ์ ์์ง ํ์ธ
|
| 144 |
-
if total_images < 10:
|
| 145 |
-
issues.append(f"โ ๏ธ ์ด๋ฏธ์ง ์๊ฐ ์ ์ต๋๋ค ({total_images}๊ฐ). ์ต์ 50๊ฐ ๊ถ์ฅ")
|
| 146 |
-
|
| 147 |
-
# 2. ๋ผ๋ฒจ๋ง ๋น์จ ํ์ธ
|
| 148 |
-
if labeled_images / total_images < 0.5:
|
| 149 |
-
issues.append(f"โ ๏ธ ๋ผ๋ฒจ๋ง ๋น์จ์ด ๋ฎ์ต๋๋ค ({labeled_images/total_images*100:.1f}%). 50% ์ด์ ๊ถ์ฅ")
|
| 150 |
-
|
| 151 |
-
# 3. ํ๊ท ๋ฐ์ค ์ ํ์ธ
|
| 152 |
-
if labeled_images > 0 and total_boxes / labeled_images < 0.5:
|
| 153 |
-
issues.append(f"โ ๏ธ ์ด๋ฏธ์ง๋น ํ๊ท ๋ฐ์ค ์๊ฐ ์ ์ต๋๋ค ({total_boxes/labeled_images:.2f}๊ฐ)")
|
| 154 |
-
|
| 155 |
-
# 4. ์ ๋ขฐ๋ ํ์ธ
|
| 156 |
-
if confidences and sum(confidences)/len(confidences) < 0.2:
|
| 157 |
-
issues.append(f"โ ๏ธ ํ๊ท ์ ๋ขฐ๋๊ฐ ๋ฎ์ต๋๋ค ({sum(confidences)/len(confidences):.3f}). ๊ฒ์ถ ํ์ง ํ์ธ ํ์")
|
| 158 |
-
|
| 159 |
-
if issues:
|
| 160 |
-
print("\n๋ฌธ์ ์ :")
|
| 161 |
-
for issue in issues:
|
| 162 |
-
print(f" {issue}")
|
| 163 |
-
else:
|
| 164 |
-
print("\nโ
๋ชจ๋ ๊ฒ์ฆ ํต๊ณผ!")
|
| 165 |
-
|
| 166 |
-
# ๋ค์ ๋จ๊ณ ์ ์
|
| 167 |
-
print(f"\n{'=' * 60}")
|
| 168 |
-
print(f"๐ ๋ค์ ๋จ๊ณ")
|
| 169 |
-
print(f"{'=' * 60}")
|
| 170 |
-
|
| 171 |
-
if total_images < 50:
|
| 172 |
-
print(f"\n1. ๋ ๋ง์ ์ด๋ฏธ์ง ๋ผ๋ฒจ๋ง")
|
| 173 |
-
print(f" - ํ์ฌ: {total_images}์ฅ")
|
| 174 |
-
print(f" - ๋ชฉํ: 50์ฅ ์ด์ (Phase 1)")
|
| 175 |
-
print(f" - ๊ถ์ฅ: 100~200์ฅ (Phase 2~3)")
|
| 176 |
-
|
| 177 |
-
if labeled_images > 10:
|
| 178 |
-
print(f"\n2. ์ ๋์ ํ๊ฐ ์คํ")
|
| 179 |
-
print(f" ```bash")
|
| 180 |
-
print(f" python test_quantitative_evaluation.py")
|
| 181 |
-
print(f" ```")
|
| 182 |
-
|
| 183 |
-
print(f"\n3. ๊ณ์ ๋ผ๋ฒจ๋ง")
|
| 184 |
-
print(f" - ๋ธ๋ผ์ฐ์ ์์ http://localhost:7862 ์ ์")
|
| 185 |
-
print(f" - ๋ ๋ง์ ํด๋ ์์
")
|
| 186 |
-
|
| 187 |
-
print(f"\n{'=' * 60}\n")
|
| 188 |
-
|
| 189 |
-
if __name__ == "__main__":
|
| 190 |
-
validate_ground_truth()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,135 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
YOLO ๋ฐ์ดํฐ์
๋ฐ์ด๋ฉ ๋ฐ์ค ์๊ฐํ
|
| 4 |
-
"""
|
| 5 |
-
import sys
|
| 6 |
-
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
-
|
| 8 |
-
import os
|
| 9 |
-
from PIL import Image, ImageDraw, ImageFont
|
| 10 |
-
from pathlib import Path
|
| 11 |
-
import random
|
| 12 |
-
|
| 13 |
-
def visualize_yolo_annotation(img_path, label_path, output_path):
|
| 14 |
-
"""YOLO ํ์ ๋ผ๋ฒจ์ ์ด๋ฏธ์ง์ ๊ทธ๋ฆฌ๊ธฐ"""
|
| 15 |
-
|
| 16 |
-
# ์ด๋ฏธ์ง ๋ก๋
|
| 17 |
-
img = Image.open(img_path)
|
| 18 |
-
draw = ImageDraw.Draw(img)
|
| 19 |
-
img_width, img_height = img.size
|
| 20 |
-
|
| 21 |
-
# ํฐํธ ์ค์
|
| 22 |
-
try:
|
| 23 |
-
font = ImageFont.truetype("arial.ttf", 30)
|
| 24 |
-
font_small = ImageFont.truetype("arial.ttf", 20)
|
| 25 |
-
except:
|
| 26 |
-
font = ImageFont.load_default()
|
| 27 |
-
font_small = ImageFont.load_default()
|
| 28 |
-
|
| 29 |
-
# ๋ผ๋ฒจ ํ์ผ ์ฝ๊ธฐ
|
| 30 |
-
if not os.path.exists(label_path):
|
| 31 |
-
print(f"โ ๏ธ ๋ผ๋ฒจ ํ์ผ ์์: {label_path}")
|
| 32 |
-
return False
|
| 33 |
-
|
| 34 |
-
with open(label_path, 'r') as f:
|
| 35 |
-
lines = f.readlines()
|
| 36 |
-
|
| 37 |
-
print(f"\n๐ {os.path.basename(img_path)}")
|
| 38 |
-
print(f" ์ด๋ฏธ์ง ํฌ๊ธฐ: {img_width} x {img_height}")
|
| 39 |
-
print(f" ๋ฐ์ค ๊ฐ์: {len(lines)}")
|
| 40 |
-
|
| 41 |
-
# ๊ฐ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
|
| 42 |
-
for idx, line in enumerate(lines, 1):
|
| 43 |
-
parts = line.strip().split()
|
| 44 |
-
if len(parts) != 5:
|
| 45 |
-
continue
|
| 46 |
-
|
| 47 |
-
class_id = int(parts[0])
|
| 48 |
-
x_center_norm = float(parts[1])
|
| 49 |
-
y_center_norm = float(parts[2])
|
| 50 |
-
width_norm = float(parts[3])
|
| 51 |
-
height_norm = float(parts[4])
|
| 52 |
-
|
| 53 |
-
# ์ ๊ทํ๋ ์ขํ๋ฅผ ํฝ์
์ขํ๋ก ๋ณํ
|
| 54 |
-
x_center = x_center_norm * img_width
|
| 55 |
-
y_center = y_center_norm * img_height
|
| 56 |
-
width = width_norm * img_width
|
| 57 |
-
height = height_norm * img_height
|
| 58 |
-
|
| 59 |
-
# ๋ฐ์ด๋ฉ ๋ฐ์ค ์ขํ ๊ณ์ฐ
|
| 60 |
-
x1 = x_center - width / 2
|
| 61 |
-
y1 = y_center - height / 2
|
| 62 |
-
x2 = x_center + width / 2
|
| 63 |
-
y2 = y_center + height / 2
|
| 64 |
-
|
| 65 |
-
# ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ (๋
น์, ๋๊ป๊ฒ)
|
| 66 |
-
draw.rectangle([x1, y1, x2, y2], outline="lime", width=8)
|
| 67 |
-
|
| 68 |
-
# ๋ผ๋ฒจ (๊ฒ์ ๋ฐฐ๊ฒฝ์ ํฐ ๊ธ์จ)
|
| 69 |
-
label = f"Shrimp #{idx}"
|
| 70 |
-
bbox = draw.textbbox((x1, y1 - 40), label, font=font)
|
| 71 |
-
draw.rectangle([bbox[0]-5, bbox[1]-5, bbox[2]+5, bbox[3]+5], fill="lime")
|
| 72 |
-
draw.text((x1, y1 - 40), label, fill="black", font=font)
|
| 73 |
-
|
| 74 |
-
# ์ขํ ์ ๋ณด (์๊ฒ)
|
| 75 |
-
info = f"YOLO: {x_center_norm:.3f} {y_center_norm:.3f} {width_norm:.3f} {height_norm:.3f}"
|
| 76 |
-
draw.text((x1, y2 + 10), info, fill="lime", font=font_small)
|
| 77 |
-
|
| 78 |
-
print(f" #{idx}: center=({x_center:.0f}, {y_center:.0f}), size=({width:.0f}x{height:.0f})")
|
| 79 |
-
|
| 80 |
-
# ์ ์ฅ
|
| 81 |
-
img.save(output_path, quality=95)
|
| 82 |
-
print(f" โ
์ ์ฅ: {output_path}")
|
| 83 |
-
|
| 84 |
-
return True
|
| 85 |
-
|
| 86 |
-
def main():
|
| 87 |
-
print("=" * 60)
|
| 88 |
-
print("๐ YOLO ๋ฐ์ดํฐ์
๋ฐ์ด๋ฉ ๋ฐ์ค ์๊ฐํ")
|
| 89 |
-
print("=" * 60)
|
| 90 |
-
|
| 91 |
-
# ๊ฒฝ๋ก ์ค์
|
| 92 |
-
dataset_dir = Path("data/yolo_dataset")
|
| 93 |
-
train_img_dir = dataset_dir / "images" / "train"
|
| 94 |
-
train_label_dir = dataset_dir / "labels" / "train"
|
| 95 |
-
output_dir = Path("data/yolo_visualization")
|
| 96 |
-
output_dir.mkdir(exist_ok=True)
|
| 97 |
-
|
| 98 |
-
# Train ์ด๋ฏธ์ง ๋ฆฌ์คํธ
|
| 99 |
-
img_files = list(train_img_dir.glob("*.jpg"))
|
| 100 |
-
|
| 101 |
-
if not img_files:
|
| 102 |
-
print("โ Train ์ด๋ฏธ์ง ์์!")
|
| 103 |
-
return
|
| 104 |
-
|
| 105 |
-
print(f"\n๐ Train ์ด๋ฏธ์ง: {len(img_files)}๊ฐ")
|
| 106 |
-
|
| 107 |
-
# ๋๋ค 3๊ฐ ์ํ
|
| 108 |
-
random.seed(42)
|
| 109 |
-
samples = random.sample(img_files, min(3, len(img_files)))
|
| 110 |
-
|
| 111 |
-
print(f"\n๐ฒ ๋๋ค ์ํ 3๊ฐ ์๊ฐํ:")
|
| 112 |
-
|
| 113 |
-
for img_path in samples:
|
| 114 |
-
# ๋์ํ๋ ๋ผ๋ฒจ ํ์ผ
|
| 115 |
-
label_filename = img_path.stem + ".txt"
|
| 116 |
-
label_path = train_label_dir / label_filename
|
| 117 |
-
|
| 118 |
-
# ์ถ๋ ฅ ํ์ผ
|
| 119 |
-
output_path = output_dir / f"{img_path.stem}_visualized.jpg"
|
| 120 |
-
|
| 121 |
-
# ์๊ฐํ
|
| 122 |
-
visualize_yolo_annotation(img_path, label_path, output_path)
|
| 123 |
-
|
| 124 |
-
print("\n" + "=" * 60)
|
| 125 |
-
print("โ
์๊ฐํ ์๋ฃ!")
|
| 126 |
-
print("=" * 60)
|
| 127 |
-
print(f"\n๐ ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ: {output_dir}")
|
| 128 |
-
print(f"\n์์ฑ๋ ํ์ผ:")
|
| 129 |
-
for file in sorted(output_dir.glob("*_visualized.jpg")):
|
| 130 |
-
print(f" - {file.name}")
|
| 131 |
-
|
| 132 |
-
print(f"\n๐ก ์ด๋ฏธ์ง๋ฅผ ์ด์ด์ ๋ฐ์ด๋ฉ ๋ฐ์ค๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํ์ธ์!")
|
| 133 |
-
|
| 134 |
-
if __name__ == "__main__":
|
| 135 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|