File size: 12,490 Bytes
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0035c01
a37d3d7
0035c01
66534eb
0035c01
9741354
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0035c01
a37d3d7
 
 
 
 
0035c01
a37d3d7
0035c01
 
a37d3d7
 
 
 
 
0510371
a37d3d7
 
0035c01
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
0035c01
a37d3d7
 
0035c01
 
 
 
a37d3d7
 
 
 
 
 
 
 
 
 
 
9741354
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
9741354
 
a37d3d7
 
 
 
 
0035c01
 
 
a37d3d7
9741354
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0035c01
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4b0dc0
 
a37d3d7
 
 
 
 
 
 
 
c4b0dc0
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4b0dc0
a37d3d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4b0dc0
0035c01
a37d3d7
0035c01
a37d3d7
 
 
 
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
275
276
277
278
279
280
281
282
283
284
# -*- 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. Aplikasi ini dirancang untuk menjadi alat
evaluasi yang praktis dan mudah digunakan.

Fitur Utama:
- Perbandingan langsung antara model Generalis (YOLOv5) dan Spesialis (YOLOv8).
- Antarmuka pengguna (GUI) yang interaktif dan mudah dipahami.
- Pemrosesan video dengan batasan durasi dan resolusi untuk stabilitas.
- Menampilkan video hasil dengan anotasi deteksi (bounding box).
- Memberikan ringkasan hasil analisis kualitatif dan kuantitatif.
- Menyediakan dokumentasi, cara penggunaan, dan kredit yang jelas.

