用形态学的方法实现图像的角点检测的算法原理详解和代码实现(Pyton和C++代码)

Posted 昊虹图像算法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用形态学的方法实现图像的角点检测的算法原理详解和代码实现(Pyton和C++代码)相关的知识,希望对你有一定的参考价值。

​本篇博文基础知识请参考博文:https://blog.csdn.net/wenhao_ir/article/details/51888042
特别是其中的博文 https://blog.csdn.net/wenhao_ir/article/details/124763833
和博文 https://blog.csdn.net/wenhao_ir/article/details/125157967

在正式介绍用形态学的方法实现图像的角点检测的原理前,大家首先要知道以下两点:
①因为角点只是一个点,所以对角点的检测是在一个比较微小的结构进行的运算,我们应该在八邻域的尺度上来观察、分析和处理这个问题。
②在八邻域的尺度上角分为两类:
第一类为由水平边缘和垂直边缘形成的直角,在下文中我们姑且把它称为A型角。这种角的典形代表为下面这个角:

第二类为含45度斜线的角,在下文中我们姑且把它称为B型角。这种角的典型代表为下面这两个角:

好,有了以上准备知识,我们开始来介绍如何用形态学的方法实现图像的角点检测。

先说步骤,再具体分析为什么。
第01步—用十字形结构对原图像作膨胀处理得到图像①;
第02步—用菱形结构对图像①进行腐蚀操作得到图像②;
第03步—用X形结构对原图像作膨胀操作得到图像③;
第04步—用矩形结构对图像③进行腐蚀操作得到图像④;
第05步—用图像④减去图像②并求绝对值,得到角点的初步图像⑤;
第06步—对图像⑤作二值化阈值处理,得到真正的角点。

上面六步中,最难理解的是前五步,所以我们不妨将上面照片中的三种角代入具体的程序,看每一步作了怎样的处理。

下面两幅图中就含了上面提到的三种角。

其中左边这幅图中的四个角为上面提到的A型角,右边这幅图中的三个角为上面提到的B型角。

先上完整的代码,然后观察每一步的运行结果:
代码中用到的两幅图像的下载地址如下:
https://pan.baidu.com/s/1L4hgDEqlElaOIw7rUvMBHA?pwd=hikt

# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也请联系博主
# 图像处理技术交流QQ群 271891601

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1

import numpy as np
import cv2 as cv
import sys

# 读取图像
A1 = cv.imread('F:/material/images/Corner/A_corner.bmp', 0)
B1 = cv.imread('F:/material/images/Corner/B_corner.bmp', 0)
# 判断图片是否读取成功
if A1 is None:
    print('Error,Failed to read Image.')
    sys.exit()
if B1 is None:
    print('Error,Failed to read Image.')
    sys.exit()

# 第1步:十字形对原图进行膨胀操作
crossMat = cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))
A1_step01 = cv.dilate(A1, crossMat)
B1_step01 = cv.dilate(B1, crossMat)

# 第2步:菱形对原图进行腐蚀操作
diamondMat = cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))
diamondMat[1, 1] = 1
diamondMat[1, 3] = 1
diamondMat[3, 1] = 1
diamondMat[3, 3] = 1
A1_step02 = cv.erode(A1_step01, diamondMat)
B1_step02 = cv.erode(B1_step01, diamondMat)

# 第3步:X形对原图进行膨胀
xMat = np.eye(5, dtype='uint8')
xMat[0, 4] = 1
xMat[1, 3] = 1
xMat[3, 1] = 1
xMat[4, 0] = 1
A1_step03 = cv.dilate(A1, xMat)
B1_step03 = cv.dilate(B1, xMat)

# 第4步:正方形对上一步得的图像进行腐蚀
squareMat = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
A1_step04 = cv.erode(A1_step03, squareMat)
B1_step04 = cv.erode(B1_step03, squareMat)

# 第5步:用第4步得到的图像和第2步得到的图像作差值
A1_step05 = cv.absdiff(A1_step04, A1_step02)
B1_step05 = cv.absdiff(B1_step04, B1_step02)

# 第6步:把第5步得到的结果进行二值化处理
_, A1_step06 = cv.threshold(A1_step05, 100, 255, cv.THRESH_BINARY)

B1_step06 = B1_step05.copy()

# 由于B1_step05一个阈值无法将三个角点提取出来,所以这里分区域用不同的阈值进行处理。
B1_step06_1 = B1_step06[7:14, 24:31]
B1_step06_2 = B1_step06[36:42, 10:46]
_, B1_step06_1 = cv.threshold(B1_step06_1, 150, 255, cv.THRESH_BINARY)
_, B1_step06_2 = cv.threshold(B1_step06_2, 75, 255, cv.THRESH_BINARY)

