
- Set up FastAPI project structure with SQLite database - Create database models for tomato images and severity classifications - Implement image upload and processing endpoints - Develop a segmentation model for tomato disease severity detection - Add API endpoints for analysis and results retrieval - Implement health check endpoint - Set up Alembic for database migrations - Update project documentation
148 lines
5.6 KiB
Python
148 lines
5.6 KiB
Python
import numpy as np
|
|
import cv2
|
|
import time
|
|
from typing import Dict, Optional, Any
|
|
|
|
from app.core.config import settings
|
|
from app.utils.image import load_image, resize_image, normalize_image
|
|
|
|
|
|
class TomatoSegmentationModel:
|
|
"""
|
|
Model for tomato disease severity segmentation.
|
|
|
|
This class implements a simple image segmentation model using
|
|
conventional computer vision techniques as a placeholder.
|
|
In a production scenario, this would be replaced with a proper
|
|
deep learning model like U-Net, DeepLabV3, etc.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.model_name = settings.DEFAULT_MODEL_NAME
|
|
self.model_version = "0.1.0"
|
|
self.severity_classes = settings.SEVERITY_CLASSES
|
|
self.input_size = (224, 224) # Standard input size
|
|
|
|
def preprocess_image(self, image_path: str) -> Optional[np.ndarray]:
|
|
"""Preprocess an image for analysis."""
|
|
# Load image
|
|
image = load_image(image_path)
|
|
if image is None:
|
|
return None
|
|
|
|
# Resize
|
|
image = resize_image(image, self.input_size)
|
|
|
|
# Normalize
|
|
image = normalize_image(image)
|
|
|
|
return image
|
|
|
|
def analyze_image(self, image_path: str) -> Dict[str, Any]:
|
|
"""
|
|
Analyze a tomato image to determine disease severity.
|
|
|
|
Args:
|
|
image_path: Path to the image file
|
|
|
|
Returns:
|
|
Dictionary with analysis results including segmentation masks and severity scores
|
|
"""
|
|
start_time = time.time()
|
|
|
|
# Preprocess image
|
|
image = self.preprocess_image(image_path)
|
|
if image is None:
|
|
return {
|
|
"error": "Failed to load or process image",
|
|
"success": False
|
|
}
|
|
|
|
# Simple color-based segmentation to identify potential disease areas
|
|
# This is a simplified placeholder implementation
|
|
|
|
# Convert back to BGR for OpenCV
|
|
image_bgr = (image * 255).astype(np.uint8)
|
|
image_bgr = cv2.cvtColor(image_bgr, cv2.COLOR_RGB2BGR)
|
|
|
|
# Convert to HSV for better color segmentation
|
|
image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV)
|
|
|
|
# Define color ranges for different severity classes
|
|
# These are placeholder values and would need to be calibrated for real use
|
|
color_ranges = {
|
|
"healthy": [(35, 50, 50), (85, 255, 255)], # Green healthy leaves
|
|
"early_blight": [(15, 50, 50), (35, 255, 255)], # Yellowish
|
|
"late_blight": [(0, 50, 50), (15, 255, 255)], # Reddish-brown
|
|
"bacterial_spot": [(0, 0, 0), (180, 50, 100)], # Dark spots
|
|
"septoria_leaf_spot": [(0, 0, 100), (180, 50, 255)] # Light spots
|
|
}
|
|
|
|
# Create segmentation masks and calculate affected areas
|
|
masks = {}
|
|
severity_details = []
|
|
total_pixels = image.shape[0] * image.shape[1]
|
|
|
|
for severity_class, (lower, upper) in color_ranges.items():
|
|
# Create mask for this color range
|
|
lower = np.array(lower, dtype=np.uint8)
|
|
upper = np.array(upper, dtype=np.uint8)
|
|
mask = cv2.inRange(image_hsv, lower, upper)
|
|
|
|
# Apply morphological operations to clean up the mask
|
|
kernel = np.ones((5, 5), np.uint8)
|
|
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
|
|
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
|
|
|
|
# Calculate affected area percentage
|
|
affected_pixels = np.count_nonzero(mask)
|
|
affected_percentage = (affected_pixels / total_pixels) * 100
|
|
|
|
# Generate a random confidence score for demo purposes
|
|
# In a real model, this would be the model's actual confidence
|
|
confidence = min(affected_percentage / 50, 1.0)
|
|
if severity_class == "healthy" and affected_percentage < 10:
|
|
confidence = 0.9 # Boost confidence for low healthy area (likely diseased)
|
|
|
|
# Store results
|
|
masks[severity_class] = mask.tolist() # Convert to list for JSON serialization
|
|
severity_details.append({
|
|
"severity_class": severity_class,
|
|
"confidence": float(confidence),
|
|
"affected_area_percentage": float(affected_percentage)
|
|
})
|
|
|
|
# Determine primary severity class
|
|
severity_details.sort(key=lambda x: x["confidence"], reverse=True)
|
|
primary_severity = severity_details[0]["severity_class"]
|
|
severity_confidence = severity_details[0]["confidence"]
|
|
|
|
# If healthy has high confidence, it takes precedence
|
|
for detail in severity_details:
|
|
if detail["severity_class"] == "healthy" and detail["confidence"] > 0.7:
|
|
primary_severity = "healthy"
|
|
severity_confidence = detail["confidence"]
|
|
break
|
|
|
|
# Prepare results
|
|
processing_time = int((time.time() - start_time) * 1000) # ms
|
|
|
|
result = {
|
|
"success": True,
|
|
"model_name": self.model_name,
|
|
"model_version": self.model_version,
|
|
"primary_severity": primary_severity,
|
|
"severity_confidence": severity_confidence,
|
|
"severity_details": severity_details,
|
|
"segmentation_data": {
|
|
"image_size": self.input_size,
|
|
"masks": masks
|
|
},
|
|
"processing_time_ms": processing_time
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
# Singleton instance
|
|
segmentation_model = TomatoSegmentationModel() |