├── .gitignore ├── README.md ├── __init__.py ├── api ├── __init__.py ├── config_api.py ├── database_api.py ├── health_api.py └── scenario_api.py ├── config.py ├── config.yaml ├── core ├── __init__.py ├── backend │ ├── __init__.py │ ├── classification │ │ ├── __init__.py │ │ ├── classification_model_1.py │ │ └── classification_model_2.py │ └── detection │ │ ├── __init__.py │ │ └── detection_model_1.py └── util │ ├── __init__.py │ ├── plots.py │ └── utils.py ├── data ├── __init__.py └── config │ ├── __init__.py │ ├── classification_config.yaml │ └── detection_config.yaml ├── database ├── __init__.py ├── schema.py └── schema.sql ├── docs ├── README.md ├── api │ └── README.md ├── database │ └── README.md ├── engine │ ├── classification │ │ └── README.md │ └── detection │ │ └── README.md ├── scenario │ └── README.md └── tests │ └── README.md ├── engine └── __init__.py ├── main.py ├── requirements.txt ├── scenarios ├── __init__.py ├── scenario_1.py ├── scenario_2.py └── scenario_3.py ├── test_main.py └── tests ├── __init__.py ├── test_apis.py ├── test_classification.py ├── test_detection.py ├── test_scenarios.py └── test_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | uploads/ 3 | uploads/* 4 | scenes.db -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VisionAVI -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monalsingh/VisionAVI/d3ee5525bf828a4da6be12bc17488e6c6f846008/__init__.py -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /api/config_api.py: -------------------------------------------------------------------------------- 1 | # Python module: config_api.py 2 | -------------------------------------------------------------------------------- /api/database_api.py: -------------------------------------------------------------------------------- 1 | """ 2 | Object Detection API using FastAPI and YOLOv5 3 | 4 | This module provides a REST API for object detection using YOLOv5 model. 5 | It supports multiple input sources including images, videos, webcam feeds, 6 | and RTSP streams. All detections are stored in a SQLite database for 7 | further analysis. 8 | 9 | Endpoints: 10 | POST /detect/image: 11 | Process uploaded image files for object detection 12 | POST /detect/video: 13 | Process uploaded video files for object detection 14 | POST /detect/webcam: 15 | Capture and process webcam feed for specified duration 16 | POST /detect/rtsp: 17 | Process RTSP stream for specified duration 18 | 19 | Dependencies: 20 | - FastAPI 21 | - OpenCV (cv2) 22 | - YOLOv5 (custom implementation) 23 | - SQLite database 24 | 25 | Environment Setup: 26 | 1. Install required packages: 27 | pip install fastapi uvicorn python-multipart opencv-python 28 | 2. Ensure YOLOv5 model file (yolov5s.pt) is present 29 | 3. Initialize database using schema.sql 30 | 31 | Usage Examples: 32 | # Start the API server 33 | $ uvicorn api.database_api:app --reload 34 | 35 | # Test endpoints using curl: 36 | $ curl -X POST "http://localhost:8000/detect/image" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "file=@test.jpg" 37 | $ curl -X POST "http://localhost:8000/detect/video" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "file=@test.mp4" 38 | $ curl -X POST "http://localhost:8000/detect/webcam?duration=10" 39 | $ curl -X POST "http://localhost:8000/detect/rtsp?rtsp_url=rtsp://example.com/stream&duration=30" 40 | 41 | Configuration: 42 | - Model Path: Update YOLOv5 model path in initialization 43 | - Database: Update database path in SceneDatabase initialization 44 | - Upload Directory: Files are saved to 'uploads/' directory 45 | 46 | Author: Anantha Krishna B 47 | Version: 1.0.0 48 | """ 49 | 50 | # Import the required libraries 51 | from fastapi import FastAPI, UploadFile, File, HTTPException, Query 52 | from fastapi.responses import JSONResponse 53 | from typing import Optional 54 | import cv2 55 | import numpy as np 56 | from datetime import datetime 57 | import uuid 58 | import os 59 | from pathlib import Path 60 | 61 | # Import your custom modules 62 | from core.backend.detection.detection_model_1 import YOLOv5 63 | from database.schema import SceneDatabase 64 | 65 | app = FastAPI() 66 | 67 | # Initialize YOLO model 68 | model = YOLOv5("yolov5s.pt") # Update with your model path 69 | db = SceneDatabase("scenes.db") 70 | 71 | ALLOWED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/jpg"] 72 | ALLOWED_VIDEO_TYPES = ["video/mp4", "video/avi", "video/mpeg"] 73 | 74 | 75 | # Helper function to save uploaded file 76 | async def save_upload_file(upload_file: UploadFile) -> str: 77 | """ 78 | Save an uploaded file to the server. 79 | 80 | Args: 81 | upload_file (UploadFile): The file uploaded by the user. 82 | 83 | Returns: 84 | str: The file path where the uploaded file is saved. 85 | """ 86 | file_location = f"uploads/{upload_file.filename}" 87 | os.makedirs("uploads", exist_ok=True) 88 | 89 | with open(file_location, "wb+") as file_object: 90 | file_object.write(await upload_file.read()) 91 | return file_location 92 | 93 | 94 | @app.post("/detect/image") 95 | async def detect_image(file: UploadFile = File(...)): 96 | """Process image with content type validation.""" 97 | if file.content_type not in ALLOWED_IMAGE_TYPES: 98 | raise HTTPException( 99 | status_code=400, 100 | detail=f"Invalid file type. Must be one of: {', '.join(ALLOWED_IMAGE_TYPES)}", 101 | ) 102 | 103 | try: 104 | file_path = await save_upload_file(file) 105 | if not os.path.exists(file_path): 106 | raise HTTPException(status_code=500, detail="Failed to save uploaded file") 107 | 108 | detections, metadata = model.detect_image(file_path) 109 | 110 | # Debug print 111 | print("Received detections:", detections[0] if detections else "No detections") 112 | 113 | scene_metadata = { 114 | "timestamp": datetime.now().isoformat(), 115 | "camera_id": "image_upload", 116 | "media_path": file_path, 117 | "resolution": metadata["resolution"], 118 | } 119 | 120 | scene_id = db.add_scene(scene_metadata) 121 | db.add_detections(scene_id, detections) # No need to format detections anymore 122 | 123 | # Draw detections on image for visualization 124 | img = cv2.imread(file_path) 125 | for det in detections: 126 | x1, y1, x2, y2 = ( 127 | int(det["x_min"]), 128 | int(det["y_min"]), 129 | int(det["x_max"]), 130 | int(det["y_max"]), 131 | ) 132 | cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) 133 | label = f"{det['class']}: {det['confidence']:.2f}" 134 | cv2.putText( 135 | img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2 136 | ) 137 | 138 | # Save annotated image 139 | output_path = f"uploads/annotated_{os.path.basename(file_path)}" 140 | cv2.imwrite(output_path, img) 141 | 142 | return JSONResponse( 143 | { 144 | "scene_id": scene_id, 145 | "detections": detections, 146 | "annotated_image": output_path, 147 | } 148 | ) 149 | 150 | except FileNotFoundError as e: 151 | raise HTTPException(status_code=400, detail=str(e)) 152 | except Exception as e: 153 | print(f"Error in detect_image: {str(e)}") # Debug print 154 | raise HTTPException(status_code=500, detail=str(e)) 155 | 156 | 157 | @app.post("/detect/video") 158 | async def detect_video(file: UploadFile = File(...)): 159 | file_path = await save_upload_file(file) 160 | 161 | # Use the new detect_video method 162 | detections = model.detect_video(file_path) 163 | 164 | scene_metadata = { 165 | "timestamp": datetime.now().isoformat(), 166 | "camera_id": "video_upload", 167 | "media_path": file_path, 168 | } 169 | 170 | scene_id = db.add_scene(scene_metadata) 171 | db.add_detections(scene_id, detections) 172 | 173 | return JSONResponse({"scene_id": scene_id, "total_detections": len(detections)}) 174 | 175 | 176 | @app.post("/detect/webcam") 177 | async def start_webcam_detection( 178 | duration: int = Query(..., gt=0, description="Duration in seconds") 179 | ): 180 | """Start webcam detection with duration validation.""" 181 | try: 182 | cap = cv2.VideoCapture(0) 183 | if not cap.isOpened(): 184 | raise HTTPException(status_code=500, detail="Failed to open webcam") 185 | 186 | scene_metadata = { 187 | "timestamp": datetime.now().isoformat(), 188 | "camera_id": "webcam", 189 | "media_path": f"webcam_stream_{uuid.uuid4()}", 190 | } 191 | 192 | scene_id = db.add_scene(scene_metadata) 193 | 194 | start_time = datetime.now() 195 | while (datetime.now() - start_time).seconds < duration: 196 | success, frame = cap.read() 197 | if not success: 198 | break 199 | 200 | # Use the new detect_frame method 201 | frame_detections = model.detect_frame(frame) 202 | if frame_detections: 203 | db.add_detections(scene_id, frame_detections) 204 | for det in frame_detections: 205 | x1, y1, x2, y2 = ( 206 | det["x_min"], 207 | det["y_min"], 208 | det["x_max"], 209 | det["y_max"], 210 | ) 211 | cv2.rectangle( 212 | frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2 213 | ) 214 | label = f"{det['class']}: {det['confidence']:.2f}" 215 | cv2.putText( 216 | frame, 217 | label, 218 | (int(x1), int(y1) - 10), 219 | cv2.FONT_HERSHEY_SIMPLEX, 220 | 0.5, 221 | (0, 255, 0), 222 | 2, 223 | ) 224 | 225 | cap.release() 226 | 227 | return JSONResponse( 228 | { 229 | "scene_id": scene_id, 230 | "message": f"Webcam detection completed for {duration} seconds", 231 | } 232 | ) 233 | 234 | except Exception as e: 235 | raise HTTPException(status_code=500, detail=str(e)) 236 | 237 | 238 | @app.post("/detect/rtsp") 239 | async def start_rtsp_detection( 240 | rtsp_url: str = Query(..., regex="^rtsp://.*"), duration: int = Query(..., gt=0) 241 | ): 242 | """Process RTSP stream with URL validation.""" 243 | try: 244 | cap = cv2.VideoCapture(rtsp_url) 245 | if not cap.isOpened(): 246 | raise HTTPException( 247 | status_code=400, detail="Failed to connect to RTSP stream" 248 | ) 249 | 250 | scene_metadata = { 251 | "timestamp": datetime.now().isoformat(), 252 | "camera_id": "rtsp_stream", 253 | "media_path": rtsp_url, 254 | } 255 | 256 | scene_id = db.add_scene(scene_metadata) 257 | 258 | start_time = datetime.now() 259 | while (datetime.now() - start_time).seconds < duration: 260 | success, frame = cap.read() 261 | if not success: 262 | break 263 | 264 | # Use the model's detect_frame method instead of direct predict 265 | frame_detections = model.detect_frame(frame) 266 | 267 | if frame_detections: 268 | db.add_detections(scene_id, frame_detections) 269 | # Optionally add visualization like in webcam endpoint 270 | for det in frame_detections: 271 | x1, y1, x2, y2 = ( 272 | det["x_min"], 273 | det["y_min"], 274 | det["x_max"], 275 | det["y_max"], 276 | ) 277 | cv2.rectangle( 278 | frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2 279 | ) 280 | 281 | cap.release() 282 | 283 | return JSONResponse( 284 | { 285 | "scene_id": scene_id, 286 | "message": f"RTSP stream detection completed for {duration} seconds", 287 | } 288 | ) 289 | 290 | except Exception as e: 291 | raise HTTPException(status_code=500, detail=str(e)) 292 | -------------------------------------------------------------------------------- /api/health_api.py: -------------------------------------------------------------------------------- 1 | # Python module: health_api.py 2 | -------------------------------------------------------------------------------- /api/scenario_api.py: -------------------------------------------------------------------------------- 1 | # Python module: scenario_api.py 2 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import os 3 | 4 | class ConfigManager: 5 | def __init__(self, config_path: str): 6 | self.config_path = config_path 7 | self.config = self._load_config() 8 | 9 | def _load_config(self): 10 | if not os.path.exists(self.config_path): 11 | raise FileNotFoundError(f"Configuration file not found at {self.config_path}") 12 | with open(self.config_path, 'r') as file: 13 | return yaml.safe_load(file) 14 | 15 | def get(self, key: str, default=None): 16 | # Get a specific configuration value 17 | return self.config.get(key, default) 18 | 19 | def get_all(self): 20 | # Get the entire configuration as a dictionary 21 | return self.config 22 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | app: 2 | name: "VisionAVI" 3 | version: "1.0.0" 4 | batch: KNCVB1 5 | 6 | database: 7 | host: "localhost" 8 | port: 5432 9 | user: "user" 10 | password: "password" 11 | database_name: "visionavi" 12 | 13 | logging: 14 | enable : False 15 | level: "level-1" 16 | file: "logs/app.log" 17 | 18 | dummy: 19 | variable_1 : 10 20 | variable_2 : 20 21 | task : "add" 22 | 23 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /core/backend/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /core/backend/classification/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /core/backend/classification/classification_model_1.py: -------------------------------------------------------------------------------- 1 | # Python module: classification_model_1.py 2 | -------------------------------------------------------------------------------- /core/backend/classification/classification_model_2.py: -------------------------------------------------------------------------------- 1 | # Python module: classification_model_2.py 2 | -------------------------------------------------------------------------------- /core/backend/detection/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /core/backend/detection/detection_model_1.py: -------------------------------------------------------------------------------- 1 | # Python module: detection_model_1.py 2 | 3 | # Import the required libraries 4 | from ultralytics import YOLO 5 | import cv2 6 | import numpy as np 7 | from typing import List, Dict, Union, Tuple 8 | 9 | 10 | # Class for YOLOv5 API 11 | class YOLOv5: 12 | """A wrapper class for YOLOv5 object detection model. 13 | 14 | This class provides a high-level interface for object detection using YOLOv5. 15 | It supports detection on images, videos, and real-time frames, with standardized 16 | output formats and metadata collection. 17 | 18 | Attributes: 19 | model: An instance of YOLO model loaded from the specified weights file. 20 | 21 | Methods: 22 | process_detections(results): 23 | Processes raw YOLOv5 results into a standardized format. 24 | 25 | detect_image(image_path): 26 | Performs object detection on a single image file. 27 | 28 | detect_frame(frame): 29 | Performs object detection on a single video frame. 30 | 31 | detect_video(video_path): 32 | Performs object detection on a video file. 33 | 34 | info(): 35 | Returns model architecture and parameter information. 36 | 37 | save_results(results, output_path): 38 | Saves detection results to specified path. 39 | 40 | Example: 41 | ```python 42 | # Initialize detector with model weights 43 | detector = YOLOv5("yolov5s.pt") 44 | 45 | # Perform detection on an image 46 | detections, metadata = detector.detect_image("image.jpg") 47 | 48 | # Process video file 49 | video_detections = detector.detect_video("video.mp4") 50 | 51 | # Get model information 52 | model_info = detector.info() 53 | ``` 54 | 55 | Note: 56 | This implementation uses the Ultralytics YOLOv5 package and requires 57 | the model weights to be compatible with YOLOv5 architecture. 58 | """ 59 | 60 | def __init__(self, model_path: str): 61 | self.model = YOLO(model_path) 62 | 63 | def process_detections(self, results) -> List[Dict]: 64 | """Process detection results into a standardized format.""" 65 | detections = [] 66 | for result in results: 67 | boxes = result.boxes 68 | for box in boxes: 69 | x1, y1, x2, y2 = box.xyxy[0].tolist() 70 | confidence = box.conf[0].item() 71 | class_id = int(box.cls[0].item()) 72 | class_name = result.names[class_id] 73 | 74 | detections.append( 75 | { 76 | "class": class_name, 77 | "confidence": confidence, 78 | "x_min": x1, 79 | "y_min": y1, 80 | "x_max": x2, 81 | "y_max": y2, 82 | "class_id": class_id, 83 | } 84 | ) 85 | return detections 86 | 87 | def detect_image(self, image_path: str) -> Tuple[List[Dict], Dict]: 88 | """ 89 | Perform detection on a single image. 90 | 91 | Returns: 92 | Tuple[List[Dict], Dict]: (detections, metadata) 93 | """ 94 | results = self.model.predict(source=image_path) 95 | detections = self.process_detections(results) 96 | metadata = { 97 | "resolution": f"{results[0].orig_shape[0]}x{results[0].orig_shape[1]}" 98 | } 99 | return detections, metadata 100 | 101 | def detect_frame(self, frame: np.ndarray) -> List[Dict]: 102 | """ 103 | Perform detection on a single frame. 104 | """ 105 | results = self.model.predict(source=frame, stream=True) 106 | return self.process_detections(results) 107 | 108 | def detect_video(self, video_path: str) -> List[Dict]: 109 | """ 110 | Perform detection on a video file. 111 | """ 112 | results = self.model.predict(source=video_path) 113 | return self.process_detections(results) 114 | 115 | def info(self): 116 | """ 117 | Get model information. 118 | """ 119 | return self.model.info() 120 | 121 | def save_results(self, results, output_path: str): 122 | """ 123 | Save detection results to file. 124 | """ 125 | results.save(output_path) 126 | 127 | def get_bbox_coordinates(self, detection: Dict) -> List[float]: 128 | """ 129 | Helper method to get bbox coordinates in array format for visualization. 130 | 131 | Args: 132 | detection (Dict): Detection dictionary with x_min, y_min, x_max, y_max 133 | 134 | Returns: 135 | List[float]: [x_min, y_min, x_max, y_max] 136 | """ 137 | return [ 138 | detection["x_min"], 139 | detection["y_min"], 140 | detection["x_max"], 141 | detection["y_max"], 142 | ] 143 | 144 | 145 | ####################################################################### TESTING CODE ####################################################################################### 146 | 147 | 148 | if __name__ == "__main__": 149 | """ 150 | Test code for YOLOv5 class functionality. 151 | Uncomment sections to test different features. 152 | """ 153 | 154 | # Initialize the model 155 | model = YOLOv5("yolov5s.pt") 156 | 157 | # Test 1: Get model information 158 | print("\n=== Model Information ===") 159 | model_info = model.info() 160 | print(model_info) 161 | 162 | # Test 2: Image detection 163 | """ 164 | print("\n=== Image Detection Test ===") 165 | image_path = "path/to/test/image.jpg" 166 | detections, metadata = model.detect_image(image_path) 167 | print(f"Image Resolution: {metadata['resolution']}") 168 | print(f"Number of detections: {len(detections)}") 169 | for det in detections: 170 | print(f"Detected {det['class']} with confidence {det['confidence']:.2f}") 171 | print(f"Bounding box: {det['bbox']}") 172 | """ 173 | 174 | # Test 3: Video detection 175 | """ 176 | print("\n=== Video Detection Test ===") 177 | video_path = "path/to/test/video.mp4" 178 | video_detections = model.detect_video(video_path) 179 | print(f"Total detections in video: {len(video_detections)}") 180 | """ 181 | 182 | # Test 4: Webcam frame detection 183 | """ 184 | print("\n=== Webcam Frame Detection Test ===") 185 | cap = cv2.VideoCapture(0) 186 | ret, frame = cap.read() 187 | if ret: 188 | frame_detections = model.detect_frame(frame) 189 | print(f"Detections in frame: {len(frame_detections)}") 190 | for det in frame_detections: 191 | print(f"Detected {det['class']} with confidence {det['confidence']:.2f}") 192 | cap.release() 193 | """ 194 | 195 | # Test 5: Save detection results 196 | """ 197 | print("\n=== Save Results Test ===") 198 | test_image = "path/to/test/image.jpg" 199 | results = model.model.predict(source=test_image) 200 | model.save_results(results, "test_output") 201 | print("Results saved to 'test_output'") 202 | """ 203 | 204 | print("\nTests completed!") 205 | -------------------------------------------------------------------------------- /core/util/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /core/util/plots.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monalsingh/VisionAVI/d3ee5525bf828a4da6be12bc17488e6c6f846008/core/util/plots.py -------------------------------------------------------------------------------- /core/util/utils.py: -------------------------------------------------------------------------------- 1 | # Python module: utils.py 2 | 3 | def dummy_print(project_name, project_version, batch_name): 4 | print("="*30) 5 | print("Getting project details ....") 6 | print(f"Project name is : {project_name}") 7 | print(f"Project verison : {project_version}") 8 | print(f"batch_name name is : {batch_name}") 9 | print("="*30) -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monalsingh/VisionAVI/d3ee5525bf828a4da6be12bc17488e6c6f846008/data/__init__.py -------------------------------------------------------------------------------- /data/config/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /data/config/classification_config.yaml: -------------------------------------------------------------------------------- 1 | # Configuration file 2 | -------------------------------------------------------------------------------- /data/config/detection_config.yaml: -------------------------------------------------------------------------------- 1 | # Configuration file 2 | -------------------------------------------------------------------------------- /database/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /database/schema.py: -------------------------------------------------------------------------------- 1 | # Python module: schema.py 2 | 3 | # Import the required libraries 4 | import sqlite3 5 | import threading 6 | from datetime import datetime 7 | from typing import List, Dict, Optional 8 | 9 | 10 | class SceneDatabase: 11 | """ 12 | A class to manage a SQLite database for storing scenes, detections, and annotations. 13 | """ 14 | 15 | _local = threading.local() 16 | 17 | def __init__(self, db_path: str = "scenes.db"): 18 | """ 19 | Initialize the SceneDatabase instance and create the required tables. 20 | 21 | Args: 22 | db_path (str): Path to the SQLite database file. Defaults to "scenes.db". 23 | """ 24 | self.db_path = db_path 25 | self._init_db() 26 | 27 | def _get_conn(self): 28 | """Get thread-local database connection.""" 29 | if not hasattr(self._local, "conn"): 30 | self._local.conn = sqlite3.connect(self.db_path) 31 | return self._local.conn 32 | 33 | def _init_db(self): 34 | """Initialize the database schema.""" 35 | conn = self._get_conn() 36 | conn.execute( 37 | """CREATE TABLE IF NOT EXISTS scenes ( 38 | id INTEGER PRIMARY KEY AUTOINCREMENT, 39 | timestamp TEXT NOT NULL, 40 | latitude REAL, 41 | longitude REAL, 42 | resolution TEXT, 43 | camera_id TEXT NOT NULL, 44 | media_path TEXT NOT NULL 45 | )""" 46 | ) 47 | conn.execute( 48 | """ 49 | CREATE TABLE IF NOT EXISTS detections ( 50 | id INTEGER PRIMARY KEY AUTOINCREMENT, 51 | scene_id INTEGER NOT NULL, 52 | class TEXT NOT NULL, 53 | confidence REAL NOT NULL, 54 | x_min REAL NOT NULL, 55 | y_min REAL NOT NULL, 56 | x_max REAL NOT NULL, 57 | y_max REAL NOT NULL, 58 | FOREIGN KEY (scene_id) REFERENCES scenes (id) 59 | ) 60 | """ 61 | ) 62 | conn.commit() 63 | 64 | def add_scene(self, metadata: Dict) -> int: 65 | """ 66 | Add a new scene to the database. 67 | 68 | Args: 69 | metadata (Dict): A dictionary containing scene metadata, including: 70 | - timestamp (str): Timestamp of the scene. 71 | - latitude (float, optional): Latitude of the scene location. 72 | - longitude (float, optional): Longitude of the scene location. 73 | - resolution (str, optional): Resolution of the scene. 74 | - camera_id (str): ID of the camera capturing the scene. 75 | - media_path (str): Path to the media file. 76 | 77 | Returns: 78 | int: The ID of the newly inserted scene. 79 | """ 80 | conn = self._get_conn() 81 | cursor = conn.cursor() 82 | sql = """INSERT INTO scenes ( 83 | timestamp, latitude, longitude, resolution, 84 | camera_id, media_path 85 | ) VALUES (?, ?, ?, ?, ?, ?)""" 86 | values = ( 87 | metadata["timestamp"], 88 | metadata.get("latitude"), 89 | metadata.get("longitude"), 90 | metadata.get("resolution"), 91 | metadata["camera_id"], 92 | metadata["media_path"], 93 | ) 94 | cursor.execute(sql, values) 95 | conn.commit() 96 | return cursor.lastrowid 97 | 98 | def add_detections(self, scene_id: int, detections: List[Dict]): 99 | """ 100 | Add multiple detections for a specific scene. 101 | 102 | Args: 103 | scene_id (int): The ID of the scene to associate the detections with. 104 | detections (List[Dict]): A list of dictionaries, each containing: 105 | - class (str): The class label of the detection. 106 | - confidence (float): Confidence score of the detection. 107 | - bbox (List[float]): Bounding box coordinates [x_min, y_min, x_max, y_max]. 108 | """ 109 | conn = self._get_conn() 110 | cursor = conn.cursor() 111 | sql = """INSERT INTO detections ( 112 | scene_id, class, confidence, 113 | x_min, y_min, x_max, y_max 114 | ) VALUES (?, ?, ?, ?, ?, ?, ?)""" 115 | values = [ 116 | ( 117 | scene_id, 118 | d["class"], 119 | d["confidence"], 120 | d["x_min"], # Using individual coordinates 121 | d["y_min"], 122 | d["x_max"], 123 | d["y_max"], 124 | ) 125 | for d in detections 126 | ] 127 | cursor.executemany(sql, values) 128 | conn.commit() 129 | 130 | def add_annotation(self, scene_id: int, annotation: Dict): 131 | """ 132 | Add an annotation for a specific scene. 133 | 134 | Args: 135 | scene_id (int): The ID of the scene to associate the annotation with. 136 | annotation (Dict): A dictionary containing annotation details, including: 137 | - label_type (str): Type of the label (e.g., "manual", "auto"). 138 | - description (str, optional): Description of the annotation. 139 | - class_label (str, optional): Class label of the annotation. 140 | - x_min, y_min, x_max, y_max (float, optional): Bounding box coordinates. 141 | - annotated_by (str): Name or ID of the annotator. 142 | """ 143 | sql = """INSERT INTO annotations ( 144 | scene_id, label_type, description, 145 | class_label, x_min, y_min, x_max, y_max, annotated_by 146 | ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""" 147 | 148 | values = ( 149 | scene_id, 150 | annotation["label_type"], 151 | annotation.get("description"), 152 | annotation.get("class_label"), 153 | annotation.get("x_min"), 154 | annotation.get("y_min"), 155 | annotation.get("x_max"), 156 | annotation.get("y_max"), 157 | annotation.get("annotated_by"), 158 | ) 159 | with self._get_conn(): 160 | self._get_conn().execute(sql, values) 161 | 162 | def get_scenes_by_time_range(self, start: datetime, end: datetime) -> List[Dict]: 163 | """ 164 | Retrieve scenes within a specific time range. 165 | 166 | Args: 167 | start (datetime): Start of the time range. 168 | end (datetime): End of the time range. 169 | 170 | Returns: 171 | List[Dict]: A list of scenes within the specified time range. 172 | """ 173 | sql = """SELECT * FROM scenes 174 | WHERE timestamp BETWEEN ? AND ?""" 175 | return ( 176 | self._get_conn() 177 | .execute(sql, (start.isoformat(), end.isoformat())) 178 | .fetchall() 179 | ) 180 | 181 | def get_detections_by_class( 182 | self, class_label: str, confidence_threshold: float = 0.5 183 | ) -> List[Dict]: 184 | """ 185 | Retrieve detections for a specific class label with a confidence threshold. 186 | 187 | Args: 188 | class_label (str): The class label to filter detections. 189 | confidence_threshold (float): Minimum confidence score. Defaults to 0.5. 190 | 191 | Returns: 192 | List[Dict]: A list of detections matching the criteria. 193 | """ 194 | sql = """SELECT * FROM detections 195 | WHERE class_label = ? AND confidence >= ?""" 196 | return ( 197 | self._get_conn() 198 | .execute(sql, (class_label, confidence_threshold)) 199 | .fetchall() 200 | ) 201 | 202 | def assign_to_dataset(self, scene_ids: List[int], dataset_type: str): 203 | """ 204 | Assign scenes to a specific dataset type (e.g., "train", "test"). 205 | 206 | Args: 207 | scene_ids (List[int]): List of scene IDs to assign. 208 | dataset_type (str): The dataset type to assign the scenes to. 209 | """ 210 | sql = """INSERT INTO datasets (scene_id, dataset_type) 211 | VALUES (?, ?)""" 212 | values = [(sid, dataset_type) for sid in scene_ids] 213 | with self._get_conn(): 214 | self._get_conn().executemany(sql, values) 215 | 216 | def get_training_data(self) -> List[Dict]: 217 | """ 218 | Retrieve all scenes and descriptions assigned to the "train" dataset. 219 | 220 | Returns: 221 | List[Dict]: A list of training data scenes with descriptions. 222 | """ 223 | sql = """SELECT s.*, d.description 224 | FROM scenes s 225 | JOIN datasets ds ON s.scene_id = ds.scene_id 226 | LEFT JOIN scene_descriptions d ON s.scene_id = d.scene_id 227 | WHERE ds.dataset_type = 'train' """ 228 | return self._get_conn().execute(sql).fetchall() 229 | 230 | def add_scene_description( 231 | self, scene_id: int, description: str, confidence: float, model_version: str 232 | ): 233 | """ 234 | Add a description for a specific scene. 235 | 236 | Args: 237 | scene_id (int): The ID of the scene to describe. 238 | description (str): The description text. 239 | confidence (float): Confidence score of the description. 240 | model_version (str): Version of the model generating the description. 241 | """ 242 | sql = """INSERT INTO scene_descriptions 243 | (scene_id, description, confidence, model_version) 244 | VALUES (?, ?, ?, ?)""" 245 | with self._get_conn(): 246 | self._get_conn().execute( 247 | sql, (scene_id, description, confidence, model_version) 248 | ) 249 | 250 | def incremental_update(self, new_scenes: List[Dict]): 251 | """ 252 | Perform an incremental update by adding new scenes, detections, and descriptions. 253 | 254 | Args: 255 | new_scenes (List[Dict]): A list of dictionaries, each containing: 256 | - metadata (Dict): Scene metadata. 257 | - detections (List[Dict], optional): List of detections. 258 | - description (str, optional): Scene description. 259 | - confidence (float, optional): Confidence score of the description. 260 | - model_version (str, optional): Model version for the description. 261 | """ 262 | with self._get_conn(): 263 | for scene in new_scenes: 264 | scene_id = self.add_scene(scene["metadata"]) 265 | if "detections" in scene: 266 | self.add_detections(scene_id, scene["detections"]) 267 | if "description" in scene: 268 | self.add_scene_description( 269 | scene_id, 270 | scene["description"], 271 | scene["confidence"], 272 | scene["model_version"], 273 | ) 274 | 275 | def close(self): 276 | """ 277 | Close the database connection. 278 | """ 279 | if hasattr(self._local, "conn"): 280 | self._local.conn.close() 281 | del self._local.conn 282 | 283 | def get_scene(self, scene_id: int) -> Optional[Dict]: 284 | """Read a single scene by ID""" 285 | sql = """SELECT * FROM scenes WHERE id = ?""" 286 | cursor = self._get_conn().execute(sql, (scene_id,)) 287 | row = cursor.fetchone() 288 | return self._row_to_dict(row, cursor.description) if row else None 289 | 290 | def update_scene(self, scene_id: int, updates: Dict): 291 | """Update scene metadata""" 292 | set_clause = ", ".join([f"{k} = ?" for k in updates.keys()]) 293 | sql = f"""UPDATE scenes SET {set_clause} WHERE id = ?""" 294 | values = list(updates.values()) + [scene_id] 295 | 296 | with self._get_conn() as conn: 297 | conn.execute(sql, values) 298 | 299 | def _row_to_dict(self, row, description): 300 | """Convert SQLite row to dictionary""" 301 | if row is None: 302 | return None 303 | return {description[i][0]: value for i, value in enumerate(row)} 304 | -------------------------------------------------------------------------------- /database/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS scenes ( 2 | scene_id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | timestamp DATETIME NOT NULL, 4 | latitude REAL, 5 | longitude REAL, 6 | resolution TEXT, 7 | camera_id TEXT NOT NULL, 8 | media_path TEXT NOT NULL, 9 | processed BOOLEAN DEFAULT 0 10 | ); 11 | 12 | CREATE TABLE IF NOT EXISTS detections ( 13 | detection_id INTEGER PRIMARY KEY AUTOINCREMENT, 14 | scene_id INTEGER NOT NULL, 15 | class_label TEXT NOT NULL, 16 | confidence REAL NOT NULL, 17 | x_min REAL NOT NULL, 18 | y_min REAL NOT NULL, 19 | x_max REAL NOT NULL, 20 | y_max REAL NOT NULL, 21 | FOREIGN KEY(scene_id) REFERENCES scenes(scene_id) ON DELETE CASCADE 22 | ); 23 | 24 | CREATE TABLE IF NOT EXISTS scene_descriptions ( 25 | description_id INTEGER PRIMARY KEY AUTOINCREMENT, 26 | scene_id INTEGER NOT NULL, 27 | description TEXT NOT NULL, 28 | confidence REAL NOT NULL, 29 | model_version TEXT NOT NULL, 30 | FOREIGN KEY(scene_id) REFERENCES scenes(scene_id) ON DELETE CASCADE 31 | ); 32 | 33 | CREATE TABLE IF NOT EXISTS annotations ( 34 | annotation_id INTEGER PRIMARY KEY AUTOINCREMENT, 35 | scene_id INTEGER NOT NULL, 36 | label_type TEXT CHECK(label_type IN ('manual', 'ground_truth')), 37 | description TEXT, 38 | class_label TEXT, 39 | x_min REAL, 40 | y_min REAL, 41 | x_max REAL, 42 | y_max REAL, 43 | annotated_by TEXT, 44 | annotation_time DATETIME DEFAULT CURRENT_TIMESTAMP, 45 | FOREIGN KEY(scene_id) REFERENCES scenes(scene_id) ON DELETE CASCADE 46 | ); 47 | 48 | CREATE TABLE IF NOT EXISTS datasets ( 49 | dataset_id INTEGER PRIMARY KEY AUTOINCREMENT, 50 | scene_id INTEGER NOT NULL, 51 | dataset_type TEXT CHECK(dataset_type IN ('train', 'val', 'test')), 52 | added_date DATETIME DEFAULT CURRENT_TIMESTAMP, 53 | FOREIGN KEY(scene_id) REFERENCES scenes(scene_id) ON DELETE CASCADE 54 | ); 55 | 56 | CREATE INDEX IF NOT EXISTS idx_scenes_timestamp ON scenes(timestamp); 57 | CREATE INDEX IF NOT EXISTS idx_detections_class ON detections(class_label); 58 | CREATE INDEX IF NOT EXISTS idx_annotations_type ON annotations(label_type); -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # DOCS 2 | 3 | This page contains all the document related to api's, models and scenarios. -------------------------------------------------------------------------------- /docs/api/README.md: -------------------------------------------------------------------------------- 1 | # Object Detection API Documentation (database_api.py) 2 | 3 | ## Overview 4 | The database_api.py module provides a RESTful API for object detection using YOLOv5, supporting multiple input sources and storing results in a SQLite database. 5 | 6 | ## Table of Contents 7 | 1. [API Endpoints](#api-endpoints) 8 | 2. [Installation](#installation) 9 | 3. [Configuration](#configuration) 10 | 4. [Usage Examples](#usage-examples) 11 | 5. [Data Structures](#data-structures) 12 | 6. [Error Handling](#error-handling) 13 | 14 | ## API Endpoints 15 | 16 | ### 1. Image Detection 17 | ```http 18 | POST /detect/image 19 | Content-Type: multipart/form-data 20 | ``` 21 | - **Purpose**: Process uploaded images for object detection 22 | - **Parameters**: 23 | - `file`: Image file (jpeg, png, jpg) 24 | - **Response**: 25 | ```json 26 | { 27 | "scene_id": int, 28 | "detections": List[Detection], 29 | "annotated_image": string 30 | } 31 | ``` 32 | 33 | ### 2. Video Detection 34 | ```http 35 | POST /detect/video 36 | Content-Type: multipart/form-data 37 | ``` 38 | - **Parameters**: 39 | - `file`: Video file (mp4, avi, mpeg) 40 | - **Response**: 41 | ```json 42 | { 43 | "scene_id": int, 44 | "total_detections": int 45 | } 46 | ``` 47 | 48 | ### 3. Webcam Detection 49 | ```http 50 | POST /detect/webcam 51 | ``` 52 | - **Parameters**: 53 | - `duration`: int (seconds) 54 | - **Response**: 55 | ```json 56 | { 57 | "scene_id": int, 58 | "message": string 59 | } 60 | ``` 61 | 62 | ### 4. RTSP Stream Detection 63 | ```http 64 | POST /detect/rtsp 65 | ``` 66 | - **Parameters**: 67 | - `rtsp_url`: string (must start with "rtsp://") 68 | - `duration`: int (seconds) 69 | - **Response**: 70 | ```json 71 | { 72 | "scene_id": int, 73 | "message": string 74 | } 75 | ``` 76 | 77 | ## Installation 78 | 79 | ### Dependencies 80 | ```bash 81 | pip install fastapi uvicorn python-multipart opencv-python 82 | ``` 83 | 84 | ### Required Files 85 | - YOLOv5 model file (`yolov5s.pt`) 86 | - Database schema (`schema.sql`) 87 | 88 | ## Configuration 89 | 90 | ### Model Settings 91 | ```python 92 | model = YOLOv5("yolov5s.pt") # Update path as needed 93 | db = SceneDatabase("scenes.db") 94 | ``` 95 | 96 | ### File Type Restrictions 97 | ```python 98 | ALLOWED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/jpg"] 99 | ALLOWED_VIDEO_TYPES = ["video/mp4", "video/avi", "video/mpeg"] 100 | ``` 101 | 102 | ## Usage Examples 103 | 104 | ### Starting the Server 105 | ```bash 106 | uvicorn api.database_api:app --reload --port 8000 107 | ``` 108 | 109 | ### API Requests 110 | 111 | #### Image Detection 112 | ```bash 113 | curl -X POST "http://localhost:8000/detect/image" \ 114 | -H "accept: application/json" \ 115 | -H "Content-Type: multipart/form-data" \ 116 | -F "file=@test.jpg" 117 | ``` 118 | 119 | #### Video Detection 120 | ```bash 121 | curl -X POST "http://localhost:8000/detect/video" \ 122 | -H "accept: application/json" \ 123 | -H "Content-Type: multipart/form-data" \ 124 | -F "file=@test.mp4" 125 | ``` 126 | 127 | ## Data Structures 128 | 129 | ### Detection Format 130 | ```python 131 | Detection = { 132 | "class": str, # Object class name 133 | "confidence": float, # Detection confidence (0-1) 134 | "x_min": float, # Bounding box coordinates 135 | "y_min": float, 136 | "x_max": float, 137 | "y_max": float 138 | } 139 | ``` 140 | 141 | ### Scene Metadata 142 | ```python 143 | SceneMetadata = { 144 | "timestamp": str, # ISO format datetime 145 | "camera_id": str, # Source identifier 146 | "media_path": str, # File/stream path 147 | "resolution": str # Optional resolution 148 | } 149 | ``` 150 | 151 | ## Error Handling 152 | 153 | ### HTTP Status Codes 154 | - 400: Bad Request (invalid file type, invalid parameters) 155 | - 500: Internal Server Error (file saving, processing errors) 156 | 157 | ### Validation 158 | ```python 159 | if file.content_type not in ALLOWED_IMAGE_TYPES: 160 | raise HTTPException(status_code=400, detail="Invalid file type") 161 | ``` 162 | 163 | ## Performance Considerations 164 | 165 | ### File Handling 166 | - Asynchronous file uploads 167 | - Stream processing for videos 168 | - Temporary file cleanup 169 | 170 | ### Database Operations 171 | - Batch detection storage 172 | - Thread-safe database connections 173 | - Efficient query patterns 174 | 175 | ## Development 176 | Author: Anantha Krishna B 177 | Version: 1.0.0 -------------------------------------------------------------------------------- /docs/database/README.md: -------------------------------------------------------------------------------- 1 | # Database System Documentation 2 | A comprehensive guide to the object detection database system implementation. 3 | 4 | ## Table of Contents 5 | 1. [Overview](#overview) 6 | 2. [Database Schema](#database-schema) 7 | 3. [Core Components](#core-components) 8 | 4. [Implementation Details](#implementation-details) 9 | 5. [Usage Examples](#usage-examples) 10 | 6. [Performance Considerations](#performance-considerations) 11 | 12 | ## Overview 13 | The database system provides a thread-safe SQLite implementation for storing and managing: 14 | - Scene metadata and images/videos 15 | - Object detections with bounding boxes 16 | - Manual and automated annotations 17 | - Dataset assignments 18 | - Scene descriptions 19 | 20 | ## Database Schema 21 | 22 | ### Tables Structure 23 | 24 | #### 1. Scenes Table 25 | ```sql 26 | CREATE TABLE scenes ( 27 | scene_id INTEGER PRIMARY KEY AUTOINCREMENT, 28 | timestamp DATETIME NOT NULL, 29 | latitude REAL, 30 | longitude REAL, 31 | resolution TEXT, 32 | camera_id TEXT NOT NULL, 33 | media_path TEXT NOT NULL, 34 | processed BOOLEAN DEFAULT 0 35 | ); 36 | ``` 37 | 38 | #### 2. Detections Table 39 | ```sql 40 | CREATE TABLE detections ( 41 | detection_id INTEGER PRIMARY KEY AUTOINCREMENT, 42 | scene_id INTEGER NOT NULL, 43 | class_label TEXT NOT NULL, 44 | confidence REAL NOT NULL, 45 | x_min REAL NOT NULL, 46 | y_min REAL NOT NULL, 47 | x_max REAL NOT NULL, 48 | y_max REAL NOT NULL, 49 | FOREIGN KEY(scene_id) REFERENCES scenes(scene_id) ON DELETE CASCADE 50 | ); 51 | ``` 52 | 53 | ### Performance Optimizations 54 | - Indexed timestamps for scene queries 55 | - Indexed class labels for detection filtering 56 | - Indexed annotation types for quick filtering 57 | 58 | ## Core Components 59 | 60 | ### SceneDatabase Class 61 | Thread-safe database manager implementing: 62 | - Connection pooling 63 | - Transaction management 64 | - CRUD operations 65 | - Batch processing 66 | 67 | #### Key Methods 68 | 1. Scene Management: 69 | ```python 70 | def add_scene(metadata: Dict) -> int 71 | def get_scene(scene_id: int) -> Optional[Dict] 72 | def update_scene(scene_id: int, updates: Dict) 73 | ``` 74 | 75 | 2. Detection Operations: 76 | ```python 77 | def add_detections(scene_id: int, detections: List[Dict]) 78 | def get_detections_by_class(class_label: str, confidence_threshold: float = 0.5) 79 | ``` 80 | 81 | ## Implementation Details 82 | 83 | ### Thread Safety 84 | ```python 85 | class SceneDatabase: 86 | _local = threading.local() 87 | 88 | def _get_conn(self): 89 | if not hasattr(self._local, "conn"): 90 | self._local.conn = sqlite3.connect(self.db_path) 91 | return self._local.conn 92 | ``` 93 | 94 | ### Data Types 95 | 96 | #### Scene Metadata 97 | ```python 98 | metadata = { 99 | "timestamp": str, # ISO format 100 | "latitude": float, # Optional 101 | "longitude": float, # Optional 102 | "resolution": str, # "WxH" 103 | "camera_id": str, 104 | "media_path": str 105 | } 106 | ``` 107 | 108 | #### Detection Format 109 | ```python 110 | detection = { 111 | "class": str, # Object class 112 | "confidence": float, # 0.0-1.0 113 | "x_min": float, # Normalized coordinates 114 | "y_min": float, 115 | "x_max": float, 116 | "y_max": float 117 | } 118 | ``` 119 | 120 | ## Usage Examples 121 | 122 | ### 1. Adding a New Scene 123 | ```python 124 | db = SceneDatabase() 125 | scene_id = db.add_scene({ 126 | "timestamp": datetime.now().isoformat(), 127 | "camera_id": "cam_01", 128 | "media_path": "/path/to/image.jpg", 129 | "resolution": "1920x1080" 130 | }) 131 | ``` 132 | 133 | ### 2. Recording Detections 134 | ```python 135 | detections = [{ 136 | "class": "person", 137 | "confidence": 0.95, 138 | "x_min": 0.1, 139 | "y_min": 0.2, 140 | "x_max": 0.3, 141 | "y_max": 0.4 142 | }] 143 | db.add_detections(scene_id, detections) 144 | ``` 145 | 146 | ## Performance Considerations 147 | 148 | ### 1. Connection Management 149 | - Thread-local connections prevent concurrency issues 150 | - Connections are reused within threads 151 | - Automatic cleanup on thread termination 152 | 153 | ### 2. Query Optimization 154 | ```sql 155 | CREATE INDEX idx_scenes_timestamp ON scenes(timestamp); 156 | CREATE INDEX idx_detections_class ON detections(class_label); 157 | CREATE INDEX idx_annotations_type ON annotations(label_type); 158 | ``` 159 | 160 | ### 3. Batch Operations 161 | - Uses `executemany` for multiple inserts 162 | - Transaction management for atomic operations 163 | - Optimized for bulk data processing 164 | 165 | ## Error Handling 166 | 167 | ### 1. Database Errors 168 | ```python 169 | try: 170 | cursor.execute(sql, values) 171 | except sqlite3.Error as e: 172 | logger.error(f"Database error: {str(e)}") 173 | raise DatabaseError(str(e)) 174 | ``` 175 | 176 | ### 2. Data Validation 177 | - Type checking before insertion 178 | - Constraint enforcement 179 | - Coordinate validation 180 | 181 | ## Dependencies 182 | - sqlite3 183 | - threading 184 | - datetime 185 | - typing (List, Dict, Optional) -------------------------------------------------------------------------------- /docs/engine/classification/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monalsingh/VisionAVI/d3ee5525bf828a4da6be12bc17488e6c6f846008/docs/engine/classification/README.md -------------------------------------------------------------------------------- /docs/engine/detection/README.md: -------------------------------------------------------------------------------- 1 | # YOLOv5 Object Detection Wrapper Documentation (detection_model_1.py) 2 | 3 | ## Overview 4 | The `YOLOv5` class provides a high-level wrapper around the Ultralytics YOLOv5 implementation for object detection tasks. This documentation details the internal workings and implementation specifics of the wrapper. 5 | 6 | ## Table of Contents 7 | - [Dependencies](#dependencies) 8 | - [Class Architecture](#class-architecture) 9 | - [Core Methods](#core-methods) 10 | - [Data Structures](#data-structures) 11 | - [Usage Examples](#usage-examples) 12 | - [Testing](#testing) 13 | 14 | ## Dependencies 15 | ```python 16 | from ultralytics import YOLO # Base YOLOv5 implementation 17 | import cv2 # OpenCV for image/video processing 18 | import numpy as np # Numerical computations 19 | from typing import List, Dict, Union, Tuple # Type hints 20 | ``` 21 | 22 | ## Class Architecture 23 | 24 | ### Class: YOLOv5 25 | A wrapper class that encapsulates YOLOv5 functionality with standardized inputs/outputs. 26 | 27 | #### Instance Variables 28 | - `self.model`: YOLO 29 | - Holds the loaded YOLOv5 model instance 30 | - Initialized with model weights file path 31 | 32 | ## Core Methods 33 | 34 | ### 1. Constructor 35 | ```python 36 | def __init__(self, model_path: str): 37 | self.model = YOLO(model_path) 38 | ``` 39 | - **Purpose**: Initializes YOLOv5 model instance 40 | - **Parameters**: 41 | - `model_path`: Path to YOLOv5 weights file 42 | - **Returns**: None 43 | 44 | ### 2. Process Detections 45 | ```python 46 | def process_detections(self, results) -> List[Dict]: 47 | ``` 48 | - **Purpose**: Standardizes YOLOv5 detection results 49 | - **Input Processing**: 50 | - Iterates through detection results 51 | - Extracts bounding boxes, confidence scores, and class information 52 | - **Output Format**: 53 | ```python 54 | { 55 | "class": str, # Class name 56 | "confidence": float, # Detection confidence (0-1) 57 | "x_min": float, # Left boundary 58 | "y_min": float, # Top boundary 59 | "x_max": float, # Right boundary 60 | "y_max": float, # Bottom boundary 61 | "class_id": int # Numeric class identifier 62 | } 63 | ``` 64 | 65 | ### 3. Image Detection 66 | ```python 67 | def detect_image(self, image_path: str) -> Tuple[List[Dict], Dict]: 68 | ``` 69 | - **Purpose**: Performs detection on single images 70 | - **Process Flow**: 71 | 1. Loads image from path 72 | 2. Runs YOLOv5 prediction 73 | 3. Processes detections 74 | 4. Extracts metadata 75 | - **Returns**: 76 | - Detections list 77 | - Metadata dictionary with resolution 78 | 79 | ### 4. Frame Detection 80 | ```python 81 | def detect_frame(self, frame: np.ndarray) -> List[Dict]: 82 | ``` 83 | - **Purpose**: Real-time detection on video frames 84 | - **Input**: NumPy array representing image frame 85 | - **Process**: 86 | 1. Performs streaming prediction 87 | 2. Returns processed detections 88 | - **Performance**: Optimized for real-time processing 89 | 90 | ### 5. Video Detection 91 | ```python 92 | def detect_video(self, video_path: str) -> List[Dict]: 93 | ``` 94 | - **Purpose**: Batch detection on video files 95 | - **Process**: 96 | - Loads video file 97 | - Performs detection on all frames 98 | - Aggregates results 99 | 100 | ### 6. Utility Methods 101 | 102 | #### Get Bounding Box Coordinates 103 | ```python 104 | def get_bbox_coordinates(self, detection: Dict) -> List[float]: 105 | ``` 106 | - **Purpose**: Extracts coordinates for visualization 107 | - **Returns**: [x_min, y_min, x_max, y_max] 108 | 109 | #### Save Results 110 | ```python 111 | def save_results(self, results, output_path: str): 112 | ``` 113 | - **Purpose**: Persists detection results 114 | - **Format**: Native YOLOv5 format 115 | 116 | ## Data Structures 117 | 118 | ### Detection Dictionary 119 | ```python 120 | { 121 | "class": str, # Object class name 122 | "confidence": float, # Detection confidence 123 | "x_min": float, # Bounding box coordinates 124 | "y_min": float, 125 | "x_max": float, 126 | "y_max": float, 127 | "class_id": int # Class identifier 128 | } 129 | ``` 130 | 131 | ### Metadata Dictionary 132 | ```python 133 | { 134 | "resolution": str # Format: "heightxwidth" 135 | } 136 | ``` 137 | 138 | ## Usage Examples 139 | 140 | ### Basic Image Detection 141 | ```python 142 | detector = YOLOv5("yolov5s.pt") 143 | detections, metadata = detector.detect_image("image.jpg") 144 | ``` 145 | 146 | ### Real-time Video Processing 147 | ```python 148 | detector = YOLOv5("yolov5s.pt") 149 | frame = cv2.imread("frame.jpg") 150 | detections = detector.detect_frame(frame) 151 | ``` 152 | 153 | ## Testing 154 | 155 | ### Test Cases 156 | 1. Model Information Retrieval 157 | 2. Image Detection Pipeline 158 | 3. Video Processing 159 | 4. Webcam Integration 160 | 5. Result Persistence 161 | 162 | ### Running Tests 163 | ```python 164 | if __name__ == "__main__": 165 | model = YOLOv5("yolov5s.pt") 166 | # Run specific test cases 167 | ``` 168 | 169 | ## Performance Considerations 170 | 171 | ### Memory Usage 172 | - Loads model weights once at initialization 173 | - Streams video frames when possible 174 | - Processes detections in batches 175 | 176 | ### Speed Optimizations 177 | - Uses CUDA if available 178 | - Streaming mode for real-time processing 179 | - Batch processing for videos 180 | 181 | ## Error Handling 182 | - Validates input paths 183 | - Checks frame integrity 184 | - Ensures model compatibility 185 | 186 | ## Limitations 187 | - Requires compatible YOLOv5 weights 188 | - Memory usage scales with video length 189 | - Real-time performance depends on hardware -------------------------------------------------------------------------------- /docs/scenario/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monalsingh/VisionAVI/d3ee5525bf828a4da6be12bc17488e6c6f846008/docs/scenario/README.md -------------------------------------------------------------------------------- /docs/tests/README.md: -------------------------------------------------------------------------------- 1 | # Documentation for `test_apis.py` 2 | 3 | This document provides a detailed explanation of the `test_apis.py` module, which is designed to test various endpoints of a FastAPI application. The module uses `pytest` as the testing framework and `TestClient` from FastAPI for making HTTP requests to the API. 4 | 5 | ## Overview 6 | 7 | The `test_apis.py` module includes tests for the following API endpoints: 8 | 9 | 1. **Image Detection Endpoint** (`/detect/image`) 10 | 2. **Video Detection Endpoint** (`/detect/video`) 11 | 3. **Webcam Detection Endpoint** (`/detect/webcam`) 12 | 4. **RTSP Stream Detection Endpoint** (`/detect/rtsp`) 13 | 5. **Invalid File Uploads** 14 | 6. **Missing File Uploads** 15 | 7. **Invalid RTSP URL Handling** 16 | 17 | The tests ensure that the API behaves as expected under various scenarios, including valid and invalid inputs. 18 | 19 | --- 20 | 21 | ## Prerequisites 22 | 23 | Before running the tests, ensure the following: 24 | 25 | 1. **Python Environment**: Python 3.8 or higher is installed. 26 | 2. **Dependencies**: Install the required dependencies using `pip install -r requirements.txt`. The module requires: 27 | - `pytest` 28 | - `fastapi` 29 | - `opencv-python` 30 | - `numpy` 31 | 3. **Test Data Directory**: The script automatically creates a `tests/data` directory for storing test files (images and videos). 32 | 33 | --- 34 | 35 | ## Test Setup 36 | 37 | ### Test Data Creation 38 | 39 | The module includes helper functions to create dummy test files: 40 | 41 | - **`create_test_image()`**: Generates a 100x100 black image with a white rectangle in the center and saves it as `test.jpg`. 42 | - **`create_test_video()`**: Generates a 1-second video (30 frames) with a similar black background and white rectangle, saved as `test.mp4`. 43 | 44 | These files are stored in the `tests/data` directory. 45 | 46 | ### Pytest Fixture 47 | 48 | A `pytest` fixture named `test_files` is used to create the test files before the tests run and clean them up afterward. 49 | 50 | --- 51 | 52 | ## Test Cases 53 | 54 | ### 1. Image Detection Endpoint 55 | 56 | - **Function**: `test_detect_image` 57 | - **Description**: Tests the `/detect/image` endpoint by uploading a valid image file. 58 | - **Assertions**: 59 | - Status code is `200`. 60 | - Response contains `scene_id` and `detections` fields. 61 | - `detections` is a list. 62 | 63 | --- 64 | 65 | ### 2. Video Detection Endpoint 66 | 67 | - **Function**: `test_detect_video` 68 | - **Description**: Tests the `/detect/video` endpoint by uploading a valid video file. 69 | - **Assertions**: 70 | - Status code is `200`. 71 | - Response contains `scene_id` and `total_detections` fields. 72 | - `total_detections` is an integer. 73 | 74 | --- 75 | 76 | ### 3. Webcam Detection Endpoint 77 | 78 | #### a. Invalid Duration 79 | - **Function**: `test_webcam_detection_invalid_duration` 80 | - **Description**: Tests the `/detect/webcam` endpoint with an invalid duration (`-1`). 81 | - **Assertions**: 82 | - Status code is `422`. 83 | - Error message indicates the duration must be greater than 0. 84 | 85 | #### b. Valid Duration 86 | - **Function**: `test_webcam_detection` 87 | - **Description**: Tests the `/detect/webcam` endpoint with a valid duration (`1` second). 88 | - **Assertions**: 89 | - Status code is `200`. 90 | - Response contains `scene_id` and a `message` indicating completion. 91 | 92 | --- 93 | 94 | ### 4. RTSP Stream Detection Endpoint 95 | 96 | #### a. Valid RTSP URL 97 | - **Function**: `test_rtsp_detection` 98 | - **Description**: Tests the `/detect/rtsp` endpoint with a valid RTSP URL. 99 | - **Assertions**: 100 | - Status code is `200`. 101 | - Response contains `scene_id` and a `message` indicating completion. 102 | 103 | #### b. Invalid RTSP URL 104 | - **Function**: `test_invalid_rtsp_url` 105 | - **Description**: Tests the `/detect/rtsp` endpoint with an invalid RTSP URL. 106 | - **Assertions**: 107 | - Status code is `422`. 108 | - Error message indicates an invalid URL format. 109 | 110 | --- 111 | 112 | ### 5. Invalid File Uploads 113 | 114 | - **Function**: `test_invalid_image_upload` 115 | - **Description**: Tests the `/detect/image` endpoint by uploading an invalid file (e.g., a text file). 116 | - **Assertions**: 117 | - Status code is `400`. 118 | - Error message indicates an invalid file type. 119 | 120 | --- 121 | 122 | ### 6. Missing File Uploads 123 | 124 | - **Function**: `test_missing_file` 125 | - **Description**: Tests the `/detect/image` endpoint without uploading any file. 126 | - **Assertions**: 127 | - Status code is `422`. 128 | - Error message indicates a validation error. 129 | 130 | --- 131 | 132 | ## Running the Tests 133 | 134 | To execute the tests, follow these steps: 135 | 136 | 1. Open a terminal and navigate to the directory containing `test_apis.py`. 137 | 2. Run the tests using the following command: 138 | 139 | ```bash 140 | pytest -v test_apis.py -------------------------------------------------------------------------------- /engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monalsingh/VisionAVI/d3ee5525bf828a4da6be12bc17488e6c6f846008/engine/__init__.py -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Python module: main.py 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML 2 | opencv-python -------------------------------------------------------------------------------- /scenarios/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /scenarios/scenario_1.py: -------------------------------------------------------------------------------- 1 | # Python module: scenario_1.py 2 | -------------------------------------------------------------------------------- /scenarios/scenario_2.py: -------------------------------------------------------------------------------- 1 | # Python module: scenario_2.py 2 | -------------------------------------------------------------------------------- /scenarios/scenario_3.py: -------------------------------------------------------------------------------- 1 | # Python module: scenario_3.py 2 | -------------------------------------------------------------------------------- /test_main.py: -------------------------------------------------------------------------------- 1 | import os 2 | from core.util.utils import dummy_print 3 | from config import ConfigManager 4 | 5 | def main(): 6 | # Define the path to the central_config.yaml file 7 | config_path = "config.yaml" 8 | 9 | # Load the configuration using ConfigManager 10 | config_manager = ConfigManager(config_path) 11 | app_config = config_manager.get_all() 12 | 13 | # Access specific configurations 14 | # Using get() method with key and value 15 | project_name = config_manager.get("app")["name"] 16 | project_version = config_manager.get("app")["version"] 17 | batch_name = config_manager.get("app")["batch"] 18 | 19 | if app_config['logging']['enable']: 20 | if app_config['logging']['level'] == 'level-1': 21 | dummy_print(project_name, project_version, batch_name) 22 | 23 | # Using get() method with only key 24 | dummy_config = config_manager.get("dummy") 25 | 26 | if dummy_config['task'] == 'add': 27 | print(f"Addition answer is : {dummy_config['variable_1'] + dummy_config['variable_2']}") 28 | else: 29 | print("Please specify task in config or implement task mentioned in config.") 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Python module: __init__.py 2 | -------------------------------------------------------------------------------- /tests/test_apis.py: -------------------------------------------------------------------------------- 1 | # Python module: test_apis.py 2 | import pytest 3 | from fastapi.testclient import TestClient 4 | 5 | 6 | import sys 7 | from pathlib import Path 8 | 9 | # Add project root to path 10 | project_root = Path(__file__).parent.parent 11 | sys.path.append(str(project_root)) 12 | 13 | from api.database_api import app 14 | import os 15 | import cv2 16 | import numpy as np 17 | 18 | client = TestClient(app) 19 | 20 | # Test data setup 21 | TEST_DATA_DIR = Path("tests/data") 22 | TEST_DATA_DIR.mkdir(parents=True, exist_ok=True) 23 | 24 | 25 | def create_test_image(): 26 | """Create a dummy test image for testing.""" 27 | img_path = TEST_DATA_DIR / "test.jpg" 28 | if not img_path.exists(): 29 | img = np.zeros((100, 100, 3), dtype=np.uint8) 30 | cv2.rectangle(img, (30, 30), (70, 70), (255, 255, 255), -1) 31 | cv2.imwrite(str(img_path), img) 32 | return img_path 33 | 34 | 35 | def create_test_video(): 36 | """Create a dummy test video for testing.""" 37 | video_path = TEST_DATA_DIR / "test.mp4" 38 | if not video_path.exists(): 39 | out = cv2.VideoWriter( 40 | str(video_path), cv2.VideoWriter_fourcc(*"mp4v"), 30, (100, 100) 41 | ) 42 | for _ in range(30): # 1 second video 43 | frame = np.zeros((100, 100, 3), dtype=np.uint8) 44 | cv2.rectangle(frame, (30, 30), (70, 70), (255, 255, 255), -1) 45 | out.write(frame) 46 | out.release() 47 | return video_path 48 | 49 | 50 | @pytest.fixture(scope="module") 51 | def test_files(): 52 | """Create test files and clean up after tests.""" 53 | img_path = create_test_image() 54 | video_path = create_test_video() 55 | yield {"image": img_path, "video": video_path} 56 | 57 | 58 | def test_detect_image(test_files): 59 | """Test image detection endpoint.""" 60 | img_path = test_files["image"] 61 | with open(img_path, "rb") as f: 62 | files = {"file": ("test.jpg", f, "image/jpeg")} 63 | response = client.post("/detect/image", files=files) 64 | 65 | assert response.status_code == 200 66 | json_response = response.json() 67 | assert "scene_id" in json_response 68 | assert "detections" in json_response 69 | assert isinstance(json_response["detections"], list) 70 | 71 | 72 | def test_detect_video(test_files): 73 | """Test video detection endpoint.""" 74 | video_path = test_files["video"] 75 | with open(video_path, "rb") as f: 76 | files = {"file": ("test.mp4", f, "video/mp4")} 77 | response = client.post("/detect/video", files=files) 78 | 79 | assert response.status_code == 200 80 | json_response = response.json() 81 | assert "scene_id" in json_response 82 | assert "total_detections" in json_response 83 | assert isinstance(json_response["total_detections"], int) 84 | 85 | 86 | def test_webcam_detection_invalid_duration(): 87 | """Test webcam detection with invalid duration.""" 88 | response = client.post("/detect/webcam?duration=-1") 89 | assert response.status_code == 422 # FastAPI validation error 90 | assert "greater than" in response.json()["detail"][0]["msg"] 91 | 92 | 93 | def test_webcam_detection(): 94 | """Test webcam detection endpoint.""" 95 | response = client.post("/detect/webcam?duration=1") 96 | assert response.status_code == 200 97 | json_response = response.json() 98 | assert "scene_id" in json_response 99 | assert "message" in json_response 100 | assert "completed" in json_response["message"] 101 | 102 | 103 | def test_rtsp_detection(): 104 | """Test RTSP stream detection endpoint.""" 105 | test_url = "rtsp://example.com/test" 106 | response = client.post(f"/detect/rtsp?rtsp_url={test_url}&duration=1") 107 | assert response.status_code == 200 108 | json_response = response.json() 109 | assert "scene_id" in json_response 110 | assert "message" in json_response 111 | assert "completed" in json_response["message"] 112 | 113 | 114 | def test_invalid_image_upload(): 115 | """Test uploading invalid image file.""" 116 | files = {"file": ("test.txt", b"invalid content", "text/plain")} 117 | response = client.post("/detect/image", files=files) 118 | assert response.status_code == 400 # Bad request 119 | assert "Invalid file type" in response.json()["detail"] 120 | 121 | 122 | def test_missing_file(): 123 | """Test API response when no file is uploaded.""" 124 | response = client.post("/detect/image") 125 | assert response.status_code == 422 # Validation error 126 | 127 | 128 | def test_invalid_rtsp_url(): 129 | """Test RTSP endpoint with invalid URL.""" 130 | response = client.post("/detect/rtsp?rtsp_url=invalid_url&duration=1") 131 | assert response.status_code == 422 # Invalid URL format 132 | assert "regex" in response.json()["detail"][0]["msg"] 133 | 134 | 135 | if __name__ == "__main__": 136 | pytest.main(["-v", __file__]) 137 | -------------------------------------------------------------------------------- /tests/test_classification.py: -------------------------------------------------------------------------------- 1 | # Python module: test_classification.py 2 | -------------------------------------------------------------------------------- /tests/test_detection.py: -------------------------------------------------------------------------------- 1 | # Python module: test_detection.py 2 | -------------------------------------------------------------------------------- /tests/test_scenarios.py: -------------------------------------------------------------------------------- 1 | # Python module: test_scenarios.py 2 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Python module: test_utils.py 2 | --------------------------------------------------------------------------------