Ferdinann commited on
Commit
631a8b8
·
verified ·
1 Parent(s): c130d76

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +17 -0
  2. app.py +182 -0
  3. best_model.h5 +3 -0
  4. requirements.txt +12 -0
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+
3
+ WORKDIR /code
4
+
5
+ # Copy requirements dan install dependencies
6
+ COPY ./requirements.txt /code/requirements.txt
7
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ # Copy seluruh file ke dalam container
10
+ COPY . /code
11
+
12
+ # Pengaturan permission folder cache (Wajib untuk Hugging Face)
13
+ RUN mkdir -p /code/cache && chmod -R 777 /code/cache
14
+ ENV GRADIO_TEMP_DIR="/code/cache"
15
+
16
+ # Jalankan aplikasi
17
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ from PIL import Image
5
+
6
+ # --- KONFIGURASI ---
7
+ IMG_SIZE = (224, 224) # Sesuaikan dengan input shape model Anda
8
+ MODEL_PATH = "best_model.h5" # Pastikan nama file sesuai
9
+
10
+ # Definisi Kelas (Sesuai urutan training model Anda)
11
+ class_names = [
12
+ 'Abrasions', 'Bruises', 'Burns', 'Cut', 'Diabetic Wounds',
13
+ 'Laceration', 'Normal', 'Pressure Wounds', 'Surgical Wounds', 'Venous Wounds'
14
+ ]
15
+
16
+ # Mapping Bahasa Inggris -> Indonesia untuk UI
17
+ translation_map = {
18
+ 'Abrasions': 'Luka Lecet (Abrasi)',
19
+ 'Bruises': 'Memar',
20
+ 'Burns': 'Luka Bakar',
21
+ 'Cut': 'Luka Sayat',
22
+ 'Diabetic Wounds': 'Luka Diabetes',
23
+ 'Laceration': 'Luka Robek',
24
+ 'Normal': 'Normal/Sehat',
25
+ 'Pressure Wounds': 'Luka Tekan (Dekubitus)',
26
+ 'Surgical Wounds': 'Luka Operasi',
27
+ 'Venous Wounds': 'Luka Vena'
28
+ }
29
+
30
+ # --- LOAD MODEL ---
31
+ try:
32
+ best_model = tf.keras.models.load_model(MODEL_PATH)
33
+ print("✅ Model berhasil dimuat.")
34
+ except Exception as e:
35
+ best_model = None
36
+ print(f"❌ Error memuat model: {e}")
37
+
38
+ # --- LOGIKA REKOMENDASI ---
39
+ def get_first_aid_recommendation(wound_class):
40
+ rekomendasi = {
41
+ 'Abrasions': (
42
+ "1. Bersihkan luka dengan air mengalir untuk membuang kotoran.\n"
43
+ "2. Gunakan sabun lembut di sekitar luka (jangan terkena luka langsung).\n"
44
+ "3. Oleskan salep antibiotik atau petroleum jelly untuk menjaga kelembapan.\n"
45
+ "4. Tutup dengan plester bersih jika luka berada di area yang mudah tergesek."
46
+ ),
47
+ 'Bruises': (
48
+ "1. Istirahatkan area yang memar.\n"
49
+ "2. Kompres dingin selama 15-20 menit untuk mengurangi pembengkakan.\n"
50
+ "3. Posisikan bagian tubuh yang memar lebih tinggi dari jantung.\n"
51
+ "4. Gunakan obat pereda nyeri jika diperlukan."
52
+ ),
53
+ 'Burns': (
54
+ "1. Alirkan air biasa (bukan air es) pada area luka selama 10-20 menit.\n"
55
+ "2. Lepaskan perhiasan atau pakaian yang ketat sebelum area membengkak.\n"
56
+ "3. Tutup longgar dengan kain bersih atau kasa steril.\n"
57
+ "4. **Jangan** memecahkan lepuh yang muncul."
58
+ ),
59
+ 'Cut': (
60
+ "1. Tekan luka kuat-kuat dengan kain bersih selama 5-10 menit hingga darah berhenti.\n"
61
+ "2. Bersihkan luka dengan air mengalir.\n"
62
+ "3. Oleskan antiseptik pada pinggiran luka.\n"
63
+ "4. Rekatkan plester atau perban steril."
64
+ ),
65
+ 'Diabetic Wounds': (
66
+ "**Peringatan:** Luka diabetes membutuhkan perhatian khusus.\n"
67
+ "1. Cuci luka dengan cairan saline (NaCl 0.9%).\n"
68
+ "2. Jaga luka tetap kering dan tutup dengan kasa steril.\n"
69
+ "3. Periksa tanda-tanda infeksi seperti nanah atau bau.\n"
70
+ "4. Segera hubungi dokter spesialis luka."
71
+ ),
72
+ 'Laceration': (
73
+ "1. Hentikan perdarahan dengan menekan luka secara stabil.\n"
74
+ "2. Jika luka dalam dan robekannya lebar, segera tutup dengan kasa.\n"
75
+ "3. Jangan mencoba mencuci luka jika perdarahan sangat deras.\n"
76
+ "4. **Segera ke IGD** karena mungkin memerlukan jahitan medis."
77
+ ),
78
+ 'Normal': "Kulit terlihat normal dan sehat. Tetap jaga kebersihan area tersebut.",
79
+ 'Pressure Wounds': (
80
+ "1. Hilangkan tekanan pada area luka dengan mengubah posisi tubuh.\n"
81
+ "2. Gunakan bantal atau bantalan pelindung khusus.\n"
82
+ "3. Bersihkan area secara lembut dengan cairan saline.\n"
83
+ "4. Jaga kulit di sekitarnya tetap lembap."
84
+ ),
85
+ 'Surgical Wounds': (
86
+ "1. Ikuti instruksi penggantian perban dari dokter bedah Anda.\n"
87
+ "2. Jangan menggaruk area jahitan.\n"
88
+ "3. Jaga agar luka tetap kering saat mandi (gunakan penutup tahan air).\n"
89
+ "4. Lapor dokter jika jahitan terbuka atau muncul kemerahan hebat."
90
+ ),
91
+ 'Venous Wounds': (
92
+ "1. Gunakan stoking kompresi jika disarankan oleh medis.\n"
93
+ "2. Angkat kaki lebih tinggi dari jantung saat berbaring.\n"
94
+ "3. Lakukan aktivitas fisik ringan secara teratur.\n"
95
+ "4. Jaga kelembapan kulit di sekitar luka."
96
+ )
97
+ }
98
+ return rekomendasi.get(wound_class, "Rekomendasi belum tersedia. Silakan konsultasi dengan petugas medis.")
99
+
100
+ # --- FUNGSI PREDIKSI ---
101
+ def predict_image(image_input):
102
+ if best_model is None:
103
+ return {}, "⚠️ Model tidak ditemukan. Harap upload file model (.h5)."
104
+
105
+ if image_input is None:
106
+ return {}, "Silakan upload gambar."
107
+
108
+ # Preprocessing
109
+ img = image_input.resize(IMG_SIZE)
110
+ img_array = tf.keras.utils.img_to_array(img)
111
+ img_array = tf.expand_dims(img_array, 0)
112
+ img_array = img_array / 255.0
113
+
114
+ # Prediksi
115
+ predictions = best_model.predict(img_array)
116
+ scores = tf.nn.softmax(predictions[0]).numpy()
117
+
118
+ # Mapping hasil untuk UI (Bahasa Indonesia)
119
+ translated_output_dict = {
120
+ translation_map.get(class_names[i], class_names[i]): float(scores[i])
121
+ for i in range(len(class_names))
122
+ }
123
+
124
+ # Ambil label tertinggi
125
+ top_idx = np.argmax(scores)
126
+ top_label_en = class_names[top_idx]
127
+ top_confidence = scores[top_idx]
128
+
129
+ # --- LOGIKA THRESHOLD ---
130
+ THRESHOLD = 0.50
131
+
132
+ if top_confidence < THRESHOLD:
133
+ top_label_id = "Normal (Tidak Terdeteksi)"
134
+ rekomendasi_teks = (
135
+ "**Mohon Maaf:** Model kurang akurat dalam menganalisis foto ini.\n\n"
136
+ "**Saran:**\n"
137
+ "1. Pastikan area luka terlihat jelas dan tidak blur.\n"
138
+ "2. Gunakan pencahayaan yang cukup (terang).\n"
139
+ "3. Ambil foto dari sudut tegak lurus ke arah luka.\n\n"
140
+ "Jika Anda merasa luka ini serius, segera hubungi tenaga medis meskipun hasil analisis tidak muncul."
141
+ )
142
+ else:
143
+ top_label_id = translation_map.get(top_label_en, top_label_en)
144
+ rekomendasi_teks = get_first_aid_recommendation(top_label_en)
145
+
146
+ # Format Markdown untuk Gradio
147
+ formatted_output = (
148
+ f"### Analisis: **{top_label_id}**\n\n"
149
+ f"**Langkah Pertolongan:**\n{rekomendasi_teks}\n\n"
150
+ f"--- \n*Tingkat Keyakinan AI: {top_confidence:.2%}*"
151
+ )
152
+
153
+ return translated_output_dict, formatted_output
154
+
155
+ # --- UI INTERFACE ---
156
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="red", secondary_hue="slate")) as demo:
157
+ gr.Markdown("# 🚨 FirstAidLens")
158
+ gr.Markdown("Deteksi jenis luka secara instan dan dapatkan panduan pertolongan pertama yang tepat.")
159
+
160
+ with gr.Row():
161
+ with gr.Column(scale=1):
162
+ input_img = gr.Image(
163
+ sources=["upload", "webcam"],
164
+ type="pil",
165
+ label="Ambil Foto Luka"
166
+ )
167
+ gr.Markdown("> **Penting:** Hasil AI ini hanya referensi awal. Jika luka parah atau pendarahan tidak berhenti, segera hubungi **112**.")
168
+
169
+ with gr.Column(scale=1):
170
+ output_label = gr.Label(num_top_classes=3, label="Hasil Analisis Jenis Luka")
171
+ output_markdown = gr.Markdown("### Panduan Pertolongan Pertama\n_Upload atau ambil foto untuk melihat rekomendasi._")
172
+
173
+ # Trigger otomatis saat gambar diupload/diambil
174
+ input_img.change(
175
+ fn=predict_image,
176
+ inputs=input_img,
177
+ outputs=[output_label, output_markdown]
178
+ )
179
+
180
+ # Launch (Server Name 0.0.0.0 wajib untuk Docker)
181
+ if __name__ == "__main__":
182
+ demo.launch(server_name="0.0.0.0", server_port=7860)
best_model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3b6af1fe4a4df83df84f66f1bf4414ce961b34b433a955b23064bdb84dfbedab
3
+ size 42378632
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tensorflow==2.19.0
2
+ keras==3.10.0
3
+ numpy==2.0.2
4
+ matplotlib==3.10.0
5
+ seaborn==0.13.2
6
+ gradio
7
+ tensorflow-hub==0.16.1
8
+ kagglehub==0.3.13
9
+ scikit-learn==1.6.1
10
+ tensorflowjs==4.22.0
11
+ Pillow==12.1.0
12
+ pandas==2.2.2