OpenCV扫描地图自动地理配准:折痕干扰下的角点定位方案问询
扫描地图四角定位:排除中心折痕干扰的OpenCV解决方案
问题背景
需基于OpenCV实现扫描地图的自动地理配准,核心目标是精准定位扫描地图内图的四个角点。先后尝试两段轮廓检测代码:
- 初始轮廓检测代码未成功识别目标轮廓,仅得到无效结果;
- 优化后的代码虽提升了边缘检测效果,但受扫描旧地图的中心折痕干扰,仅能检测单侧区域,无法完整定位内图四角。
需求是通过让OpenCV忽略图像中心区域,排除折痕干扰,完成角点定位。
核心解决思路
通过创建图像掩码屏蔽中心区域,让后续的边缘检测和轮廓提取仅关注图像边缘区域,避开中心折痕的干扰。具体步骤:
- 获取图像尺寸,计算需要屏蔽的中心区域范围;
- 生成全白掩码,将中心区域涂黑;
- 将掩码与边缘检测结果做按位与操作,过滤掉中心区域的边缘信息。
修改后的完整代码
import cv2 import numpy as np file = "samplebad.jpg" img = cv2.imread(file) orig = img.copy() # 图像锐化:加权减去高斯模糊结果,增强边缘 blur = cv2.GaussianBlur(img, (9,9), 0) sharp = cv2.addWeighted(img, 1.5, blur, -0.5, 0) # 转灰度图 gray = cv2.cvtColor(sharp, cv2.COLOR_BGR2GRAY) # 双边滤波:平滑图像同时保留边缘 gray = cv2.bilateralFilter(gray, 11, 17, 17) # Canny边缘检测 edged = cv2.Canny(gray, 100, 200, apertureSize=3, L2gradient=True) cv2.imwrite('./edges.jpg', edged) # ------------------- 新增:创建掩码屏蔽中心区域 ------------------- height, width = edged.shape # 定义中心区域的比例(可根据实际调整,这里取宽高的70%作为中心屏蔽区域) center_ratio = 0.7 center_w = int(width * center_ratio) center_h = int(height * center_ratio) # 计算中心区域的左上角和右下角坐标 x_start = (width - center_w) // 2 y_start = (height - center_h) // 2 x_end = x_start + center_w y_end = y_start + center_h # 创建全白掩码 mask = np.ones_like(edged) * 255 # 将中心区域涂黑(屏蔽) mask[y_start:y_end, x_start:x_end] = 0 # 将边缘图与掩码做按位与,过滤中心区域的边缘 edged = cv2.bitwise_and(edged, mask) cv2.imwrite('edges_masked.jpg', edged) # ----------------------------------------------------------------- # 膨胀边缘,增强轮廓连续性 kernel = np.ones((3,3),np.uint8) edged = cv2.dilate(edged, kernel, iterations=1) cv2.imwrite('edges2.jpg', edged) # 寻找轮廓,保留面积前10的大轮廓 cnts, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:10] screenCnt = None # 遍历轮廓,筛选四边形 for c in cnts: peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * peri, True) # 筛选出四个顶点的轮廓(内图的四角) if len(approx) == 4: screenCnt = approx print(screenCnt) cv2.drawContours(img, [screenCnt], -1, (0, 255, 0), 10) cv2.imwrite('contours.jpg', img) break # 重新排序轮廓点,按左上、右上、右下、左下的顺序 if screenCnt is not None: pts = screenCnt.reshape(4, 2) rect = np.zeros((4, 2), dtype = "float32") # 按坐标和排序:左上最小,右下最大 s = pts.sum(axis = 1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # 按坐标差排序:右上最小,左下最大 diff = np.diff(pts, axis = 1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] # 计算透视变换后的图像尺寸 (tl, tr, br, bl) = rect widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2)) heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2)) heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2)) maxWidth = max(int(widthA), int(widthB)) maxHeight = max(int(heightA), int(heightB)) # 构建目标透视坐标 dst = np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype = "float32") # 透视变换并裁剪 M = cv2.getPerspectiveTransform(rect, dst) warp = cv2.warpPerspective(orig, M, (maxWidth, maxHeight)) cropBuffer = 105 height_warp, width_warp = warp.shape[:2] cropped = warp[cropBuffer:height_warp-cropBuffer, cropBuffer:width_warp-cropBuffer] cv2.imwrite('cropped.jpg', cropped) else: print("未检测到有效四角轮廓")
关键说明
- 中心区域屏蔽:代码中
center_ratio参数控制屏蔽的中心区域大小,默认设置为0.7(即屏蔽图像70%的中心区域),可根据实际地图折痕的范围调整该值; - 轮廓筛选优化:原代码中只要
len(approx) > 0就直接取轮廓,修改为仅保留四个顶点的轮廓,避免误识别非目标轮廓; - 容错处理:添加了
screenCnt is not None的判断,避免未检测到轮廓时后续代码报错。
内容的提问来源于stack exchange,提问作者Dwayne Dibbley
相关产品推荐
相关产品推荐