Pengembang: Faisal Fahmi Yuliawan
Berdasarkan Analisis pada Artikel Ilmiah.
"""

# --- 1. IMPORT LIBRARY ---
import gradio as gr
from ultralytics import YOLO
import cv2
import numpy as np
import os
import tempfile
import time
import logging

# --- 2. SETUP & KONFIGURASI AWAL ---

# Konfigurasi logging untuk memantau proses
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# --- 3. PEMUATAN MODEL AI ---
# Memuat kedua model saat aplikasi dimulai untuk respons yang lebih cepat.
# Ini menggunakan memori lebih, tetapi meningkatkan pengalaman pengguna.

try:
    logging.info("Memulai pemuatan model AI...")
    # Model 1: Spesialis (Fokus pada Helm)
    model_spesialis = YOLO('keremberke/yolov8m-hard-hat-detection')
    model_spesialis.overrides['conf'] = 0.25
    model_spesialis.overrides['iou'] = 0.45
    logging.info("βœ… Model Spesialis (YOLOv8m-HH) berhasil dimuat.")

    # Model 2: Generalis (Mendeteksi banyak objek konstruksi)
    model_generalis = YOLO('keremberke/yolov5m-construction-safety')
    model_generalis.overrides['conf'] = 0.25
    model_generalis.overrides['iou'] = 0.45
    logging.info("βœ… Model Generalis (YOLOv5m-CS) berhasil dimuat.")

    # Membuat dictionary untuk kemudahan akses model
    models = {
        "YOLOv8m (Spesialis Helm)": model_spesialis,
        "YOLOv5m (Generalis Konstruksi)": model_generalis
    }
    logging.info("Semua model siap digunakan.")

except Exception as e:
    logging.error(f"Gagal memuat salah satu model AI: {e}")
    # Jika model gagal dimuat, aplikasi tidak dapat berjalan.
    # Gradio akan menangani error ini dan menampilkannya di UI.
    raise RuntimeError(f"Tidak dapat memuat model AI. Aplikasi tidak dapat dijalankan. Error: {e}")


# --- 4. 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, melakukan deteksi objek,
    dan mengembalikan video hasil beserta analisisnya.
    """
    if video_path is None:
        return None, "Status: Silakan unggah video terlebih dahulu.", ""

    try:
        logging.info(f"Memulai pemrosesan video: {video_path} menggunakan model: {selected_model_name}")
        start_time = time.time()
        
        # Pilih model berdasarkan input dari UI
        model = models[selected_model_name]
        
        # Buka file video
        cap = cv2.VideoCapture(video_path)
        if not cap.isOpened():
            logging.error(f"Gagal membuka file video: {video_path}")
            return None, "Error: Gagal membuka file video. Format mungkin tidak didukung.", ""

        # Dapatkan properti video
        fps = cap.get(cv2.CAP_PROP_FPS) or 30
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        
        # Batasi resolusi untuk performa yang lebih baik
        target_width = 640
        target_height = int(height * (target_width / width)) if width > 0 else 480

        # Konfigurasi video output
        # Menggunakan tempfile untuk file output sementara
        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))

        # Variabel untuk analisis
        detection_count = 0
        helm_detected_count = 0
        
        progress(0, desc="Memulai Pemrosesan...")
        for frame_idx in range(total_frames):
            ret, frame = cap.read()
            if not ret:
                break
            
            # Update progress bar
            progress(frame_idx / total_frames, desc=f"Memproses Frame {frame_idx+1}/{total_frames}")

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

            # Lakukan prediksi (inferensi)
            results = model.predict(frame_resized, verbose=False)
            
            # Render hasil deteksi ke frame
            annotated_frame = results[0].plot()
            
            # Hitung deteksi
            detection_count += len(results[0].boxes)
            # Hitung deteksi helm secara spesifik
            if selected_model_name == "YOLOv8m (Spesialis Helm)":
                for box in results[0].boxes:
                    class_id = int(box.cls)
                    # Kelas '0' biasanya 'Hardhat' di model ini
                    if model.names[class_id].lower() in ['hardhat', 'helmet']:
                        helm_detected_count += 1
            elif selected_model_name == "YOLOv5m (Generalis Konstruksi)":
                 for box in results[0].boxes:
                    class_id = int(box.cls)
                    if model.names[class_id].lower() == 'helmet':
                        helm_detected_count += 1
            
            out.write(annotated_frame)
            
        end_time = time.time()
        processing_time = end_time - start_time

        # Tutup file video
        cap.release()
        out.release()
        
        logging.info(f"Video berhasil diproses dalam {processing_time:.2f} detik.")
        
        # Siapkan teks analisis
        analysis_text = f"""
        ### Analisis Kinerja Model: {selected_model_name}

        - **Waktu Proses Total:** {processing_time:.2f} detik
        - **Total Frame Diproses:** {total_frames}
        - **Jumlah Deteksi Keseluruhan:** {detection_count} objek
        - **Jumlah Deteksi Helm:** {helm_detected_count} objek

        **Catatan:**
        - **Model Spesialis (YOLOv8m):** Diharapkan memiliki akurasi deteksi helm yang tinggi, namun hanya mendeteksi kelas terkait helm ('Hardhat', 'NO-Hardhat').
        - **Model Generalis (YOLOv5m):** Mampu mendeteksi berbagai objek (11 kelas termasuk 'person', 'vest'), namun akurasi untuk 'helmet' mungkin lebih rendah karena fokusnya terbagi.
        """

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

    except Exception as e:
        logging.error(f"Terjadi error saat memproses video: {e}", exc_info=True)
        return None, f"Error: Terjadi kesalahan internal - {e}", ""


