File size: 6,871 Bytes
7790c53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
163
164
165
166
167
168
169
170
171
172
173
174
175
import io
import json
import os
import tempfile
import zipfile
import datetime
import requests
import random
import gradio as gr
from PIL import Image, ImageDraw, ImageFont

API_URL = os.getenv("API_URL")
API_KEY = os.getenv("API_KEY")
MODEL = os.getenv("MODEL")

ALLOWED_EXTENSIONS = (
    ".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp", 
    ".tif", ".tiff", ".avif", ".ico", ".ppm", ".pgm", ".pbm", ".eps", ".icns"
)

def get_color_for_label(label, color_mapping):
    if label in color_mapping:
        return color_mapping[label]
    color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
    color_mapping[label] = color
    return color

def process_single_image(image, prompt, confidence, file_format):
    if "," in prompt:
        prompt_list = [p.strip() for p in prompt.split(",")]
    else:
        prompt_list = [prompt.strip()]
    prompt_list_lower = [p.lower() for p in prompt_list]

    buffered = io.BytesIO()
    image.save(buffered, format="PNG")
    buffered.seek(0)
    
    files = {"image": buffered}
    data = {
        "prompts": prompt_list,
        "model": MODEL,
        "confidence": confidence
    }
    headers = {"Authorization": "Basic " + API_KEY}

    response = requests.post(API_URL, files=files, data=data, headers=headers)
    response_data = response.json()
    print("Response data:", response_data)

    detections_list = response_data.get("data", [])
    if detections_list and isinstance(detections_list, list):
        detections = detections_list[0]
    else:
        detections = []

    annotated_image = image.copy()
    draw = ImageDraw.Draw(annotated_image)
    try:
        font = ImageFont.truetype("arial.ttf", size=15)
    except IOError:
        font = ImageFont.load_default()

    color_mapping = {}
    for det in detections:
        box = det["bounding_box"]
        score = det["score"]
        detection_label = det.get("label", prompt).strip()
        box_color = get_color_for_label(detection_label, color_mapping)
        draw.rectangle(box, outline=box_color, width=2)
        text = f"{detection_label} {score:.2f}"
        bbox = draw.textbbox((0, 0), text, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]
        text_origin = (box[0], box[1] - text_height)
        draw.rectangle([text_origin, (box[0] + text_width, box[1])], fill=box_color)
        draw.text(text_origin, text, fill="white", font=font)

    img_width, img_height = image.size

    if file_format.lower() == "json":
        detection_info = {"detections": detections}
        ext = ".json"
        content = json.dumps(detection_info, indent=2)
    elif file_format.lower() == "txt":
        lines = []
        for det in detections:
            detection_label = det.get("label", prompt).strip().lower()
            if detection_label not in prompt_list_lower:
                continue
            class_id = prompt_list_lower.index(detection_label)
            x1, y1, x2, y2 = det["bounding_box"]
            x_center = ((x1 + x2) / 2) / img_width
            y_center = ((y1 + y2) / 2) / img_height
            bbox_width = (x2 - x1) / img_width
            bbox_height = (y2 - y1) / img_height
            line = f"{class_id} {x_center:.6f} {y_center:.6f} {bbox_width:.6f} {bbox_height:.6f}"
            lines.append(line)
        content = "\n".join(lines)
        ext = ".txt"
    else:
        detection_info = {"detections": detections}
        ext = ".json"
        content = json.dumps(detection_info, indent=2)

    temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=ext, mode="w", encoding="utf-8")
    temp_file.write(content)
    temp_file.close()

    return annotated_image, temp_file.name

def auto_annotate_batch(input_files, prompt, confidence, file_format):
    images_and_names = []
    if len(input_files) == 1 and str(input_files[0]).lower().endswith(".zip"):
        zip_path = input_files[0]
        extract_dir = tempfile.mkdtemp()
        with zipfile.ZipFile(zip_path, 'r') as zip_ref:
            zip_ref.extractall(extract_dir)
        for root, _, files in os.walk(extract_dir):
            for file in files:
                if file.lower().endswith(ALLOWED_EXTENSIONS):
                    img_path = os.path.join(root, file)
                    try:
                        img = Image.open(img_path).convert("RGB")
                        images_and_names.append((img, file))
                    except Exception as e:
                        print(f"Gagal membuka {img_path}: {e}")
    else:
        for file_path in input_files:
            if file_path.lower().endswith(ALLOWED_EXTENSIONS):
                try:
                    img = Image.open(file_path).convert("RGB")
                    original_filename = os.path.basename(file_path)
                    images_and_names.append((img, original_filename))
                except Exception as e:
                    print(f"Gagal membuka {file_path}: {e}")

    annotated_images = []
    detection_file_paths = []
    for img, original_filename in images_and_names:
        ann_img, temp_det_file = process_single_image(img, prompt, confidence, file_format)
        annotated_images.append(ann_img)
        base_name, _ = os.path.splitext(original_filename)
        new_name = base_name + (".json" if file_format.lower() == "json" else ".txt")
        new_path = os.path.join(os.path.dirname(temp_det_file), new_name)
        os.rename(temp_det_file, new_path)
        detection_file_paths.append(new_path)

    timestamp = datetime.datetime.now().strftime("%d-%m-%Y_%H-%M-%S")
    zip_filename = f"Data_Annotate_{timestamp}.zip"
    zip_out_path = os.path.join(tempfile.gettempdir(), zip_filename)
    
    with zipfile.ZipFile(zip_out_path, 'w') as zipf:
        for file_path in detection_file_paths:
            zipf.write(file_path, os.path.basename(file_path))
    
    return annotated_images, zip_out_path, detection_file_paths

iface = gr.Interface(
    fn=auto_annotate_batch,
    inputs=[
        gr.File(file_count="multiple", type="filepath", label="Upload Image (multiple files or ZIP)"),
        gr.Textbox(lines=1, placeholder="Enter object prompt (separate with comma if more than one)", label="Prompt"),
        gr.Slider(minimum=0.0, maximum=1.0, step=0.05, value=0.25, label="Confidence Threshold"),
        gr.Radio(choices=["json", "txt"], value="json", label="Format Annotation File")
    ],
    outputs=[
        gr.Gallery(label="Annotated Images"),
        gr.File(label="Download All Annotations (ZIP)"),
        gr.File(label="Download Individual Annotation Files")
    ],
    title="Auto Annotate")

if __name__ == "__main__":
    iface.launch(debug=True)