Python-OpenCV轮廓峰值检测:利用局部最大值计数遮挡人群
Got it, let's tackle this problem step by step. Since you're dealing with occluded people and already have the contours, extracting local maxima (head positions) is a really solid approach—way better than trying to count full contours when occlusion messes things up. You already know how to get the global topmost point, so expanding that to find all local peaks makes perfect sense.
核心思路
In image coordinates, the y-axis points downward, so the "topmost" point of a head will have the smallest y-value in its local neighborhood. We can scan each contour to find all points that are the lowest y-value (i.e., highest in the image) within a small window of surrounding contour points, then filter out duplicates to get distinct head positions.
具体实现步骤与代码
Here’s a practical OpenCV-based Python implementation that builds on the global topmost logic you already have:
import numpy as np import cv2 def find_local_head_points(contour, window_size=7, distance_threshold=10): # Convert contour points to a clean array of (x,y) coordinates contour_points = contour.squeeze().astype(np.int32) local_peaks = [] # Skip edge points to avoid index errors when checking neighbors for idx in range(window_size, len(contour_points) - window_size): current_y = contour_points[idx][1] # Get y-values of all points in the surrounding window neighbor_ys = contour_points[idx-window_size:idx+window_size+1][:, 1] # Check if current point is the topmost (smallest y) in the window # Add a small buffer to ignore tiny contour fluctuations if current_y == np.min(neighbor_ys) and current_y < np.mean(neighbor_ys) - 2: local_peaks.append(tuple(contour_points[idx])) # Deduplicate nearby points (same head might have multiple adjacent peaks) filtered_heads = [] for peak in local_peaks: keep_point = True for existing in filtered_heads: # Calculate Euclidean distance between points if np.linalg.norm(np.array(peak) - np.array(existing)) < distance_threshold: keep_point = False break if keep_point: filtered_heads.append(peak) return filtered_heads # --- Example usage --- # Assume you already have your contours loaded (replace with your code) # img = cv2.imread("your_image.jpg", 0) # _, binary_img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) # contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) all_head_positions = [] for contour in contours: # Skip tiny contours that are definitely not people if cv2.contourArea(contour) < 1000: continue # Find head points for this contour heads = find_local_head_points(contour) all_head_positions.extend(heads) # Final count of people person_count = len(all_head_positions) print(f"Total people detected: {person_count}") # Optional: Visualize the results # color_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # for point in all_head_positions: # cv2.circle(color_img, point, 6, (0, 0, 255), -1) # cv2.imshow("Detected Heads", color_img) # cv2.waitKey(0) # cv2.destroyAllWindows()
关键参数调整说明
window_size: Controls the size of the neighborhood to check for local peaks. Larger values work better for smoother contours; adjust based on your image resolution.distance_threshold: Filters out duplicate points from the same head. If your image is high-res, increase this value slightly.contourAreafilter: Weeds out noise contours that could generate false peaks—tweak the 1000 value based on how big people are in your images.
额外优化技巧
- Preprocess your binary image with morphological operations (like
cv2.morphologyEx) to clean up noise and smooth contours before extracting them. - If you’re dealing with complex occlusion, you can combine this method with a vertical projection of the binary image: find peaks in the projection to cross-verify your contour-based head counts.
- For better accuracy, you can add a check that the local peak is within a reasonable x-range relative to the contour’s width (heads shouldn’t be wider than a certain percentage of the body contour).
内容的提问来源于stack exchange,提问作者Maham

