Harris角点学习[通俗易懂]

Harris角点学习[通俗易懂]人们普遍认为角点是二维图像亮度变化剧烈的点或图像边缘曲线上曲率极大值的点。这些点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。其在三维场景重建、运动估计、目标跟踪、目标识别、图像配准与匹配等计算机视觉领域起着非常重要的作用。  角点的检测主要有两类基于图像边缘的方法和基于图像灰度的方法。前者很

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

    人们普遍认为角点是二维图像亮度变化剧烈的点或图像边缘曲线上曲率极大值的点。这些点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。其在三维场景重建、运动估计、目标跟踪、目标识别、图像配准与匹配等计算机视觉领域起着非常重要的作用。

   角点的检测主要有两类基于图像边缘的方法和基于图像灰度的方法。前者很大程度上依赖于图像的分割和边缘提取,一旦待检测目标发生局部变化,很可能导致操作失败,因此该类方法使用范围较小;后者有很多方法,包括Harris算子,Moravec算子,Susan算子等等。

  本文的目的是在学习了Harris算子后,进行一个总结。文献:《A COMBINED CORNER AND EDGE DETECTOR》,1988,Chris Harris & Mike Stephens

一、角点边缘的直观概念:

角点:最直观的印象就是在水平和竖直两个方向变化均较大的两个点,即X,Y方向梯度都较大

边缘:仅在水平或竖直方向有较大的变化量,即X、Y方向梯度只有一个较大

平坦区域:在水平和竖直方向的变化量均较小,即X、Y方向梯度都较小

下图是理想情况的角点、边缘和平坦区域的示意图:

Harris角点学习[通俗易懂]

二、原理:

  由于CSDN不知道怎么把编辑的公式弄进来,所以整段粘帖论文了,英文不难懂:

Harris角点学习[通俗易懂]

Harris角点学习[通俗易懂]

Harris角点学习[通俗易懂]

论文后面那三个注意点讲得不够详细而且要求特征值。1988年的论文中提出,可以用以下方法代替求特征值,并给出了角点相应函数R:

Harris角点学习[通俗易懂]

其中,A和B分别表示C(x,y)矩阵主对角线两个元素,C代表其副对角线元素。k和较大值判断的阈值T,在下面代码中给出,为经验数值。

如下图所示,当R为较大正数时,该区域为角点区域,当R为较大负数时,该区域为边缘区域,当R的绝对值较小时,该区域为平坦区域。

Harris角点学习[通俗易懂]

三、具体实现

我将分别给出matlab和 c 两个版本。其中,c 版本不是使用什么goodfeaturetotrack来实现的

matlab版本:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Harris角点提取算法                                          %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear;
%filename = 'Lena.jpg';
filename='xx.png';
X = imread(filename);     % 读取图像
% imshow(X);
Info = imfinfo(filename); %获取图像相关信息
if (Info.BitDepth > 8)
    f = rgb2gray(X);
end
%《基于特征点的图像配准与拼接技术研究》
%计算图像亮度f(x,y)在点(x,y)处的梯度-----------------------------------------------
% fx = [5 0 -5;8 0 -8;5 0 -5];          % 高斯函数一阶微分,x方向(用于改进的Harris角点提取算法)
ori_im = double(f) / 255;                   %unit8转化为64为双精度double64
fx = [-2 -1 0 1 2];                     % x方向梯度算子(用于Harris角点提取算法)
Ix = filter2(fx, ori_im);                % x方向滤波
% fy = [5 8 5;0 0 0;-5 -8 -5];          % 高斯函数一阶微分,y方向(用于改进的Harris角点提取算法)
fy = [-2; -1; 0; 1; 2];                     % y方向梯度算子(用于Harris角点提取算法)
Iy = filter2(fy, ori_im);                % y方向滤波
%构造自相关矩阵---------------------------------------------------------------
Ix2 = Ix .^ 2;
Iy2 = Iy .^ 2;
Ixy = Ix .* Iy;
clear Ix;
clear Iy;
h= fspecial('gaussian', [7 7], 2);        % 产生7*7的高斯窗函数,sigma=2
Ix2 = filter2(h,Ix2);
Iy2 = filter2(h,Iy2);
Ixy = filter2(h,Ixy);
%提取特征点---------------------------------------------------------------
height = size(ori_im, 1);
width = size(ori_im, 2);
result = zeros(height, width);           % 纪录角点位置,角点处值为1
R = zeros(height, width);
Rmax = 0;                              % 图像中最大的R值
k = 0.06; %k为常系数,经验取值范围为0.04~0.06
for i = 1 : height
    for j = 1 : width
        M = [Ix2(i, j) Ixy(i, j); Ixy(i, j) Iy2(i, j)];             % auto correlation matrix
        R(i,j) = det(M) - k * (trace(M)) ^ 2;                     % 计算R
        if R(i,j) > Rmax
            Rmax = R(i, j);
        end;
    end;
