File size: 6,651 Bytes
bd977a3
bf1fd9f
 
 
 
 
 
0f3d01e
bf1fd9f
 
b60852c
bf1fd9f
 
 
 
 
 
 
 
540d913
b60852c
bf1fd9f
6f2c705
bf1fd9f
 
 
 
540d913
 
 
15df14e
 
 
540d913
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6f2c705
540d913
 
15df14e
540d913
6f2c705
 
 
 
 
 
 
15df14e
6f2c705
 
bf1fd9f
15df14e
bf1fd9f
 
 
540d913
bf1fd9f
540d913
bf1fd9f
15df14e
540d913
 
 
56ff081
540d913
6f2c705
 
540d913
6f2c705
 
 
 
 
 
540d913
6f2c705
 
540d913
6f2c705
 
 
15df14e
 
 
 
 
 
 
 
 
 
 
6f2c705
540d913
bf1fd9f
15df14e
bf1fd9f
 
b60852c
 
 
540d913
 
15df14e
b60852c
 
 
540d913
 
 
bf1fd9f
 
15df14e
 
 
 
540d913
bf1fd9f
 
540d913
bf1fd9f
b60852c
 
bf1fd9f
b60852c
bf1fd9f
 
0f3d01e
bf1fd9f
 
 
15df14e
 
bf1fd9f
15df14e
 
bf1fd9f
15df14e
 
bf1fd9f
15df14e
 
bf1fd9f
b60852c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import gradio as gr
from dotenv import load_dotenv
from roboflow import Roboflow
import tempfile
import os
import requests
import cv2
import numpy as np
import subprocess

# ========== Load Environment Variables ==========
load_dotenv()

# Roboflow Config
rf_api_key = os.getenv("ROBOFLOW_API_KEY")
workspace = os.getenv("ROBOFLOW_WORKSPACE")
project_name = os.getenv("ROBOFLOW_PROJECT")
model_version = int(os.getenv("ROBOFLOW_MODEL_VERSION"))

# CountGD Config (Replace DINO-X)
COUNTGD_API_KEY = os.getenv("COUNTGD_API_KEY")

# Inisialisasi YOLO Model dari Roboflow
rf = Roboflow(api_key=rf_api_key)
project = rf.workspace(workspace).project(project_name)
yolo_model = project.version(model_version).model

# ========== Fungsi untuk Mengecek Overlap antara YOLO dan CountGD ==========
def is_overlap(box1, boxes2, threshold=0.5):
    """
    Mengecek apakah box1 (format: (x_min, y_min, x_max, y_max)) overlap
    dengan salah satu box di boxes2 (format: (x_center, y_center, width, height))
    berdasarkan IoU, menggunakan threshold yang ditetapkan.
    """
    x1_min, y1_min, x1_max, y1_max = box1
    for b2 in boxes2:
        x_center, y_center, w2, h2 = b2
        x2_min = x_center - w2 / 2
        x2_max = x_center + w2 / 2
        y2_min = y_center - h2 / 2
        y2_max = y_center + h2 / 2

        dx = min(x1_max, x2_max) - max(x1_min, x2_min)
        dy = min(y1_max, y2_max) - max(y1_min, y2_min)
        if dx > 0 and dy > 0:
            area_overlap = dx * dy
            area_box1 = (x1_max - x1_min) * (y1_max - y1_min)
            if area_box1 > 0 and (area_overlap / area_box1) > threshold:
                return True
    return False

# ========== Fungsi untuk Menghitung IoU antar dua bounding box ==========
def iou(boxA, boxB):
    """
    Menghitung Intersection over Union (IoU) antara dua bounding box.
    Masing-masing box dalam format (x_min, y_min, x_max, y_max).
    """
    xA = max(boxA[0], boxB[0])
    yA = max(boxA[1], boxB[1])
    xB = min(boxA[2], boxB[2])
    yB = min(boxA[3], boxB[3])
    interArea = max(0, xB - xA) * max(0, yB - yA)
    boxAArea = (boxA[2] - boxA[0]) * (boxA[3] - boxA[1])
    boxBArea = (boxB[2] - boxB[0]) * (boxB[3] - boxB[1])
    return interArea / float(boxAArea + boxBArea - interArea) if (boxAArea + boxBArea - interArea) > 0 else 0

