File size: 14,595 Bytes
3c32191
6b49084
 
 
11672f5
6b49084
11672f5
 
6b49084
 
29d2e28
3bab7f4
49982e9
29d2e28
49982e9
 
 
 
29d2e28
 
49982e9
 
 
 
 
29d2e28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6b49084
 
 
3bab7f4
 
6b49084
3bab7f4
49982e9
29d2e28
 
 
 
6b49084
 
 
 
49982e9
 
 
 
4978f67
 
 
 
 
 
49982e9
 
 
 
 
4978f67
 
 
 
 
 
49982e9
 
 
 
 
4978f67
 
 
 
 
 
49982e9
 
 
 
 
4978f67
 
 
 
 
 
49982e9
 
 
 
 
29d2e28
4978f67
29d2e28
 
4978f67
49982e9
 
29d2e28
49982e9
 
29d2e28
 
 
 
 
 
 
 
 
 
 
4978f67
 
29d2e28
 
 
4978f67
49982e9
29d2e28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6b49084
 
 
 
 
 
 
 
 
 
 
 
49982e9
6b49084
49982e9
 
 
 
6b49084
 
79ab3a4
 
6b49084
79ab3a4
49982e9
 
79ab3a4
 
 
 
49982e9
 
6b49084
79ab3a4
49982e9
 
06c19dc
6b49084
79ab3a4
 
49982e9
 
 
79ab3a4
49982e9
79ab3a4
49982e9
79ab3a4
49982e9
79ab3a4
 
49982e9
 
79ab3a4
49982e9
79ab3a4
49982e9
79ab3a4
 
 
6b49084
79ab3a4
6b49084
49982e9
6b49084
49982e9
79ab3a4
49982e9
 
79ab3a4
805280d
 
 
 
 
6b49084
49982e9
5107171
49982e9
79ab3a4
 
6b49084
5107171
 
49982e9
79ab3a4
46329a1
 
 
 
49982e9
46329a1
 
 
 
 
 
 
 
 
49982e9
46329a1
79ab3a4
46329a1
49982e9
46329a1
 
 
bf863db
 
 
 
6b49084
79ab3a4
6b49084
49982e9
0604226
805280d
 
 
 
 
6b49084
 
 
79ab3a4
 
 
 
 
 
 
 
49982e9
79ab3a4
 
 
 
6b49084
79ab3a4
 
6b49084
 
 
 
79ab3a4
6b49084
 
 
49982e9
 
 
 
 
6b49084
49982e9
79ab3a4
46329a1
523bfa2
 
 
 
 
5107171
46329a1
 
49982e9
bf863db
 
 
 
 
0604226
 
49982e9
 
 
 
 
 
4978f67
 
 
 
 
 
49982e9
 
 
 
 
 
 
 
 
 
4978f67
946449e
4978f67
 
 
 
46329a1
523bfa2
79ab3a4
 
 
 
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
import streamlit as st
from ultralytics import YOLO
from PIL import Image
import numpy as np
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
MODEL_PATH = os.path.join(SCRIPT_DIR, "best.pt")

SYMPTOM_QUESTIONS = {
    "G1": "Daun berwarna putih kecoklatan memanjang seperti mengering pada bagian tepi daun",
    "G2": "Pada pagi hari, dapat ditemukan cairan bakteri terlihat seperti butiran air pada bagian terinfeksi",
    "G3": "Gejala terlihat seperti anak panah terdapat di antara urat daun",
    "G4": "Daun berwarna kuning kecoklatan",
    "G5": "Luka pada daun terlihat transparan bila dihadapkan cahaya matahari",
    "G6": "Daun berbentuk belah ketupat",
    "G7": "Pada daun bagian tengah berwarna abu-abu atau berwarna putih dengan bagian tepi berwarna kecoklatan",
    "G8": "Infeksi pada malai/leher berwarna abu-abu",
    "G9": "Tanaman kerdil dan gejala pada daun terjadi perubahan warna dari hijau menjadi jingga atau kemerahan",
    
    "G10": "Bercak cokelat pada daun, pelepah, dan bulir gabah",
    "G11": "Bercak daun sering menutupi permukaan daun, menyebabkan daun menjadi layu",
    "G12": "Bercak cokelat pada batang/leher malai",
    "G13": "Terdapat bercak berbentuk oval berwarna abu-abu kehijauan pada pelepah (tulang daun)",
    "G14": "Batang tanaman padi menjadi rapuh dan mudah rebah/jatuh",
    "G15": "Bibit yang sakit menjadi layu dan akhirnya mati",

    "G20": "Daun menebal, tekstur kasar/berbintil (warty), serta kaku dan tegak",
    "G21": "Daun bagian atas melintir (twisted) atau mengeriting",
    "G22": "Tanaman kerdil dengan anakan berlebihan membentuk gumpalan padat",
    "G23": "Malai gagal keluar atau mengalami deformasi (melengkung/membesar)",

    "G24": "Pucuk tengah (pupus) mengering dan berwarna coklat kemerahan",
    "G25": "Pucuk yang kering mudah dicabut dari pangkalnya dengan tangan",
    "G26": "Terdapat lubang gerekan kecil pada pangkal batang atau kotoran ulat",

    "G27": "Terdapat garis-garis putih transparan sejajar tulang daun (akibat Imago)",
    "G28": "Permukaan atas daun terkelupas/dikikis menyisakan kulit ari bawah",
    "G29": "Terdapat lepuhan/kantung putih tidak beraturan pada daun (akibat Larva)",
    "G30": "Hamparan sawah tampak memutih seperti terbakar (hopperburn)"
}

