diff --git a/ultralytics/__init__.py b/ultralytics/__init__.py index 160ace687f..a8283a253d 100644 --- a/ultralytics/__init__.py +++ b/ultralytics/__init__.py @@ -1,6 +1,6 @@ # Ultralytics YOLO 🚀, AGPL-3.0 license -__version__ = "8.3.32" +__version__ = "8.3.33" import os diff --git a/ultralytics/solutions/object_counter.py b/ultralytics/solutions/object_counter.py index 6374920734..ac372e5f6a 100644 --- a/ultralytics/solutions/object_counter.py +++ b/ultralytics/solutions/object_counter.py @@ -46,13 +46,12 @@ class ObjectCounter(BaseSolution): self.show_in = self.CFG["show_in"] self.show_out = self.CFG["show_out"] - def count_objects(self, track_line, box, track_id, prev_position, cls): + def count_objects(self, current_centroid, track_id, prev_position, cls): """ Counts objects within a polygonal or linear region based on their tracks. Args: - track_line (Dict): Last 30 frame track record for the object. - box (List[float]): Bounding box coordinates [x1, y1, x2, y2] for the specific track in the current frame. + current_centroid (Tuple[float, float]): Current centroid values in the current frame. track_id (int): Unique identifier for the tracked object. prev_position (Tuple[float, float]): Last frame position coordinates (x, y) of the track. cls (int): Class index for classwise count updates. @@ -69,29 +68,50 @@ class ObjectCounter(BaseSolution): if prev_position is None or track_id in self.counted_ids: return - centroid = self.r_s.centroid - dx = (box[0] - prev_position[0]) * (centroid.x - prev_position[0]) - dy = (box[1] - prev_position[1]) * (centroid.y - prev_position[1]) - - if len(self.region) >= 3 and self.r_s.contains(self.Point(track_line[-1])): - self.counted_ids.append(track_id) - # For polygon region - if dx > 0: - self.in_count += 1 - self.classwise_counts[self.names[cls]]["IN"] += 1 - else: - self.out_count += 1 - self.classwise_counts[self.names[cls]]["OUT"] += 1 - - elif len(self.region) < 3 and self.LineString([prev_position, box[:2]]).intersects(self.r_s): - self.counted_ids.append(track_id) - # For linear region - if dx > 0 and dy > 0: - self.in_count += 1 - self.classwise_counts[self.names[cls]]["IN"] += 1 - else: - self.out_count += 1 - self.classwise_counts[self.names[cls]]["OUT"] += 1 + if len(self.region) == 2: # Linear region (defined as a line segment) + line = self.LineString(self.region) # Check if the line intersects the trajectory of the object + if line.intersects(self.LineString([prev_position, current_centroid])): + # Determine orientation of the region (vertical or horizontal) + if abs(self.region[0][0] - self.region[1][0]) < abs(self.region[0][1] - self.region[1][1]): + # Vertical region: Compare x-coordinates to determine direction + if current_centroid[0] > prev_position[0]: # Moving right + self.in_count += 1 + self.classwise_counts[self.names[cls]]["IN"] += 1 + else: # Moving left + self.out_count += 1 + self.classwise_counts[self.names[cls]]["OUT"] += 1 + else: + # Horizontal region: Compare y-coordinates to determine direction + if current_centroid[1] > prev_position[1]: # Moving downward + self.in_count += 1 + self.classwise_counts[self.names[cls]]["IN"] += 1 + else: # Moving upward + self.out_count += 1 + self.classwise_counts[self.names[cls]]["OUT"] += 1 + self.counted_ids.append(track_id) + + elif len(self.region) > 2: # Polygonal region + polygon = self.Polygon(self.region) + if polygon.contains(self.Point(current_centroid)): + # Determine motion direction for vertical or horizontal polygons + region_width = max([p[0] for p in self.region]) - min([p[0] for p in self.region]) + region_height = max([p[1] for p in self.region]) - min([p[1] for p in self.region]) + + if region_width < region_height: # Vertical-oriented polygon + if current_centroid[0] > prev_position[0]: # Moving right + self.in_count += 1 + self.classwise_counts[self.names[cls]]["IN"] += 1 + else: # Moving left + self.out_count += 1 + self.classwise_counts[self.names[cls]]["OUT"] += 1 + else: # Horizontal-oriented polygon + if current_centroid[1] > prev_position[1]: # Moving downward + self.in_count += 1 + self.classwise_counts[self.names[cls]]["IN"] += 1 + else: # Moving upward + self.out_count += 1 + self.classwise_counts[self.names[cls]]["OUT"] += 1 + self.counted_ids.append(track_id) def store_classwise_counts(self, cls): """ @@ -174,12 +194,12 @@ class ObjectCounter(BaseSolution): self.annotator.draw_centroid_and_tracks( self.track_line, color=colors(int(cls), True), track_thickness=self.line_width ) - + current_centroid = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2) # store previous position of track for object counting prev_position = None if len(self.track_history[track_id]) > 1: prev_position = self.track_history[track_id][-2] - self.count_objects(self.track_line, box, track_id, prev_position, cls) # Perform object counting + self.count_objects(current_centroid, track_id, prev_position, cls) # Perform object counting self.display_counts(im0) # Display the counts on the frame self.display_output(im0) # display output with base class function