草庐IT

计算机视觉实践之角点检测--Python实现--sobel滤波--Harris矩阵--角点检测器R--非极大值抑制

能智工人_Leo 2025-01-03 原文

文章目录

前言

实现对图像的角点检测,编写Python程序能够对输入图像进行角点检测,并返回角点检测的结果,并且在可视化显示输出结果。
而具体的文件和代码,在如下链接里:计算机视觉实践之角点检测Python实现

使用的是pycharm,提前导入numpy和opencv库

一、方法原理

1. Harris角点检测

算法的核心是利用局部窗口在图像上进行移动,判断灰度是否发生较大的变化。如果窗口内的灰度值(在梯度图上)都有较大的变化,那么这个窗口所在区域就存在角点。
具体的方法是,计算像素点在水平方向和竖直方向的梯度组成的Harris矩阵,其矩阵特征值就反映了该点的像素值变化。
如果该点所对应的两个特征值都比较大,说明像素点的梯度分布比较散,梯度变化程度比较大,符合角点在窗口区域的特点;
如果是平坦区域,那么像素点的梯度所构成的点集比较集中在原点附近,因为窗口区域内的像素点的梯度幅值非常小,此时Harris矩阵的对角化的两个特征值都比较小;
如果是边缘区域,在计算像素点的水平方向和竖直方向上的梯度时,边缘上的像素点的某个方向的梯度幅值变化比较明显,另一个方向上的梯度幅值变化较弱,其余部分的点都还是集中原点附近,这样Harris矩阵的两个特征值理论应该是一个比较大,一个比较小,当然对于边缘,某些情况下致使计算出的特征值并不是都特别的大,但仍跟含有角点的窗口的分布情况具有不同。 Harris角点检测的性质:旋转不变性,光照不变性、比度变化部分不变性;同时不具有尺度不变性

2. Sobel滤波

sobel 算子是一个主要用做边缘检测的离散微分算子,Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。这本次实践中,用sobel滤波器计算梯度。

3.角点响应函数R

定义了角点响应函数R ,通过判定R 大小来判断窗口是否有角点,即 R > threshold 时则有角点(threshold是我们自定义的一个阈值。)
角点应该满足基本性质:窗口最小的特征值尽量的大,此时R =λmin ;当然还有更有效的响应函数,计算公式为:R = det(M)−k(trace(M))2 其中的k是超参数
(一般的,增大k的值,降低角点检测的灵敏度,减少被检测角点的数量;减少k值,增加角点检测的灵敏度,增加被检测角点的数量。)

二、代码实现

1. main函数

main函数主要功能对图像进行读取,并进行灰度图转化,同时调用corner_detect函数,对检测结果进行可视化展示


