123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
- from ultralytics.solutions.solutions import BaseSolution
- from ultralytics.utils import LOGGER
- from ultralytics.utils.plotting import Annotator, colors
- class RegionCounter(BaseSolution):
- """
- A class designed for real-time counting of objects within user-defined regions in a video stream.
- This class inherits from `BaseSolution` and offers functionalities to define polygonal regions in a video
- frame, track objects, and count those objects that pass through each defined region. This makes it useful
- for applications that require counting in specified areas, such as monitoring zones or segmented sections.
- Attributes:
- region_template (dict): A template for creating new counting regions with default attributes including
- the name, polygon coordinates, and display colors.
- counting_regions (list): A list storing all defined regions, where each entry is based on `region_template`
- and includes specific region settings like name, coordinates, and color.
- Methods:
- add_region: Adds a new counting region with specified attributes, such as the region's name, polygon points,
- region color, and text color.
- count: Processes video frames to count objects in each region, drawing regions and displaying counts
- on the frame. Handles object detection, region definition, and containment checks.
- """
- def __init__(self, **kwargs):
- """Initializes the RegionCounter class for real-time counting in different regions of the video streams."""
- super().__init__(**kwargs)
- self.region_template = {
- "name": "Default Region",
- "polygon": None,
- "counts": 0,
- "dragging": False,
- "region_color": (255, 255, 255),
- "text_color": (0, 0, 0),
- }
- self.counting_regions = []
- def add_region(self, name, polygon_points, region_color, text_color):
- """
- Adds a new region to the counting list based on the provided template with specific attributes.
- Args:
- name (str): Name assigned to the new region.
- polygon_points (list[tuple]): List of (x, y) coordinates defining the region's polygon.
- region_color (tuple): BGR color for region visualization.
- text_color (tuple): BGR color for the text within the region.
- """
- region = self.region_template.copy()
- region.update(
- {
- "name": name,
- "polygon": self.Polygon(polygon_points),
- "region_color": region_color,
- "text_color": text_color,
- }
- )
- self.counting_regions.append(region)
- def count(self, im0):
- """
- Processes the input frame to detect and count objects within each defined region.
- Args:
- im0 (numpy.ndarray): Input image frame where objects and regions are annotated.
- Returns:
- im0 (numpy.ndarray): Processed image frame with annotated counting information.
- """
- self.annotator = Annotator(im0, line_width=self.line_width)
- self.extract_tracks(im0)
- # Region initialization and conversion
- if self.region is None:
- self.initialize_region()
- regions = {"Region#01": self.region}
- else:
- regions = self.region if isinstance(self.region, dict) else {"Region#01": self.region}
- # Draw regions and process counts for each defined area
- for idx, (region_name, reg_pts) in enumerate(regions.items(), start=1):
- if not isinstance(reg_pts, list) or not all(isinstance(pt, tuple) for pt in reg_pts):
- LOGGER.warning(f"Invalid region points for {region_name}: {reg_pts}")
- continue # Skip invalid entries
- color = colors(idx, True)
- self.annotator.draw_region(reg_pts=reg_pts, color=color, thickness=self.line_width * 2)
- self.add_region(region_name, reg_pts, color, self.annotator.get_txt_color())
- # Prepare regions for containment check
- for region in self.counting_regions:
- region["prepared_polygon"] = self.prep(region["polygon"])
- # Process bounding boxes and count objects within each region
- for box, cls in zip(self.boxes, self.clss):
- self.annotator.box_label(box, label=self.names[cls], color=colors(cls, True))
- bbox_center = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
- for region in self.counting_regions:
- if region["prepared_polygon"].contains(self.Point(bbox_center)):
- region["counts"] += 1
- # Display counts in each region
- for region in self.counting_regions:
- self.annotator.text_label(
- region["polygon"].bounds,
- label=str(region["counts"]),
- color=region["region_color"],
- txt_color=region["text_color"],
- )
- region["counts"] = 0 # Reset count for next frame
- self.display_output(im0)
- return im0
|