Spaces:
Running
Running
| import gradio as gr | |
| import tensorflow as tf | |
| import numpy as np | |
| from PIL import Image | |
| # --- KONFIGURASI --- | |
| IMG_SIZE = (224, 224) | |
| MODEL_PATH = "best_model.h5" | |
| # Definisi Kelas (Sesuai urutan training model Anda) | |
| class_names = [ | |
| 'Abrasions', 'Bruises', 'Burns', 'Cut', 'Diabetic Wounds', | |
| 'Laceration', 'Normal', 'Pressure Wounds', 'Surgical Wounds', 'Venous Wounds' | |
| ] | |
| # Mapping Bahasa Inggris -> Indonesia untuk UI | |
| translation_map = { | |
| 'Abrasions': 'Luka Lecet (Abrasi)', | |
| 'Bruises': 'Memar', | |
| 'Burns': 'Luka Bakar', | |
| 'Cut': 'Luka Sayat', | |
| 'Diabetic Wounds': 'Luka Diabetes', | |
| 'Laceration': 'Luka Robek', | |
| 'Normal': 'Normal/Sehat', | |
| 'Pressure Wounds': 'Luka Tekan (Dekubitus)', | |
| 'Surgical Wounds': 'Luka Operasi', | |
| 'Venous Wounds': 'Luka Vena' | |
| } | |
| # --- LOAD MODEL --- | |
| try: | |
| best_model = tf.keras.models.load_model(MODEL_PATH) | |
| print("✅ Model berhasil dimuat.") | |
| except Exception as e: | |
| best_model = None | |
| print(f"❌ Error memuat model: {e}") | |
| # --- LOGIKA REKOMENDASI --- | |
| def get_first_aid_recommendation(wound_class): | |
| rekomendasi = { | |
| 'Abrasions': ( | |
| "1. Bersihkan luka dengan air mengalir untuk membuang kotoran.\n" | |
| "2. Gunakan sabun lembut di sekitar luka (jangan terkena luka langsung).\n" | |
| "3. Oleskan salep antibiotik atau petroleum jelly untuk menjaga kelembapan.\n" | |
| "4. Tutup dengan plester bersih jika luka berada di area yang mudah tergesek." | |
| ), | |
| 'Bruises': ( | |
| "1. Istirahatkan area yang memar.\n" | |
| "2. Kompres dingin selama 15-20 menit untuk mengurangi pembengkakan.\n" | |
| "3. Posisikan bagian tubuh yang memar lebih tinggi dari jantung.\n" | |
| "4. Gunakan obat pereda nyeri jika diperlukan." | |
| ), | |
| 'Burns': ( | |
| "1. Alirkan air biasa (bukan air es) pada area luka selama 10-20 menit.\n" | |
| "2. Lepaskan perhiasan atau pakaian yang ketat sebelum area membengkak.\n" | |
| "3. Tutup longgar dengan kain bersih atau kasa steril.\n" | |
| "4. **Jangan** memecahkan lepuh yang muncul." | |
| ), | |
| 'Cut': ( | |
| "1. Tekan luka kuat-kuat dengan kain bersih selama 5-10 menit hingga darah berhenti.\n" | |
| "2. Bersihkan luka dengan air mengalir.\n" | |
| "3. Oleskan antiseptik pada pinggiran luka.\n" | |
| "4. Rekatkan plester atau perban steril." | |
| ), | |
| 'Diabetic Wounds': ( | |
| "**Peringatan:** Luka diabetes membutuhkan perhatian khusus.\n" | |
| "1. Cuci luka dengan cairan saline (NaCl 0.9%).\n" | |
| "2. Jaga luka tetap kering dan tutup dengan kasa steril.\n" | |
| "3. Periksa tanda-tanda infeksi seperti nanah atau bau.\n" | |
| "4. Segera hubungi dokter spesialis luka." | |
| ), | |
| 'Laceration': ( | |
| "1. Hentikan perdarahan dengan menekan luka secara stabil.\n" | |
| "2. Jika luka dalam dan robekannya lebar, segera tutup dengan kasa.\n" | |
| "3. Jangan mencoba mencuci luka jika perdarahan sangat deras.\n" | |
| "4. **Segera ke IGD** karena mungkin memerlukan jahitan medis." | |
| ), | |
| 'Normal': "Kulit terlihat normal dan sehat. Tetap jaga kebersihan area tersebut.", | |
| 'Pressure Wounds': ( | |
| "1. Hilangkan tekanan pada area luka dengan mengubah posisi tubuh.\n" | |
| "2. Gunakan bantal atau bantalan pelindung khusus.\n" | |
| "3. Bersihkan area secara lembut dengan cairan saline.\n" | |
| "4. Jaga kulit di sekitarnya tetap lembap." | |
| ), | |
| 'Surgical Wounds': ( | |
| "1. Ikuti instruksi penggantian perban dari dokter bedah Anda.\n" | |
| "2. Jangan menggaruk area jahitan.\n" | |
| "3. Jaga agar luka tetap kering saat mandi (gunakan penutup tahan air).\n" | |
| "4. Lapor dokter jika jahitan terbuka atau muncul kemerahan hebat." | |
| ), | |
| 'Venous Wounds': ( | |
| "1. Gunakan stoking kompresi jika disarankan oleh medis.\n" | |
| "2. Angkat kaki lebih tinggi dari jantung saat berbaring.\n" | |
| "3. Lakukan aktivitas fisik ringan secara teratur.\n" | |
| "4. Jaga kelembapan kulit di sekitar luka." | |
| ) | |
| } | |
| return rekomendasi.get(wound_class, "Rekomendasi belum tersedia. Silakan konsultasi dengan petugas medis.") | |
| # --- FUNGSI PREDIKSI --- | |
| def predict_image(image_input): | |
| if best_model is None: | |
| return {}, "⚠️ Model tidak ditemukan. Harap upload file model (.h5)." | |
| if image_input is None: | |
| return {}, "Silakan upload gambar." | |
| # Preprocessing | |
| img = image_input.resize(IMG_SIZE) | |
| img_array = tf.keras.utils.img_to_array(img) | |
| img_array = tf.expand_dims(img_array, 0) | |
| img_array = img_array / 255.0 | |
| # Prediksi | |
| predictions = best_model.predict(img_array) | |
| scores = tf.nn.softmax(predictions[0]).numpy() | |
| # Mapping hasil untuk UI (Bahasa Indonesia) | |
| translated_output_dict = { | |
| translation_map.get(class_names[i], class_names[i]): float(scores[i]) | |
| for i in range(len(class_names)) | |
| } | |
| # Ambil label tertinggi | |
| top_idx = np.argmax(scores) | |
| top_label_en = class_names[top_idx] | |
| top_confidence = scores[top_idx] | |
| # --- LOGIKA THRESHOLD --- | |
| THRESHOLD = 0.20 | |
| if top_confidence < THRESHOLD: | |
| top_label_id = "Normal (Tidak Terdeteksi)" | |
| rekomendasi_teks = ( | |
| "**Mohon Maaf:** Model kurang akurat dalam menganalisis foto ini.\n\n" | |
| "**Saran:**\n" | |
| "1. Pastikan area luka terlihat jelas dan tidak blur.\n" | |
| "2. Gunakan pencahayaan yang cukup (terang).\n" | |
| "3. Ambil foto dari sudut tegak lurus ke arah luka.\n\n" | |
| "Jika Anda merasa luka ini serius, segera hubungi tenaga medis meskipun hasil analisis tidak muncul." | |
| ) | |
| else: | |
| top_label_id = translation_map.get(top_label_en, top_label_en) | |
| rekomendasi_teks = get_first_aid_recommendation(top_label_en) | |
| # Format Markdown untuk Gradio | |
| formatted_output = ( | |
| f"### Analisis: **{top_label_id}**\n\n" | |
| f"**Langkah Pertolongan:**\n{rekomendasi_teks}\n\n" | |
| f"--- \n*Tingkat Keyakinan AI: {top_confidence:.2%}*" | |
| ) | |
| return translated_output_dict, formatted_output | |
| # --- UI INTERFACE --- | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue="red", secondary_hue="slate")) as demo: | |
| gr.Markdown("# 🚨 FirstAidLens") | |
| gr.Markdown("Deteksi jenis luka secara instan dan dapatkan panduan pertolongan pertama yang tepat.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| input_img = gr.Image( | |
| sources=["upload", "webcam"], | |
| type="pil", | |
| label="Ambil Foto Luka" | |
| ) | |
| gr.Markdown("> **Penting:** Hasil AI ini hanya referensi awal. Jika luka parah atau pendarahan tidak berhenti, segera hubungi **112**.") | |
| with gr.Column(scale=1): | |
| output_label = gr.Label(num_top_classes=3, label="Hasil Analisis Jenis Luka") | |
| output_markdown = gr.Markdown("### Panduan Pertolongan Pertama\n_Upload atau ambil foto untuk melihat rekomendasi._") | |
| # Trigger otomatis saat gambar diupload/diambil | |
| input_img.change( | |
| fn=predict_image, | |
| inputs=input_img, | |
| outputs=[output_label, output_markdown] | |
| ) | |
| # Launch (Server Name 0.0.0.0 wajib untuk Docker) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) |