YOLO_PROMPTS = {
    "bacterial_leaf_blight": ["G1", "G2", "G3"], 
    "bacterial_leaf_streak": ["G4", "G5"],
    "blast": ["G6", "G7", "G8"], 
    "tungro": ["G9"],
    "brown_spot": ["G10", "G11", "G12"], 
    "downy_mildew": ["G20", "G21", "G22", "G23"],
    "dead_heart": ["G24", "G25", "G26"],           
    "hispa": ["G27", "G28", "G29", "G30"],         
    "bacterial_panicle_blight": [],                
    "normal": []
}

PRODUCTION_RULES = [
    {
        "yolo_class": "bacterial_leaf_blight", 
        "disease": "Bacterial Leaf Blight (Hawar Daun Bakteri)", 
        "symptoms": ["G1", "G2", "G3"], 
        "source": "Literatur 1",
        "solutions": [
            "Gunakan varietas tahan (contoh: Inpari 32, Inpari 48, atau Ciherang).",
            "Terapkan pengairan berselang (intermittent), hindari penggenangan terus-menerus.",
            "Hindari pemupukan Nitrogen berlebih saat musim hujan."
        ]
    },
    {
        "yolo_class": "bacterial_leaf_streak",
        "disease": "Bakteri Daun Bergaris (Bacterial Leaf Streak)", 
        "symptoms": ["G4", "G5"], 
        "source": "Literatur 1",
        "solutions": [
            "Lakukan perlakuan benih dengan air panas (52°C selama 30 menit) atau antibiotik.",
            "Semprotkan bakterisida berbahan tembaga (Copper oxychloride) jika serangan parah.",
            "Musnahkan gulma inang di sekitar pematang."
        ]
    },
    {
        "yolo_class": "blast",
        "disease": "Blas (Blast)", 
        "symptoms": ["G6", "G7", "G8"], 
        "source": "Literatur 1",
        "solutions": [
            "Rendam benih dengan fungisida sistemik (misal: trisiklazol) sebelum semai.",
            "Hindari waktu tanam terlambat dari jadwal petani sekitar.",
            "Gunakan varietas tahan akan penyakit."
        ]
    },
    {
        "yolo_class": "tungro",
        "disease": "Tungro", 
        "symptoms": ["G9"], 
        "source": "Literatur 1",
        "solutions": [
            "Wajib tanam serempak dalam satu hamparan luas.",
            "Kendalikan vektor Wereng Hijau dengan insektisida tepat sasaran.",
            "Eradikasi (cabut dan musnahkan) tanaman yang menunjukkan gejala kerdil."
        ]
    },
    {
        "yolo_class": "brown_spot",
        "disease": "Bercak Daun Coklat (Brown Spot)", 
        "symptoms": ["G10", "G11", "G12"], 
        "source": "Literatur 2",
        "solutions": [
             "Hindari aplikasi pupuk nitrogen yang terlalu banyak.",
             "Ada beberapa fungisida yang efektif tetapi secara ekonomi tidak direkomendasikan."
        ]
    },
    {
        "yolo_class": None, 
        "disease": "Hawar Pelepah (Sheath Blight)", 
        "symptoms": ["G13", "G14", "G15"], 
        "source": "Literatur 2",
        "solutions": [
            "Berikan pupuk Kalium (K) dan bahan organik (kompos jerami) untuk menyehatkan tanah.",
            "Lakukan perlakuan benih (seed treatment) dengan fungisida sebelum semai.",
            "Gunakan varietas yang tahan (cara paling praktis)."
        ]
    },
    {
        "yolo_class": "downy_mildew",
        "disease": "Downy Mildew (Embun Bulu)", 
        "symptoms": ["G20", "G21", "G22", "G23"], 
        "source": "Literatur 3",
        "solutions": [
            "Perbaiki drainase lahan (permukaan dan bawah tanah) agar lahan tidak tergenang >24 jam.",
            "Bersihkan gulma rumput-rumputan di sekitar sawah yang menjadi inang alternatif jamur.",
            "Gunakan benih yang bersih dan bebas dari sisa tanaman sakit sebelumnya."
        ]
    },
    {
        "yolo_class": "dead_heart",
        "disease": "Dead Heart / Sundep (Penggerek Batang)", 
        "symptoms": ["G24", "G25", "G26"], 
        "source": "Literatur 4",
        "solutions": [
            "Kumpulkan dan musnahkan kelompok telur yang ditemukan pada persemaian secara manual.",
            "Potong jerami serendah mungkin saat panen untuk mematikan larva yang bersembunyi di pangkal batang.",
            "Lakukan tanam serempak untuk memutus siklus hidup hama."
        ]
    },
    {
        "yolo_class": "hispa",
        "disease": "Rice Hispa (Hama Putih)", 
        "symptoms": ["G27", "G28", "G29", "G30"], 
        "source": "Literatur 5",
        "solutions": [
            "Pangkas ujung daun bibit sebelum pindah tanam untuk membuang telur hama yang menempel.",
            "Lakukan penyapuan (sweeping) dengan jaring serangga pada pagi hari untuk menangkap kumbang dewasa.",
            "Bersihkan gulma di sekitar sawah dan hindari jarak tanam yang terlalu rapat."
        ]
    }
]

