File size: 10,617 Bytes
6808993
 
 
 
 
 
 
21e62cb
6808993
 
 
0035c01
21e62cb
0035c01
66534eb
0035c01
9741354
6808993
 
1d3debf
6808993
 
 
1d3debf
6808993
21e62cb
 
 
1d3debf
6808993
1d3debf
 
6808993
 
1d3debf
 
 
6808993
1d3debf
6808993
 
1d3debf
 
 
6808993
 
 
 
 
1d3debf
6808993
1d3debf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6808993
1d3debf
6808993
 
 
0035c01
1d3debf
6808993
1d3debf
 
6808993
1d3debf
 
0035c01
 
1d3debf
 
 
 
 
 
 
 
6808993
 
1d3debf
0510371
1d3debf
 
 
 
 
 
6808993
 
 
 
 
 
 
 
 
1d3debf
 
 
 
6808993
 
 
 
1d3debf
 
6808993
1d3debf
0035c01
 
 
1d3debf
 
 
 
6808993
 
 
 
1d3debf
 
 
 
 
 
21e62cb
0ed2702
 
 
 
 
 
 
 
 
 
 
1d3debf
9741354
1d3debf
6808993
 
 
0035c01
 
1d3debf
 
 
 
 
 
 
 
 
6808993
 
 
1d3debf
6808993
 
 
1d3debf
0ed2702
6808993
 
 
 
0035c01
1d3debf
 
 
 
 
 
 
 
 
6808993
 
 
 
1d3debf
6808993
 
 
c4b0dc0
 
6808993
 
 
1d3debf
420868b
c4b0dc0
6808993
420868b
6808993
 
 
 
 
 
1d3debf
 
 
 
6808993
 
1d3debf
c4b0dc0
1d3debf
6808993
 
 
 
 
1d3debf
6808993
21e62cb
 
 
 
 
 
 
 
6808993
 
1d3debf
 
6808993
 
 
1d3debf
 
 
 
 
6808993
 
 
 
 
 
 
 
c4b0dc0
1d3debf
0035c01
1d3debf
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# -*- coding: utf-8 -*-
"""
Aplikasi Gradio untuk Analisis Komparatif Deteksi Helm Keselamatan
==================================================================
Deskripsi:
Aplikasi ini memungkinkan pengguna untuk mengunggah video dari lingkungan konstruksi
dan membandingkan kinerja dua model AI (YOLOv5m dan YOLOv8m) dalam mendeteksi
helm keselamatan secara real-time.
"""

# --- 1. IMPORT LIBRARY ---
import gradio as gr
from ultralyticsplus import YOLO, render_result
import cv2
import numpy as np
import os
import tempfile
import time
import logging
import subprocess

# --- 2. SETUP & KONFIGURASI AWAL ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Set YOLO_CONFIG_DIR untuk mengatasi masalah izin
os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"

# --- 3. PEMUATAN MODEL AI ---
try:
    logger.info("Memulai pemuatan model AI...")
    model_spesialis = YOLO('keremberke/yolov8m-hard-hat-detection')
    model_spesialis.overrides['conf'] = 0.25
    model_spesialis.overrides['iou'] = 0.45
    model_spesialis.overrides['agnostic_nms'] = False
    model_spesialis.overrides['max_det'] = 1000
    logger.info("βœ… Model Spesialis (YOLOv8m-HH) berhasil dimuat.")

    model_generalis = YOLO('keremberke/yolov5m-construction-safety')
    model_generalis.overrides['conf'] = 0.25
    model_generalis.overrides['iou'] = 0.45
    model_generalis.overrides['agnostic_nms'] = False
    model_generalis.overrides['max_det'] = 1000
    logger.info("βœ… Model Generalis (YOLOv5m-CS) berhasil dimuat.")

    models = {
        "YOLOv8m (Spesialis Helm)": model_spesialis,
        "YOLOv5m (Generalis Konstruksi)": model_generalis
    }
    logger.info("Semua model siap digunakan.")
except Exception as e:
    logger.error(f"Gagal memuat model AI: {e}")
    raise RuntimeError(f"Tidak dapat memuat model AI. Error: {e}")

