Rice-Doctor / src /streamlit_app.py
devaartana's picture
feat: update knowledge base
29d2e28
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()