end;
%T = 0.01 * Rmax;%固定阈值,当R(i, j) > T时,则被判定为候选角点
T = 0.1 * Rmax;%固定阈值,当R(i, j) > T时,则被判定为候选角点

%在计算完各点的值后,进行局部非极大值抑制-------------------------------------
cnt = 0;
for i = 2 : height-1
    for j = 2 : width-1
        % 进行非极大抑制,窗口大小3*3
        if (R(i, j) > T && R(i, j) > R(i-1, j-1) && R(i, j) > R(i-1, j) && R(i, j) > R(i-1, j+1) && R(i, j) > R(i, j-1) && ...
                R(i, j) > R(i, j+1) && R(i, j) > R(i+1, j-1) && R(i, j) > R(i+1, j) && R(i, j) > R(i+1, j+1))
            result(i, j) = 1;
            cnt = cnt+1;
        end;
    end;
end;
i = 1;
    for j = 1 : height
        for k = 1 : width
            if result(j, k) == 1;
                corners1(i, 1) = j;
                corners1(i, 2) = k;
                i = i + 1;
            end;
        end;
    end;
[posc, posr] = find(result == 1);
figure,imshow(ori_im);
hold on;
plot(posr, posc, 'r+');

运行效果图:

Harris角点学习[通俗易懂]Harris角点学习[通俗易懂]

下面给出 c 语言版本,读图采用Opencv函数:

