</pre><pre name="code" class="cpp">#include "stdafx.h"
#include<opencv2/opencv.hpp>
using namespace cv;class MorphoFeatures{private://用于生成二值图像的阈值int threshold;//角点检测用到的结构元素Mat cross;Mat diamond;Mat square;Mat x;public://检测角点//opencv没有直接实现它,需要定义四种不同的结构元素,//包括:菱形,方形,十字形,x形。并在构造函数中完成MorphoFeatures() :threshold(-1),cross(5, 5, CV_8U, Scalar(0)),diamond(5, 5, CV_8U, Scalar(1)),square(5, 5, CV_8U, Scalar(1)),x(5, 5, CV_8U, Scalar(0)){//创建十字形元素for (int i = 0; i < 5; i++){cross.at<uchar>(2, i) = 1;cross.at<uchar>(i, 2) = 1;}//创建菱形diamond.at<uchar>(0, 0) = 0;diamond.at<uchar>(0, 1) = 0;diamond.at<uchar>(1, 0) = 0;diamond.at<uchar>(4, 4) = 0;diamond.at<uchar>(3, 4) = 0;diamond.at<uchar>(4, 3) = 0;diamond.at<uchar>(4, 0) = 0;diamond.at<uchar>(4, 1) = 0;diamond.at<uchar>(3, 0) = 0;diamond.at<uchar>(0, 4) = 0;diamond.at<uchar>(0, 3) = 0;diamond.at<uchar>(1, 4) = 0;//创建x形for (int i = 0; i < 5; i++){x.at<char>(i, i) = 1;x.at<char>(4 - i, i) = 1;}}Mat getEdges(const Mat &image){//得到梯度图Mat result;//morphology函数配上合适的滤波器就可以实现直线检测morphologyEx(image, result, MORPH_GRADIENT, Mat());applyThreshold(result);return result;}void setThreshold(int t){threshold = t;}void applyThreshold(Mat & result){if (threshold > 0)cv::threshold(result, result, threshold, 255, THRESH_BINARY);}//需要连接使用这些结构元素,得到最终的角点映射图Mat getCorners(const Mat&image){Mat result;//十字形膨胀dilate(image, result, cross);//菱形腐蚀erode(result, result, diamond);Mat result2;//x形膨胀dilate(image, result2, x);//方形腐蚀erode(result2, result2, square);//通过对两张图像做差值,得到角点图像absdiff(result2, result, result);//阈值化得到二值图像applyThreshold(result);return result;}//为了可视化,在在二值图像中每个监测点绘制一个圆void drawOnImage(const Mat & binary, Mat & image){Mat_<uchar>::const_iterator it = binary.begin<uchar>();Mat_<uchar>::const_iterator itend = binary.end<uchar>();//遍历每个像素for (int i = 0; it != itend; ++i, ++it){if (!*it)circle(image, Point(i%image.step, i / image.step), 5, Scalar(0, 255, 0));}}
};
int _tmain(int argc, _TCHAR* argv[])
{//直线检测Mat image = imread("building.jpg", 0);//imshow("d", image);if (!image.data)return -1;MorphoFeatures morpho;morpho.setThreshold(40);Mat edges;edges = morpho.getEdges(image);imshow("d", edges);//角点检测morpho.setThreshold(-1);Mat corners;corners = morpho.getCorners(image);morphologyEx(corners, corners, MORPH_TOPHAT, Mat());threshold(corners, corners, 40, 255, THRESH_BINARY_INV);//imshow("角点",corners);  //展示图片上的角点  morpho.drawOnImage(corners, image);imshow("jj", image);waitKey(0);return 0;
}

首先如何理解对灰度图像进行形态学操作?

一种比较形象的方法是将灰度图像看做是“等高线”:亮的区域代表山峰,而暗的区域代表山谷,图像的边沿就对应于峭壁。如果腐蚀一幅图像,会导致山谷被扩展,而峭壁减少了。相反的,如果膨胀一幅图像,峭壁则会增加。但是这两种情况下,中间的部分(大片的谷底和高原)基本保持不变。

