基于OpenCV的正方形检测及直线交点圆内判断技术求助
优化方案:正方形检测、直线交点判断问题解决
一、解决正方形轮廓检测异常问题
针对正方形内外轮廓误检、亮度/噪点干扰导致的检测不稳定问题,调整预处理与轮廓筛选逻辑:
1. 替换边缘检测流程,适配亮度不均场景
放弃固定阈值的Canny,改用自适应阈值二值化,自动适配图像不同区域的亮度差异,减少噪点干扰:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 修正原代码BGR转灰度的错误 # 自适应阈值二值化,blockSize为奇数,C为减去的常数 binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 形态学闭运算,填补小缝隙强化轮廓 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)) binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
2. 优化轮廓筛选逻辑,避免内外轮廓误检
使用cv2.RETR_EXTERNAL只提取最外层轮廓,通过多维度条件过滤无效轮廓:
- 过滤面积过小的噪点轮廓
- 用实心度(轮廓面积/外接矩形面积)区分实心外轮廓与空心内轮廓
- 优化多边形逼近参数,提升正方形顶点识别精度
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 减少轮廓点数量 for contour in contours: area = cv2.contourArea(contour) if area < 500: # 过滤小噪点 continue perimeter = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True) x, y, w, h = cv2.boundingRect(approx) aspect_ratio = float(w)/h solidity = area / (w * h) # 实心度筛选 # 正方形判断:4个顶点、宽高比接近1、实心度达标 if len(approx) == 4 and 0.9 <= aspect_ratio <= 1.1 and solidity > 0.8: # 用外接矩形中心替代矩计算,更稳定 square_center = (x + w//2, y + h//2) # 用正方形半边长替代pointPolygonTest,避免逼近误差 distance = min(w, h) // 2 square_radius = 4 * distance # 绘制操作 cv2.drawContours(image_with_results, [approx], -1, (0,0,255), 2) cv2.circle(image_with_results, square_center, 5, (0,255,255), 3) cv2.circle(image_with_results, square_center, square_radius, (0,255,255), 2) break # 假设单目标正方形
二、解决直线交点检测与判断问题
针对噪点、清晰度不一的相交直线,用概率霍夫变换提取有效直线,再计算交点并判断位置:
1. 直线检测(概率霍夫变换)
edges_lines = cv2.Canny(gray, 50, 150) # 针对直线调整Canny阈值 # 概率霍夫变换,过滤短噪线段、合并断续直线 lines = cv2.HoughLinesP(edges_lines, 1, np.pi/180, threshold=50, minLineLength=100, maxLineGap=20)
2. 计算交点并判断是否在圆内
编写交点计算函数,通过距离公式判断位置:
def get_intersection(line1, line2): x1,y1,x2,y2 = line1[0] x3,y3,x4,y4 = line2[0] denom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4) if denom == 0: return None # 平行无交点 t_num = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4) u_num = (x1-x3)*(y1-y2) - (y1-y3)*(x1-x2) t = t_num / denom u = u_num / denom if 0<=t<=1 and 0<=u<=1: x = x1 + t*(x2-x1) y = y1 + t*(y2-y1) return (int(x), int(y)) return None # 提取前两条有效直线并处理 if lines is not None and len(lines)>=2: line1, line2 = lines[0], lines[1] cv2.line(image_with_results, (line1[0][0], line1[0][1]), (line1[0][2], line1[0][3]), (255,0,0), 2) cv2.line(image_with_results, (line2[0][0], line2[0][1]), (line2[0][2], line2[0][3]), (255,0,0), 2) intersection = get_intersection(line1, line2) if intersection is not None: cv2.circle(image_with_results, intersection, 5, (0,0,255), 3) # 判断交点与圆心的距离 dist = np.sqrt((intersection[0]-square_center[0])**2 + (intersection[1]-square_center[1])**2) if dist <= square_radius: print(f"图像{file_name}:交点{intersection}在圆内") else: print(f"图像{file_name}:交点{intersection}在圆外")
完整优化代码
import cv2 import os import numpy as np def get_intersection(line1, line2): x1,y1,x2,y2 = line1[0] x3,y3,x4,y4 = line2[0] denom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4) if denom == 0: return None t_num = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4) u_num = (x1-x3)*(y1-y2) - (y1-y3)*(x1-x2) t = t_num / denom u = u_num / denom if 0<=t<=1 and 0<=u<=1: x = x1 + t*(x2-x1) y = y1 + t*(y2-y1) return (int(x), int(y)) return None def detect_squares_and_lines(img_path): file_name = os.path.basename(img_path) img = cv2.imread(img_path) image_with_results = img.copy() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 正方形检测 binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)) binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) square_center = None square_radius = None for contour in contours: area = cv2.contourArea(contour) if area < 500: continue perimeter = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True) x, y, w, h = cv2.boundingRect(approx) aspect_ratio = float(w)/h solidity = area / (w * h) if len(approx) == 4 and 0.9 <= aspect_ratio <= 1.1 and solidity > 0.8: square_center = (x + w//2, y + h//2) distance = min(w, h) // 2 square_radius = 4 * distance cv2.drawContours(image_with_results, [approx], -1, (0,0,255), 2) cv2.circle(image_with_results, square_center, 5, (0,255,255), 3) cv2.circle(image_with_results, square_center, square_radius, (0,255,255), 2) break # 直线检测与交点判断 if square_center is not None: edges_lines = cv2.Canny(gray, 50, 150) lines = cv2.HoughLinesP(edges_lines, 1, np.pi/180, threshold=50, minLineLength=100, maxLineGap=20) if lines is not None and len(lines)>=2: line1, line2 = lines[0], lines[1] cv2.line(image_with_results, (line1[0][0], line1[0][1]), (line1[0][2], line1[0][3]), (255,0,0), 2) cv2.line(image_with_results, (line2[0][0], line2[0][1]), (line2[0][2], line2[0][3]), (255,0,0), 2) intersection = get_intersection(line1, line2) if intersection is not None: cv2.circle(image_with_results, intersection, 5, (0,0,255), 3) dist = np.sqrt((intersection[0]-square_center[0])**2 + (intersection[1]-square_center[1])**2) if dist <= square_radius: print(f"图像{file_name}:交点{intersection}在圆内") else: print(f"图像{file_name}:交点{intersection}在圆外") cv2.imshow(f"检测结果 - {file_name}", image_with_results) cv2.waitKey(0) cv2.destroyAllWindows() # 调用示例 # detect_squares_and_lines("test_image.jpg")
关键优化点说明
- 预处理:自适应阈值替代固定Canny,解决亮度不均问题;形态学闭运算强化轮廓
- 轮廓筛选:用面积、实心度过滤噪点和内轮廓,外接矩形中心计算更稳定
- 直线检测:概率霍夫变换筛选有效直线,避免噪点干扰
- 交点判断:通过距离公式判断位置,逻辑简单可靠
内容的提问来源于stack exchange,提问作者Ringo
相关产品推荐
相关产品推荐

