asvs commited on
Commit
d1424b3
·
0 Parent(s):

somewhat working commit of people counter

Browse files
Files changed (6) hide show
  1. .gitignore +14 -0
  2. .python-version +1 -0
  3. README.md +48 -0
  4. main.py +141 -0
  5. pyproject.toml +12 -0
  6. uv.lock +0 -0
.gitignore ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Videos and Models ckpts
13
+ *.pt
14
+ *.mp4
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.12
README.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CCTV People Counter
2
+
3
+ This program counts the number of people entering through a specified line in a video feed (e.g., store entrance). It uses YOLOv8 for person detection and tracking.
4
+
5
+ ## Features
6
+
7
+ - Real-time person detection using YOLOv8
8
+ - Object tracking to count unique entries
9
+ - Customizable counting line position
10
+ - Optional video output saving
11
+ - Visual display of count and tracking
12
+
13
+ ## Setup
14
+
15
+ 1. Ensure you have Python 3.12+ installed
16
+ 2. Install dependencies:
17
+ ```bash
18
+ pip install .
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ Basic usage:
24
+ ```bash
25
+ python main.py video_path
26
+ ```
27
+
28
+ With optional parameters:
29
+ ```bash
30
+ python main.py video_path --line-position 0.6 --output custom_output.mp4
31
+ ```
32
+
33
+ ### Parameters
34
+
35
+ - `video_path`: Path to input video file (required)
36
+ - `--line-position`: Position of counting line (0-1, as fraction of frame height, default: 0.5)
37
+ - `--output`: Path to save output video (default: result.mp4)
38
+
39
+ ### Controls
40
+
41
+ - Press 'q' to quit the program
42
+
43
+ ## How it Works
44
+
45
+ 1. Uses YOLOv8 to detect people in each frame
46
+ 2. Tracks detected people across frames
47
+ 3. Counts when a tracked person crosses the specified line from top to bottom
48
+ 4. Displays real-time count and visualizations
main.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from ultralytics import YOLO
4
+ from collections import defaultdict
5
+ import argparse
6
+
7
+ class PersonCounter:
8
+ def __init__(self, line_position=0.5):
9
+ """Initialize person counter.
10
+
11
+ Args:
12
+ line_position (float): Virtual line position as fraction of frame height (0-1)
13
+ """
14
+ self.model = YOLO("yolov8n.pt") # Load pretrained YOLOv8 model
15
+ self.tracker = defaultdict(list) # Track object IDs
16
+ self.crossed_ids = set() # Store IDs that have crossed the line
17
+ self.line_position = line_position
18
+ self.count = 0
19
+
20
+ def _calculate_center(self, box):
21
+ """Calculate center point of detection box."""
22
+ x1, y1, x2, y2 = box
23
+ return (x1 + x2) / 2, (y1 + y2) / 2
24
+
25
+ def process_frame(self, frame):
26
+ """Process a single frame and update count.
27
+
28
+ Args:
29
+ frame: Input frame from video
30
+ Returns:
31
+ frame: Annotated frame
32
+ count: Current count of people who entered
33
+ """
34
+ height, width = frame.shape[:2]
35
+ line_y = int(height * self.line_position)
36
+
37
+ # Draw counting line
38
+ cv2.line(frame, (0, line_y), (width, line_y), (0, 255, 0), 2)
39
+
40
+ # Run detection and tracking
41
+ results = self.model.track(frame, persist=True, classes=[0]) # class 0 is person
42
+
43
+ if results[0].boxes.id is not None:
44
+ boxes = results[0].boxes.xyxy.cpu().numpy()
45
+ track_ids = results[0].boxes.id.cpu().numpy().astype(int)
46
+
47
+ # Process each detection
48
+ for box, track_id in zip(boxes, track_ids):
49
+ # Draw bounding box
50
+ cv2.rectangle(frame, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])),
51
+ (255, 0, 0), 2)
52
+
53
+ # Get center point of the bottom edge of the box (feet position)
54
+ center_x = (box[0] + box[2]) / 2
55
+ feet_y = box[3] # Bottom of the bounding box
56
+
57
+ # Draw tracking point
58
+ cv2.circle(frame, (int(center_x), int(feet_y)), 5, (0, 255, 255), -1)
59
+
60
+ # Store tracking history
61
+ if track_id in self.tracker:
62
+ prev_y = self.tracker[track_id][-1]
63
+ # Check if person has crossed the line (moving down)
64
+ if prev_y < line_y and feet_y >= line_y and track_id not in self.crossed_ids:
65
+ self.crossed_ids.add(track_id)
66
+ self.count += 1
67
+ # Draw crossing indicator
68
+ cv2.circle(frame, (int(center_x), int(line_y)), 8, (0, 0, 255), -1)
69
+
70
+ # Update tracking history
71
+ self.tracker[track_id] = [feet_y] # Only store current position
72
+
73
+ # Draw count with bigger font and background
74
+ count_text = f"Count: {self.count}"
75
+ font = cv2.FONT_HERSHEY_SIMPLEX
76
+ font_scale = 1.5
77
+ thickness = 3
78
+ (text_width, text_height), _ = cv2.getTextSize(count_text, font, font_scale, thickness)
79
+
80
+ # Draw background rectangle
81
+ cv2.rectangle(frame, (10, 10), (20 + text_width, 20 + text_height),
82
+ (0, 0, 0), -1)
83
+ # Draw text
84
+ cv2.putText(frame, count_text, (15, 15 + text_height),
85
+ font, font_scale, (0, 255, 0), thickness)
86
+
87
+ return frame, self.count
88
+
89
+ def main():
90
+ parser = argparse.ArgumentParser(description='Count people entering through a line in video.')
91
+ parser.add_argument('video_path', help='Path to input video file')
92
+ parser.add_argument('--line-position', type=float, default=0.5,
93
+ help='Position of counting line (0-1, fraction of frame height)')
94
+ parser.add_argument('--output', default='result.mp4', help='Path to output video file (default: result.mp4)')
95
+ args = parser.parse_args()
96
+
97
+ # Initialize video capture
98
+ cap = cv2.VideoCapture(args.video_path)
99
+ if not cap.isOpened():
100
+ print(f"Error: Could not open video at {args.video_path}")
101
+ return
102
+
103
+ # Get video properties
104
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
105
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
106
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
107
+
108
+ # Initialize video writer
109
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
110
+ writer = cv2.VideoWriter(args.output, fourcc, fps, (width, height))
111
+
112
+ # Initialize person counter
113
+ counter = PersonCounter(line_position=args.line_position)
114
+
115
+ while cap.isOpened():
116
+ ret, frame = cap.read()
117
+ if not ret:
118
+ break
119
+
120
+ # Process frame
121
+ processed_frame, count = counter.process_frame(frame)
122
+
123
+ # Display frame
124
+ cv2.imshow('Frame', processed_frame)
125
+
126
+ # Write processed frame to output video
127
+ writer.write(processed_frame)
128
+
129
+ # Break on 'q' press
130
+ if cv2.waitKey(1) & 0xFF == ord('q'):
131
+ break
132
+
133
+ print(f"Final count: {counter.count}")
134
+
135
+ # Clean up
136
+ cap.release()
137
+ writer.release()
138
+ cv2.destroyAllWindows()
139
+
140
+ if __name__ == "__main__":
141
+ main()
pyproject.toml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "cctv-ppl-count"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "opencv-python",
9
+ "numpy",
10
+ "ultralytics",
11
+ "pip>=25.0.1",
12
+ ]
uv.lock ADDED
The diff for this file is too large to render. See raw diff