如何使用OpenCV识别图表的四个角落?
解决图表四角定位的可行方案
针对你遇到的刻度线干扰、角点检测不准确的问题,这里提供几个实用的解决方案:
方案1:预处理清除刻度线+轮廓近似提取四角
核心思路是先通过形态学操作去除细刻度线,再提取图表外框轮廓并做多边形近似,得到四个角点。
import cv2 as cv import numpy as np img = cv.imread("image_path") gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 二值化处理 thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1] # 创建结构元素,针对性去除水平/垂直刻度线(可根据刻度粗细调整核大小) horizontal_kernel = cv.getStructuringElement(cv.MORPH_RECT, (20, 1)) vertical_kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 20)) # 开运算去除细刻度:先腐蚀后膨胀,保留大轮廓 thresh_no_horizontal = cv.morphologyEx(thresh, cv.MORPH_OPEN, horizontal_kernel, iterations=1) thresh_clean = cv.morphologyEx(thresh_no_horizontal, cv.MORPH_OPEN, vertical_kernel, iterations=1) # 提取最外层轮廓,筛选面积最大的(图表外框) contours, _ = cv.findContours(thresh_clean, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) max_contour = max(contours, key=cv.contourArea) # Douglas-Peucker算法做轮廓近似,epsilon值控制近似精度 epsilon = 0.02 * cv.arcLength(max_contour, True) approx_corners = cv.approxPolyDP(max_contour, epsilon, True) # 绘制检测结果 if len(approx_corners) == 4: img_copy = img.copy() for corner in np.int0(approx_corners): x, y = corner.ravel() cv.circle(img_copy, (x, y), 5, (0, 255, 0), -1) cv.imshow("Detected Corners", img_copy) cv.waitKey(0) cv.destroyAllWindows()
方案2:霍夫直线检测+交点计算
通过检测图表的四条边框直线,计算两两直线的交点得到四角,适合边框清晰的图表。
import cv2 as cv import numpy as np def calculate_intersection(line1, line2): """计算两条直线的交点,返回整数坐标或None""" 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 = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4)) / denom u = -((x1 - x3)*(y1 - y2) - (y1 - y3)*(x1 - x2)) / denom if 0 <= t <= 1 and 0 <= u <= 1: x = int(x1 + t*(x2 - x1)) y = int(y1 + t*(y2 - y1)) return (x, y) return None img = cv.imread("image_path") gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # Canny边缘检测 edges = cv.Canny(gray, 50, 150) # 霍夫直线检测,筛选长度足够的直线 lines = cv.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=100, maxLineGap=10) # 分类水平和垂直直线 horizontal_lines = [] vertical_lines = [] for line in lines: x1, y1, x2, y2 = line[0] angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi # 筛选接近水平的直线(角度误差±10°) if abs(angle) < 10 or abs(angle - 180) < 10: horizontal_lines.append(line) # 筛选接近垂直的直线 elif abs(angle - 90) < 10 or abs(angle - 270) < 10: vertical_lines.append(line) # 取最长的两条水平/垂直直线(图表边框) horizontal_lines.sort(key=lambda l: np.sqrt((l[0][0]-l[0][2])**2 + (l[0][1]-l[0][3])**2), reverse=True) vertical_lines.sort(key=lambda l: np.sqrt((l[0][0]-l[0][2])**2 + (l[0][1]-l[0][3])**2), reverse=True) selected_h = horizontal_lines[:2] selected_v = vertical_lines[:2] # 计算所有交点 corners = [] for h_line in selected_h: for v_line in selected_v: pt = calculate_intersection(h_line, v_line) if pt is not None: corners.append(pt) # 绘制结果 img_copy = img.copy() for corner in corners: cv.circle(img_copy, corner, 5, (0, 0, 255), -1) cv.imshow("Detected Corners", img_copy) cv.waitKey(0) cv.destroyAllWindows()
方案3:最小外接矩形提取四角
如果图表存在倾斜,用最小外接矩形直接获取四个顶点,前提是先清除刻度干扰。
import cv2 as cv import numpy as np img = cv.imread("image_path") gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1] # 闭运算填充小缝隙,强化外框轮廓 kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) thresh_clean = cv.morphologyEx(thresh, cv.MORPH_CLOSE, kernel, iterations=2) # 提取最大轮廓 contours, _ = cv.findContours(thresh_clean, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) max_contour = max(contours, key=cv.contourArea) # 计算最小外接矩形,提取四个顶点 rect = cv.minAreaRect(max_contour) box = cv.boxPoints(rect) box = np.int0(box) # 绘制结果 img_copy = img.copy() for point in box: cv.circle(img_copy, tuple(point), 5, (255, 0, 0), -1) cv.imshow("Detected Corners", img_copy) cv.waitKey(0) cv.destroyAllWindows()
内容的提问来源于stack exchange,提问作者chowx054
相关产品推荐
相关产品推荐

