| | |
| | """ |
| | ๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
|
| | 3๊ฐ์ ์ฑ์ ํ๋๋ก ํตํฉ: ์๋ ๊ฒ์ถ, ๋ผ๋ฒจ๋ง ๋๊ตฌ, ๋ฐ๋ชจ |
| | RT-DETR ๋๋ VIDraft/Shrimp ํด๋ผ์ฐ๋ ๋ชจ๋ธ ์ ํ ๊ฐ๋ฅ |
| | """ |
| | import sys |
| | sys.stdout.reconfigure(encoding='utf-8') |
| |
|
| | import gradio as gr |
| | from PIL import Image, ImageDraw, ImageFont |
| | import numpy as np |
| | import json |
| | import os |
| | import glob |
| | from datetime import datetime |
| | import torch |
| | from transformers import RTDetrForObjectDetection, RTDetrImageProcessor |
| | import requests |
| | import base64 |
| | from io import BytesIO |
| | from inference_sdk import InferenceHTTPClient |
| | import tempfile |
| |
|
| | |
| | |
| | |
| | import cv2 |
| |
|
| | def load_rtdetr_model(): |
| | """RT-DETR ๋ชจ๋ธ ๋ก๋""" |
| | print("๐ RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์ค...") |
| | processor = RTDetrImageProcessor.from_pretrained("PekingU/rtdetr_r50vd_coco_o365") |
| | model = RTDetrForObjectDetection.from_pretrained("PekingU/rtdetr_r50vd_coco_o365") |
| | model.eval() |
| | print("โ
RT-DETR ๋ก๋ฉ ์๋ฃ") |
| | return processor, model |
| |
|
| | def detect_with_rtdetr(image, processor, model, confidence=0.3): |
| | """RT-DETR๋ก ๊ฐ์ฒด ๊ฒ์ถ""" |
| | inputs = processor(images=image, return_tensors="pt") |
| | with torch.no_grad(): |
| | outputs = model(**inputs) |
| |
|
| | target_sizes = torch.tensor([image.size[::-1]]) |
| | results = processor.post_process_object_detection( |
| | outputs, |
| | target_sizes=target_sizes, |
| | threshold=confidence |
| | )[0] |
| |
|
| | detections = [] |
| | for score, label, box in zip(results["scores"], results["labels"], results["boxes"]): |
| | x1, y1, x2, y2 = box.tolist() |
| | detections.append({ |
| | 'bbox': [x1, y1, x2, y2], |
| | 'confidence': score.item(), |
| | 'label': label.item() |
| | }) |
| |
|
| | return detections |
| |
|
| | def calculate_morphological_features(bbox, image_size): |
| | """ํํํ์ ํน์ง ๊ณ์ฐ""" |
| | x1, y1, x2, y2 = bbox |
| | width = x2 - x1 |
| | height = y2 - y1 |
| |
|
| | |
| | aspect_ratio = max(width, height) / max(min(width, height), 1) |
| |
|
| | |
| | img_w, img_h = image_size |
| | area_ratio = (width * height) / (img_w * img_h) |
| |
|
| | |
| | perimeter = 2 * (width + height) |
| | compactness = (4 * np.pi * width * height) / max(perimeter ** 2, 1) |
| |
|
| | return { |
| | 'aspect_ratio': aspect_ratio, |
| | 'area_ratio': area_ratio, |
| | 'compactness': compactness, |
| | 'width': width, |
| | 'height': height |
| | } |
| |
|
| | def calculate_visual_features(image_pil, bbox): |
| | """์๊ฐ์ ํน์ง ๊ณ์ฐ (์์, ํ
์ค์ฒ)""" |
| | |
| | image_cv = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR) |
| | x1, y1, x2, y2 = [int(v) for v in bbox] |
| |
|
| | |
| | roi = image_cv[y1:y2, x1:x2] |
| | if roi.size == 0: |
| | return {'hue': 100, 'saturation': 255, 'color_std': 255} |
| |
|
| | |
| | hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) |
| |
|
| | |
| | hue_mean = np.mean(hsv[:, :, 0]) |
| |
|
| | |
| | saturation = np.mean(hsv[:, :, 1]) |
| |
|
| | |
| | color_std = np.std(hsv[:, :, 0]) |
| |
|
| | return { |
| | 'hue': hue_mean, |
| | 'saturation': saturation, |
| | 'color_std': color_std |
| | } |
| |
|
| | def apply_universal_filter(detections, image, threshold=90): |
| | """๋ฒ์ฉ ์์ฐ ํํฐ ์ ์ฉ""" |
| | img_size = image.size |
| | filtered = [] |
| |
|
| | for det in detections: |
| | bbox = det['bbox'] |
| |
|
| | |
| | morph = calculate_morphological_features(bbox, img_size) |
| |
|
| | |
| | visual = calculate_visual_features(image, bbox) |
| |
|
| | |
| | score = 0 |
| | reasons = [] |
| |
|
| | |
| | if 4.0 <= morph['aspect_ratio'] <= 9.0: |
| | score += 25 |
| | reasons.append(f"โ ์ข
ํก๋น {morph['aspect_ratio']:.1f}") |
| | elif 3.0 <= morph['aspect_ratio'] < 4.0 or 9.0 < morph['aspect_ratio'] <= 10.0: |
| | score += 12 |
| | reasons.append(f"โณ ์ข
ํก๋น {morph['aspect_ratio']:.1f}") |
| | else: |
| | score -= 5 |
| | reasons.append(f"โ ์ข
ํก๋น {morph['aspect_ratio']:.1f}") |
| |
|
| | |
| | if morph['compactness'] < 0.40: |
| | score += 30 |
| | reasons.append(f"โ ์ธ์ฅ๋ {morph['compactness']:.2f}") |
| | elif 0.40 <= morph['compactness'] < 0.50: |
| | score += 15 |
| | reasons.append(f"โณ ์ธ์ฅ๋ {morph['compactness']:.2f}") |
| | else: |
| | reasons.append(f"โ ์ธ์ฅ๋ {morph['compactness']:.2f}") |
| | score -= 20 |
| |
|
| | |
| | abs_area = morph['width'] * morph['height'] |
| | if 50000 <= abs_area <= 500000: |
| | score += 35 |
| | reasons.append(f"โ ๋ฉด์ {abs_area/1000:.0f}K") |
| | elif 500000 < abs_area <= 800000: |
| | score -= 10 |
| | reasons.append(f"โณ ๋ฉด์ {abs_area/1000:.0f}K") |
| | elif abs_area > 800000: |
| | score -= 30 |
| | reasons.append(f"โ ๋ฉด์ {abs_area/1000:.0f}K (๋๋ฌดํผ)") |
| | else: |
| | score -= 10 |
| | reasons.append(f"โ ๋ฉด์ {abs_area/1000:.0f}K (๋๋ฌด์์)") |
| |
|
| | |
| | hue = visual['hue'] |
| | if hue < 40 or hue > 130: |
| | score += 10 |
| | reasons.append(f"โ ์์ {hue:.0f}") |
| | elif 90 <= hue <= 130: |
| | score -= 5 |
| | reasons.append(f"โ ์์ {hue:.0f} (๋ฐฐ๊ฒฝ)") |
| | else: |
| | reasons.append(f"โณ ์์ {hue:.0f}") |
| |
|
| | |
| | if visual['saturation'] < 85: |
| | score += 20 |
| | reasons.append(f"โ ์ฑ๋ {visual['saturation']:.0f}") |
| | elif 85 <= visual['saturation'] < 120: |
| | score += 5 |
| | reasons.append(f"โณ ์ฑ๋ {visual['saturation']:.0f}") |
| | else: |
| | score -= 15 |
| | reasons.append(f"โ ์ฑ๋ {visual['saturation']:.0f} (๋์)") |
| |
|
| | |
| | if visual['color_std'] < 50: |
| | score += 15 |
| | reasons.append(f"โ ์์์ผ๊ด์ฑ {visual['color_std']:.1f}") |
| | elif 50 <= visual['color_std'] < 80: |
| | score += 5 |
| | reasons.append(f"โณ ์์์ผ๊ด์ฑ {visual['color_std']:.1f}") |
| | else: |
| | score -= 10 |
| | reasons.append(f"โ ์์์ผ๊ด์ฑ {visual['color_std']:.1f} (๋ถ์ผ์น)") |
| |
|
| | |
| | if 'confidence' in det: |
| | if det['confidence'] >= 0.3: |
| | score += 15 |
| | reasons.append(f"โ ์ ๋ขฐ๋ {det['confidence']:.0%}") |
| | elif det['confidence'] >= 0.1: |
| | score += 8 |
| | reasons.append(f"โณ ์ ๋ขฐ๋ {det['confidence']:.0%}") |
| | else: |
| | reasons.append(f"โ ์ ๋ขฐ๋ {det['confidence']:.0%}") |
| |
|
| | det['filter_score'] = score |
| | det['filter_reasons'] = reasons |
| | filtered.append(det) |
| |
|
| | return filtered |
| |
|
| | |
| | |
| | |
| | |
| | YOLO_MODEL_PATH = "runs/train/yolov8m_shrimp2/weights/best.pt" |
| | yolo_model = None |
| | YOLO_AVAILABLE = False |
| |
|
| | try: |
| | from ultralytics import YOLO |
| | import os |
| | if os.path.exists(YOLO_MODEL_PATH): |
| | YOLO_AVAILABLE = True |
| | print(f"โ
YOLOv8 ์ฌ์ฉ ๊ฐ๋ฅ: {YOLO_MODEL_PATH}") |
| | else: |
| | print(f"โ ๏ธ YOLOv8 ๋ชจ๋ธ ํ์ผ ์์: {YOLO_MODEL_PATH}") |
| | except ImportError: |
| | print("โ ๏ธ ultralytics ํจํค์ง ์์ - YOLOv8 ๋นํ์ฑํ") |
| |
|
| | def load_yolo_model(): |
| | """YOLOv8 ๋ชจ๋ธ ๋ก๋ฉ""" |
| | global yolo_model |
| | if not YOLO_AVAILABLE: |
| | raise Exception("YOLOv8 ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค") |
| | if yolo_model is None: |
| | print(f"๐ YOLOv8 ๋ชจ๋ธ ๋ก๋ฉ ์ค: {YOLO_MODEL_PATH}") |
| | yolo_model = YOLO(YOLO_MODEL_PATH) |
| | print("โ
YOLOv8 ๋ชจ๋ธ ๋ก๋ฉ ์๋ฃ") |
| | return yolo_model |
| |
|
| | def detect_with_yolo(image, confidence=0.1): |
| | """YOLOv8 ๋ชจ๋ธ๋ก ๊ฒ์ถ""" |
| | try: |
| | model = load_yolo_model() |
| |
|
| | |
| | results = model.predict( |
| | source=image, |
| | conf=confidence, |
| | verbose=False |
| | ) |
| |
|
| | detections = [] |
| | for result in results: |
| | boxes = result.boxes |
| | for box in boxes: |
| | x1, y1, x2, y2 = box.xyxy[0].tolist() |
| | conf = box.conf[0].item() |
| |
|
| | detections.append({ |
| | 'bbox': [x1, y1, x2, y2], |
| | 'confidence': conf |
| | }) |
| |
|
| | print(f"โ
YOLOv8 ๊ฒ์ถ ์๋ฃ: {len(detections)}๊ฐ") |
| | return detections |
| |
|
| | except Exception as e: |
| | print(f"โ YOLOv8 ๊ฒ์ถ ์ค๋ฅ: {str(e)}") |
| | import traceback |
| | traceback.print_exc() |
| | return [] |
| |
|
| | |
| | |
| | |
| | |
| | ROBOFLOW_API_KEY = os.getenv("ROBOFLOW_API_KEY", "") |
| |
|
| | |
| | roboflow_client = InferenceHTTPClient( |
| | api_url="https://serverless.roboflow.com", |
| | api_key=ROBOFLOW_API_KEY |
| | ) |
| |
|
| | def detect_with_roboflow(image, confidence=0.065): |
| | """Roboflow API๋ฅผ ์ฌ์ฉํ ์ต์ ํ๋ ๊ฒ์ถ (๋ก์ปฌ ํ
์คํธ์ ๋์ผ)""" |
| | try: |
| | |
| | image_original = image |
| | original_size = image_original.size |
| |
|
| | |
| | image_resized = image_original.copy() |
| | image_resized.thumbnail((640, 640), Image.Resampling.LANCZOS) |
| | print(f"๐ ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆ: {original_size} โ {image_resized.size}") |
| |
|
| | |
| | buffered = BytesIO() |
| | image_resized.save(buffered, format="JPEG", quality=80) |
| | img_base64 = base64.b64encode(buffered.getvalue()).decode() |
| | print(f"๐ฆ Base64 ํฌ๊ธฐ: {len(img_base64)} bytes") |
| |
|
| | print(f"๐ Roboflow API ์ถ๋ก ์์...") |
| |
|
| | |
| | response = requests.post( |
| | 'https://serverless.roboflow.com/vidraft/workflows/find-shrimp-6', |
| | headers={'Content-Type': 'application/json'}, |
| | json={ |
| | 'api_key': ROBOFLOW_API_KEY, |
| | 'inputs': { |
| | 'image': {'type': 'base64', 'value': img_base64} |
| | } |
| | }, |
| | timeout=30 |
| | ) |
| |
|
| | if response.status_code != 200: |
| | print(f"โ Roboflow API ์ค๋ฅ: {response.status_code}") |
| | print(f"์๋ต: {response.text}") |
| | return [] |
| |
|
| | result = response.json() |
| | print(f"๐ Roboflow ์๋ต: {json.dumps(result, indent=2, ensure_ascii=False)[:500]}...") |
| |
|
| | |
| | detections = [] |
| | predictions = [] |
| |
|
| | |
| | if isinstance(result, dict) and 'outputs' in result and len(result['outputs']) > 0: |
| | output = result['outputs'][0] |
| | if isinstance(output, dict) and 'predictions' in output: |
| | pred_data = output['predictions'] |
| | |
| | if isinstance(pred_data, dict) and 'predictions' in pred_data: |
| | predictions = pred_data['predictions'] |
| | |
| | elif isinstance(pred_data, list): |
| | predictions = pred_data |
| | else: |
| | predictions = [pred_data] |
| |
|
| | |
| | elif isinstance(result, dict) and 'predictions' in result: |
| | predictions = result['predictions'] |
| |
|
| | |
| | elif isinstance(result, list): |
| | predictions = result |
| |
|
| | print(f"๐ฆ ์ฐพ์ predictions: {len(predictions)}๊ฐ") |
| |
|
| | |
| | scale_x = original_size[0] / image_resized.size[0] |
| | scale_y = original_size[1] / image_resized.size[1] |
| | print(f"๐ ์ค์ผ์ผ: x={scale_x:.2f}, y={scale_y:.2f}") |
| |
|
| | for pred in predictions: |
| | |
| | pred_class = pred.get('class', '') |
| | if pred_class != 'shrimp': |
| | continue |
| |
|
| | |
| | pred_confidence = pred.get('confidence', 0) |
| | if pred_confidence < confidence: |
| | continue |
| |
|
| | |
| | x = pred.get('x', 0) |
| | y = pred.get('y', 0) |
| | width = pred.get('width', 0) |
| | height = pred.get('height', 0) |
| |
|
| | |
| | x_scaled = x * scale_x |
| | y_scaled = y * scale_y |
| | width_scaled = width * scale_x |
| | height_scaled = height * scale_y |
| |
|
| | |
| | x1 = x_scaled - width_scaled / 2 |
| | y1 = y_scaled - height_scaled / 2 |
| | x2 = x_scaled + width_scaled / 2 |
| | y2 = y_scaled + height_scaled / 2 |
| |
|
| | detections.append({ |
| | 'bbox': [x1, y1, x2, y2], |
| | 'confidence': pred_confidence |
| | }) |
| | print(f" โ ๊ฒ์ถ (shrimp): conf={pred_confidence:.2%}, bbox=[{x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f}]") |
| |
|
| | print(f"โ
Roboflow ๊ฒ์ถ ์๋ฃ: {len(detections)}๊ฐ") |
| | return detections |
| |
|
| | except Exception as e: |
| | print(f"โ Roboflow SDK ์ค๋ฅ: {str(e)}") |
| | import traceback |
| | traceback.print_exc() |
| | return [] |
| |
|
| | |
| | |
| | |
| | processor = None |
| | model = None |
| |
|
| | def load_rtdetr_on_demand(): |
| | """RT-DETR ๋ชจ๋ธ์ ํ์์์๋ง ๋ก๋ฉ""" |
| | global processor, model |
| | if processor is None or model is None: |
| | processor, model = load_rtdetr_model() |
| | return "โ
RT-DETR ๋ชจ๋ธ ๋ก๋ฉ ์๋ฃ" |
| | else: |
| | return "โน๏ธ RT-DETR ๋ชจ๋ธ์ด ์ด๋ฏธ ๋ก๋ฉ๋์ด ์์ต๋๋ค" |
| |
|
| | print("โ
VIDraft/Shrimp ํด๋ผ์ฐ๋ ๋ชจ๋ธ ์ฌ์ฉ ๊ฐ๋ฅ\n") |
| |
|
| | |
| | |
| | |
| | current_data = { |
| | 'folder': None, |
| | 'images': [], |
| | 'current_idx': 0, |
| | 'detections': {}, |
| | 'selections': {}, |
| | 'confidence_threshold': 0.2, |
| | 'image_cache': {}, |
| | 'model_type': 'RT-DETR' |
| | } |
| |
|
| | GROUND_TRUTH_FILE = "ground_truth.json" |
| | DATA_BASE = "data/ํฐ๋ค๋ฆฌ์์ฐ ์ค์ธก ๋ฐ์ดํฐ_์ตํฌ์ค์์ด์์ด(์ฃผ)" |
| |
|
| | |
| | |
| | |
| |
|
| | def detect_with_selected_model(image, confidence, model_type): |
| | """์ ํ๋ ๋ชจ๋ธ๋ก ๊ฒ์ถ""" |
| | if model_type == "RT-DETR": |
| | if processor is None or model is None: |
| | raise ValueError("โ ๏ธ RT-DETR ๋ชจ๋ธ์ด ๋ก๋ฉ๋์ง ์์์ต๋๋ค. '๐ RT-DETR ๋ก๋' ๋ฒํผ์ ๋จผ์ ํด๋ฆญํ์ธ์.") |
| | return detect_with_rtdetr(image, processor, model, confidence) |
| | elif model_type == "VIDraft/Shrimp": |
| | return detect_with_roboflow(image, confidence) |
| | elif model_type == "YOLOv8": |
| | return detect_with_yolo(image, confidence) |
| | else: |
| | return [] |
| |
|
| | |
| | |
| | |
| |
|
| | def interactive_detect(image, confidence, filter_threshold, show_all, model_type, use_filter): |
| | """๋ํํ ๊ฒ์ถ""" |
| | if image is None: |
| | return None, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ์ธ์." |
| |
|
| | try: |
| | |
| | all_detections = detect_with_selected_model(image, confidence, model_type) |
| |
|
| | |
| | if not use_filter: |
| | |
| | filtered_detections = all_detections |
| | for det in filtered_detections: |
| | det['filter_score'] = det['confidence'] * 100 |
| | det['filter_reasons'] = [f"์ ๋ขฐ๋: {det['confidence']:.0%} (ํํฐ ๋ฏธ์ฌ์ฉ)"] |
| | all_detections_scored = filtered_detections |
| | else: |
| | |
| | if model_type in ["VIDraft/Shrimp", "YOLOv8"]: |
| | |
| | for det in all_detections: |
| | det['filter_score'] = det['confidence'] * 100 |
| | det['filter_reasons'] = [f"{model_type} ์ ๋ขฐ๋: {det['confidence']:.0%}"] |
| | all_detections_scored = all_detections |
| | else: |
| | |
| | all_detections_scored = apply_universal_filter(all_detections, image, threshold=0) |
| |
|
| | |
| | filtered_detections = [det for det in all_detections_scored if det['filter_score'] >= filter_threshold] |
| |
|
| | |
| | img = image.copy() |
| | draw = ImageDraw.Draw(img) |
| |
|
| | try: |
| | font = ImageFont.truetype("arial.ttf", 14) |
| | font_large = ImageFont.truetype("arial.ttf", 18) |
| | font_small = ImageFont.truetype("arial.ttf", 10) |
| | except: |
| | font = ImageFont.load_default() |
| | font_large = ImageFont.load_default() |
| | font_small = ImageFont.load_default() |
| |
|
| | |
| | rejected_detections = [det for det in all_detections_scored if det['filter_score'] < filter_threshold] |
| | for idx, det in enumerate(rejected_detections, 1): |
| | x1, y1, x2, y2 = det['bbox'] |
| | score = det['filter_score'] |
| |
|
| | |
| | draw.rectangle([x1, y1, x2, y2], outline="red", width=8) |
| |
|
| | |
| | label = f"โ{idx} {score:.0f}์ " |
| | bbox = draw.textbbox((x1, y1 - 20), label, font=font_small) |
| | draw.rectangle(bbox, fill="red") |
| | draw.text((x1, y1 - 20), label, fill="white", font=font_small) |
| |
|
| | |
| | if show_all: |
| | for det in all_detections_scored: |
| | if det not in filtered_detections and det not in rejected_detections: |
| | x1, y1, x2, y2 = det['bbox'] |
| | draw.rectangle([x1, y1, x2, y2], outline="gray", width=4) |
| |
|
| | |
| | for idx, det in enumerate(filtered_detections, 1): |
| | x1, y1, x2, y2 = det['bbox'] |
| | score = det['filter_score'] |
| |
|
| | |
| | if score >= 75: |
| | color = "lime" |
| | elif score >= 50: |
| | color = "yellow" |
| | else: |
| | color = "orange" |
| |
|
| | |
| | draw.rectangle([x1, y1, x2, y2], outline=color, width=10) |
| |
|
| | |
| | label = f"โ#{idx} {score:.0f}์ " |
| | bbox = draw.textbbox((x1, y1 - 25), label, font=font) |
| | draw.rectangle(bbox, fill=color) |
| | draw.text((x1, y1 - 25), label, fill="black", font=font) |
| |
|
| | |
| | details = f"{model_type}:{det['confidence']:.0%}" |
| | draw.text((x1, y2 + 5), details, fill=color, font=font_small) |
| |
|
| | |
| | header = f"[{model_type}] โ {len(filtered_detections)}๊ฐ / โ {len(rejected_detections)}๊ฐ (์ ์ฒด: {len(all_detections_scored)}๊ฐ)" |
| | header_bbox = draw.textbbox((10, 10), header, font=font_large) |
| | draw.rectangle([5, 5, header_bbox[2]+10, header_bbox[3]+10], |
| | fill="black", outline="lime", width=2) |
| | draw.text((10, 10), header, fill="lime", font=font_large) |
| |
|
| | |
| | info = f""" |
| | ### ๐ ๊ฒ์ถ ๊ฒฐ๊ณผ (๋ชจ๋ธ: {model_type}) |
| | |
| | - **์ ์ฒด ๊ฒ์ถ**: {len(all_detections_scored)}๊ฐ |
| | - **ํํฐ๋ง ํ**: {len(filtered_detections)}๊ฐ |
| | - **์ ๊ฑฐ๋จ**: {len(rejected_detections)}๊ฐ |
| | |
| | --- |
| | |
| | ### ๐ฏ ๊ฒ์ถ๋ ๊ฐ์ฒด ์์ธ (โ
ํต๊ณผ) |
| | |
| | """ |
| |
|
| | for idx, det in enumerate(filtered_detections, 1): |
| | info += f""" |
| | **#{idx} - ์ ์: {det['filter_score']:.0f}์ ** ({model_type} ์ ๋ขฐ๋: {det['confidence']:.0%}) |
| | |
| | """ |
| | |
| | for reason in det['filter_reasons'][:5]: |
| | info += f"- {reason}\n" |
| |
|
| | if not filtered_detections: |
| | info += """ |
| | โ ๏ธ **๊ฒ์ถ๋ ๊ฐ์ฒด๊ฐ ์์ต๋๋ค.** |
| | |
| | """ |
| |
|
| | |
| | if rejected_detections: |
| | info += f""" |
| | |
| | --- |
| | |
| | ### โ ์ ๊ฑฐ๋ ๊ฐ์ฒด ({len(rejected_detections)}๊ฐ) |
| | |
| | """ |
| | for idx, det in enumerate(rejected_detections[:3], 1): |
| | info += f""" |
| | **์ ๊ฑฐ #{idx} - ์ ์: {det['filter_score']:.0f}์ ** (์๊ณ๊ฐ ๋ฏธ๋ฌ) |
| | - {model_type} ์ ๋ขฐ๋: {det['confidence']:.0%} |
| | |
| | """ |
| | |
| | for reason in det['filter_reasons'][:3]: |
| | info += f"- {reason}\n" |
| |
|
| | return img, info |
| |
|
| | except Exception as e: |
| | import traceback |
| | error_detail = traceback.format_exc() |
| | return None, f"โ ์ค๋ฅ ๋ฐ์:\n\n```\n{error_detail}\n```" |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def detect_with_rtdetr_fast(image, confidence=0.3): |
| | """RT-DETR ๋น ๋ฅธ ๊ฒ์ถ""" |
| | inputs = processor(images=image, return_tensors="pt") |
| | with torch.no_grad(): |
| | outputs = model(**inputs) |
| |
|
| | target_sizes = torch.tensor([image.size[::-1]]) |
| | results = processor.post_process_object_detection( |
| | outputs, |
| | target_sizes=target_sizes, |
| | threshold=confidence |
| | )[0] |
| |
|
| | detections = [] |
| | for score, label, box in zip(results["scores"], results["labels"], results["boxes"]): |
| | x1, y1, x2, y2 = box.tolist() |
| | detections.append({ |
| | 'bbox': [x1, y1, x2, y2], |
| | 'confidence': score.item() |
| | }) |
| |
|
| | return detections |
| |
|
| |
|
| | def load_existing_ground_truth(): |
| | """๊ธฐ์กด ground_truth.json ๋ก๋""" |
| | if os.path.exists(GROUND_TRUTH_FILE): |
| | with open(GROUND_TRUTH_FILE, 'r', encoding='utf-8') as f: |
| | return json.load(f) |
| | return {} |
| |
|
| |
|
| | def save_ground_truth(data): |
| | """ground_truth.json ์ ์ฅ""" |
| | backup_dir = "backups" |
| | if not os.path.exists(backup_dir): |
| | os.makedirs(backup_dir) |
| |
|
| | if os.path.exists(GROUND_TRUTH_FILE): |
| | backup_name = f"ground_truth_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" |
| | backup_path = os.path.join(backup_dir, backup_name) |
| | import shutil |
| | shutil.copy2(GROUND_TRUTH_FILE, backup_path) |
| |
|
| | with open(GROUND_TRUTH_FILE, 'w', encoding='utf-8') as f: |
| | json.dump(data, f, ensure_ascii=False, indent=2) |
| |
|
| | print(f"โ
Ground Truth ์ ์ฅ ์๋ฃ: {len(data)}๊ฐ ์ด๋ฏธ์ง") |
| |
|
| |
|
| | def get_folders(): |
| | """์ฌ์ฉ ๊ฐ๋ฅํ ํด๋ ๋ชฉ๋ก""" |
| | folders = sorted(glob.glob(os.path.join(DATA_BASE, "2*"))) |
| | return [os.path.basename(f) for f in folders if os.path.isdir(f)] |
| |
|
| |
|
| | def start_labeling(folder, conf_threshold, model_type): |
| | """๋ผ๋ฒจ๋ง ์์""" |
| | if not folder: |
| | return None, "โ ํด๋๋ฅผ ์ ํํ์ธ์.", "" |
| |
|
| | current_data['folder'] = folder |
| | current_data['confidence_threshold'] = conf_threshold |
| | current_data['model_type'] = model_type |
| |
|
| | folder_path = os.path.join(DATA_BASE, folder) |
| | all_images = sorted(glob.glob(os.path.join(folder_path, "*.jpg"))) |
| |
|
| | |
| | import re |
| | images = [img for img in all_images if not re.search(r'-\d+\.jpg$', os.path.basename(img))] |
| |
|
| | if not images: |
| | return None, "โ ์ด๋ฏธ์ง ์์", "" |
| |
|
| | print(f"๐ ํด๋: {folder}") |
| | print(f" ์ ์ฒด ์ด๋ฏธ์ง: {len(all_images)}๊ฐ") |
| | print(f" ๋ผ๋ฒจ๋ง ๋์: {len(images)}๊ฐ (-์ซ์ ํ์ผ ์ ์ธ)") |
| |
|
| | current_data['images'] = images |
| | current_data['current_idx'] = 0 |
| | current_data['detections'] = {} |
| | current_data['selections'] = {} |
| |
|
| | |
| | gt = load_existing_ground_truth() |
| |
|
| | |
| | for i, img_path in enumerate(images): |
| | filename = os.path.basename(img_path) |
| | if filename in gt: |
| | current_data['selections'][filename] = [j for j in range(len(gt[filename]))] |
| | print(f"โญ๏ธ ๊ฑด๋๋ฐ๊ธฐ: {filename} (์ด๋ฏธ ๋ผ๋ฒจ๋ง๋จ)") |
| |
|
| | |
| | while current_data['current_idx'] < len(images): |
| | filename = os.path.basename(images[current_data['current_idx']]) |
| | if filename not in current_data['selections']: |
| | break |
| | current_data['current_idx'] += 1 |
| |
|
| | if current_data['current_idx'] >= len(images): |
| | return None, "โ
๋ชจ๋ ์ด๋ฏธ์ง ๋ผ๋ฒจ๋ง ์๋ฃ!", "" |
| |
|
| | return show_current_image() |
| |
|
| |
|
| | def show_current_image(): |
| | """ํ์ฌ ์ด๋ฏธ์ง ํ์""" |
| | if current_data['current_idx'] >= len(current_data['images']): |
| | return None, "โ
์๋ฃ!", "" |
| |
|
| | img_path = current_data['images'][current_data['current_idx']] |
| | filename = os.path.basename(img_path) |
| |
|
| | |
| | if filename in current_data['image_cache']: |
| | image = current_data['image_cache'][filename] |
| | else: |
| | image = Image.open(img_path) |
| | current_data['image_cache'][filename] = image |
| |
|
| | |
| | if filename not in current_data['detections']: |
| | if current_data['model_type'] == 'RT-DETR': |
| | detections = detect_with_rtdetr_fast(image, current_data['confidence_threshold']) |
| | elif current_data['model_type'] == 'YOLOv8': |
| | detections = detect_with_yolo(image, current_data['confidence_threshold']) |
| | else: |
| | detections = detect_with_roboflow(image, current_data['confidence_threshold']) |
| | current_data['detections'][filename] = detections |
| | else: |
| | detections = current_data['detections'][filename] |
| |
|
| | |
| | selected_indices = current_data['selections'].get(filename, []) |
| |
|
| | |
| | vis_image = draw_detections(image, detections, selected_indices) |
| |
|
| | info = f""" |
| | ### ๐ {current_data['folder']} - ์ด๋ฏธ์ง {current_data['current_idx']+1}/{len(current_data['images'])} |
| | |
| | **ํ์ผ**: {filename} |
| | **๋ชจ๋ธ**: {current_data['model_type']} |
| | |
| | **๊ฒ์ถ**: {len(detections)}๊ฐ |
| | **์ ํ**: {len(selected_indices)}๊ฐ |
| | |
| | --- |
| | |
| | ### ๐ฑ๏ธ ์ฌ์ฉ ๋ฐฉ๋ฒ: |
| | 1. ์ด๋ฏธ์ง๋ฅผ ํด๋ฆญํ์ฌ ๋ฐ์ค ์ ํ/ํด์ |
| | 2. "๋ค์" ๋ฒํผ์ผ๋ก ์ ์ฅ ํ ์ด๋ |
| | 3. "๊ฑด๋๋ฐ๊ธฐ"๋ก ์ ํ ์์ด ์ด๋ |
| | """ |
| |
|
| | return vis_image, info, filename |
| |
|
| |
|
| | def draw_detections(image, detections, selected_indices): |
| | """๊ฒ์ถ ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ""" |
| | img = image.copy() |
| | draw = ImageDraw.Draw(img) |
| |
|
| | try: |
| | font_tiny = ImageFont.truetype("arial.ttf", 10) |
| | font_large = ImageFont.truetype("arial.ttf", 40) |
| | except: |
| | font_tiny = ImageFont.load_default() |
| | font_large = ImageFont.load_default() |
| |
|
| | |
| | for idx, det in enumerate(detections): |
| | if idx not in selected_indices: |
| | x1, y1, x2, y2 = det['bbox'] |
| | draw.rectangle([x1, y1, x2, y2], outline="lime", width=20) |
| | corner_label = f"#{idx+1}" |
| | draw.rectangle([x1-2, y1-24, x1+30, y1-2], fill="lime") |
| | draw.text((x1, y1 - 22), corner_label, fill="white", font=font_tiny) |
| |
|
| | |
| | for idx, det in enumerate(detections): |
| | if idx in selected_indices: |
| | x1, y1, x2, y2 = det['bbox'] |
| | draw.rectangle([x1, y1, x2, y2], outline="blue", width=28) |
| | corner_label = f"โ#{idx+1}" |
| | draw.rectangle([x1-2, y1-24, x1+40, y1-2], fill="blue") |
| | draw.text((x1, y1 - 22), corner_label, fill="white", font=font_tiny) |
| |
|
| | |
| | for idx, det in enumerate(detections): |
| | x1, y1, x2, y2 = det['bbox'] |
| | center_x = (x1 + x2) / 2 |
| | center_y = (y1 + y2) / 2 |
| |
|
| | selected = idx in selected_indices |
| | btn_color = "blue" if selected else "lime" |
| | btn_text = f"โ{idx+1}" if selected else f"{idx+1}" |
| |
|
| | box_width = x2 - x1 |
| | box_height = y2 - y1 |
| | radius = min(55, box_width * 0.18, box_height * 0.35) |
| |
|
| | |
| | draw.ellipse( |
| | [center_x - radius, center_y - radius, |
| | center_x + radius, center_y + radius], |
| | fill=btn_color, outline="white", width=4 |
| | ) |
| | draw.text((center_x - radius*0.5, center_y - radius*0.6), |
| | btn_text, fill="white", font=font_large) |
| |
|
| | return img |
| |
|
| |
|
| | def labeling_click(image, filename, evt: gr.SelectData): |
| | """์ด๋ฏธ์ง ํด๋ฆญ ์ด๋ฒคํธ""" |
| | if not filename or filename not in current_data['detections']: |
| | return image, "โ ๏ธ ์ด๋ฏธ์ง๋ฅผ ๋จผ์ ๋ก๋ํ์ธ์." |
| |
|
| | click_x, click_y = evt.index[0], evt.index[1] |
| | detections = current_data['detections'][filename] |
| | selected_indices = set(current_data['selections'].get(filename, [])) |
| |
|
| | |
| | clicked_idx = None |
| | button_candidates = [] |
| |
|
| | |
| | for idx, det in enumerate(detections): |
| | x1, y1, x2, y2 = det['bbox'] |
| | center_x = (x1 + x2) / 2 |
| | center_y = (y1 + y2) / 2 |
| |
|
| | box_width = x2 - x1 |
| | box_height = y2 - y1 |
| | radius = min(55, box_width * 0.18, box_height * 0.35) |
| |
|
| | distance = ((click_x - center_x) ** 2 + (click_y - center_y) ** 2) ** 0.5 |
| |
|
| | if distance <= radius: |
| | button_candidates.append((idx, distance)) |
| |
|
| | |
| | if button_candidates: |
| | button_candidates.sort(key=lambda x: x[1]) |
| | clicked_idx = button_candidates[0][0] |
| | else: |
| | |
| | for idx, det in enumerate(detections): |
| | x1, y1, x2, y2 = det['bbox'] |
| | if x1 <= click_x <= x2 and y1 <= click_y <= y2: |
| | clicked_idx = idx |
| | break |
| |
|
| | |
| | if clicked_idx is not None: |
| | if clicked_idx in selected_indices: |
| | selected_indices.remove(clicked_idx) |
| | print(f"โ ์ ํ ํด์ : ๋ฐ์ค #{clicked_idx+1}") |
| | else: |
| | selected_indices.add(clicked_idx) |
| | print(f"โ
์ ํ: ๋ฐ์ค #{clicked_idx+1}") |
| |
|
| | current_data['selections'][filename] = list(selected_indices) |
| |
|
| | |
| | img_path = current_data['images'][current_data['current_idx']] |
| | image = Image.open(img_path) |
| | vis_image = draw_detections(image, detections, list(selected_indices)) |
| |
|
| | info = f"โ
๋ฐ์ค #{clicked_idx+1} {'์ ํ' if clicked_idx in selected_indices else 'ํด์ '}" |
| | return vis_image, info |
| |
|
| | return image, "โ ๋ฐ์ค๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค." |
| |
|
| |
|
| | def save_and_next(): |
| | """์ ์ฅ ํ ๋ค์""" |
| | if current_data['current_idx'] >= len(current_data['images']): |
| | return None, "โ
์๋ฃ!", "" |
| |
|
| | img_path = current_data['images'][current_data['current_idx']] |
| | filename = os.path.basename(img_path) |
| |
|
| | |
| | gt = load_existing_ground_truth() |
| | selected_indices = current_data['selections'].get(filename, []) |
| |
|
| | if selected_indices: |
| | detections = current_data['detections'][filename] |
| | gt[filename] = [ |
| | { |
| | 'bbox': detections[i]['bbox'], |
| | 'folder': current_data['folder'] |
| | } |
| | for i in selected_indices |
| | ] |
| | save_ground_truth(gt) |
| | print(f"๐พ ์ ์ฅ: {filename} - {len(selected_indices)}๊ฐ ๋ฐ์ค") |
| | else: |
| | print(f"โญ๏ธ ๊ฑด๋๋ฐ๊ธฐ: {filename} - ์ ํ ์์") |
| |
|
| | |
| | current_data['current_idx'] += 1 |
| |
|
| | |
| | while current_data['current_idx'] < len(current_data['images']): |
| | next_filename = os.path.basename(current_data['images'][current_data['current_idx']]) |
| | if next_filename not in current_data['selections']: |
| | break |
| | current_data['current_idx'] += 1 |
| |
|
| | if current_data['current_idx'] >= len(current_data['images']): |
| | return None, "โ
๋ชจ๋ ์ด๋ฏธ์ง ๋ผ๋ฒจ๋ง ์๋ฃ!", "" |
| |
|
| | return show_current_image() |
| |
|
| |
|
| | def skip_image(): |
| | """๊ฑด๋๋ฐ๊ธฐ""" |
| | current_data['current_idx'] += 1 |
| |
|
| | if current_data['current_idx'] >= len(current_data['images']): |
| | return None, "โ
์๋ฃ!", "" |
| |
|
| | return show_current_image() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | with gr.Blocks(title="๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
", theme=gr.themes.Soft()) as demo: |
| |
|
| | gr.Markdown(""" |
| | # ๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
|
| | |
| | **2๊ฐ์ง ํญ์ผ๋ก ์์ฐ๋ฅผ ์ ํํ๊ฒ ๊ฒ์ถํ๊ณ ๊ด๋ฆฌํ์ธ์** |
| | |
| | --- |
| | """) |
| |
|
| | |
| | with gr.Row(): |
| | with gr.Column(scale=3): |
| | |
| | model_choices = ["RT-DETR", "VIDraft/Shrimp"] |
| | default_model = "RT-DETR" |
| | if YOLO_AVAILABLE: |
| | model_choices.append("YOLOv8") |
| | default_model = "YOLOv8" |
| |
|
| | model_selector = gr.Radio( |
| | choices=model_choices, |
| | value=default_model, |
| | label="๐ค ๊ฒ์ถ ๋ชจ๋ธ ์ ํ", |
| | info="๋ชจ๋ ํญ์ ์ ์ฉ๋ฉ๋๋ค" |
| | ) |
| | with gr.Column(scale=1): |
| | load_rtdetr_btn = gr.Button("๐ RT-DETR ๋ก๋", size="sm", variant="secondary") |
| | rtdetr_status = gr.Textbox(label="๋ชจ๋ธ ์ํ", value="โธ๏ธ RT-DETR ๋ฏธ๋ก๋ (VIDraft/Shrimp ํด๋ผ์ฐ๋ ๋ชจ๋ธ ์ฌ์ฉ ๊ฐ๋ฅ)", interactive=False, lines=1) |
| |
|
| | |
| | load_rtdetr_btn.click( |
| | load_rtdetr_on_demand, |
| | inputs=[], |
| | outputs=[rtdetr_status] |
| | ) |
| |
|
| | gr.Markdown("---") |
| |
|
| | with gr.Tabs(): |
| | |
| | with gr.TabItem("๐ค ์๋ ๊ฒ์ถ & ๊ฒ์ฆ"): |
| | gr.Markdown(""" |
| | ### ์ค์๊ฐ์ผ๋ก ํ๋ผ๋ฏธํฐ๋ฅผ ์กฐ์ ํ๋ฉฐ ๊ฒ์ถ ๊ฒฐ๊ณผ๋ฅผ ํ์ธ |
| | ์ต์ ํ๋ ํ๋ผ๋ฏธํฐ๋ก ์์ฐ ๊ฒ์ถ์ ํ
์คํธํ์ธ์. |
| | """) |
| |
|
| | with gr.Row(): |
| | with gr.Column(): |
| | input_image_detect = gr.Image(label="์
๋ ฅ ์ด๋ฏธ์ง", type="pil") |
| |
|
| | confidence_slider_detect = gr.Slider( |
| | 0.01, 1.0, 0.1, |
| | step=0.01, |
| | label="์ ๋ขฐ๋ ์๊ณ๊ฐ", |
| | info="RT-DETR: 0.065 | VIDraft/Shrimp: 0.3~0.5 | YOLOv8: 0.1~0.3 ๊ถ์ฅ" |
| | ) |
| |
|
| | use_filter_check = gr.Checkbox( |
| | label="๐ ํํฐ ์ ์ ์๊ณ๊ฐ ์ฌ์ฉ", |
| | value=False, |
| | info="์ฒดํฌํ๋ฉด ํํฐ ์ ์ ๊ธฐ์ค์ผ๋ก ์ถ๊ฐ ํํฐ๋ง" |
| | ) |
| |
|
| | filter_slider_detect = gr.Slider( |
| | 0, 100, 90, |
| | step=5, |
| | label="ํํฐ ์ ์ ์๊ณ๊ฐ", |
| | info="RT-DETR: Universal Filter | VIDraft/Shrimp: ์ ๋ขฐ๋ ๊ธฐ๋ฐ", |
| | visible=True |
| | ) |
| |
|
| | show_all_check = gr.Checkbox( |
| | label="์ ์ฒด ๊ฒ์ถ ๊ฒฐ๊ณผ ํ์ (ํ์)", |
| | value=False |
| | ) |
| |
|
| | detect_btn = gr.Button("๐ ๊ฒ์ถ ์คํ", variant="primary", size="lg") |
| |
|
| | |
| | example_images = [ |
| | "examples/250818_03.jpg", |
| | "examples/test_shrimp_tank.png", |
| | "examples/250818_05.jpg", |
| | ] |
| |
|
| | |
| | example_images = [img for img in example_images if os.path.exists(img)] |
| |
|
| | if example_images: |
| | gr.Examples( |
| | examples=[[img] for img in example_images], |
| | inputs=[input_image_detect], |
| | label="๐ท ์์ ์ด๋ฏธ์ง" |
| | ) |
| |
|
| | with gr.Column(): |
| | output_image_detect = gr.Image(label="๊ฒ์ถ ๊ฒฐ๊ณผ") |
| | output_info_detect = gr.Markdown() |
| |
|
| | detect_btn.click( |
| | interactive_detect, |
| | [input_image_detect, confidence_slider_detect, filter_slider_detect, show_all_check, model_selector, use_filter_check], |
| | [output_image_detect, output_info_detect] |
| | ) |
| |
|
| | |
| | def update_filter_interactivity(use_filter): |
| | return gr.update(interactive=use_filter) |
| |
|
| | use_filter_check.change( |
| | update_filter_interactivity, |
| | inputs=[use_filter_check], |
| | outputs=[filter_slider_detect] |
| | ) |
| |
|
| | gr.Markdown(""" |
| | ### ๐ก ์ฌ์ฉ ํ |
| | - ๋ชจ๋ธ์ ์ ํํ๊ณ ์ ๋ขฐ๋๋ฅผ ์กฐ์ ํ์ฌ ๊ฒ์ถ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ์ธ์ |
| | - ๊ฒ์ถ์ด ์ ์ ๋๋ ์ ๋ขฐ๋๋ฅผ ๋ฎ์ถ๊ณ , ์ค๊ฒ์ถ์ด ๋ง์ ๋๋ ๋์ด์ธ์ |
| | - ํํฐ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค |
| | |
| | **๋ฐ์ค ์์:** ๐ข ๋
น์(๋์ ํ๋ฅ ) | ๐ก ๋
ธ๋์(์ค๊ฐ ํ๋ฅ ) | ๐ ์ฃผํฉ์(๋ฎ์ ํ๋ฅ ) | ๐ด ๋นจ๊ฐ์(์ ๊ฑฐ๋จ) |
| | """) |
| |
|
| | |
| | with gr.TabItem("๐ Ground Truth ๋ผ๋ฒจ๋ง"): |
| | gr.Markdown(""" |
| | ### ์ ํ๋ ๋ชจ๋ธ์ ๊ฒ์ถ ๊ฒฐ๊ณผ์์ ์ฌ๋ฐ๋ฅธ ๋ฐ์ค๋ง ์ ํํ์ฌ ๋ผ๋ฒจ๋ง |
| | ์ด๋ฏธ์ง๋ฅผ ํด๋ฆญํ์ฌ ์์ฐ ๋ฐ์ค๋ฅผ ์ ํ/ํด์ ํ์ธ์. |
| | """) |
| |
|
| | with gr.Row(): |
| | with gr.Column(scale=1): |
| | folder_dropdown = gr.Dropdown( |
| | choices=get_folders(), |
| | label="๐ ํด๋ ์ ํ", |
| | info="๋ผ๋ฒจ๋งํ ํด๋๋ฅผ ์ ํํ์ธ์" |
| | ) |
| |
|
| | conf_slider_label = gr.Slider( |
| | 0.01, 0.5, 0.2, |
| | step=0.05, |
| | label="์ ๋ขฐ๋", |
| | info="๊ฒ์ถ ๋ฏผ๊ฐ๋ ์กฐ์ " |
| | ) |
| |
|
| | start_btn = gr.Button("โถ๏ธ ๋ผ๋ฒจ๋ง ์์", variant="primary", size="lg") |
| |
|
| | gr.Markdown("---") |
| |
|
| | next_btn = gr.Button("โญ๏ธ ์ ์ฅ & ๋ค์", variant="secondary", size="lg") |
| | skip_btn = gr.Button("โฉ ๊ฑด๋๋ฐ๊ธฐ", size="lg") |
| |
|
| | labeling_info = gr.Markdown("ํด๋๋ฅผ ์ ํํ๊ณ '๋ผ๋ฒจ๋ง ์์'์ ํด๋ฆญํ์ธ์.") |
| |
|
| | with gr.Column(scale=2): |
| | labeling_image = gr.Image( |
| | label="๐ฑ๏ธ ํด๋ฆญํ์ฌ ๋ฐ์ค ์ ํ/ํด์ ", |
| | type="pil", |
| | interactive=True |
| | ) |
| |
|
| | labeling_filename = gr.Textbox(visible=False) |
| | click_info = gr.Markdown() |
| |
|
| | |
| | start_btn.click( |
| | start_labeling, |
| | [folder_dropdown, conf_slider_label, model_selector], |
| | [labeling_image, labeling_info, labeling_filename] |
| | ) |
| |
|
| | labeling_image.select( |
| | labeling_click, |
| | [labeling_image, labeling_filename], |
| | [labeling_image, click_info] |
| | ) |
| |
|
| | next_btn.click( |
| | save_and_next, |
| | [], |
| | [labeling_image, labeling_info, labeling_filename] |
| | ) |
| |
|
| | skip_btn.click( |
| | skip_image, |
| | [], |
| | [labeling_image, labeling_info, labeling_filename] |
| | ) |
| |
|
| | gr.Markdown(""" |
| | ### ๐ฑ๏ธ ์ฌ์ฉ ๋ฐฉ๋ฒ |
| | 1. **๋ชจ๋ธ ์ ํ** (์ต์๋จ์์ ์ ํ) |
| | 2. ํด๋ ์ ํ ํ "๋ผ๋ฒจ๋ง ์์" |
| | 3. ์ด๋ฏธ์ง์์ **์ํ ๋ฒํผ ํด๋ฆญ** ๋๋ **๋ฐ์ค ์์ญ ํด๋ฆญ**์ผ๋ก ์ ํ/ํด์ |
| | 4. "์ ์ฅ & ๋ค์"์ผ๋ก ๋ค์ ์ด๋ฏธ์ง๋ก ์ด๋ (์๋ ์ ์ฅ) |
| | 5. "๊ฑด๋๋ฐ๊ธฐ"๋ก ์ ํ ์์ด ๋ค์ ์ด๋ฏธ์ง๋ก |
| | |
| | **๐พ ์ ์ฅ ์์น:** `ground_truth.json` (์๋ ๋ฐฑ์
: `backups/`) |
| | """) |
| |
|
| | gr.Markdown(""" |
| | --- |
| | |
| | ### ๐ค ๋ชจ๋ธ ์ค๋ช
|
| | - **RT-DETR**: ๋ก์ปฌ ๋ชจ๋ธ, ๋น ๋ฅธ ์ถ๋ก ์๋, ์คํ๋ผ์ธ ์ฌ์ฉ ๊ฐ๋ฅ |
| | - **VIDraft/Shrimp**: ํด๋ผ์ฐ๋ ๋ชจ๋ธ, ์ธํฐ๋ท ์ฐ๊ฒฐ ํ์ |
| | - **YOLOv8**: ๋ก์ปฌ ์ปค์คํ
ํ์ต ๋ชจ๋ธ, ๋น ๋ฅธ ์ถ๋ก ์๋ |
| | |
| | --- |
| | |
| | ยฉ 2025 VIDraft. All rights reserved. |
| | """) |
| |
|
| | if __name__ == "__main__": |
| | print("\n" + "="*60) |
| | print("๐ฆ ์์ฐ ๊ฒ์ถ ํตํฉ ์์คํ
v2.2 ์์") |
| | print("="*60) |
| | print("๐ค ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ธ:") |
| | print(" 1. RT-DETR (๋ก์ปฌ)") |
| | print(" 2. VIDraft/Shrimp (ํด๋ผ์ฐ๋)") |
| | print(" 3. YOLOv8 (๋ก์ปฌ ํ์ต) โญ ๊ธฐ๋ณธ๊ฐ") |
| | print(f"\n๐ฆ YOLOv8 ๋ชจ๋ธ: {YOLO_MODEL_PATH}") |
| | print("="*60) |
| |
|
| | demo.launch( |
| | server_name="0.0.0.0", |
| | server_port=None, |
| | share=False |
| | ) |
| |
|