使用Python OpenCV检测矩形角点时的异常问题求助
光学标记识别(OMR)轮廓检测不稳定问题
我正尝试使用Python OpenCV实现光学标记识别(OMR)。程序流程为先通过预处理“滤镜”将原始图像转换为二值图像,随后进行轮廓检测。二值图像乍看效果不错,但对部分相似图像应用轮廓检测时,结果时好时坏。
现有代码
预处理函数
def PreProcessing(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) ret, imgThresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) return imgThresh
角点检测函数
def findCorners(canny): contourCounter = 0 points1 = [] points2 = [] contours, _ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area = cv2.contourArea(cnt) if area > 400000 and area < 2500000: epsilon = 0.001 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) for point in approx: x, y = point[0] if contourCounter == 0: points1.append((x, y)) elif contourCounter == 1: points2.append((x, y)) contourCounter += 1 if contourCounter != 2: raise Exception(f"the number of big contours detacted is {contourCounter}") return points1 , points2
示例情况
- 有效示例:原始图像经处理后能正确检测到2个目标轮廓,输出对应角点。
- 无效示例:原始图像处理后无法检测到2个目标轮廓,触发异常。
问题分析与解决方案
1. 预处理环节鲁棒性不足
无效示例的二值图可能因光照不均导致阈值分割失效,或目标区域出现断裂。可通过以下方式优化:
- 改用自适应阈值分割应对光照差异:
def PreProcessing(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) # 自适应阈值适配局部光照 imgThresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) return imgThresh
- 增加形态学操作修复二值图断裂区域:
def PreProcessing(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) ret, imgThresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 膨胀操作修复目标区域断裂 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) imgThresh = cv2.dilate(imgThresh, kernel, iterations=1) return imgThresh
2. 轮廓筛选条件过于刚性
固定的面积阈值无法适配不同尺寸的图像,可改为基于图像总面积的相对比例筛选:
def findCorners(canny): contourCounter = 0 points1 = [] points2 = [] contours, _ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 计算图像总面积,设置相对面积阈值 img_area = canny.shape[0] * canny.shape[1] min_area = img_area * 0.1 max_area = img_area * 0.4 for cnt in contours: area = cv2.contourArea(cnt) if min_area < area < max_area: epsilon = 0.01 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) # 额外筛选矩形轮廓(4个顶点) if len(approx) == 4: for point in approx: x, y = point[0] if contourCounter == 0: points1.append((x, y)) elif contourCounter == 1: points2.append((x, y)) contourCounter += 1 if contourCounter != 2: raise Exception(f"the number of big contours detected is {contourCounter}") return points1 , points2
3. 轮廓近似参数优化
固定的epsilon系数可能导致轮廓近似失效,可将系数调整为0.01-0.02区间,根据实际图像效果微调。
内容的提问来源于stack exchange,提问作者guy blau
相关产品推荐
相关产品推荐

