Android OpenCV(五十二):Harris角点检测

什么是角点?

角点就是极值点,即在某方面属性特别突出的点。当然,你可以自己定义角点的属性(设置特定熵值进行角点检测)。角点可以是两条线的交叉处,也可以是位于相邻的两个主要方向不同的事物上的点。角点通常被定义为两条边的交点,或者说,角点的局部邻域应该具有两个不同区域的不同方向的边界。常见的角点有:

  • 灰度梯度的最大值对应的像素点;
  • 两条直线或者曲线的交点;
  • 一阶梯度的导数最大值和梯度方向变化率最大的像素点;
  • 一阶导数最大,二阶导数为零的像素点(指示物体边缘变化不连续的方向)。

为什么要检测角点?

角点是图像很重要的特征,对图像图形的理解和分析有很重要的作用。角点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。角点在三维场景重建、运动估计、目标跟踪、目标识别、图像配准与匹配等计算机视觉领域起着非常重要的作用。

Harris角点检测

人眼对角点的识别通常是在一个局部的小区域或小窗口完成的。如果在各个方向上移动这个特征的小窗口,窗口内区域的灰度发生了较大的变化,那么就认为在窗口内遇到了角点。如果这个特定的窗口在图像各个方向上移动时,窗口内图像的灰度没有发生变化,那么窗口内就不存在角点;如果窗口在某一个方向移动时,窗口内图像的灰度发生了较大的变化,而在另一些方向上没有发生变化,那么,窗口内的图像可能就是一条直线的线段。

基本原理

Harris角点检测原理用公式演算如下:
E ( u , v ) = ∑ x , y w ( x , y ) [ I ( x + u , y + v ) − I ( x , y ) ] 2 = ∑ x , y w ( x , y ) [ I ( x , y ) + I x u + I y v + O ( u 2 , v 2 ) − I ( x , y ) ] 2 ≈ ∑ x , y w ( x , y ) [ I x u + I y v ] 2 = ∑ x , y w ( x , y ) [ u 2 I x 2 + 2 u v I x I y + v 2 I y 2 ] = ∑ x , y w ( x , y ) [ u v ] [ I x 2 I x I y I x I y I y 2 ] [ u v ] = [ u v ] ( ∑ x , y w ( x , y ) [ I x 2 I x I y I x I y I y 2 ] ) [ u v ] E(u,v)= \sum_{x,y}w(x,y)[I(x+u,y+v)-I(x,y)]^2 \\ =\sum_{x,y}w(x,y)[I(x,y)+I_xu+I_yv+O(u^2,v^2)-I(x,y)]^2 \\ \approx\sum_{x,y}w(x,y)[I_xu+I_yv]^2 \\ =\sum_{x,y}w(x,y)[u^2I_x^2+2uvI_xI_y+v^2I_y^2]\\ =\sum_{x,y}w(x,y)\begin{bmatrix}u & v\end{bmatrix}\begin{bmatrix} I_x^2 & I_xIy \\ I_xI_y & I_y^2\end{bmatrix}\begin{bmatrix}u \\ v\end{bmatrix}\\ =\begin{bmatrix}u & v\end{bmatrix}(\sum_{x,y}w(x,y)\begin{bmatrix} I_x^2 & I_xIy \\ I_xI_y & I_y^2\end{bmatrix})\begin{bmatrix}u \\ v\end{bmatrix} E(u,v)=x,yw(x,y)[I(x+u,y+v)I(x,y)]2=x,yw(x,y)[I(x,y)+Ixu+Iyv+O(u2,v2)I(x,y)]2x,yw(x,y)[Ixu+Iyv]2=x,yw(x,y)[u2Ix2+2uvIxIy+v2Iy2]=x,yw(x,y)[uv][Ix2IxIyIxIyIy2][uv]=[uv](x,yw(x,y)[Ix2IxIyIxIyIy2])[uv]
用M代替中间内容:
E ( u , v ) = [ u v ] M [ u v ] E(u,v)=\begin{bmatrix}u & v\end{bmatrix}M\begin{bmatrix}u \\ v\end{bmatrix} E(u,v)=[uv]M[uv]
其中
M = ∑ x , y w ( x , y ) [ I x 2 I x I y I x I y I y 2 ] I x , I y 分 别 为 窗 口 内 像 素 点 ( x , y ) 在 x 方 向 上 和 y 方 向 上 的 梯 度 值 。 M=\sum_{x,y}w(x,y)\begin{bmatrix} I_x^2 & I_xIy \\ I_xI_y & I_y^2\end{bmatrix}\\ I_x,I_y分别为窗口内像素点(x,y)在x方向上和y方向上的梯度值。 M=x,yw(x,y)[Ix2IxIyIxIyIy2]Ix,Iy(x,y)xy
其中w(x,y)表示滑动窗口权重函数,可以是常数也可以是高斯函数。E(u,v)表示滑动窗口向各个方向移动时像素值衡量系数的变化。

