real-time-object-detection 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
main.py ADDED
@@ -0,0 +1,178 @@
1
+ import os
2
+ os.environ["YOLO_VERBOSE"] = "False"
3
+ import sys
4
+ import argparse
5
+ from src import __version__
6
+ from src.pipeline import run_pipeline
7
+ from src.config_loader import load_config
8
+ from src.model import load_model
9
+ from src.camera import open_camera, open_video
10
+ from src.logger import setup_logger, get_logger
11
+ from src.validation import ValidationError
12
+ from src.device_utils import get_device_info, validate_and_fallback
13
+
14
+ class CustomHelpFormatter(argparse.RawDescriptionHelpFormatter):
15
+ def __init__(self, prog):
16
+ super().__init__(prog, max_help_position=42, width=110)
17
+
18
+ def parse_args() -> argparse.Namespace:
19
+ is_uv = any(k.startswith("UV") for k in os.environ)
20
+ is_rtod = sys.argv[0].endswith("rtod") or sys.argv[0].endswith("rtod.exe")
21
+ if is_uv:
22
+ cmd = "uv run rtod" if is_rtod else "uv run main.py"
23
+ else:
24
+ cmd = "rtod" if is_rtod else "python main.py"
25
+ parser = argparse.ArgumentParser(
26
+ description="Real-Time Object Detection using YOLOv8",
27
+ formatter_class=CustomHelpFormatter,
28
+ epilog=f"""
29
+ Examples:
30
+ {cmd}
31
+ {cmd} --video video.mp4
32
+ {cmd} --confidence 0.7
33
+ {cmd} --headless
34
+ {cmd} --model models/yolov8s.pt
35
+ """
36
+ )
37
+
38
+ parser.add_argument("--config", type=str, default="config.yaml",
39
+ help="Config file path (default: config.yaml)")
40
+ parser.add_argument("--mode", type=str, choices=["live", "video", "headless", "benchmark"],
41
+ help="Execution mode")
42
+ parser.add_argument("--confidence", type=float, metavar="[0-1]",
43
+ help="Detection confidence threshold")
44
+ parser.add_argument("--camera", type=int, metavar="INDEX",
45
+ help="Camera device index")
46
+ parser.add_argument("--video", type=str, metavar="PATH",
47
+ help="Video file path (sets mode to video)")
48
+ parser.add_argument("--output", type=str, metavar="DIR",
49
+ help="Output directory")
50
+ parser.add_argument("--model", type=str, metavar="PATH",
51
+ help="Model file path")
52
+ parser.add_argument("--device", type=str, choices=["cpu", "cuda", "mps"],
53
+ help="Compute device")
54
+ parser.add_argument("--max-fps", type=float, metavar="N",
55
+ help="Maximum FPS limit")
56
+ parser.add_argument("--headless", action="store_true",
57
+ help="No display window (headless mode)")
58
+ parser.add_argument("--benchmark", action="store_true",
59
+ help="Benchmark mode (no display, print metrics)")
60
+ parser.add_argument("--save-video", action="store_true",
61
+ help="Save annotated video output to file")
62
+ parser.add_argument("-v", "--version", action="version", version=__version__)
63
+
64
+ return parser.parse_args()
65
+
66
+ def main() -> None:
67
+ args = parse_args()
68
+
69
+ logger = setup_logger()
70
+
71
+ dev_info = get_device_info()
72
+ if dev_info["cuda_available"]:
73
+ logger.info(f"GPU available: {dev_info['cuda_device_name']}")
74
+ else:
75
+ logger.info("GPU not available, using CPU")
76
+
77
+ try:
78
+ logger.info("Loading configuration...")
79
+ cfg = load_config(args.config)
80
+ logger.info("Configuration loaded successfully")
81
+ except FileNotFoundError as e:
82
+ logger.error(f"Config not found: {e}")
83
+ sys.exit(1)
84
+ except ValidationError as e:
85
+ logger.error(f"Config invalid: {e}")
86
+ sys.exit(1)
87
+
88
+ runtime = cfg.get("runtime", {})
89
+ model_cfg = cfg.get("model", {})
90
+
91
+ if args.mode:
92
+ runtime["mode"] = args.mode
93
+ if args.video:
94
+ runtime["mode"] = "video"
95
+ runtime["video_path"] = args.video
96
+ if args.confidence is not None:
97
+ runtime["confidence"] = args.confidence
98
+ if args.camera is not None:
99
+ runtime["camera_index"] = args.camera
100
+ if args.output:
101
+ runtime["output_dir"] = args.output
102
+ if args.max_fps is not None:
103
+ runtime["max_fps"] = args.max_fps
104
+ if args.headless:
105
+ runtime["mode"] = "headless"
106
+ if args.benchmark:
107
+ runtime["mode"] = "benchmark"
108
+ if args.save_video:
109
+ runtime["save_video"] = True
110
+ if args.model:
111
+ model_cfg["path"] = args.model
112
+ if args.device:
113
+ model_cfg["device"] = args.device
114
+
115
+ mode = runtime.get("mode", "live")
116
+ conf = runtime.get("confidence", 0.5)
117
+ max_fps = runtime.get("max_fps")
118
+ window_name = runtime.get("window_name", "Real-Time Object Detector")
119
+ output_dir = runtime.get("output_dir") or "runs/latest"
120
+ save_video = runtime.get("save_video", False)
121
+
122
+ logger.info(f"Mode: {mode}, Confidence: {conf}, FPS: {max_fps}")
123
+
124
+ allowed_classes = runtime.get("allowed_classes")
125
+ if isinstance(allowed_classes, (list, tuple, set)):
126
+ allowed_classes = set(allowed_classes)
127
+ else:
128
+ allowed_classes = None
129
+
130
+ try:
131
+ logger.info(f"Loading model from {model_cfg.get('path')}...")
132
+ model = load_model(cfg)
133
+ logger.info(f"Model loaded on device: {model_cfg.get('device')}")
134
+ except (ValidationError, RuntimeError) as e:
135
+ logger.error(f"Model load failed: {e}")
136
+ sys.exit(1)
137
+
138
+ is_video = mode == "video"
139
+ headless = mode in ("headless", "benchmark")
140
+ benchmark = mode == "benchmark"
141
+
142
+ try:
143
+ if mode == "video":
144
+ logger.info(f"Opening video: {runtime.get('video_path')}")
145
+ cap = open_video(runtime.get("video_path"))
146
+ else:
147
+ logger.info(f"Opening camera at index {runtime.get('camera_index', 0)}")
148
+ cap = open_camera(runtime.get("camera_index", 0))
149
+ logger.info("Camera/video opened successfully")
150
+
151
+ logger.info("Starting detection pipeline...")
152
+ run_pipeline(
153
+ cap=cap,
154
+ model=model,
155
+ logger=logger,
156
+ conf=conf,
157
+ allowed_classes=allowed_classes,
158
+ window_name=window_name,
159
+ output_dir=output_dir,
160
+ max_fps=max_fps,
161
+ is_video=is_video,
162
+ headless=headless,
163
+ benchmark=benchmark,
164
+ save_video=save_video,
165
+ )
166
+ logger.info("Pipeline completed successfully")
167
+ except KeyboardInterrupt:
168
+ logger.info("Keyboard interrupt received (Ctrl+C). Exiting pipeline cleanly.")
169
+ sys.exit(0)
170
+ except (ValidationError, RuntimeError) as e:
171
+ logger.error(f"Failed to open camera/video: {e}")
172
+ sys.exit(1)
173
+ except Exception as e:
174
+ logger.error(f"Pipeline failed: {e}")
175
+ sys.exit(1)
176
+
177
+ if __name__ == "__main__":
178
+ main()
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.4
2
+ Name: real-time-object-detection
3
+ Version: 1.1.0
4
+ Summary: A real-time computer vision project that uses a webcam and the YOLOv8 model to detect and label objects as they appear on screen, drawing bounding boxes around them using OpenCV.
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: numpy>=2.4.1
8
+ Requires-Dist: opencv-python>=4.13.0.90
9
+ Requires-Dist: pytest>=9.0.2
10
+ Requires-Dist: pyyaml>=6.0.3
11
+ Requires-Dist: scipy>=1.17.0
12
+ Requires-Dist: ultralytics>=8.4.8
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=7.0; extra == "dev"
15
+
16
+ # Real-Time Object Detection - Computer Vision & YOLOv8 Inference Pipeline
17
+
18
+ An intelligent computer vision application built with **Python**, **OpenCV**, **Ultralytics YOLOv8**, **SciPy**, and **PyYAML**. It enables real-time object detection and multi-object centroid tracking across live webcam feeds and video files with full analytics reporting (CSV/JSON), optional annotated video recording, class filtering, and high-performance execution modes (`live`, `video`, `headless`, `benchmark`).
19
+
20
+ ---
21
+
22
+ ## Project Overview & Application Architecture
23
+
24
+ The **Real-Time Object Detection** system leverages an end-to-end vision pipeline to process video frames, identify multi-class targets using neural networks, track object movement across frames, and export structured operational analytics.
25
+
26
+ The core application architecture follows a modular vision & tracking pipeline:
27
+
28
+ 1. **Configuration & Validation (`src/config_loader.py` & `src/validation.py`):** Loads and validates YAML settings (`config.yaml`) and CLI parameters, validating confidence thresholds, camera indices, frame rates, image sizes (`imgsz`), model paths, and device parameters (`cpu`, `cuda`, `mps`).
29
+ 2. **Device Detection & Fallback (`src/device_utils.py`):** Checks GPU availability (CUDA / Apple Silicon MPS) and automatically selects the optimal hardware compute device with automatic CPU fallback.
30
+ 3. **Model Loader & Downloader (`src/model.py`):** Initializes the Ultralytics YOLOv8 architecture (e.g. `yolov8n.pt`, `yolov8s.pt`), automatically fetching standard pretrained weights if missing locally.
31
+ 4. **Video Stream Handling (`src/camera.py`):** Wraps OpenCV `VideoCapture` streams for low-latency frame reading from webcams or video files with graceful stream termination.
32
+ 5. **Detector Engine & Class Filtering (`src/detector.py`):** Runs YOLOv8 inference per frame, converts bounding box structures, applies confidence thresholds, and filters detections against allowed COCO target classes.
33
+ 6. **Centroid Multi-Object Tracker (`src/tracker.py`):** Class-aware and box-aware `CentroidTracker` using SciPy Euclidean distance matrices to track object movement, assign unique IDs, record lifetime durations, and detect frame entry/exit events.
34
+ 7. **Runtime Analytics & Serialized Exporter (`src/runtime_tracker.py` & `src/exporter.py`):** Aggregates frame rates, object counts, per-class lifetime statistics, and exports structured reports (`summary.json`, `summary.csv`, `run_index.json`).
35
+ 8. **Pipeline Engine & GUI Overlay (`src/pipeline.py` & `main.py`):** Drives the processing loop, renders bounding boxes and performance overlays, records output video (`VideoWriter`), and provides the `main.py` entry point with custom formatted help menus.
36
+
37
+ ---
38
+
39
+ ## Core Modules & Features
40
+
41
+ * **Real-Time Object Detection:** Powered by Ultralytics YOLOv8 for precise multi-class object detection across live video feeds.
42
+ * **Class-Aware Centroid Tracking:** Stable multi-object tracking with persistent object IDs, entrance/exit event triggers, and lifetime duration monitoring.
43
+ * **Dual Input Sources:** Supports live webcam hardware feeds as well as video file inputs (`--video path/to/video.mp4`).
44
+ * **Flexible Execution Modes:** Four distinct runtime operating modes:
45
+ * `live`: Interactive GUI window displaying real-time webcam feed with object bounding boxes.
46
+ * `video`: Processes input video files with on-screen bounding boxes and FPS overlays.
47
+ * `headless`: Background processing without rendering GUI display windows (ideal for headless servers).
48
+ * `benchmark`: Automated performance benchmark mode calculating total frames, elapsed time, and average FPS.
49
+ * **Annotated Video Saving (`--save-video`):** Optionally records fully annotated detection feeds to MP4 video files using OpenCV `VideoWriter`.
50
+ * **Configurable Class Filtering:** Filter detections to specific target COCO classes (e.g., `person`, `car`, `dog`) via CLI or configuration.
51
+ * **Automatic Hardware Acceleration:** Auto-detects CUDA GPUs or Apple Silicon MPS, gracefully falling back to CPU when acceleration is unavailable.
52
+ * **Comprehensive Analytics Export:** Automatically generates per-run structured reports (`summary.csv`, `summary.json`, `run_index.json`) containing object statistics, total frames, and lifetime aggregates.
53
+
54
+ ---
55
+
56
+ ## Data & Pipeline Details
57
+
58
+ The system processes video feeds through structured pipeline modules:
59
+
60
+ | Component / Module | Implementation File | Description & Functionality |
61
+ | :--- | :--- | :--- |
62
+ | **Config Loader** | `src/config_loader.py` | Parses YAML configurations and merges command-line parameter overrides. |
63
+ | **Validator Engine** | `src/validation.py` | Validates model paths, confidence ranges, device selections, FPS limits, and `imgsz`. |
64
+ | **Device Utility** | `src/device_utils.py` | Detects CUDA/MPS hardware capabilities and enforces CPU fallback when needed. |
65
+ | **Model Loader** | `src/model.py` | Loads YOLO neural network instances and auto-downloads standard pretrained weights. |
66
+ | **Camera Interface** | `src/camera.py` | Opens, reads frames, and safely releases webcam hardware or video file streams. |
67
+ | **Detection Engine** | `src/detector.py` | Performs YOLOv8 object inference, confidence thresholding, and target class filtering. |
68
+ | **Centroid Tracker** | `src/tracker.py` | Associates bounding box centroids across frames using Euclidean distance matrix calculations. |
69
+ | **Runtime Tracker** | `src/runtime_tracker.py` | Tracks object entrance/exit events, active frame counts, and per-class aggregate metrics. |
70
+ | **Results Exporter** | `src/exporter.py` | Serializes session analytics to `summary.csv`, `summary.json`, and `run_index.json`. |
71
+ | **Logger Module** | `src/logger.py` | Configures structured logging to console streams and log files. |
72
+ | **Pipeline Runner** | `src/pipeline.py` | Orchestrates the main execution loop, rendering, video recording, and resource teardown. |
73
+ | **Entry Point** | `main.py` | Development entry point featuring custom help formatting, signal handling, and argument parsing. |
74
+
75
+ ---
76
+
77
+ ## Tech Stack & Specifications
78
+
79
+ * **Language:** Python `>=3.11`
80
+ * **Computer Vision:** OpenCV (`opencv-python>=4.13.0`)
81
+ * **Deep Learning & Inference:** Ultralytics YOLOv8 (`ultralytics>=8.4.8`), PyTorch
82
+ * **Mathematical Utilities:** SciPy (`scipy>=1.17.0`), NumPy (`numpy>=2.4.1`)
83
+ * **Configuration & Serialization:** PyYAML (`pyyaml>=6.0.3`)
84
+ * **Packaging & Tools:** Setuptools (`setuptools>=61.0`)
85
+ * **Testing Suite:** Pytest (`pytest>=9.0.2`)
86
+
87
+ ---
88
+
89
+ ## Environment Setup & Local Development
90
+
91
+ Follow these steps to set up the development environment and run the application locally:
92
+
93
+ ### Prerequisites
94
+ * **Python 3.11** or higher installed.
95
+ * **Git** installed.
96
+ * **Webcam** (optional, required for live mode).
97
+
98
+ ### Step 1: Clone the Repository
99
+ ```bash
100
+ git clone https://github.com/AP-Abhishek/Real-Time-Object-Detector.git
101
+ cd Real-Time-Object-Detector
102
+ ```
103
+
104
+ ### Step 2: Create & Activate Virtual Environment
105
+ ```bash
106
+ # On Windows
107
+ python -m venv .venv
108
+ .venv\Scripts\activate
109
+
110
+ # On macOS / Linux
111
+ python3 -m venv .venv
112
+ source .venv/bin/activate
113
+ ```
114
+
115
+ ### Step 3: Install Dependencies
116
+ ```bash
117
+ pip install -r requirements.txt
118
+ ```
119
+
120
+ ### Step 4: Run Development Script (`main.py`)
121
+
122
+ #### 1. Live Webcam Detection (Default)
123
+ ```bash
124
+ python main.py
125
+ ```
126
+
127
+ #### 2. Video File Detection
128
+ ```bash
129
+ python main.py --video path/to/video.mp4
130
+ ```
131
+
132
+ #### 3. Save Annotated Video Output
133
+ ```bash
134
+ python main.py --save-video --output runs/detection_run
135
+ ```
136
+
137
+ #### 4. Headless & Benchmark Modes
138
+ ```bash
139
+ # Run without rendering display window
140
+ python main.py --headless
141
+
142
+ # Performance benchmark mode
143
+ python main.py --benchmark
144
+ ```
145
+
146
+ #### 5. Custom Model & Confidence Threshold
147
+ ```bash
148
+ python main.py --model models/yolov8s.pt --confidence 0.7 --device cpu
149
+ ```
150
+
151
+ ### Command Line Arguments Reference
152
+
153
+ ```text
154
+ usage: python main.py [-h] [--config CONFIG] [--mode {live,video,headless,benchmark}] [--confidence [0-1]]
155
+ [--camera INDEX] [--video PATH] [--output DIR] [--model PATH] [--device {cpu,cuda,mps}]
156
+ [--max-fps N] [--headless] [--benchmark] [--save-video] [-v]
157
+
158
+ Real-Time Object Detection using YOLOv8
159
+
160
+ options:
161
+ -h, --help show this help message and exit
162
+ --config CONFIG Config file path (default: config.yaml)
163
+ --mode {live,video,headless,benchmark} Execution mode
164
+ --confidence [0-1] Detection confidence threshold
165
+ --camera INDEX Camera device index
166
+ --video PATH Video file path (sets mode to video)
167
+ --output DIR Output directory
168
+ --model PATH Model file path
169
+ --device {cpu,cuda,mps} Compute device
170
+ --max-fps N Maximum FPS limit
171
+ --headless No display window (headless mode)
172
+ --benchmark Benchmark mode (no display, print metrics)
173
+ --save-video Save annotated video output to file
174
+ -v, --version show program's version number and exit
175
+ ```
176
+
177
+ ### Step 5: Run Automated Test Suite
178
+ ```bash
179
+ python -m pytest tests/ -v
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Project Structure
185
+
186
+ ```text
187
+ Real-Time-Object-Detector/
188
+ ├── models/ # YOLOv8 model weights (.pt files)
189
+ ├── src/
190
+ │ ├── __init__.py # Package initializer
191
+ │ ├── camera.py # OpenCV VideoCapture wrappers for camera and video
192
+ │ ├── config.py # Config dataclass and schema definitions
193
+ │ ├── config_loader.py # YAML config loader and validator integration
194
+ │ ├── detector.py # YOLOv8 inference engine and class filtering
195
+ │ ├── device_utils.py # CUDA / MPS hardware detection and CPU fallback
196
+ │ ├── exporter.py # Export summary JSON, CSV, and index files
197
+ │ ├── logger.py # Logging setup and stream/file handler configuration
198
+ │ ├── model.py # Ultralytics model loader and auto-downloader
199
+ │ ├── pipeline.py # Main detection pipeline, rendering, and recording
200
+ │ ├── runtime_tracker.py # Per-session object lifecycle and class aggregate tracker
201
+ │ ├── tracker.py # CentroidTracker for multi-object tracking and distance association
202
+ │ └── validation.py # Input validation rules and ValidationError exception
203
+ ├── tests/ # Automated test suite
204
+ │ ├── __init__.py
205
+ │ ├── conftest.py # Pytest fixtures and mock objects
206
+ │ ├── test_config_loader.py # Config loading and schema validation tests
207
+ │ ├── test_detector.py # YOLO inference wrapper tests
208
+ │ ├── test_exporter.py # CSV/JSON summary export tests
209
+ │ ├── test_logger.py # Logging output tests
210
+ │ ├── test_tracker.py # Centroid tracker association tests
211
+ │ └── test_validation.py # Input parameter validation tests
212
+ ├── config.yaml # Default system configuration file
213
+ ├── main.py # Development entry point and argument parser
214
+ ├── pyproject.toml # Project dependencies and metadata manifest
215
+ ├── requirements.txt # Dependency requirements list
216
+ ├── verify_setup.py # Setup verification utility script
217
+ └── README.md # Comprehensive project documentation
218
+ ```
@@ -0,0 +1,27 @@
1
+ main.py,sha256=3lOh4_Gv-vknASipKxpdpNYeI1zF-TthKez7k5OoJzo,6575
2
+ src/__init__.py,sha256=LGVQyDsWifdACo7qztwb8RWWHds1E7uQ-ZqD8SAjyw4,22
3
+ src/camera.py,sha256=JqlBHHIlgjXjcbjOThSGcF2wLuOcBuvfCIA2eYc3OpA,1505
4
+ src/config.py,sha256=mQI6YGULx5o3CT9CSsGOnCRYxZaO122uYirTuv4LAtM,749
5
+ src/config_loader.py,sha256=wmoBANwUGlWXj3rZWPoIuxcmEKEG6xFhVhG7im13SWw,618
6
+ src/detector.py,sha256=v5jca3hTYggIxF4dvzLcXtJ68UTBN8iNaiMEMF11w0Q,1420
7
+ src/device_utils.py,sha256=X_SHq_xMiqwO6QYq3-2cT0k3GljVfh2IMLVGIhiHvjI,1189
8
+ src/exporter.py,sha256=CP05b_7Z9qbKNlq1WYeq-lWDREXPw5196bzPMpdhWXc,2857
9
+ src/logger.py,sha256=Od97RKQV0IZuiLN9xnjjG3F2QHxYJqVhU7Th6uPlkpY,1027
10
+ src/model.py,sha256=7-Nud-iuRdGcgu8Q2530rdy-TrsX09rLVaYX-1g5qq4,906
11
+ src/pipeline.py,sha256=xsuDiLD4IjOXGkwTE4UgZG2hsj90P_xQjIkuYDB2Pc8,7512
12
+ src/runtime_tracker.py,sha256=dKfYLsJHlhzsEHe6JssR_GuULK1eTzt13026vKjON3w,5134
13
+ src/tracker.py,sha256=84t_x8O1nVxVl2vJ0lJ8F21kkpoQNHke3BzEncvL3S0,6085
14
+ src/validation.py,sha256=i3jm7yTONR3f5jObqRcyqMGelLlmlOaVww6W2Ipvl-E,4062
15
+ tests/__init__.py,sha256=XRNuNflqPDQupfhmPTpojlgPyH5Gk0mLKdV9cdeZTPU,17
16
+ tests/conftest.py,sha256=-RBG04ukzjTqZiokH_163lVp8N6vQ97u_IMz4ixOK4c,138
17
+ tests/test_config_loader.py,sha256=j3cvszd_gJUiJ2fbUadpNITBvp1FBxmDVYBd94V-b2M,2448
18
+ tests/test_detector.py,sha256=h9zQm-MUk65VOY4A1Qg7SNnCf96R5hcUbSsewOXMwkQ,2581
19
+ tests/test_exporter.py,sha256=g4PHevVzgypxNhlMKSuAONX-EohJMbpyT4gGiFRryDI,2780
20
+ tests/test_logger.py,sha256=14J6fLqEaNcyMXPocDXy8k1R8QzCyh7F5eB781X4tFs,958
21
+ tests/test_tracker.py,sha256=gYtxY6FWi9TWLpydWn9zYsgUruAaNKDyTQzQX9ou1NA,1361
22
+ tests/test_validation.py,sha256=q91weipSkpurB2uDhCct-2PsbxvPDrClw7jAtCrWUGQ,2725
23
+ real_time_object_detection-1.1.0.dist-info/METADATA,sha256=B2j3OHcuIFc8XM8mJjn3xlj7rYB42I4gGm91M_w3iCE,12601
24
+ real_time_object_detection-1.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
25
+ real_time_object_detection-1.1.0.dist-info/entry_points.txt,sha256=mvAEkIsyHtgbrChMQJwAnd6AvUWp3JfmGp1V4srBHSk,35
26
+ real_time_object_detection-1.1.0.dist-info/top_level.txt,sha256=GiEHYkrw-QVC6VaRiNrxR8m0WWJ4uZnbZKcsPEHplD4,15
27
+ real_time_object_detection-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ rtod = main:main
@@ -0,0 +1,3 @@
1
+ main
2
+ src
3
+ tests
src/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "1.1.0"
src/camera.py ADDED
@@ -0,0 +1,46 @@
1
+ import cv2
2
+ from typing import Tuple, Optional, Any
3
+ from src.validation import ValidationError, validate_video_path
4
+
5
+ def open_camera(index: int = 0) -> cv2.VideoCapture:
6
+ if not isinstance(index, int) or index < 0:
7
+ raise ValidationError(f"Camera index must be non-negative int")
8
+
9
+ cap = cv2.VideoCapture(index)
10
+ if not cap.isOpened():
11
+ raise RuntimeError(f"Unable to open camera at index {index}")
12
+
13
+ ret, frame = cap.read()
14
+ if not ret or frame is None:
15
+ cap.release()
16
+ raise RuntimeError(f"Camera opened but failed to read frames")
17
+
18
+ return cap
19
+
20
+ def open_video(path: str) -> cv2.VideoCapture:
21
+ validate_video_path(path)
22
+ cap = cv2.VideoCapture(path)
23
+ if not cap.isOpened():
24
+ raise RuntimeError(f"Unable to open video: {path}")
25
+
26
+ ret, frame = cap.read()
27
+ if not ret or frame is None:
28
+ cap.release()
29
+ raise RuntimeError(f"Video opened but contains no readable frames: {path}")
30
+
31
+ cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
32
+ return cap
33
+
34
+ def read_frame(cap: cv2.VideoCapture) -> Tuple[bool, Optional[Any]]:
35
+ try:
36
+ ret, frame = cap.read()
37
+ return ret, frame
38
+ except Exception as e:
39
+ raise RuntimeError(f"Error reading frame: {e}")
40
+
41
+ def release_camera(cap: cv2.VideoCapture) -> None:
42
+ if cap is not None:
43
+ try:
44
+ cap.release()
45
+ except Exception as e:
46
+ print(f"Warning: Error releasing camera: {e}")
src/config.py ADDED
@@ -0,0 +1,25 @@
1
+ import yaml
2
+ from pathlib import Path
3
+
4
+ class Config:
5
+ def __init__(self, path):
6
+ self.path = Path(path)
7
+ if not self.path.exists():
8
+ raise FileNotFoundError(f"Config not found: {path}")
9
+ with open(self.path, "r") as f:
10
+ self.data = yaml.safe_load(f)
11
+ self._validate()
12
+
13
+ def _validate(self):
14
+ required = ["model", "input", "output", "runtime"]
15
+ for k in required:
16
+ if k not in self.data:
17
+ raise ValueError(f"Missing config section: {k}")
18
+
19
+ def get(self, *keys, default=None):
20
+ ref = self.data
21
+ for k in keys:
22
+ if k not in ref:
23
+ return default
24
+ ref = ref[k]
25
+ return ref
src/config_loader.py ADDED
@@ -0,0 +1,21 @@
1
+ import yaml
2
+ from pathlib import Path
3
+ from typing import Dict, Any
4
+ from src.validation import validate_config, ValidationError
5
+
6
+ def load_config(path: str = "config.yaml") -> Dict[str, Any]:
7
+ config_path = Path(path)
8
+
9
+ if not config_path.exists():
10
+ raise FileNotFoundError(f"Config file not found: {path}")
11
+
12
+ try:
13
+ with open(config_path, "r") as f:
14
+ config = yaml.safe_load(f)
15
+ except yaml.YAMLError as e:
16
+ raise ValidationError(f"Failed to parse YAML: {e}")
17
+
18
+ if config is None:
19
+ config = {}
20
+
21
+ return validate_config(config)
src/detector.py ADDED
@@ -0,0 +1,43 @@
1
+ from typing import List, Tuple, Optional, Set
2
+ import numpy as np
3
+ from ultralytics import YOLO
4
+ from pathlib import Path
5
+ from src.validation import ValidationError, validate_model_path, validate_frame
6
+ from src.device_utils import validate_and_fallback
7
+
8
+ from src.model import load_model
9
+
10
+ def detect(model: YOLO, frame: np.ndarray, conf_threshold: float = 0.5, allowed_classes: Optional[Set] = None) -> List[Tuple[int, int, int, int, str, float]]:
11
+ validate_frame(frame)
12
+
13
+ if not (0 <= conf_threshold <= 1):
14
+ raise ValidationError(f"Confidence must be 0-1")
15
+
16
+ try:
17
+ results = model(frame, conf=conf_threshold, verbose=False)
18
+ except Exception as e:
19
+ raise RuntimeError(f"Inference failed: {e}")
20
+
21
+ if not results or results[0].boxes is None:
22
+ return []
23
+
24
+ boxes = results[0].boxes
25
+ names = model.names
26
+ detections = []
27
+
28
+ for box in boxes:
29
+ cls_id = int(box.cls[0])
30
+ label = names.get(cls_id, f"Unknown({cls_id})")
31
+
32
+ if allowed_classes is not None and label not in allowed_classes and cls_id not in allowed_classes and str(cls_id) not in allowed_classes:
33
+ continue
34
+
35
+ conf = float(box.conf[0])
36
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
37
+
38
+ if x2 <= x1 or y2 <= y1:
39
+ continue
40
+
41
+ detections.append((x1, y1, x2, y2, label, conf))
42
+
43
+ return detections
src/device_utils.py ADDED
@@ -0,0 +1,36 @@
1
+ import torch
2
+ from typing import Literal
3
+
4
+ def detect_available_device() -> Literal["cpu", "cuda", "mps"]:
5
+ if torch.cuda.is_available():
6
+ return "cuda"
7
+ elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
8
+ return "mps"
9
+ return "cpu"
10
+
11
+ def get_device_info() -> dict:
12
+ info = {
13
+ "cuda_available": torch.cuda.is_available(),
14
+ "cuda_count": torch.cuda.device_count() if torch.cuda.is_available() else 0,
15
+ "mps_available": hasattr(torch.backends, "mps") and torch.backends.mps.is_available(),
16
+ "torch_version": torch.__version__,
17
+ }
18
+
19
+ if torch.cuda.is_available():
20
+ info["cuda_device"] = torch.cuda.current_device()
21
+ info["cuda_device_name"] = torch.cuda.get_device_name(0)
22
+
23
+ return info
24
+
25
+ def validate_and_fallback(device: str) -> str:
26
+ if device == "cuda":
27
+ if torch.cuda.is_available():
28
+ return "cuda"
29
+ else:
30
+ return "cpu"
31
+ elif device == "mps":
32
+ if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
33
+ return "mps"
34
+ else:
35
+ return "cpu"
36
+ return "cpu"