在上述理解的基础上,如果我们对图像的腐蚀和膨胀的结果做差,就能提取图像的边界:因为边界区域,二者完全不同。(实际上,我们也可以用腐蚀或者膨胀的结果与源图像做差得出类似结果,但提取的边界会比较细)。可以看出,结构元越大,边界越粗。在OpenCV中,将形态学操作函数morphologyEx 的第4个参数设为MORPH_GRADIENT,就能完成上述工作。

开运算可以 用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

闭运算能够排除小型黑洞(黑色区域)

形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来保留物体的边缘轮廓

顶帽运算(Top Hat)为原图像与“开运算“的结果图之差,因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差,黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。所以,黑帽运算用来分离比邻近点暗一些的斑块。

void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,InputArray kernel, Point anchor, int iterations,int borderType, const Scalar& borderValue )
{Mat src = _src.getMat(), temp;_dst.create(src.size(), src.type());Mat dst = _dst.getMat();switch( op ){case MORPH_ERODE:erode( src, dst, kernel, anchor, iterations, borderType, borderValue );break;case MORPH_DILATE:dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );break;case MORPH_OPEN:erode( src, dst, kernel, anchor, iterations, borderType, borderValue );dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );break;case CV_MOP_CLOSE:dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );break;case CV_MOP_GRADIENT:erode( src, temp, kernel, anchor, iterations, borderType, borderValue );dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );dst -= temp;break;case CV_MOP_TOPHAT:if( src.data != dst.data )temp = dst;erode( src, temp, kernel, anchor, iterations, borderType, borderValue );dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );dst = src - temp;break;case CV_MOP_BLACKHAT:if( src.data != dst.data )temp = dst;dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );dst = temp - src;break;default:CV_Error( CV_StsBadArg, "unknown morphological operation" );}
}
  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
  • 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
  • 第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
    • MORPH_OPEN – 开运算(Opening operation)
    • MORPH_CLOSE – 闭运算(Closing operation)
    • MORPH_GRADIENT -形态学梯度(Morphological gradient)
    • MORPH_TOPHAT - “顶帽”(“Top hat”)
    • MORPH_BLACKHAT - “黑帽”(“Black hat“)
  • 第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:

其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:

  • 矩形: MORPH_RECT
  • 交叉形: MORPH_CROSS
  • 椭圆形: MORPH_ELLIPSE

而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。

我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。

getStructuringElement函数相关的调用示例代码如下:

int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸//获取自定义核
Mat element =getStructuringElement(MORPH_RECT,Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),Point(g_nStructElementSize, g_nStructElementSize ));

调用这样之后,我们便可以在接下来调用erode、dilate或morphologyEx 函数时,kernel参数填保存getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。

  • 第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
  • 第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
  • 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
  • 第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

