You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 21:38:14