def main():
    # 读取图片
    image = cv2.imread("./test1.jpg")
    height, width, channel = image.shape
    cv2.imshow('image', image)
    cv2.waitKey(1000)
    cv2.destroyAllWindows()

    # 灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
	# 调用corner_detect函数计算角点
    dst = corner_detect(gray,block_size=3, nms_size=3, method='harris', k=0.04
)
	
	# 直接用opencv中的角点检测函数  dst = cv2.cornerHarris(gray, blockSize=3, ksize=3,k=0.05) 
    image_dst = image[:, :, :]
    image_dst[dst > 0.01 * dst.max()] = [0, 0, 255]
    #可视化结果
    cv2.imshow('dst', image_dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

2. corner_detect函数

主要功能对输入灰度图进行角点检测,并返回角点检测的结果corner
# 对输入图像进行Sobel滤波并计算梯度、计算每个像素的H矩阵、计算角点检测响应图R。
def corner_detect(img, block_size, nms_size, method, k):
    """
    img:待检测的灰度图
    block_size:领域范围
    nms_size:非最大值抑制的邻域范围
    method:检测角点的方法
    k:超参数
    """
    H, W = img.shape[:2]

    # 1.sobel滤波
    pad = block_size // 2  # pad=1
    out = np.zeros((H + pad * 2, W + pad * 2), dtype=float)
    out[pad:pad + H, pad:pad + W] = img.copy().astype(float)

    tmp = out.copy()
    out_v = out.copy()
    out_h = out.copy()

    K_v = [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]
    K_h = [[1, 0, -1], [2, 0, -2], [1, 0, -1]]
    for y in range(H):
        for x in range(W):
            out_v[pad + y, pad + x] = sum(K_v * (tmp[y:y + block_size, x:x + block_size]))
            out_h[pad + y, pad + x] = sum(K_h * (tmp[y:y + block_size, x:x + block_size]))
    out_v = np.clip(out_v, 0, 255)
    out_h = np.clip(out_h, 0, 255)
    out_v = out_v[pad:pad + H, pad:pad + W].astype(np.uint8)
    out_h = out_h[pad:pad + H, pad:pad + W].astype(np.uint8)

    # 2.计算梯度
    grad = np.zeros((H, W, 2), dtype=float)
    grad[:, :, 0] = out_v
    grad[:, :, 1] = out_h
    # 3.计算H矩阵
    H_m = np.zeros((H, W, 3), dtype=float)
    H_m[:, :, 0] = grad[:, :, 0] ** 2
    H_m[:, :, 1] = grad[:, :, 1] ** 2
    H_m[:, :, 2] = grad[:, :, 0] * grad[:, :, 1]
    # 取最小特征值作为检测值,记在H_e矩阵
    H_e = np.zeros((H, W), dtype=float)
    for i in range(H):
        for j in range(W):
            if H_m[i, j, 0] > H_m[i, j, 1]:
                H_e[i, j] = H_m[i, j, 1]
            else:
                H_e[i, j] = H_m[i, j, 0]

    # 4.计算角点检测响应图R
    H_m = [np.array([[H_m[i, j, 0], H_m[i, j, 2]], [H_m[i, j, 2], H_m[i, j, 1]]]) for i in range(H) for j in range(W)]
    D, T = list(map(np.linalg.det, H_m)), list(map(np.trace, H_m))
    R = np.array([abs(d - k * t ** 2) for d, t in zip(D, T)])

    # 5.将计算出响应函数的值R进行非极大值抑制,滤除一些不是角点的点,同时要满足大于设定的阈值
    R_max = np.max(R)
    R = R.reshape(H, W)
    H_max = np.max(H_e)
    corner = np.zeros_like(R, dtype=uint8)

    for i in range(H):
        for j in range(W):
            if method == 'harris':
                # 角点检测器,以及对3x3邻域内非极大值进行抑制
                if R[i, j] > R_max * 0.01 and R[i, j] == np.max(
                        R[max(0, i - 1):min(i + nms_size - 1, H - 1), max(0, j - 1):min(j + nms_size - 1, W - 1)]):
                    corner[i, j] = 255
            else:
                # 依据H矩阵最小特征值进行检测
                if H_e[i, j] > H_max * 0.01:
                    corner[i, j] = 255

    cv2.imshow('corner', corner)
    # cv2.waitKey(0)
    return corner

三、结果对比

1.不同检测方法(对比一下使用角点检测器R与H矩阵最小特征值)

图1为角点检测器R,图2为只使用最小特征值,显然使用检测器R的效果不如最小特征值,检测出的角点更多倾向于边缘线条而不是单独的点

2.不同超参数k(使用角点检测器R,改变k值)

图3为k=0.05,图4为k=0.03,可知增大k的值,降低角点检测的灵敏度,减少被检测角点的数量;减少k值,增加角点检测的灵敏度,增加被检测角点的数量。

3.非最大值抑制(对3x3的邻域范围内非最大值抑制)

加入之后能解决角点检测器R的线条问题,对于检测连续的点进行抑制,角点效果更好
(几乎看不出原图的美女有角点)

四、参考资料

  1. CSDN博主「果壳乄」的文章《角点特征之Harris角点检测算法》原文链接:
    https://blog.csdn.net/my_kun/article/details/106918857

  2. CSDN博主「weixin_39745345」的文章《python sobel滤波_Sobel滤波器》原文链接:https://blog.csdn.net/weixin_39745345/article/details/111515227

  3. 博客园博主wyu123《学习OpenCV中边缘检测的各种算子和滤波器——Canny算子,Sobel算子,Laplace算子以及Scharr滤波器》原文链接:https://www.cnblogs.com/wyuzl/p/6286083.html

有关计算机视觉实践之角点检测--Python实现--sobel滤波--Harris矩阵--角点检测器R--非极大值抑制的更多相关文章

  1. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  2. 旋转矩阵的几何意义 - 2

    点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度;     在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。

  3. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  4. ruby - 检测由 RSpec、Ruby 运行的代码 - 2

    我想知道我的代码是否在rspec下运行。这可能吗?原因是我正在加载一些错误记录器,这些记录器在测试期间会被故意错误(expect{x}.toraise_error)弄得乱七八糟。我查看了我的ENV变量,没有(明显的)测试环境变量的迹象。 最佳答案 在spec_helper.rb的开头添加:ENV['RACK_ENV']='test'现在您可以在代码中检查RACK_ENV是否经过测试。 关于ruby-检测由RSpec、Ruby运行的代码,我们在StackOverflow上找到一个类似的问题

  5. ruby - 使用 Ruby Daemons gem 检测停止 - 2

    我正在使用rubydaemongem。想知道如何向停止操作添加一些额外的步骤?希望我能检测到停止被调用,并向其添加一些额外的代码。任何人都知道我如何才能做到这一点? 最佳答案 查看守护程序gem代码,它似乎没有用于此目的的明显扩展点。但是,我想知道(在守护进程中)您是否可以捕获守护进程在发生“停止”时发送的KILL/TERM信号...?trap("TERM")do#executeyourextracodehereend或者你可以安装一个at_exit钩子(Hook):-at_exitdo#executeyourextracodehe

  6. ruby - Ruby 脚本如何检测到它正在 irb 中运行? - 2

    我有一个定义类的Ruby脚本。我希望脚本执行语句BoolParser.generate:file_base=>'bool_parser'仅当脚本作为可执行文件被调用时,而不是当它被irbrequire(或通过-r在命令行上传递)时。我可以用什么来包装上面的语句,以防止它在我的Ruby文件加载时执行? 最佳答案 条件$0==__FILE__...!/usr/bin/ruby1.8classBoolParserdefself.generate(args)p['BoolParser.generate',args]endendif$0==_

  7. Ruby 无法检测字符串中的换行符 - 2

    我有以下字符串,我想检测那里的换行符。但是Ruby的字符串方法include?检测不到它。我正在运行Ruby1.9.2p290。我哪里出错了?"/'ædres/\nYour".include?('\n')=>false 最佳答案 \n需要在双引号内,否则无法转义。>>"\n".include?'\n'=>false>>"\n".include?"\n"=>true 关于Ruby无法检测字符串中的换行符,我们在StackOverflow上找到一个类似的问题: h

  8. 华为OD机试真题 C++ 实现【带传送阵的矩阵游离】【2023 Q2 | 200分】 - 2

            所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。

  9. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  10. ruby - 是否有 chrome 开关来抑制 'external protocol request' ? - 2

    是否有chrome开关来抑制“外部协议(protocol)请求”?我正在使用selenium-ruby​​-watirwebdriver自动化应用程序。我在网上搜索了绕过此窗口和对话的解决方案:http://productforums.google.com/forum/#!topic/chrome/K22hXwRy6zQ概述了我们如何手动执行此操作。但是对于Selenium-Chrome-Ruby,我需要通过可能设置一个chrome开关(chorme开关列表:=http://src.chromium.org/svn/trunk/src/chrome/common/chrome_swit

随机推荐