# --- 5. PEMBUATAN ANTARMUKA PENGGUNA (GRADIO UI) ---
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky")) as iface:
    
    # --- Bagian Judul dan Deskripsi ---
    gr.Markdown(
        """
        # πŸ›‘οΈ Analisis Komparatif Deteksi Helm Keselamatan
        Selamat datang di prototipe aplikasi untuk analisis perbandingan model AI dalam mendeteksi helm keselamatan.
        Aplikasi ini memungkinkan Anda untuk melihat perbedaan kinerja antara model AI yang **spesialis** (hanya untuk helm)
        dan model AI yang **generalis** (untuk berbagai objek konstruksi).
        """
    )
    
    with gr.Row():
        with gr.Column(scale=1):
            # --- Bagian Input Pengguna ---
            gr.Markdown("### **Langkah 1: Konfigurasi & Unggah**")
            
            selected_model_ui = gr.Radio(
                choices=list(models.keys()),
                label="Pilih Model AI untuk Perbandingan",
                value="YOLOv8m (Spesialis Helm)",
                info="Pilih algoritma yang ingin Anda gunakan untuk mendeteksi objek."
            )
            
            video_input = gr.Video(
                label="Unggah Video Anda",
                info="Format video yang direkomendasikan adalah .mp4. Durasi maksimal ~30 detik."
            )
            
            detect_btn = gr.Button("πŸš€ Mulai Deteksi", variant="primary")
            
            gr.Markdown("---")
            gr.Markdown("### **Cara Penggunaan**")
            gr.Markdown(
                """
                1.  **Pilih Model AI** yang ingin Anda uji dari opsi di atas.
                2.  **Unggah file video** dari perangkat Anda atau **pilih contoh** di bawah.
                3.  Klik tombol **"Mulai Deteksi"** dan tunggu proses selesai.
                4.  Lihat **video hasil** dan **analisis kinerja** di sebelah kanan.
                5.  Ulangi dengan model lain untuk melihat perbandingannya.
                """
            )
            
        with gr.Column(scale=1):
            # --- Bagian Output Hasil ---
            gr.Markdown("### **Langkah 2: Lihat Hasil Deteksi**")
            
            video_output = gr.Video(label="Video Hasil Deteksi")
            status_text = gr.Textbox(label="Status Proses", interactive=False)
            
            gr.Markdown("---")
            gr.Markdown("### **Ringkasan Analisis**")
            analysis_output = gr.Markdown(label="Analisis Kinerja")

    # --- Bagian Contoh Video ---
    gr.Examples(
        examples=[
            [os.path.join(os.path.dirname(__file__), "video_contoh_1.mp4"), "YOLOv8m (Spesialis Helm)"],
            [os.path.join(os.path.dirname(__file__), "video_contoh_2.mp4"), "YOLOv5m (Generalis Konstruksi)"]
        ],
        inputs=[video_input, selected_model_ui],
        outputs=[video_output, status_text, analysis_output],
        fn=process_video_and_analyze,
        cache_examples=True,
        label="Contoh Video (Klik untuk mencoba)"
    )

    # --- Bagian Informasi dan Kredit ---
    with gr.Accordion("ℹ️ Informasi, Kredit, dan Potensi Pengembangan", open=False):
        gr.Markdown(
            """
            ### **Identitas Prototipe**
            - **Pengembang:** Faisal Fahmi Yuliawan (sebagai bagian dari studi ilmiah).
            - **Tujuan:** Menyediakan alat bantu visual untuk membandingkan kinerja model AI dalam konteks keselamatan kerja (K3), khususnya deteksi helm.
            - **Relevansi:** Masalah kepatuhan penggunaan APD (Alat Pelindung Diri) seperti helm adalah kasus nyata dan krusial di industri konstruksi untuk mencegah kecelakaan fatal.

            ### **Kredit Pihak Ketiga**
            - **Model AI:** - `keremberke/yolov8m-hard-hat-detection` (Model Spesialis) dari Hugging Face.
                - `keremberke/yolov5m-construction-safety` (Model Generalis) dari Hugging Face.
            - **Teknologi:** - **Ultralytics YOLO:** Framework utama untuk model AI.
                - **Gradio:** Framework untuk membangun antarmuka web interaktif ini.
                - **OpenCV & FFmpeg:** Untuk pemrosesan dan manipulasi video.
            
            ### **Potensi Pengembangan & Komersialisasi**
            - **Skalabilitas:** Arsitektur aplikasi ini dapat dengan mudah diperluas untuk mencakup lebih banyak model AI atau jenis APD lain (misalnya, rompi, sepatu bot, sarung tangan).
            - **Integrasi:** Dapat diintegrasikan dengan sistem CCTV di lokasi konstruksi untuk pemantauan otomatis dan sistem peringatan *real-time*.
            - **Potensi Bisnis:** Memiliki potensi komersial sebagai produk SaaS (*Software as a Service*) untuk perusahaan konstruksi yang ingin meningkatkan standar K3 dan melakukan audit kepatuhan secara digital.
            """
        )

    # --- Hubungkan Aksi Tombol ke Fungsi ---
    detect_btn.click(
        fn=process_video_and_analyze,
        inputs=[video_input, selected_model_ui],
        outputs=[video_output, status_text, analysis_output]
    )


# --- 6. LUNCURKAN APLIKASI ---
if __name__ == "__main__":
    # Untuk menjalankan, simpan kode ini sebagai file .py (misal: app.py)
    # Buat 2 file video contoh: video_contoh_1.mp4 dan video_contoh_2.mp4 di folder yang sama.
    # Jalankan dari terminal dengan: python app.py
    iface.launch(debug=True, share=True)