// My_corner.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include<iostream>#include <opencv2/core/core.hpp>  #include <opencv2/highgui/highgui.hpp>  #include <opencv2/imgproc/imgproc.hpp>#include <GL/glut.h>using namespace std;using namespace cv;void m_filter(double *src,double *&dst,int height,int width,double *mask,int mW,int mH){	double a;	for (int i = 0;i < height;i++)	{		for (int j = 0;j < width;j++)		{			a = 0.0;			//去掉靠近边界的行列,无法滤波,超出范围			if (i > int(mH/2) && i < height - int(mH/2) && j > int(mW) && j < width - int(mW/2))			{				for (int m = 0;m < mH;m++)				{					for(int n = 0;n < mW;n++)					{						a += src[(i+m-int(mH/2))*width+(j+n-int(mW))]*mask[m*mW+n];					}				}			}			dst[i*width+j] = a;		}	}}int _tmain(int argc, _TCHAR* argv[]){	Mat img_src = imread("test1.jpg");	Mat img_g,corner;	double *src,*img_x,*img_y,*img_x2,*img_y2,*img_xy;	Size size = img_src.size();	img_g = Mat(size,CV_8UC1);	corner = Mat(size,CV_8UC1);	src = new double[size.width*size.height];	img_x = new double[size.width*size.height];	img_y = new double[size.width*size.height];	img_x2 = new double[size.width*size.height];	img_y2 = new double[size.width*size.height];	img_xy = new double[size.width*size.height];	cvtColor(img_src,img_g,CV_BGR2GRAY);	for (int i=0;i<size.height;i++)	{		for (int j=0;j<size.width;j++)		{			src[i*size.width+j]=(double)img_g.data[i*size.width+j]/255;		}	}		//定义水平方向差分算子并求img_x	double dx[25] = {-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,0,1,2};	m_filter(src,img_x,size.height,size.width,dx,5,5);	//定义垂直方向差分算子并求img_y	double dy[25] = {-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2};	m_filter(src,img_y,size.height,size.width,dy,5,5);	FILE *fp;	//fp=fopen("test.txt","w+");	//计算img_x2、img_y2、img_xy	for (int i = 0;i < size.height;i++)	{		for (int j = 0;j < size.width;j++)		{			img_x2[i*size.width+j] = img_x[i*size.width+j] * img_x[i*size.width+j];			img_y2[i*size.width+j] = img_y[i*size.width+j] * img_y[i*size.width+j];			img_xy[i*size.width+j] = img_x[i*size.width+j] * img_y[i*size.width+j];			//fprintf(fp,"%f,%f,%f,%f,%f",img_x[i*size.width+j],img_y[i*size.width+j],img_x2[i*size.width+j],img_y2[i*size.width+j],img_xy[i*size.width+j]);			//fprintf(fp,"\n");		}	}	//fclose(fp);	//对img_x2、img_y2、img_xy进行高斯平滑,本例中使用5×5的高斯模板	// 计算模版参数	int gausswidth = 7;	double sigma = 2;	double *g = new double[gausswidth*gausswidth];	for(int i = 0;i < gausswidth;i++)		for(int j = 0;j < gausswidth;j++)		{			g[i*gausswidth+j] = exp(-((i-int(gausswidth/2))*(i-int(gausswidth/2))+(j-int(gausswidth/2))*(j-int(gausswidth/2)))/(2*sigma));		}		//归一化:使模板参数之和为1	double total=0;	for(int i = 0;i < gausswidth*gausswidth;i++)		total += g[i];	for(int i =  0;i < gausswidth;i++)		for(int j = 0;j < gausswidth;j++){			g[i*gausswidth+j] /= total;		}	//进行高斯平滑	m_filter(img_x2,img_x2,size.height,size.width,g,gausswidth,gausswidth);	m_filter(img_y2,img_y2,size.height,size.width,g,gausswidth,gausswidth);	m_filter(img_xy,img_xy,size.height,size.width,g,gausswidth,gausswidth);	//计算角点量R,R(i,j) = det(M) - k * (trace(M)) ^ 2	//Tr(M)=img_x2+img_y2 ,det(M)=img_x2*img_y2-img_xy^2	double *R = new double[size.width*size.height];	double k = 0.06; //k为常系数,经验取值范围为0.04~0.06	double Rmax=0;	for(int i = 0; i < size.height; i++)	{		for(int j = 0; j < size.width; j++)		{			R[i*size.width+j] = img_x2[i*size.width+j] * img_y2[i*size.width+j] - img_xy[i*size.width+j] * img_xy[i*size.width+j] - k * (img_x2[i*size.width+j] + img_y2[i*size.width+j]) * (img_x2[i*size.width+j] + img_y2[i*size.width+j]);			if (R[i*size.width+j] > Rmax)			{				Rmax = R[i*size.width+j];			}			//printf("%f\n",R[i*size.width+j]);		}	}	double max;	double T = 0.1 * Rmax;//固定阈值,当R(i, j) > T时,则被判定为候选角点	double *mx = new double[size.width*size.height];	//在计算完各点的值后,进行局部非极大值抑制	for (int i=1;i<size.height-1;i++)	{		for (int j=1;j<size.width-1;j++)		{			//写不下了,分两行。。。进行非极大抑制,窗口大小3*3			if (R[i*size.width+j]> T && R[i*size.width+j] > R[(i-1)*size.width+j-1] && R[i*size.width+j] > R[(i-1)*size.width+j] && R[i*size.width+j] >R[(i-1)*size.width+j+1] && R[i*size.width+j] >R[i*size.width+j-1])			{				if (R[i*size.width+j]>R[i*size.width+j+1]&&R[i*size.width+j]>R[(i+1)*size.width+j-1]&&R[i*size.width+j]>R[(i+1)*size.width+j]&&R[i*size.width+j]>R[(i+1)*size.width+j+1])				{					corner.data[i*size.width+j]=1; 				}			}		}	}	//作图	Scalar sca(0,255,0);	for(int i = 0; i < size.height; i++)	{		for(int j = 0; j < size.width; j++)		{			if (corner.data[i*size.width+j] == 1)			{				Point p(j,i);				circle(img_src,p,20,sca);			}		}	}	imshow("find corner :)",img_src);	waitKey(0);	free(g);free(R);free(mx);	return 0;}

