
- 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
88 lines
2.4 KiB
Python
88 lines
2.4 KiB
Python
import cv2
|
|
import numpy as np
|
|
from pathlib import Path
|
|
import uuid
|
|
import os
|
|
from datetime import datetime
|
|
from typing import Tuple, Dict, Any, Optional
|
|
|
|
from app.core.config import settings
|
|
|
|
|
|
def save_uploaded_image(file_data: bytes, filename: str) -> Dict[str, Any]:
|
|
"""
|
|
Save an uploaded image to the storage directory and return metadata.
|
|
|
|
Args:
|
|
file_data: The binary content of the uploaded file
|
|
filename: Original filename of the uploaded file
|
|
|
|
Returns:
|
|
Dict with image metadata including path, size, etc.
|
|
"""
|
|
# Generate a unique filename to avoid conflicts
|
|
extension = Path(filename).suffix.lower()
|
|
date_prefix = datetime.now().strftime("%Y%m%d")
|
|
unique_id = str(uuid.uuid4())
|
|
safe_filename = f"{date_prefix}_{unique_id}{extension}"
|
|
|
|
# Create full path
|
|
file_path = settings.IMAGE_DIR / safe_filename
|
|
|
|
# Write file to disk
|
|
with open(file_path, "wb") as f:
|
|
f.write(file_data)
|
|
|
|
# Get image dimensions if it's an image file
|
|
width, height = None, None
|
|
try:
|
|
img = cv2.imread(str(file_path))
|
|
if img is not None:
|
|
height, width, _ = img.shape
|
|
except Exception:
|
|
pass
|
|
|
|
return {
|
|
"file_path": str(file_path),
|
|
"filename": filename,
|
|
"file_size": len(file_data),
|
|
"width": width,
|
|
"height": height,
|
|
"mime_type": get_mime_type(extension)
|
|
}
|
|
|
|
|
|
def get_mime_type(extension: str) -> str:
|
|
"""Map file extension to MIME type."""
|
|
mime_types = {
|
|
".jpg": "image/jpeg",
|
|
".jpeg": "image/jpeg",
|
|
".png": "image/png",
|
|
".gif": "image/gif",
|
|
".bmp": "image/bmp",
|
|
".tiff": "image/tiff",
|
|
".tif": "image/tiff",
|
|
}
|
|
return mime_types.get(extension.lower(), "application/octet-stream")
|
|
|
|
|
|
def load_image(file_path: str) -> Optional[np.ndarray]:
|
|
"""Load an image from file path."""
|
|
if not os.path.exists(file_path):
|
|
return None
|
|
|
|
img = cv2.imread(file_path)
|
|
if img is None:
|
|
return None
|
|
|
|
return cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Convert to RGB
|
|
|
|
|
|
def resize_image(image: np.ndarray, target_size: Tuple[int, int]) -> np.ndarray:
|
|
"""Resize image to target size."""
|
|
return cv2.resize(image, target_size, interpolation=cv2.INTER_AREA)
|
|
|
|
|
|
def normalize_image(image: np.ndarray) -> np.ndarray:
|
|
"""Normalize image pixel values to [0, 1]."""
|
|
return image.astype(np.float32) / 255.0 |