File size: 6,110 Bytes
bd977a3
bf1fd9f
 
 
 
 
 
0f3d01e
bf1fd9f
 
b60852c
bf1fd9f
 
 
 
 
 
 
 
6f2c705
b60852c
bf1fd9f
6f2c705
bf1fd9f
 
 
 
6f2c705
 
 
 
 
 
 
 
 
 
 
 
 
 
bf1fd9f
 
 
 
6f2c705
bf1fd9f
6f2c705
bf1fd9f
 
6f2c705
bf1fd9f
 
 
 
6f2c705
bf1fd9f
6f2c705
 
56ff081
0aa4aac
6f2c705
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b978f6
6f2c705
bf1fd9f
6f2c705
 
56ff081
bf1fd9f
 
56ff081
6f2c705
b60852c
6f2c705
b60852c
 
6f2c705
 
bf1fd9f
 
 
b60852c
 
 
6f2c705
 
b60852c
 
 
6f2c705
 
bf1fd9f
 
 
6f2c705
bf1fd9f
 
6f2c705
bf1fd9f
b60852c
 
bf1fd9f
b60852c
bf1fd9f
 
0f3d01e
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
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
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 Menghitung IoU ==========
def iou(boxA, boxB):
    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):
    with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file:
        image.save(temp_file, format="JPEG")
        temp_path = temp_file.name

    try:
        # YOLO Detection (Produk Nestlé)
        yolo_pred = yolo_model.predict(temp_path, confidence=50, overlap=80).json()
        nestle_class_count = {}
        nestle_boxes = []  # (x_center, y_center, width, height)
        for pred in yolo_pred['predictions']:
            class_name = pred['class']
            nestle_class_count[class_name] = nestle_class_count.get(class_name, 0) + 1
            nestle_boxes.append((pred['x'], pred['y'], pred['width'], pred['height']))

        total_nestle = sum(nestle_class_count.values())

        # CountGD Detection (Produk Kompetitor)
        url = "https://api.landing.ai/v1/tools/text-to-object-detection"
        competitor_class_count = {}
        competitor_boxes = []
        COUNTGD_PROMPTS = ["cans", "bottle", "mixed box"]
        headers = {"Authorization": f"Basic {COUNTGD_API_KEY}"}

        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]
                detections_sorted = sorted(detections, key=lambda obj: obj.get('confidence', 0), reverse=True)

                for obj in detections_sorted:
                    if 'bounding_box' in obj:
                        x1, y1, x2, y2 = obj['bounding_box']
                        countgd_box = (x1, y1, x2, y2)

                        # Hapus duplikasi dengan deteksi YOLO
                        if any(iou(countgd_box, yolo_box) > 0.3 for yolo_box in nestle_boxes):
                            continue

                        # Hapus duplikasi antar deteksi CountGD
                        if any(iou(countgd_box, existing_box) > 0.3 for existing_box in competitor_boxes):
                            continue

                        label = obj.get('label', prompt)
                        
                        # Hapus "mixed box" jika ada "cans" atau "bottle" yang lebih spesifik
                        if label == "mixed box" and ("cans" in competitor_class_count or "bottle" in competitor_class_count):
                            continue

                        competitor_class_count[label] = competitor_class_count.get(label, 0) + 1
                        competitor_boxes.append(countgd_box)

        total_competitor = sum(competitor_class_count.values())

        # Format Output Text
        result_text = "Product Nestlé\n\n"
        for class_name, count in nestle_class_count.items():
            result_text += f"{class_name}: {count}\n"
        result_text += f"\nTotal Products Nestlé: {total_nestle}\n\n"
        
        if total_competitor:
            result_text += f"\nTotal Unclassified Products: {total_competitor}\n"
        else:
            result_text += "No Unclassified Products detected\n"

        # Visualisasi Bounding Box
        img = cv2.imread(temp_path)
        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)

        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)
        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")
            detect_image_button = gr.Button("Detect Image")
            output_image = gr.Image(label="Detect Object")
            output_text = gr.Textbox(label="Counting Object")
            detect_image_button.click(fn=detect_combined, inputs=input_image, outputs=[output_image, output_text])

iface.launch()