权重函数

定义Harris角点评价系数R:
R = d e t ( M ) − k ( t r a c e ( M ) ) 2 d e t ( M ) = λ 1 λ 2 t r a c e ( M ) = λ 1 + λ 2 R=det(M)-k(trace(M))^2 \\ det(M)=λ_1λ_2 \\ trace(M)=λ_1+λ_2 \\ R=det(M)k(trace(M))2det(M)=λ1λ2trace(M)=λ1+λ2
这里λ1,λ2是矩阵M的2个特征值,k是一个指定值,这是一个经验参数,需要实验确定它的合适大小,通常它的值在0.04和0.06之间,它的存在只是调节函数的形状而已。R取决于M的特征值,对于角点|R|很大,平坦的区域|R|很小,边缘的R为负值;

R评价系数

API

public static void cornerHarris(Mat src, Mat dst, int blockSize, int ksize, double k, int borderType) 
  • 参数一:src,输入源图像。必须是单通道8U或者32F类型。
  • 参数二:dst,输出评价系数R的矩阵。尺寸与src相同,类型为单通道32F。
  • 参数三:blockSize,领域大小。
  • 参数四:ksize,Sobel算子的半径。
  • 参数五:k,计算Harris评价系数R的权重系统。
  • 参数六:borderType,像素外推算法标志位。

dst为评价系数R的输出矩阵,由于评价系数有正有负且范围较广,计算结束后通常需要进行归一化处理。然后通过经验阈值比较判断像素点是否为Harris角点。阈值越大,提取的Harris角点越少,阈值越小,提取的Harris角点越多。

操作

/**
 * Harris角点检测
 * author: yidong
 * 2020/12/30
 */
class HarrisActivity : AppCompatActivity() {
    private val mBinding: ActivityHarrisBinding by lazy {
        ActivityHarrisBinding.inflate(layoutInflater)
    }

    private val gray by lazy {
        this.getBgrFromResId(R.drawable.lena).toGray()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(mBinding.root)

        mBinding.ivLena.showMat(gray)
        wrapCoroutine({ showLoading() }, { doCornerHarris() }, { hideLoading() })
    }

    private fun doCornerHarris() {
        val dst = Mat()
        val dstNorm = Mat()
        val dstNormal8U = Mat()
        Imgproc.cornerHarris(gray, dst, 2, 3, 0.04)
        Core.normalize(dst, dstNorm, 0.0, 255.0, Core.NORM_MINMAX)
        Core.convertScaleAbs(dstNorm, dstNormal8U)
        Imgproc.threshold(dstNormal8U, dstNormal8U, 120.0, 255.0, Imgproc.THRESH_BINARY)
        GlobalScope.launch(Dispatchers.Main) {
            mBinding.ivResult.showMat(dstNormal8U)
        }
    }

    private fun showLoading() {
        mBinding.isLoading = true
    }

    private fun hideLoading() {
        mBinding.isLoading = false
    }
    
    override fun onDestroy() {
        super.onDestroy()
        gray.release()
    }
}

效果

如下图,图中白点即为评价系数大于120的角点检测结果。代码中的参数可自行调整测试。

在这里插入图片描述

源码

https://github.com/onlyloveyd/LearningAndroidOpenCV

onlyloveyd CSDN认证博客专家 Android Kotlin OpenCV
个人公众号【OpenCV or Android】,热爱Android、Kotlin、Flutter和OpenCV。毕业于华中科技大学计算机专业,曾就职于华为武汉研究所。目前在三线小城市生活,专注Android、OpenCV、Kotlin、Flutter等有趣的技术。
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页