B1_step06[7:14, 24:31] = B1_step06_1
B1_step06[36:42, 10:46] = B1_step06_2


cv.imshow("A1_step06", A1_step06)
cv.imshow("B1_step06", B1_step06)

cv.waitKey()

运行结果及分析如下:
首先我们来看两个原图中角点附近的数据情况:
A1图中的四个角实际上是一样的,所以我们只看其左上角的角:

再看B1中的三个角点的情况,由于B1中的左下角和右下角实际上是一样的,所以我们只看其最上面的角和左下的角即可。


下面我们来观察分析各步的运行结果:

目录

第01步的运行结果及分析





从第01步的运行结果可以看出,A型角没有被膨胀,而边缘和B型角都被膨胀了。

第02步的运行结果及分析





从这一步的运行结果可以看出,相对于原图:A型角的角点被腐蚀了,而边缘和B型角经过这一步的腐蚀处理,恢复成原状了,这样在第02步得到的图像中便只包含B型角和边缘了。

第03步的运行结果及分析





从第03步的运行结果来看,A型角和边缘被膨胀了,而B型角没有被膨胀。

第04步的运行结果及分析





从第04步的运行结果来看,相对于原图,A型角和边缘恢复成原图那样,而B型角的角点被腐蚀了,这样在第04步得到的图像中便只包含A型角和边缘了。

第05步的运行结果及分析

在第02步得到的图像中包含B型角和边缘,在第04步得到的图像中包含A型角和边缘,我们用第02步和第04步的图像相减并求绝对值,就可以把边缘抵消,并且把A型角和B型角凸显出来,第05步便是这个操作。

第05步的运行结果如下:



从第 05步的结果可以看出,各角点附近的点都被检测出来了,这些有值的点作为角点其实我们都可以接受,但角点只有一个,所以我们下一步中我们需要用阈值进行二值化处理,从而选出唯一的角点。

第06步的运行结果及分析





从第06步的运行结果可以看出,通过一系列形态学处理,第一幅图中的四个A型角点和第二幅图中的三个B型角点被检测出来。
总结一下:算法的第01步和第02步用于检测A型角点;第03步和第04步用于检测B型角点。

附C++代码

首先是适用于第一幅图的C++源码:

//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理开发需求也请联系博主
//图像处理技术交流QQ群 271891601

//OpenCV版本:3.0.0
//VS版本:2013


#include <opencv2/opencv.hpp>

#include <iostream>

using namespace cv;
using namespace std;

int main()

	cv::Mat srcImage = cv::imread("F:/material/images/Corner/A_corner.bmp");
	if (!srcImage.data)
		return 1;
	cv::Mat srcGray;
	cv::cvtColor(srcImage, srcGray, CV_RGB2GRAY);
	cv::imshow("srcGray", srcGray);
	// 定义结构元素
	Mat CrossMat(5, 5, CV_8U, Scalar(0));
	Mat diamondMat(5, 5, CV_8U, Scalar(1));
	Mat squareMat(5, 5, CV_8U, Scalar(1));
	Mat x(5, 5, CV_8U, Scalar(0));
	//  十字形形状  
	for (int i = 0; i<5; i++)
	
		CrossMat.at<uchar>(2, i) = 1;
		CrossMat.at<uchar>(i, 2) = 1;
	
	// 菱形形状
	diamondMat.at<uchar>(0, 0) = 0;
	diamondMat.at<uchar>(0, 1) = 0;
	diamondMat.at<uchar>(1, 0) = 0;
	diamondMat.at<uchar>(4, 4) = 0;
	diamondMat.at<uchar>(3, 4) = 0;
	diamondMat.at<uchar>(4, 3) = 0;
	diamondMat.at<uchar>(4, 0) = 0;
	diamondMat.at<uchar>(4, 1) = 0;
	diamondMat.at<uchar>(3, 0) = 0;
	diamondMat.at<uchar>(0, 4) = 0;
	diamondMat.at<uchar>(0, 3) = 0;
	diamondMat.at<uchar>(1, 4) = 0;
	// X形状
	for (int i = 0; i<5; i++)
		x.at<uchar>(i, i) = 1;
		x.at<uchar>(4 - i, i) = 1;
	

	// 第1步:十字形对原图进行膨胀
	Mat cross_exp;
	dilate(srcGray, cross_exp, CrossMat);
	//cv::imshow("cross_exp", cross_exp);

	// 第2步:菱形对上步进行腐蚀
	Mat diamond_ero;
	erode(cross_exp, diamond_ero, diamondMat);
	//cv::imshow("diamond_ero", diamond_ero);

	// 第3步:X形对原图进行膨胀
	Mat X_shape_exp;
	dilate(srcGray, X_shape_exp, x);
	//cv::imshow("X_shape_exp", X_shape_exp);

	// 第4步:正方形对上步进行腐蚀
	Mat square_ero;
	erode(X_shape_exp, square_ero, squareMat);
	//cv::imshow("square_ero", square_ero);

	// 第5步:计算差值
	Mat result_diff;
	absdiff(square_ero, diamond_ero, result_diff);
	//cv::imshow("result_diff", result_diff);

	//第6步:阈值化处理
	Mat result_bin;
	threshold(result_diff, result_bin, 100, 255, THRESH_BINARY);
	//cv::imshow("result_bin", result_bin);
	
	// 绘图
	for (int i = 0; i < result_bin.rows; i++)
	
		// 获取行指针
		const uchar* data = result_bin.ptr<uchar>(i);
		for (int j = 0; j < result_bin.cols; j++)
		
			// 如果是角点 则进行绘制圆圈
			if (data[j])
				circle(srcGray, Point(j, i), 3,
				Scalar(255, 255, 255));
		
	
	cv::imshow("result", srcGray);
	cv::waitKey(0);
	return 0;