@st.cache_resource
def load_model():
    try:
        return YOLO(MODEL_PATH)
    except Exception as e:
        st.error(f"Error loading model: {e}")
        return None

model = load_model()

st.title("Rice Doctor: Sistem Pakar Penyakit Padi")
st.markdown("""
Dalam sistem **Rice Doctor**, pengguna dapat:
1. **Unggah Gambar Padi**. Untuk mendeteksi penyakit padi.
2. **Verifikasi Gejala Penyakit Padi**. Untuk memverifikasi hasil deteksi penyakit padi.
3. **Pengecekan Gejala Penyakit Padi Manual**. Jika hasil deteksi penyakit padi sebelummnya tidak terverifikasi.
""")

if 'step' not in st.session_state:
    st.session_state.step = 1
if 'prediction' not in st.session_state:
    st.session_state.prediction = None
if 'forward_matches' not in st.session_state:
    st.session_state.forward_matches = []
if 'hypothesis_codes' not in st.session_state:
    st.session_state.hypothesis_codes = []
if 'user_facts' not in st.session_state:
    st.session_state.user_facts = set()
if 'system_method' not in st.session_state:
    st.session_state.system_method = None

if st.session_state.step == 1:
    st.divider()
    st.subheader("Langkah 1: Unggah Gambar Padi")
    uploaded_file = st.file_uploader("Unggah Gambar", type=["jpg", "png", "jpeg"])

    if uploaded_file is not None:
        image = Image.open(uploaded_file)
        col1, col2, col3 = st.columns([1, 2, 1])
        with col2: 
            st.image(image, caption="Gambar yang Diunggah", width=300)
        
        if st.button("Deteksi Penyakit Padi", type="primary"):
            if model:
                with st.spinner("Model AI sedang menganalisis..."):
                    results = model.predict(image, imgsz=640)
                    
                    raw_name = results[0].names[results[0].probs.top1]
                    top_class_name = raw_name.lower().strip()
                    probs = results[0].probs
                    top_conf = probs.top1conf.item()
                    
                    st.session_state.image = image
                    st.session_state.prediction = top_class_name
                    st.session_state.prediction_conf = top_conf
                    st.session_state.hypothesis_codes = YOLO_PROMPTS.get(top_class_name, [])
                    st.session_state.step = 2 
                    st.rerun()