# ========== Fungsi Deteksi Kombinasi ==========
def detect_combined(image):
    # Simpan image ke file sementara
    with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file:
        image.save(temp_file, format="JPEG")
        temp_path = temp_file.name
    
    try:
        # ===== YOLO Detection =====
        yolo_pred = yolo_model.predict(temp_path, confidence=50, overlap=80).json()
        # Get YOLO boxes as (x_center, y_center, width, height)
        nestle_boxes = [(pred['x'], pred['y'], pred['width'], pred['height']) for pred in yolo_pred['predictions']]
        
        # ===== CountGD Detection =====
        url = "https://api.landing.ai/v1/tools/text-to-object-detection"
        headers = {"Authorization": f"Basic {COUNTGD_API_KEY}"}
        competitor_boxes = []
        COUNTGD_PROMPTS = ["cans", "bottle", "mixed box"]
        
        for prompt in COUNTGD_PROMPTS:
            with open(temp_path, "rb") as f:
                files = {"image": f}
                data = {"prompts": [prompt], "model": "countgd"}
                response = requests.post(url, files=files, data=data, headers=headers)
            result = response.json()
            
            if 'data' in result and result['data']:
                detections = result['data'][0]
                for obj in detections:
                    if 'bounding_box' in obj:
                        x1, y1, x2, y2 = obj['bounding_box']
                        countgd_box = (x1, y1, x2, y2)
                        # Prioritaskan deteksi YOLO: hapus jika overlap dengan YOLO (threshold 0.5)
                        if is_overlap(countgd_box, nestle_boxes, threshold=0.5):
                            continue
                        # Hindari duplikasi antar CountGD: jika IoU dengan deteksi lain > 0.4, lewati
                        duplicate = False
                        for existing_box in competitor_boxes:
                            if iou(countgd_box, existing_box) > 0.4:
                                duplicate = True
                                break
                        if not duplicate:
                            competitor_boxes.append(countgd_box)
        
        # ===== Visualisasi =====
        img = cv2.imread(temp_path)
        # Gambar bounding box YOLO (hijau)
        for pred in yolo_pred['predictions']:
            x, y, w, h = pred['x'], pred['y'], pred['width'], pred['height']
            pt1 = (int(x - w/2), int(y - h/2))
            pt2 = (int(x + w/2), int(y + h/2))
            cv2.rectangle(img, pt1, pt2, (0, 255, 0), 2)
            cv2.putText(img, pred['class'], (pt1[0], pt1[1]-10),
                        cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0,255,0), 3)
        # Gambar bounding box CountGD (merah)
        for box in competitor_boxes:
            x1, y1, x2, y2 = box
            cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2)
            cv2.putText(img, "unclassified", (int(x1), int(y1)-10),
                        cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0,0,255), 3)
        
        output_path = "/tmp/combined_output.jpg"
        cv2.imwrite(output_path, img)
        
        # Buat result text untuk counting object
        result_text = f"Total Produk Nestlé: {len(nestle_boxes)}\nTotal Unclassified Products: {len(competitor_boxes)}"
        return output_path, result_text
    
    except Exception as e:
        return temp_path, f"Error: {str(e)}"
    
    finally:
        if os.path.exists(temp_path):
            os.remove(temp_path)

# ========== Gradio Interface ==========
with gr.Blocks(theme=gr.themes.Base(primary_hue="teal", secondary_hue="teal", neutral_hue="slate")) as iface:
    gr.Markdown("""<div style="text-align: center;"><h1>NESTLE - STOCK COUNTING</h1></div>""")
    
    with gr.Row():
        with gr.Column():
            input_image = gr.Image(type="pil", label="Input Image")
    with gr.Row():
        with gr.Column():
            detect_image_button = gr.Button("Detect Image")
    with gr.Row():
        with gr.Column():
            output_image = gr.Image(label="Detect Object")
    with gr.Row():
        with gr.Column():
            output_text = gr.Textbox(label="Counting Object")
    
    detect_image_button.click(fn=detect_combined, inputs=input_image, outputs=[output_image, output_text])

iface.launch()