运行结果如下图所示:

再上适用于第二幅图的C++源码:

//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理开发需求也请联系博主
//图像处理技术交流QQ群 271891601

//OpenCV版本:3.0.0
//VS版本:2013


#include <opencv2/opencv.hpp>

#include <iostream>

using namespace cv;
using namespace std;

int main()

	cv::Mat srcImage = cv::imread("F:/material/images/Corner/B_corner.bmp");
	if (!srcImage.data)
		return 1;
	cv::Mat srcGray;
	cv::cvtColor(srcImage, srcGray, CV_RGB2GRAY);
	cv::imshow("srcGray", srcGray);
	// 定义结构元素
	Mat CrossMat(5, 5, CV_8U, Scalar(0));
	Mat diamondMat(5, 5, CV_8U, Scalar(1));
	Mat squareMat(5, 5, CV_8U, Scalar(1));
	Mat x(5, 5, CV_8U, Scalar(0));
	//  十字形形状  
	for (int i = 0; i<5; i++)
	
		CrossMat.at<uchar>(2, i) = 1;
		CrossMat.at<uchar>(i, 2) = 1;
	
	// 菱形形状
	diamondMat.at<uchar>(0, 0) = 0;
	diamondMat.at<uchar>(0, 1) = 0;
	diamondMat.at<uchar>(1, 0) = 0;
	diamondMat.at<uchar>(4, 4) = 0;
	diamondMat.at<uchar>(3, 4) = 0;
	diamondMat.at<uchar>(4, 3) = 0;
	diamondMat.at<uchar>(4, 0) = 0;
	diamondMat.at<uchar>(4, 1) = 0;
	diamondMat.at<uchar>(3, 0) = 0;
	diamondMat.at<uchar>(0, 4) = 0;
	diamondMat.at<uchar>(0, 3) = 0;
	diamondMat.at<uchar>(1, 4) = 0;
	// X形状
	for (int i = 0; i<5; i++)
		x.at<uchar>(i, i) = 1;
		x.at<uchar>(4 - i, i) = 1;
	

	// 第1步:十字形对原图进行膨胀
	Mat cross_exp;
	dilate(srcGray, cross_exp, CrossMat);
	//cv::imshow("cross_exp", cross_exp);

	// 第2步:菱形对上步进行腐蚀
	Mat diamond_ero;
	erode(cross_exp, diamond_ero, diamondMat);
	//cv::imshow("diamond_ero", diamond_ero);

	// 第3步:X形对原图进行膨胀
	Mat X_shape_exp;
	dilate(srcGray, X_shape_exp, x);
	//cv::imshow("X_shape_exp", X_shape_exp);

	// 第4步:正方形对上步进行腐蚀
	Mat square_ero;
	erode(X_shape_exp, square_ero, squareMat);
	//cv::imshow("square_ero", square_ero);

	// 第5步:计算差值
	Mat result_diff;
	absdiff(square_ero, diamond_ero, result_diff);
	//cv::imshow("result_diff", result_diff);

	//第6步:阈值化处理,一个阈值无法将三个角点提取出来,所以这里分区域用不同的阈值进行处理。
	Mat result_bin = result_diff.clone();
	Mat result_bin_roi_1 = result_bin(cv::Rect(24, 6, 7, 7));
	Mat result_bin_roi_2 = result

以上是关于用形态学的方法实现图像的角点检测的算法原理详解和代码实现(Pyton和C++代码)的主要内容,如果未能解决你的问题,请参考以下文章

火炉炼AI机器学习048-Harris检测图像角点

各位高手,matlab实现了两幅图像的角点检测,怎样实现角点匹配

图像的角点简介

图像角点检测

图像角点检测

特征提取算法——Harris角点提取