elif st.session_state.step == 2:
    st.divider()
    st.subheader("Langkah 2: Verifikasi Gejala Penyakit Padi")
    
    image = st.session_state.image
    pred = st.session_state.prediction
    conf = st.session_state.prediction_conf
    conf_threshold = 0.9
    codes = st.session_state.hypothesis_codes

    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        st.image(image, caption="Gambar yang Diunggah", width=300)

    
    if not codes or (conf < conf_threshold):
        st.warning("Model AI tidak dapat mendeteksi penyakit padi.")
        if st.button("Lanjut ke Pengecekan Manual"):
            st.session_state.step = 3
            st.rerun()
    else:
        st.info(f"🔍 **Hasil Deteksi Penyakit Padi:** {pred}")

        st.write("Jawablah pertanyaan berikut untuk memverifikasi penyakit:")
        
        bc_answers = {}
        for code in codes:
            question = SYMPTOM_QUESTIONS.get(code, "Unknown")
            ans = st.radio(
                f"**({code})** {question}?", 
                options=["Tidak", "Ya"], 
                index=0, 
                key=f"bc_{code}"
            )
            if ans == "Ya":
                bc_answers[code] = True
            else:
                bc_answers[code] = False
        
        if st.button("Verifikasi Penyakit Padi", type="primary"):
            all_yes = all(bc_answers.values())
            
            if all_yes:
                st.session_state.system_method = "backward"
                st.session_state.step = 4
                st.rerun()
            else:
                st.session_state.user_facts = {k for k, v in bc_answers.items() if v}
                st.toast("Sistem tidak dapat memverifikasi penyakit padi. Beralih ke Pengecekan Manual...")
                st.session_state.step = 3
                st.rerun()

elif st.session_state.step == 3:
    st.divider()
    st.subheader("Langkah 3: Pengecekan Gejala Penyakit Padi Manual")
    st.info("ℹ️ Sistem tidak dapat memverifikasi penyakit padi. Beralih ke Pengecekan Manual.")

    image = st.session_state.image
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        st.image(image, caption="Gambar yang Diunggah", width=300)
    
    all_symptoms_list = [f"{k}: {v}" for k,v in SYMPTOM_QUESTIONS.items()]
    
    pre_selected = [f"{code}: {SYMPTOM_QUESTIONS[code]}" for code in st.session_state.user_facts if code in SYMPTOM_QUESTIONS]
    
    other_checks = st.multiselect(
        "Silakan pilih semua gejala yang terlihat pada tanaman:", 
        all_symptoms_list,
        default=pre_selected
    )
    
    if st.button("Analisis Penyakit Padi", type="primary"):
        final_facts = set()
        for item in other_checks:
            code = item.split(":")[0].strip()
            final_facts.add(code)
        
        if not final_facts:
            st.error("Mohon pilih setidaknya satu gejala.")
        else:
            matches = []
            for rule in PRODUCTION_RULES:
                required = set(rule['symptoms'])
                if required.issubset(final_facts):
                    matches.append(rule)
            
            if matches:
                st.session_state.system_method = "forward"
                st.session_state.forward_matches = matches
                st.session_state.step = 4
                st.rerun()

            else:
                st.info("ℹ️ **Sistem Tidak Menemukan Penyakit Padi Secara Pasti**")
                st.write("Kombinasi gejala yang Anda pilih tidak cocok sepenuhnya dengan aturan penyakit manapun dalam basis pengetahuan.")

    st.divider()
    if st.button("Mulai Ulang Analisis"):
        st.session_state.step = 1
        st.session_state.user_facts = set()
        st.rerun()

elif st.session_state.step == 4:
    st.divider()

    image = st.session_state.image
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        st.image(image, caption="Gambar yang Diunggah", width=300)

    st.success("**✅ Penyakit Padi Ditemukan!**")
    
    if st.session_state.system_method == "forward":
        for m in st.session_state.forward_matches:
            st.markdown(f"""
            ### {m['disease']}
            - **Sumber Aturan:** {m['source']}
            """)
            
            if 'solutions' in m and m['solutions']:
                sol_text = "- **Solusi Pengendalian:**\n"  
                for sol in m['solutions']:
                    sol_text += f"  - {sol}\n"             
                st.markdown(sol_text)

    if st.session_state.system_method == "backward":
        matched_rule = next((r for r in PRODUCTION_RULES if r.get("yolo_class") == st.session_state.prediction), None)
        disease = matched_rule['disease'] if matched_rule else st.session_state.prediction
        source = matched_rule['source'] if matched_rule else "Tidak diketahui"

        st.markdown(f"""
            ### {disease}
            - **Sumber Aturan:** {source}
        """)
        
        if matched_rule and 'solutions' in matched_rule and matched_rule['solutions']:
            sol_text = "- **Solusi Pengendalian:**\n"   
            for sol in matched_rule['solutions']:
                sol_text += f"  - {sol}\n"              
            st.markdown(sol_text)
    
    st.divider()
    if st.button("Mulai Ulang Analisis"):
        st.session_state.step = 1
        st.session_state.user_facts = set()
        st.rerun()