graph TD
A[Input: Plant Images & Symptoms] --> B[Feature Extraction]
B --> C[Ontology-Based Reasoning]
C --> D[Disease Diagnosis]
D --> E[Treatment Recommendations]
E --> F[Output: Diagnosis Report]
G[Plant Disease Ontology] --> C
H[Knowledge Base] --> C
style G fill:#f9f,stroke:#333
style H fill:#9cf,stroke:#333
Plant Disease Diagnosis with Ontologies
Building an Ontology-Driven Diagnostic System
This guide demonstrates how to build a comprehensive plant disease diagnosis system using ontologies, combining formal knowledge representation with machine learning for accurate and explainable disease identification.
System Architecture
Core Components
1. Ontology Design
from rdflib import Graph, Namespace, Literal
from rdflib.namespace import RDF, RDFS, OWL, XSD
# Define namespaces
PLANT = Namespace("http://example.org/plant-ontology#")
SYMPTOM = Namespace("http://example.org/symptom-ontology#")
DISEASE = Namespace("http://example.org/disease-ontology#")
# Initialize graph
g = Graph()
# Define classes
g.add((PLANT.Plant, RDF.type, OWL.Class))
g.add((PLANT.Leaf, RDF.type, OWL.Class))
g.add((PLANT.Leaf, RDFS.subClassOf, PLANT.PlantPart))
g.add((SYMPTOM.Yellowing, RDF.type, OWL.Class))
g.add((SYMPTOM.Spots, RDF.type, OWL.Class))
# Define object properties
g.add((PLANT.hasSymptom, RDF.type, OWL.ObjectProperty))
g.add((PLANT.hasSymptom, RDFS.domain, PLANT.Plant))
g.add((PLANT.hasSymptom, RDFS.range, SYMPTOM.PlantSymptom))
# Define data properties
g.add((PLANT.hasSeverity, RDF.type, OWL.DatatypeProperty))
g.add((PLANT.hasSeverity, RDFS.domain, PLANT.Plant))
g.add((PLANT.hasSeverity, RDFS.range, XSD.integer))
# Save ontology
g.serialize("plant_disease_ontology.ttl", format="turtle")2. Symptom to Disease Mapping
import pandas as pd
from typing import List, Dict
class DiseaseDiagnoser:
def __init__(self, ontology_path: str):
self.ontology = Graph()
self.ontology.parse(ontology_path, format="turtle")
self.symptom_to_diseases = self._build_symptom_index()
def _build_symptom_index(self) -> Dict[str, List[str]]:
"""Build an index of symptoms to diseases."""
query = """
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX plant: <http://example.org/plant-ontology#>
SELECT ?symptom ?disease
WHERE {
?disease rdf:type plant:Disease ;
plant:hasSymptom ?symptom .
}
"""
results = self.ontology.query(query)
index = {}
for row in results:
symptom = str(row.symptom).split("#")[-1]
disease = str(row.disease).split("#")[-1]
if symptom not in index:
index[symptom] = []
index[symptom].append(disease)
return index
def diagnose(self, symptoms: List[str]) -> pd.DataFrame:
"""Diagnose potential diseases based on symptoms."""
disease_scores = {}
for symptom in symptoms:
for disease in self.symptom_to_diseases.get(symptom, []):
disease_scores[disease] = disease_scores.get(disease, 0) + 1
# Convert to DataFrame for better visualization
df = pd.DataFrame({
'Disease': list(disease_scores.keys()),
'Match Score': list(disease_scores.values())
}).sort_values('Match Score', ascending=False)
return dfIntegration with ML Models
1. Image-Based Symptom Detection
import torch
from torchvision import models, transforms
from PIL import Image
class SymptomDetector:
def __init__(self, model_path: str):
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model = models.resnet50(pretrained=False)
num_ftrs = self.model.fc.in_features
self.model.fc = torch.nn.Linear(num_ftrs, 10) # 10 symptom classes
self.model.load_state_dict(torch.load(model_path, map_location=self.device))
self.model.eval()
self.transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
self.classes = [
'healthy', 'yellowing', 'spots', 'wilting', 'mold',
'blight', 'rust', 'mildew', 'necrosis', 'stunting'
]
def detect(self, image_path: str, threshold: float = 0.5) -> List[Dict]:
"""Detect symptoms in a plant image."""
image = Image.open(image_path).convert('RGB')
image_tensor = self.transform(image).unsqueeze(0).to(self.device)
with torch.no_grad():
outputs = torch.sigmoid(self.model(image_tensor))
results = []
for i, score in enumerate(outputs.squeeze().cpu().numpy()):
if score > threshold:
results.append({
'symptom': self.classes[i],
'confidence': float(score)
})
return resultsEnd-to-End Diagnosis Pipeline
class PlantDiseaseDiagnosisSystem:
def __init__(self, ontology_path: str, model_path: str):
self.diagnoser = DiseaseDiagnoser(ontology_path)
self.detector = SymptomDetector(model_path)
self.knowledge_base = self._load_knowledge_base()
def _load_knowledge_base(self) -> Dict:
"""Load treatment and prevention knowledge."""
return {
'powdery_mildew': {
'treatment': 'Apply sulfur or potassium bicarbonate-based fungicides',
'prevention': 'Ensure good air circulation, avoid overhead watering',
'severity': 'moderate'
},
'late_blight': {
'treatment': 'Apply copper-based fungicides, remove infected plants',
'prevention': 'Use disease-free seeds, practice crop rotation',
'severity': 'high'
},
# Add more diseases and treatments
}
def diagnose(self, image_path: str, additional_symptoms: List[str] = None) -> Dict:
"""Perform end-to-end diagnosis."""
# Detect symptoms from image
detected_symptoms = self.detector.detect(image_path)
symptom_names = [s['symptom'] for s in detected_symptoms]
# Add any additional symptoms provided by user
if additional_symptoms:
symptom_names.extend(additional_symptoms)
# Get potential diseases
diagnosis = self.diagnoser.diagnose(symptom_names)
# Add treatment information
results = []
for _, row in diagnosis.iterrows():
disease = row['Disease']
if disease in self.knowledge_base:
result = {
'disease': disease,
'match_score': row['Match Score'],
**self.knowledge_base[disease]
}
results.append(result)
return {
'detected_symptoms': symptom_names,
'possible_diagnoses': results
}Evaluation
1. Performance Metrics
from sklearn.metrics import precision_score, recall_score, f1_score
import numpy as np
class Evaluator:
@staticmethod
def evaluate(y_true: List[str], y_pred: List[str]) -> Dict:
"""Evaluate diagnosis performance."""
# Convert to binary vectors
all_labels = sorted(list(set(y_true + y_pred)))
y_true_bin = [[1 if label in true else 0 for label in all_labels]
for true in y_true]
y_pred_bin = [[1 if label in pred else 0 for label in all_labels]
for pred in y_pred]
# Calculate metrics
precision = precision_score(y_true_bin, y_pred_bin, average='micro')
recall = recall_score(y_true_bin, y_pred_bin, average='micro')
f1 = f1_score(y_true_bin, y_pred_bin, average='micro')
return {
'precision': precision,
'recall': recall,
'f1_score': f1
}
@staticmethod
def confusion_matrix(y_true: List[str], y_pred: List[str]) -> pd.DataFrame:
"""Generate confusion matrix."""
from sklearn.metrics import confusion_matrix
# Get all unique labels
labels = sorted(list(set(y_true + y_pred)))
cm = confusion_matrix(y_true, y_pred, labels=labels)
return pd.DataFrame(cm, index=labels, columns=labels)Deployment
1. FastAPI Web Service
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
app = FastAPI(title="Plant Disease Diagnosis API")
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize system
system = PlantDiseaseDiagnosisSystem(
ontology_path="plant_disease_ontology.ttl",
model_path="symptom_detector.pth"
)
@app.post("/diagnose")
async def diagnose_plant(
image: UploadFile = File(...),
symptoms: str = ""
):
try:
# Save uploaded image
image_path = f"temp_{image.filename}"
with open(image_path, "wb") as buffer:
buffer.write(await image.read())
# Process symptoms
additional_symptoms = [s.strip() for s in symptoms.split(",") if s.strip()]
# Get diagnosis
result = system.diagnose(image_path, additional_symptoms)
# Clean up
import os
os.remove(image_path)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)Next Steps
- Expand Ontology: Add more plant species, diseases, and symptoms
- Improve Models: Train on larger datasets for better accuracy
- Mobile App: Create a mobile interface for field diagnosis
- Continuous Learning: Update the system with new cases and treatments