运行效果图:

Harris角点学习[通俗易懂]

Harris角点学习[通俗易懂]

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/189590.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 怎样创建FTP服务器

    怎样创建FTP服务器1W次浏览2016.07.21更新用IIS建立FTP服务器不是非常复杂,操作起来比较简单,类似于用IIS建立网站,其中涉及的虚拟目录等概念和网站中的虚拟目录一致。用IIS建立FTP服务器不是非常

  • bat中使用mshta调用vbs语句获取精准计算的日期

    bat中使用mshta调用vbs语句获取精准计算的日期1.在bat中,获取指定的日期格式是比较麻烦的。因为cmd环境中,日期格式随着系统的时间格式设置而变化,通常系统默认的时间格式是“\’”符号作为连接符。日期通常用在文件名上,比如日志的文件名。那么日期的格式就只能用“-”符号代替,更改系统日期格式会显得容易被外界因素改变,导致不稳定。当然也可以用bat中的set来进行替换,但这样费代码量。2.对于计算日期来说,bat是难以准确做到的,也需要堆积代码量才能完成。特别是到了每个月的一号时,无法确定昨天是否是31号还是30号还是28号。那么bat就只能陷入迷茫

  • [C#] 剖析 AssemblyInfo.cs – 了解常用的特性 Attribute

    [C#] 剖析 AssemblyInfo.cs – 了解常用的特性 Attribute剖析 AssemblyInfo.cs-了解常用的特性Attribute【博主】反骨仔【原文】http://www.cnblogs.com/liqingwen/p/5944391.h

  • css transition ease,css3 transition属性「建议收藏」

    css transition ease,css3 transition属性「建议收藏」最近打算学习css3知识,觉得css3写出来的效果好炫好酷,之前一直想要学习来着。可能之前的决心,毅力,耐心不够,所以想要重整起来,放下浮躁的心态,一步一个脚印,踏踏实实的来学习。首先学习的是css3transition属性,该属性的定义为从一个属性值平滑过渡到另一个属性值。格式为:transition:,或transition-property:transition-duration:tr…

  • ASP.NET MVC (五、HttpClient接口解析)

    ASP.NET MVC (五、HttpClient接口解析)前言:MVC对于已经跨域的接口进行解析是个比较容易的事情。况且在第四章节的时候已经通过Ajax进行了页面的解析测试,效果也比较明显。所以本章节从容应对。这个世界上只有一种真正的英雄主义:认清生活的真相,并且仍然热爱它。难道向上攀爬的那条路,不是比站在顶峰更让人心潮澎湃吗?1、MVC项目创建在解决方案上点击【鼠标右键】,依次选择【添加】【新建项目】选择【ASP.NETWeb应用程序(.NETFramework)】项目,点击【下一步】输入项目名称,这里是【M…

  • 领域驱动实践总结(基本理论总结与分析V+架构分析与代码设计+具体应用设计分析)

    领域驱动实践总结(基本理论总结与分析V+架构分析与代码设计+具体应用设计分析)领域驱动实践总结一:基本理论总结与分析一、领域驱动设计两大设计:战略设计和战术设计二、理解和分析领域+子域+核心域+通用域+支撑域三、理解和分析界限上下文,定义领域边界四、理解和分析实体和值对象五、理解和分析聚合思想:聚合和聚合根六、理解很分析领域事件来解耦微服务…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号