基于3×3卷积滤波器的旋转正方形角点检测技术问询
Let's break down how to turn those response maps into clean, single-pixel corner markers for each square. Here's what we need to do step by step:
Key Steps
1. Non-Maximum Suppression (NMS)
Each corner will produce a blob of high response values in its corresponding map. We only want the local maximum pixel (the peak of that blob) to be our corner marker. This eliminates all redundant pixels around the actual corner.
2. Threshold Filtering
Not every local maximum is a real corner—we need to filter out low-response noise by setting a threshold. Only pixels with response values above this threshold are kept as valid corners.
3. Mark or Extract Corners
Once we have the cleaned maps, we can either draw the corners on an image (with different colors for each corner type) or extract their coordinates for further use.
Modified Code
Here's the updated implementation that adds these steps to your existing code:
import numpy as np import cv2 as cv def non_max_suppression(response_map, kernel_size=3): # Use dilation to find local maxima across the response map kernel = np.ones((kernel_size, kernel_size), dtype=np.float32) local_max = cv.dilate(response_map, kernel) # Keep only pixels that equal the local maximum (suppress others) nms_map = np.where(response_map == local_max, response_map, 0) return nms_map def corners_of_square(img: np.ndarray, threshold=10.0) -> tuple[np.ndarray, list]: img_cp = img.copy() if img_cp.ndim == 3: img_cp = cv.cvtColor(img_cp, cv.COLOR_BGR2GRAY).astype(np.float32) # Define the 4 corner filters (removed duplicate definitions) Z = np.array([ # top corner [ 1, 1, 1], [ 1, -1, -1], [ 1, -1, -1] ], dtype=np.float32) D = np.array([ # right corner [ 1, 1, 1], [-1, -1, 1], [-1, -1, 1] ], dtype=np.float32) L = np.array([ # left corner [ 1, -1, -1], [ 1, -1, -1], [ 1, 1, 1] ], dtype=np.float32) S = np.array([ # bottom corner [-1, -1, 1], [-1, -1, 1], [ 1, 1, 1] ], dtype=np.float32) # Generate response maps rZ = cv.filter2D(img_cp, cv.CV_32F, Z) rD = cv.filter2D(img_cp, cv.CV_32F, D) rL = cv.filter2D(img_cp, cv.CV_32F, L) rS = cv.filter2D(img_cp, cv.CV_32F, S) # Apply non-maximum suppression to each response map nms_z = non_max_suppression(rZ) nms_d = non_max_suppression(rD) nms_l = non_max_suppression(rL) nms_s = non_max_suppression(rS) # Filter out low-response pixels using the threshold nms_z = np.where(nms_z > threshold, 255, 0).astype(np.uint8) nms_d = np.where(nms_d > threshold, 255, 0).astype(np.uint8) nms_l = np.where(nms_l > threshold, 255, 0).astype(np.uint8) nms_s = np.where(nms_s > threshold, 255, 0).astype(np.uint8) # Create a marked image with colored corners marked_img = np.zeros_like(img) # Top corners: Red marked_img[nms_z == 255] = [0, 0, 255] # Right corners: Green marked_img[nms_d == 255] = [0, 255, 0] # Left corners: Blue marked_img[nms_l == 255] = [255, 0, 0] # Bottom corners: Yellow marked_img[nms_s == 255] = [0, 255, 255] # Collect corner coordinates with their type corner_coords = [] # Convert (y, x) from argwhere to standard (x, y) coordinates for y, x in np.argwhere(nms_z == 255): corner_coords.append( (x, y, "top") ) for y, x in np.argwhere(nms_d == 255): corner_coords.append( (x, y, "right") ) for y, x in np.argwhere(nms_l == 255): corner_coords.append( (x, y, "left") ) for y, x in np.argwhere(nms_s == 255): corner_coords.append( (x, y, "bottom") ) return marked_img, corner_coords if __name__ == '__main__': # Replace with your image path rotated_squares_img = cv.imread("rotirani_kvadrati.png").astype(np.float32) / 255 # Adjust threshold based on your image's response values marked_corners, corner_list = corners_of_square(rotated_squares_img, threshold=8.0) cv.imshow("Original Squares", rotated_squares_img) cv.imshow("Marked Corners", marked_corners) print("Detected corners:", corner_list) cv.waitKey(0) cv.destroyAllWindows()
Notes
- Threshold Adjustment: The
thresholdvalue may need tuning. Check the range of your response maps (printnp.max(rZ),np.min(rZ)) to set a value that filters noise but keeps real corners. - Rotated Squares: If your squares are heavily rotated, these fixed-orientation filters might miss some corners. For better rotation invariance, you could add more filters covering intermediate angles, or switch to a rotation-invariant detector like Harris corner detection—but your current approach works great for axis-aligned or slightly rotated squares.
- Cleanup: I removed the redundant redefinitions of
LandSin your original code to keep things tidy.
内容的提问来源于stack exchange,提问作者Marcel Majhenic