OpenCV学习笔记(14):形态学滤波对图像进行边缘及角点检测相关推荐

  1. Opencv开发笔记(三):使用形态学滤波对图像进行边缘及角点检测

    形态学滤波可以用于检测图像中指定的特征点,接下来这篇文章我们运用Opencv中的形态学函数来检测灰度图中的直角和角点. 首先我们为了方便使用先封装一个名称为MorphoFeatures的类,然后就可以 ...

  2. OpenCV学习笔记(十六)——CamShift研究 OpenCV学习笔记(十七)——运动分析和物体跟踪Video OpenCV学习笔记(十八)——图像的各种变换(cvtColor*+)imgproc

    OpenCV学习笔记(十六)--CamShift研究 CamShitf算法,即Continuously Apative Mean-Shift算法,基本思想就是对视频图像的多帧进行MeanShift运算 ...

  3. OpenCV学习笔记(十七):图像修补:inpaint()

    OpenCV学习笔记(十七):图像修补:inpaint() inpaint()函数 使用区域邻域在图像中还原选定区域. void inpaint( InputArray src, // 表示要修复的图 ...

  4. OpenCV学习笔记(十):图像金字塔Pyramid和图像缩放:pyrDown(),pyrUp(),resize()

    OpenCV学习笔记(十):图像金字塔Pyramid和图像缩放:pyrDown(),pyrUp(),resize() 一.图像金字塔定义: 图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是 ...

  5. OpenCV学习笔记(三):图像对比度、亮度调整源码

    OpenCV学习笔记(三):图像对比度.亮度调整源码 主函数: #include <opencv2/opencv.hpp>using namespace cv;using namespac ...

  6. opencv学习笔记14:图像礼帽,图像黑帽

    python + OpenCV 图像礼帽 图像礼帽 也叫图像顶帽 礼帽图像=原始图像-开运算图像 得到噪声图像 开运算:先腐蚀再膨胀 使用对象:二值图像 使用方法:morphologyEx cv2.M ...

  7. Opencv学习笔记(二) 提取图像中的水平线和垂直线

    提取图像中的水平线和垂直线属于基础的形态学操作的应用,原理:根据要提取图形来定义一个特定的结构元素,然后以这个结构相素去遍历图像,进行一系列形态学操作,以此过滤掉其他特征的图形,达到提取的效果. 示例 ...

  8. OpenCV + CPP 系列(卅三)图像特征提取(Harris角点检测、Shi-Tomasi角点检测、自定义角点检测)

    文章目录 一.常用图像特征描述 二.Harris角点检测 演示Harris角点检测 三.Shi-Tomasi角点检测 四.自定义角点检测器 一.常用图像特征描述 SIFT.SURF.HOG.Haar. ...

  9. 【OpenCV学习笔记】之六 手写图像旋转函数---万丈高楼平地起

    话说,平凡之处显真格,这一点也没错!  比如,对旋转图像进行双线性插值,很简单吧?  可,对我,折腾了大半天,也没有达到预期效果!  尤其是三个误区让我抓瞎好久: 1,坐标旋转公式.   这东西,要用 ...

最新文章

  1. SOA标准发展混乱 国内业务缺少经验
  2. 西南这座城,脸,比北上广还有用
  3. 《告别失控:软件开发团队管理必读》一一1.2 成功的程序设计经理为什么难当...
  4. nunjucks渲染富文本解析错误输出字符串而不是元素
  5. 对数函数定义域和值域为r_对数函数
  6. linux mdev -s没有运行,mdev详解
  7. 安川g7接线端子图_图解西门子S7-300plc模拟量模块接线方法
  8. Variant 数组
  9. 从微软和思科的 IT 服务化转型过程中得到的几点思考
  10. 5G 商用第三年:无人驾驶的“上山”与“下海”
  11. Web Polygraph (WebAxe-4) 测试反向代理缓存服务器
  12. java 密码 星号显示_Java多线程 例子 cmd窗口下 实现输入密码星号显示
  13. CES2020即将完结!盘点这些脑洞产品,保证你看一眼就被种草
  14. 浅谈MySQL安全加固
  15. HP-UX 11.31 安装RAC 添加共享磁盘的问题
  16. 多个containers 共用一个pvc_室外成品pvc
  17. Qt定制化安装包工具
  18. top命令详解(转载学习)
  19. 电商用户数据分析报告
  20. GIS二次开发学习专题(一)C#入门

热门文章

  1. 10多个下载Photoshop Brushes的最佳网站
  2. 摄影测量手动提取立体像对同名点像素,相对定向解算,特征点提取,影像匹配同名点的python实现
  3. 粗暴解决node-gyp的configure error问题
  4. nRF2401A无线传输模块介绍
  5. oracle 僵死的进程,【原】杀掉oracle僵死进程
  6. slam第四讲Sophus库编译问题
  7. 转载一张照片,现代版的阿里巴巴与四十大盗
  8. 关于GetAsyncKeyState
  9. 如何在抖音中实现一键复制微信号并打开微信
  10. linux开启终端输出问候语,给Linux命令行加个问候语