Spaces:
Sleeping
Sleeping
| 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." | |
| ] | |
| } | |
| ] | |
| 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() |