File size: 7,410 Bytes
ce1ca74
 
 
 
 
 
63cc014
4d3afde
ce1ca74
4d3afde
ce1ca74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d3afde
 
1481953
4d3afde
ce1ca74
4d3afde
 
63cc014
4d3afde
ce1ca74
4d3afde
 
 
 
 
ce1ca74
 
 
4d3afde
ce1ca74
 
 
 
 
4d3afde
ce1ca74
 
 
 
4d3afde
 
 
 
ce1ca74
4d3afde
 
 
 
 
 
 
 
ce1ca74
 
 
 
4d3afde
ce1ca74
 
 
 
 
4d3afde
 
ce1ca74
 
4d3afde
ce1ca74
4d3afde
 
ce1ca74
4d3afde
 
 
 
 
 
 
 
 
 
 
 
 
 
ce1ca74
4d3afde
 
ce1ca74
 
4d3afde
ce1ca74
a79b65e
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
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)