region_counter.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
  2. from ultralytics.solutions.solutions import BaseSolution
  3. from ultralytics.utils import LOGGER
  4. from ultralytics.utils.plotting import Annotator, colors
  5. class RegionCounter(BaseSolution):
  6. """
  7. A class designed for real-time counting of objects within user-defined regions in a video stream.
  8. This class inherits from `BaseSolution` and offers functionalities to define polygonal regions in a video
  9. frame, track objects, and count those objects that pass through each defined region. This makes it useful
  10. for applications that require counting in specified areas, such as monitoring zones or segmented sections.
  11. Attributes:
  12. region_template (dict): A template for creating new counting regions with default attributes including
  13. the name, polygon coordinates, and display colors.
  14. counting_regions (list): A list storing all defined regions, where each entry is based on `region_template`
  15. and includes specific region settings like name, coordinates, and color.
  16. Methods:
  17. add_region: Adds a new counting region with specified attributes, such as the region's name, polygon points,
  18. region color, and text color.
  19. count: Processes video frames to count objects in each region, drawing regions and displaying counts
  20. on the frame. Handles object detection, region definition, and containment checks.
  21. """
  22. def __init__(self, **kwargs):
  23. """Initializes the RegionCounter class for real-time counting in different regions of the video streams."""
  24. super().__init__(**kwargs)
  25. self.region_template = {
  26. "name": "Default Region",
  27. "polygon": None,
  28. "counts": 0,
  29. "dragging": False,
  30. "region_color": (255, 255, 255),
  31. "text_color": (0, 0, 0),
  32. }
  33. self.counting_regions = []
  34. def add_region(self, name, polygon_points, region_color, text_color):
  35. """
  36. Adds a new region to the counting list based on the provided template with specific attributes.
  37. Args:
  38. name (str): Name assigned to the new region.
  39. polygon_points (list[tuple]): List of (x, y) coordinates defining the region's polygon.
  40. region_color (tuple): BGR color for region visualization.
  41. text_color (tuple): BGR color for the text within the region.
  42. """
  43. region = self.region_template.copy()
  44. region.update(
  45. {
  46. "name": name,
  47. "polygon": self.Polygon(polygon_points),
  48. "region_color": region_color,
  49. "text_color": text_color,
  50. }
  51. )
  52. self.counting_regions.append(region)
  53. def count(self, im0):
  54. """
  55. Processes the input frame to detect and count objects within each defined region.
  56. Args:
  57. im0 (numpy.ndarray): Input image frame where objects and regions are annotated.
  58. Returns:
  59. im0 (numpy.ndarray): Processed image frame with annotated counting information.
  60. """
  61. self.annotator = Annotator(im0, line_width=self.line_width)
  62. self.extract_tracks(im0)
  63. # Region initialization and conversion
  64. if self.region is None:
  65. self.initialize_region()
  66. regions = {"Region#01": self.region}
  67. else:
  68. regions = self.region if isinstance(self.region, dict) else {"Region#01": self.region}
  69. # Draw regions and process counts for each defined area
  70. for idx, (region_name, reg_pts) in enumerate(regions.items(), start=1):
  71. if not isinstance(reg_pts, list) or not all(isinstance(pt, tuple) for pt in reg_pts):
  72. LOGGER.warning(f"Invalid region points for {region_name}: {reg_pts}")
  73. continue # Skip invalid entries
  74. color = colors(idx, True)
  75. self.annotator.draw_region(reg_pts=reg_pts, color=color, thickness=self.line_width * 2)
  76. self.add_region(region_name, reg_pts, color, self.annotator.get_txt_color())
  77. # Prepare regions for containment check
  78. for region in self.counting_regions:
  79. region["prepared_polygon"] = self.prep(region["polygon"])
  80. # Process bounding boxes and count objects within each region
  81. for box, cls in zip(self.boxes, self.clss):
  82. self.annotator.box_label(box, label=self.names[cls], color=colors(cls, True))
  83. bbox_center = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
  84. for region in self.counting_regions:
  85. if region["prepared_polygon"].contains(self.Point(bbox_center)):
  86. region["counts"] += 1
  87. # Display counts in each region
  88. for region in self.counting_regions:
  89. self.annotator.text_label(
  90. region["polygon"].bounds,
  91. label=str(region["counts"]),
  92. color=region["region_color"],
  93. txt_color=region["text_color"],
  94. )
  95. region["counts"] = 0 # Reset count for next frame
  96. self.display_output(im0)
  97. return im0