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

OpenCV扫描地图自动地理配准:折痕干扰下的角点定位方案问询

扫描地图四角定位:排除中心折痕干扰的OpenCV解决方案

问题背景

需基于OpenCV实现扫描地图的自动地理配准,核心目标是精准定位扫描地图内图的四个角点。先后尝试两段轮廓检测代码:

  1. 初始轮廓检测代码未成功识别目标轮廓,仅得到无效结果;
  2. 优化后的代码虽提升了边缘检测效果,但受扫描旧地图的中心折痕干扰,仅能检测单侧区域,无法完整定位内图四角。

需求是通过让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("未检测到有效四角轮廓")

关键说明

  1. 中心区域屏蔽:代码中center_ratio参数控制屏蔽的中心区域大小,默认设置为0.7(即屏蔽图像70%的中心区域),可根据实际地图折痕的范围调整该值;
  2. 轮廓筛选优化:原代码中只要len(approx) > 0就直接取轮廓,修改为仅保留四个顶点的轮廓,避免误识别非目标轮廓;
  3. 容错处理:添加了screenCnt is not None的判断,避免未检测到轮廓时后续代码报错。

内容的提问来源于stack exchange,提问作者Dwayne Dibbley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:15:55