python实现亚像素轮廓

目前提取亚像素轮廓常见的就是用Halcon的sub_pixel_edges_xld函数,如果我们想要自己实现亚像素轮廓提取的话思路如下:

  1. 预处理图像(去噪、降噪)
  2. 提取轮廓(canny+findContours)
  3. 寻找角点(goodFeaturesToTrack)
  4. 亚像素角点检测(cornerSubpix)
  5. 绘制亚像素角点(十字中心点标记)

初步试验下来,在排除噪声影响情况下,边缘轮廓上的亚像素角点是要比普通角点更贴近实际轮廓。

绿色十字代表普通角点

红色十字代表亚像素角点

存在问题:

  1. 性能问题,需要在精度和性能这二者中平衡。
  2. 受噪点影响较大,需要做预处理去除噪点。
  3. 精度高了之后查找的角点数量会相应减少,如果需要根据这些角点绘制成轮廓的话可能会一定程度失真。

代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt


def test_cross_subpixel():
    # 读取图像并转换为灰度图
    image = cv2.imread(r'D:\wangtianqing\code\script\iou\data\front\front_1_1_1565.png')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 检测初始角点位置
    corners = cv2.goodFeaturesToTrack(gray, maxCorners=100, qualityLevel=0.01, minDistance=10)
    corners = np.int0(corners)

    # 绘制角点
    for corner in corners:
        x, y = corner.ravel()
        cv2.line(image, (int(x) - 2, int(y)), (int(x) + 2, int(y)), (0, 255, 0), 1)
        cv2.line(image, (int(x), int(y) - 2), (int(x), int(y) + 2), (0, 255, 0), 1)

    # 定义搜索窗口大小和迭代停止条件
    winSize = (5, 5)
    zeroZone = (-1, -1)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 40, 0.001)

    # 提高角点精度到亚像素级
    corners = cv2.cornerSubPix(gray, np.float32(corners), winSize, zeroZone, criteria)

    # 绘制亚像素角点
    for corner in corners:
        x, y = corner.ravel()
        cv2.line(image, (int(x)-2, int(y)), (int(x)+2, int(y)), (0, 0, 255), 1)
        cv2.line(image, (int(x), int(y)-2), (int(x), int(y)+2), (0, 0, 255), 1)

    cv2.imwrite("SubpixelCorners.jpg", image)


def test_matplot_subpixel():
    # 读取图像并转换为灰度图
    image = cv2.imread(r'D:\wangtianqing\code\script\iou\data\front\front_1_1_1565.png')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 转换为RGB图像以便在Matplotlib中显示
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

    # 检测初始角点位置
    corners = cv2.goodFeaturesToTrack(gray, maxCorners=100, qualityLevel=0.01, minDistance=10)
    corners = np.int0(corners)
    # 显示图像和浮点数坐标
    plt.figure(figsize=(10, 10))
    plt.imshow(image_rgb)
    for corner in corners:
        x, y = corner.ravel()
        plt.plot(x, y, color='green', marker="x")  # 使用红色圆点标记角点位置

    # 定义搜索窗口大小和迭代停止条件
    winSize = (5, 5)
    zeroZone = (-1, -1)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 40, 0.001)

    # 提高角点精度到亚像素级
    corners = cv2.cornerSubPix(gray, np.float32(corners), winSize, zeroZone, criteria)

    for corner in corners:
        x, y = corner.ravel()
        plt.plot(x, y, color='red', marker="x")  # 使用红色圆点标记角点位置

    plt.title('Sub-pixel Corners')
    plt.show()
    
def test_matplot_subpixel_mask():
    # 读取图像并转换为灰度图
    image = cv2.imread(r'D:\wangtianqing\code\script\iou\data\front\front_1_1_1565.png')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 使用 Canny 算法检测边缘
    edges = cv2.Canny(gray, 100, 200)

    # 使用 Shi-Tomasi 算法检测角点
    corners = cv2.goodFeaturesToTrack(gray, maxCorners=100, qualityLevel=0.01, minDistance=10)
    corners = np.int0(corners)

    # 创建一个掩码,用于绘制边缘和角点
    mask = np.zeros_like(image)

    # 绘制边缘
    mask[edges > 0] = [255, 255, 255]

    # 筛选角点,保留位于边缘上的角点
    filtered_corners = []
    for corner in corners:
        x, y = corner.ravel()
        if edges[y, x] > 0:  # 仅保留在边缘上的角点
            filtered_corners.append(corner)
            cv2.circle(mask, (x, y), 3, (0, 0, 255), -1)

    # 显示结果
    plt.figure(figsize=(10, 5))

    # 显示原始图像
    plt.subplot(1, 2, 1)
    plt.title('Original Image with All Corners')
    plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

    # 绘制所有角点
    for corner in corners:
        x, y = corner.ravel()
        cv2.circle(image, (x, y), 3, (0, 255, 0), -1)

    plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

    # 显示边缘和保留的角点
    plt.subplot(1, 2, 2)
    plt.title('Edges with Filtered Corners')
    plt.imshow(cv2.cvtColor(mask, cv2.COLOR_BGR2RGB))

    plt.show()


if __name__ == '__main__':
    # test_matplot_subpixel()
    test_matplot_subpixel_mask()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值