21 KiB
AGENTS.md — Panduan AI untuk Sistem Deteksi PMK
File ini adalah satu-satunya sumber kebenaran untuk AI agent yang bekerja pada project ini. WAJIB diperbarui setiap kali ada perubahan arsitektur, dependensi, struktur database, atau alur sistem.
1. GAMBARAN PROYEK
Sistem Deteksi dan Diagnosis Penyakit Mulut dan Kuku (PMK / FMD) pada Sapi
- Bahasa: Python 3.12.5
- Framework Web: Flask (Bootstrap 5 UI)
- Desktop (Legacy): Tkinter (
main.py,predict_image.py) - Deployment: Railway (Nixpacks + gunicorn)
- Repository:
https://github.com/livindra/deteksi_PMK
Dua jalur utama:
- Deteksi Berbasis Gambar — ML (KNN) multi-class (sehat + tiap jenis PMK) + Computer Vision (color moments + GLCM texture)
- Sistem Pakar — Forward chaining inference engine (29 gejala, 5 penyakit, 5 aturan)
Dataset: Folder
dataset/berisihealthy/dan folderpmk_*per jenis penyakit.train_model.pyotomatis mendeteksi semua folder berawalanpmk_*sebagai kelas terpisah.
2. STRUKTUR PROYEK
deteksi_PMK/
├── app.py # Main Flask app (~1000 baris) — routing, prediksi, sistem pakar
├── expert_system.py # Forward chaining engine + KnowledgeBase + Evaluator
├── train_model.py # Training KNN model (k=5, euclidean, uniform)
├── setup_db.py # Inisialisasi database MySQL
├── main.py # LEGACY — Tkinter desktop menu
├── predict_image.py # LEGACY — Tkinter prediction GUI
├── test_forward_chaining.py # Unit test expert system
├── test_env.py # Test environment variables & DB connection
├── show_scaler_params.py # Utility: lihat parameter scaler
├── requirements.txt # Python dependencies
├── Procfile # gunicorn app:app
├── railway.json # Railway Nixpacks builder config
├── .env # Environment variables (TIDAK DI-COMMIT)
├── .env.example # Template env
├── .gitignore
├── .python-version # 3.12.5
│
├── QUICK_START.md # Panduan cepat (Bahasa Indonesia)
├── AGENTS.md # ← FILE INI — panduan AI, HARUS DIUPDATE
│
├── dataset/ # Dataset gambar training
│ ├── healthy/ # ~200 gambar sapi sehat
│ ├── pmk_oral/ # Gambar PMK oral
│ ├── pmk_podal/ # Gambar PMK podal (kaki)
│ ├── pmk_laktasi/ # Gambar PMK laktasi (ambing)
│ └── pmk_akut_general/ # Gambar PMK akut general
│
├── features/ # CSV fitur hasil ekstraksi
│ ├── dataset.csv
│ ├── data_train.csv
│ └── data_test.csv
│
├── models/ # Model terlatih (.pkl)
│ ├── knn_model.pkl # BINARY — KNeighborsClassifier (sehat/sakit)
│ ├── scaler.pkl # BINARY — StandardScaler
│ ├── label_encoder.pkl # BINARY — LabelEncoder
│ ├── multiclass_knn_model.pkl # MULTI — KNeighborsClassifier (jenis PMK)
│ ├── multiclass_scaler.pkl # MULTI — StandardScaler
│ ├── multiclass_label_encoder.pkl# MULTI — LabelEncoder
│ └── pca.pkl # TIDAK DIGUNAKAN (legacy)
│
├── uploads/ # Gambar hasil upload user
│ └── resize/ # Hasil resize
│ └── threshold/ # Hasil threshold
│
├── utils/
│ ├── __init__.py
│ ├── mysql_db.py # SQLAlchemy ORM — models, CRUD, seed data (916 baris)
│ ├── preprocessing.py # Validasi sapi + preprocessing pipeline (340 baris)
│ ├── feature_extraction.py # Ekstraksi fitur: RGB avg + HSV + GLCM + histogram + Hu (46 fitur)
│ └── helpers.py # Load/save model, dataset prep, confidence estimation (237 baris)
│
├── templates/ # Jinja2 templates
│ ├── base.html # Layout utama (Bootstrap 5, navbar, footer)
│ ├── index.html # Halaman utama
│ ├── upload.html # Form upload multi-file (JS sederhana)
│ ├── result.html # Hasil deteksi (sehat/sakit)
│ ├── riwayat_deteksi.html # Riwayat (client localStorage + MySQL)
│ ├── detail_deteksi.html # Detail satu prediksi + diagnosis
│ ├── expert_system.html # Sistem pakar — pilih gejala + hasil
│ └── diagnosis_history.html # Riwayat diagnosis sistem pakar
│
└── static/
├── css/style.css # Custom CSS (gradient, animasi, responsive)
└── js/main.js # JS — tooltips, file preview, API fetch, dark mode
3. ARSITEKTUR & ALUR DATA
3.1 Jalur Deteksi Gambar
User upload gambar
│
▼
[app.py] validate_cattle_image()
├─ Tolak wajah manusia (Haar cascade)
├─ Deteksi mata (HoughCircles) — bonus scoring
├─ Validasi warna HSV (coklat, merah, hitam, putih)
├─ Analisis tekstur (Laplacian variance)
├─ Edge detection (Canny)
└─ Final: weighted confidence (eye 15%, color 40%, texture 30%, edge 15%), threshold ≥ 65%
│
├─ Gagal → tolak dengan pesan error
│
▼
[preprocessing.py] preprocess_pipeline()
→ resize 128×128 → grayscale → Otsu threshold → mask → masked RGB + masked gray
│
▼
[feature_extraction.py] FeatureExtractor.extract_all_features()
→ 46 fitur: [RGB avg (3), HSV mean+std (6), GLCM (6), histogram 24, Hu (7)]
│
▼
[helpers.py] scaler.transform() → [BINARY MODEL] predict()
│
├ ─ 'sehat' → Simpan ke MySQL
│
▼ sakit
[helpers.py] multiclass_scaler.transform() → [MULTI-CLASS MODEL] predict()
→ label_encoder.inverse_transform()
│
▼
[helpers.py] estimate_prediction_confidence()
→ weighted neighbor confidence (clipped 50.0 – 98.5)
│
▼
Simpan ke MySQL (primary) + CSV (fallback)
│
▼
Semua gambar diproses — jika ada yang sakit:
→ Redirect ke expert-system?symptoms=G01,G02,...&mode=image
→ Gejala otomatis tercentang sesuai jenis PMK — hanya gejala yang relevan dengan body part PMK type tersebut (tidak cross-category)
Jika semua sehat:
→ Flash "Semua X gambar sehat" → /result/healthy
3.2 Jalur Sistem Pakar
User memilih gejala di web form
│
▼
[expert_system.py] ForwardChaining.tambah_gejala(gejala_list)
│
▼
ForwardChaining.inferensi()
→ Untuk setiap aturan:
- Hitung coverage = matched_count / total_count
- Hitung support_ratio = matched_rules / total_rules_for_disease
- Hitung evidence_strength = min(best_matched / 4, 1.0)
- Combined score = best_coverage*0.5 + avg_coverage*0.25 + support_ratio*0.15 + evidence_strength*0.1 + exact_bonus
- Threshold minimum: 0.35
│
▼
ForwardChaining.get_diagnosis()
→ Urutkan penyakit berdasarkan score
→ Kembalikan top diagnosis + matched rules + solusi
→ Severity map: P01=oral, P02=podal, P03=laktasi, P05=akut umum
│
▼
Simpan ke MySQL (tabel predictions + diagnosis_history)
│
▼
Render expert_system.html
3.3 Storage Hybrid
- MySQL (primary): tabel
predictions,diagnosis_history,expert_symptoms,expert_diseases,expert_rules, join tableexpert_rules_expert_symptoms - localStorage (browser): menyimpan array ID prediksi untuk ditampilkan di halaman riwayat
- CSV fallback:
results/predictions.csv,results/diagnosis_history.csv
Batch upload: Semua gambar dari satu sesi upload disimpan dalam 1 baris tabel
predictions. Data per-gambar (filename, prediction, pmk_type, confidence) disimpan sebagai JSON di kolomimages_data. Fieldpredictiondiisi 'sakit' jika ada gambar sakit, 'sehat' jika semua sehat.
4. DATABASE (MySQL via SQLAlchemy)
4.1 Tabel
-- Tabel prediksi hasil deteksi gambar
predictions
id INT AUTO_INCREMENT PK
original_filename VARCHAR(255)
filename VARCHAR(255)
image_path VARCHAR(500)
prediction VARCHAR(50) -- 'sehat' / 'sakit'
confidence FLOAT
features TEXT -- JSON string dari extracted features
images_data TEXT -- JSON array dari per-gambar (batch upload)
timestamp DATETIME
-- Tabel riwayat diagnosis sistem pakar
diagnosis_history
id INT AUTO_INCREMENT PK
prediction_id INT FK → predictions.id (nullable)
diagnosis TEXT -- JSON string detail diagnosis
severity VARCHAR(50) -- 'ringan' / 'sedang' / 'berat'
timestamp DATETIME
-- Master gejala (29 gejala)
expert_symptoms
id INT AUTO_INCREMENT PK
code VARCHAR(10) UNIQUE -- G01–G29
description TEXT
category VARCHAR(50) -- umum, mulut, kaki, ambing, berat
display_order INT
-- Master penyakit (5 penyakit)
expert_diseases
id INT AUTO_INCREMENT PK
code VARCHAR(10) UNIQUE -- P01–P05
name VARCHAR(120)
description TEXT
solutions TEXT -- JSON array of strings
display_order INT
-- Aturan forward chaining (5 aturan)
expert_rules
id INT AUTO_INCREMENT PK
code VARCHAR(20) UNIQUE -- FC01–FC04
symptom_codes TEXT -- JSON array of symptom codes
result_disease_code VARCHAR(10) FK → expert_diseases.code
description TEXT
is_active BOOLEAN
display_order INT
-- Join table many-to-many
expert_rules_expert_symptoms
rule_id INT FK → expert_rules.id (ON DELETE CASCADE)
symptom_id INT FK → expert_symptoms.id (ON DELETE CASCADE)
4.2 Knowledge Base Default
29 Gejala (G01–G29) per kategori:
- umum (7): G01 demam >39.5°C, G11 nafsu makan turun, G12 lesu, G17 demam >40.5°C, G25 lebih sering berbaring, G28 takikardia, G29 sesak napas
- mulut (9): G02 air liur berlebih, G03 luka lidah/gusi, G04 nyeri setelah lepuh, G10 hidung berair, G14 lepuh moncong, G18 lepuh lidah meluas, G19 sulit mengunyah, G20 lepuh bantalan gigi, G21 bau mulut
- kaki (6): G05 lepuh celah kuku, G06 pincang, G15 nyeri kaki, G22 edema/radang, G23 bengkak celah kuku, G24 telapak kaki longgar
- ambing (5): G07 lepuh puting, G08 lesi puting, G09 produksi susu turun, G26 puting retak, G27 susu menggumpal
- berat (2): G13 miokarditis/kematian mendadak, G16 abortus/infertilitas
5 Penyakit (P01–P05):
- P01 = PMK_ORAL (gejala mulut)
- P02 = PMK_PODAL (gejala kaki/kuku)
- P03 = PMK_LAKTASI (gejala ambing)
- P05 = PMK_AKUT_GENERAL (gejala umum berat)
4 Aturan (FC01–FC04):
- FC01 → P01 (oral): [G01, G02, G03, G04, G11, G18, G19, G20, G21]
- FC02 → P02 (podal): [G01, G02, G05, G06, G15, G22, G23, G24, G25]
- FC03 → P03 (laktasi): [G01, G02, G07, G08, G09, G26, G27]
- FC04 → P05 (akut umum): [G01, G02, G03, G04, G05, G06, G07, G09, G11, G12, G14, G18, G20, G22, G23, G24, G26]
Auto-check gejala dari hasil deteksi gambar (di
app.py:pmk_to_symptoms): Mapping ini digunakan saat redirect ke expert system via mode=image. Hanya gejala spesifik body part yang diikutkan (tanpa gejala umum):
pmk_oral→ gejala mulut: [G02, G03, G04, G10, G14, G18, G19, G20, G21]pmk_podal→ gejala kaki: [G05, G06, G15, G22, G23, G24]
Jika menambah/mengubah gejala, penyakit, atau aturan, update data di:
utils/mysql_db.py— constantsDEFAULT_EXPERT_SYMPTOMS,DEFAULT_EXPERT_DISEASES,DEFAULT_EXPERT_RULES- File ini (AGENTS.md) — bagian ini
- Jalankan ulang
seed_expert_knowledge(force=True)atau hapus tabel agar di-reseed
5. API ENDPOINTS (Flask)
| Method | Route | Deskripsi |
|---|---|---|
| GET | / |
Homepage — model status + recent history |
| GET | /upload |
Halaman upload gambar |
| POST | /predict |
Upload + prediksi gambar multi-file. Semua gambar disimpan sbg 1 baris di DB. Jika sakit → redirect ke expert-system dgn gejala otomatis + semua gambar ditampilkan. Jika sehat → /result/healthy |
| GET | /result/healthy |
Hasil sehat |
| GET | /result/sick |
Hasil sakit (legacy, tidak dipakai di alur baru) |
| GET | /riwayat_deteksi |
Halaman riwayat deteksi |
| GET | /detail-deteksi/<int:pred_id> |
Detail prediksi + diagnosis |
| POST | /api/validate-image |
Validasi gambar real-time (apakah sapi) |
| POST | /api/get_data_riwayat_deteksi |
Ambil riwayat berdasarkan localStorage IDs |
| POST | /api/diagnosis |
API diagnosis sistem pakar |
| GET/POST | /expert-system |
Halaman sistem pakar (GET=form dgn param symptoms untuk pre-check, POST=diagnosis) |
| GET | /expert-system/from-prediction |
Redirect ke expert system mode gambar |
| GET | /riwayat-diagnosis |
Halaman riwayat diagnosis |
| GET | /export/csv |
Export prediksi ke CSV |
| GET | /export/excel |
Export prediksi ke Excel |
| GET | /clear-session |
Bersihkan Flask session |
6. ML MODEL DETAILS
Arsitektur Hierarchical (2-level):
Input image → 46 fitur (RGB+HSV+GLCM+Histogram+Hu)
│
┌──────┴──────┐
▼ ▼
[BINARY MODEL] [selamat — end]
sehat vs sakit
│ (sakit)
▼
[MULTI-CLASS MODEL]
pmk_oral / pmk_podal / pmk_laktasi / pmk_akut_general
Binary Model (prefix='')
| Parameter | Value |
|---|---|
| Task | sehat vs sakit |
| Algorithm | K-Nearest Neighbors (KNN) |
| k | Hyperparameter tuned (1–15) |
| Distance | Hyperparameter tuned (euclidean/manhattan) |
| Weight | Hyperparameter tuned (uniform/distance) |
| Accuracy | ±98% |
| Model file | models/knn_model.pkl |
Multi-class Model (prefix='multiclass_')
| Parameter | Value |
|---|---|
| Task | Jenis PMK (hanya data sakit) |
| Algorithm | K-Nearest Neighbors (KNN) |
| k | Hyperparameter tuned (1–15) |
| Distance | Hyperparameter tuned (euclidean/manhattan) |
| Weight | Hyperparameter tuned (uniform/distance) |
| Accuracy | ±60% |
| Model file | models/multiclass_knn_model.pkl |
Common
| Parameter | Value |
|---|---|
| Features | 46: avg_red, avg_green, avg_blue, hsv_h_mean, hsv_h_std, hsv_s_mean, hsv_s_std, hsv_v_mean, hsv_v_std, contrast, homogeneity, correlation, energy, dissimilarity, asm, 8-bin histogram × 3 channels (24), hu_moment_0..6 (7) |
| Image size | 256×256 |
| Preprocessing | Otsu threshold, mask, resize |
| Scaling | StandardScaler |
| Output | Binary: sehat / sakit; Multi-class: pmk_oral, pmk_podal, pmk_laktasi, pmk_akut_general, ... |
| Confidence | Custom weighted neighbor, clipped 50.0–98.5% |
| Class detection | Auto-detect all folders under dataset/ — healthy → sehat, pmk_* → nama folder |
Jika menambah/mengubah fitur, update:
utils/feature_extraction.py—FeatureExtractor.feature_namestrain_model.py— feature export dimensions- Retrain model (
python train_model.py)- AGENTS.md ini
Jika menambah kelas penyakit baru (folder
pmk_*):
- Buat folder
dataset/pmk_nama_baru/— otomatis terdeteksi olehprepare_dataset()- Retrain model (
python train_model.py)- Update AGENTS.md
7. CATTLE IMAGE VALIDATION
Lokasi: utils/preprocessing.py:validate_cattle_image()
Layer validasi berlapis:
- Face rejection — Haar cascade face detection (tolak foto manusia)
- Eye detection — HoughCircles (bonus scoring)
- Color validation — HSV threshold (brown, red, black, white)
- Texture analysis — Laplacian variance (range 45–3500)
- Edge detection — Canny edge density (range 0.01–0.35)
Final confidence = eye_score*0.15 + color_score*0.40 + texture_score*0.30 + edge_score*0.15
Threshold: ≥ 65% untuk lolos sebagai sapi.
8. DEPLOYMENT
Railway
- Builder: Nixpacks (
railway.json) - Start:
gunicorn app:app --bind 0.0.0.0:$PORT - Env:
FLASK_SECRET,DATABASE_URLatauMYSQLHOST/PORT/USER/PASSWORD/DATABASE opencv-python-headless— tidak butuh GUI library
Local
# 1. Setup database
python setup_db.py
# 2. (Opsional) Training model
python train_model.py
# 3. Jalankan app
python app.py
# Buka http://localhost:5000
9. TESTING
# Test environment & database
python test_env.py
# Test expert system forward chaining
python test_forward_chaining.py
Jika menambah fitur baru, tambahkan test case di file terkait. Jika menambah test file baru, update bagian ini.
10. CONFIGURATION
Environment Variables (.env)
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_NAME=deteksi_pmk
MYSQLHOST= # Railway
MYSQLPORT= # Railway
MYSQLUSER= # Railway
MYSQLPASSWORD= # Railway
MYSQLDATABASE= # Railway
DATABASE_URL= # Railway
FLASK_SECRET=deteksi-pmk-secret-key-2026
FLASK_DEBUG=0
11. ATURAN UNTUK AI AGENT
- Jangan pernah mengubah fungsionalitas tanpa memperbarui file ini.
- Jika menambah variabel environment, tambahkan ke
.env.exampledan dokumentasikan di sini. - Jika mengubah struktur tabel, update: (a) model SQLAlchemy di
utils/mysql_db.py, (b) skema di AGENTS.md, (c) constantsDEFAULT_EXPERT_*. - Jika menambah endpoint, dokumentasikan di bagian API ENDPOINTS.
- Jika mengubah fitur ML, update
feature_names, retrain model, update AGENTS.md. - Jika menambah dependensi, tambahkan ke
requirements.txtdan update tabel dependensi. - File ini WAJIB dibaca sebelum melakukan perubahan signifikan.
- Gunakan Bahasa Indonesia untuk komentar, UI, dan dokumentasi end-user karena target pengguna adalah peternak/inspektur Indonesia.
12. DEPENDENSI (requirements.txt)
| Package | Version | Kegunaan |
|---|---|---|
| opencv-python-headless | 4.9.0.80 | Image processing, validasi sapi |
| numpy | <2 | Numerical operations |
| pandas | ≥2.1.0 | Data manipulation |
| scikit-learn | ≥1.3.2 | KNN, StandardScaler, LabelEncoder |
| scikit-image | ≥0.22.0 | GLCM texture features |
| matplotlib | ≥3.8.0 | Feature analysis plots |
| pillow | ≥10.1.0 | Image loading fallback |
| joblib | ≥1.3.2 | Model serialization |
| Flask | ≥2.3.3 | Web framework |
| SQLAlchemy | ≥2.0.0 | ORM MySQL |
| PyMySQL | ≥1.1.0 | MySQL driver |
| python-dotenv | ≥1.0.0 | Environment loading |
| gunicorn | ≥21.2.0 | WSGI production server |
| pytz | ≥2024.1 | Timezone (Asia/Jakarta) |
| cryptography | ≥3.4.8 | MySQL password encryption |
13. LEGACY CODE (TIDAK DIGUNAKAN LAGI)
File berikut adalah kode legacy desktop (Tkinter) yang tidak aktif di web app:
main.py— Menu utama desktoppredict_image.py— Prediksi gambar via GUI Tkintermodels/pca.pkl— PCA model tidak digunakan (legacy)
Jika ada refactoring besar, pertimbangkan untuk menghapus file-file ini atau tandai secara eksplisit.