# --- 4. FUNGSI KONVERSI VIDEO ---
def convert_video_to_mp4(input_path):
    """Konversi video input ke MP4 jika format tidak didukung."""
    temp_mp4_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
    ffmpeg_command = [
        "ffmpeg",
        "-y",
        "-i", input_path,
        "-c:v", "libx264",
        "-preset", "veryfast",
        "-pix_fmt", "yuv420p",
        "-t", "30",
        temp_mp4_path
    ]
    result = subprocess.run(ffmpeg_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    if result.returncode == 0 and os.path.exists(temp_mp4_path) and os.path.getsize(temp_mp4_path) > 0:
        logger.info("Video dikonversi ke MP4: %s", temp_mp4_path)
        if input_path != temp_mp4_path:
            os.remove(input_path)
        return temp_mp4_path
    else:
        logger.error("Konversi video gagal: %s", result.stderr)
        return None

# --- 5. FUNGSI UTAMA PEMROSESAN VIDEO ---
def process_video_and_analyze(video_path, selected_model_name, progress=gr.Progress(track_tqdm=True)):
    """Fungsi utama untuk memproses video, deteksi objek, dan analisis."""
    if video_path is None:
        return None, "Status: Silakan unggah video terlebih dahulu.", ""

    try:
        logger.info(f"Memulai pemrosesan video: {video_path} menggunakan model: {selected_model_name}")
        start_time = time.time()

        # Pilih model
        model = models[selected_model_name]

        # Buka video dan coba konversi jika gagal
        cap = cv2.VideoCapture(video_path)
        if not cap.isOpened():
            logger.warning("Format video tidak didukung, mencoba konversi ke MP4...")
            converted_path = convert_video_to_mp4(video_path)
            if converted_path:
                video_path = converted_path
                cap = cv2.VideoCapture(video_path)
            if not cap.isOpened():
                logger.error("Gagal membuka video setelah konversi")
                return None, "Error: Video tidak dapat dibuka meskipun dikonversi.", ""

        # Dapatkan properti video
        fps = cap.get(cv2.CAP_PROP_FPS) or 15
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        max_duration = 30
        max_frames = int(fps * max_duration)
        if total_frames > max_frames:
            logger.warning("Video lebih dari 30 detik, dipotong ke %d frame", max_frames)
            total_frames = max_frames

        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        target_width = 640
        target_height = int(height * (target_width / width)) if width > 0 else 480

        # Konfigurasi video output
        temp_output_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
        fourcc = cv2.VideoWriter_fourcc(*"mp4v")
        out = cv2.VideoWriter(temp_output_path, fourcc, fps, (target_width, target_height))
        if not out.isOpened():
            logger.error("Gagal membuka VideoWriter")
            cap.release()
            return None, "Error: Gagal membuat file video sementara.", ""

        # Variabel untuk analisis
        detection_count = 0
        helm_detected_count = 0
        frame_count = 0

        progress(0, desc="Memulai Pemrosesan...")
        while cap.isOpened() and frame_count < max_frames:
            ret, frame = cap.read()
            if not ret:
                break
            frame_count += 1

            # Update progress
            progress(frame_count / total_frames, desc=f"Memproses Frame {frame_count}/{total_frames}")

            # Resize frame
            frame_resized = cv2.resize(frame, (target_width, target_height))

            # Proses frame (skip setiap frame ke-3 untuk performa)
            if frame_count % 3 != 0:
                out.write(frame_resized)
                continue

            # Prediksi
            results = model.predict(frame_resized)
            annotated_frame = render_result(model=model, image=frame_resized, result=results[0])
            annotated_frame = np.array(annotated_frame)  # Konversi PIL ke NumPy
            annotated_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_RGB2BGR)  # Konversi RGB ke BGR

            # Hitung deteksi
            detection_count += len(results[0].boxes)
            for box in results[0].boxes:
                class_id = int(box.cls)
                class_name = model.names[class_id].lower()
                if class_name in ['hardhat', 'helmet']:
                    helm_detected_count += 1

            out.write(annotated_frame)

        end_time = time.time()
        processing_time = end_time - start_time

        cap.release()
        out.release()

        # Verifikasi file output
        if not os.path.exists(temp_output_path) or os.path.getsize(temp_output_path) == 0:
            logger.error("File video sementara tidak valid atau kosong")
            return None, "Error: File video sementara tidak valid.", ""

        logger.info(f"Video berhasil diproses dalam {processing_time:.2f} detik.")

        # Analisis
        analysis_text = f"""
        ### Analisis Kinerja Model: {selected_model_name}
        - **Waktu Proses Total:** {processing_time:.2f} detik
        - **Total Frame Diproses:** {frame_count}
        - **Jumlah Deteksi Keseluruhan:** {detection_count} objek
        - **Jumlah Deteksi Helm:** {helm_detected_count} objek
        **Catatan:**
        - **Model Spesialis (YOLOv8m):** Fokus pada helm, akurasi tinggi untuk 'Hardhat'/'Helmet'.
        - **Model Generalis (YOLOv5m):** Deteksi berbagai objek, akurasi helm mungkin lebih rendah.
        """

        return temp_output_path, f"Status: Video berhasil diproses! ({processing_time:.2f} detik)", analysis_text

    except Exception as e:
        logger.error(f"Terjadi error saat memproses video: {e}", exc_info=True)
        return None, f"Error: Terjadi kesalahan - {e}", ""
    finally:
        if 'cap' in locals() and cap.isOpened():
            cap.release()
        if 'out' in locals() and out.isOpened():
            out.release()

# --- 6. PEMBUATAN ANTARMUKA PENGGUNA (GRADIO UI) ---
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky")) as iface:
    gr.Markdown(
        """
        # πŸ›‘οΈ Analisis Komparatif Deteksi Helm Keselamatan
        Aplikasi ini membandingkan kinerja dua model AI dalam mendeteksi helm keselamatan pada video konstruksi.
        """
    )
    
    with gr.Row():
        with gr.Column(scale=1):
            gr.Markdown("### **Langkah 1: Konfigurasi & Unggah**")
            selected_model_ui = gr.Radio(
                choices=list(models.keys()),
                label="Pilih Model AI",
                value="YOLOv8m (Spesialis Helm)"
            )
            video_input = gr.Video(
                label="Unggah Video"
            )
            detect_btn = gr.Button("πŸš€ Mulai Deteksi", variant="primary")
            
            gr.Markdown("### **Cara Penggunaan**")
            gr.Markdown(
                """
                1. Pilih model AI.
                2. Unggah video atau gunakan contoh.
                3. Klik "Mulai Deteksi".
                4. Lihat hasil dan analisis.
                """
            )
        
        with gr.Column(scale=1):
            gr.Markdown("### **Langkah 2: Hasil Deteksi**")
            video_output = gr.Video(label="Video Hasil Deteksi")
            status_text = gr.Textbox(label="Status Proses", interactive=False)
            gr.Markdown("### **Ringkasan Analisis**")
            analysis_output = gr.Markdown(label="Analisis Kinerja")

    # Contoh video
    gr.Examples(
        examples=[
            ["video.mp4", "YOLOv8m (Spesialis Helm)"]
        ],
        inputs=[video_input, selected_model_ui],
        outputs=[video_output, status_text, analysis_output],
        fn=process_video_and_analyze,
        cache_examples=False,
        label="Contoh Video"
    )

    # Informasi dan kredit
    with gr.Accordion("ℹ️ Informasi & Kredit", open=False):
        gr.Markdown(
            """
            ### **Identitas Prototipe**
            - **Pengembang:** Faisal Fahmi Yuliawan
            - **Tujuan:** Membandingkan deteksi helm dengan model AI.
            ### **Kredit**
            - **Model AI:** keremberke/yolov8m-hard-hat-detection, keremberke/yolov5m-construction-safety
            - **Teknologi:** Ultralytics YOLO, Gradio, OpenCV, FFmpeg
            """
        )

    detect_btn.click(
        fn=process_video_and_analyze,
        inputs=[video_input, selected_model_ui],
        outputs=[video_output, status_text, analysis_output]
    )

# --- 7. LUNCURKAN APLIKASI ---
if __name__ == "__main__":
    iface.launch(debug=True)