数学形态学是一门20c60s发展起来的理论,用于分析和处理离散图像。它定义了一系列运算,用预先定义的形状元素探测图像,从而实现图像的转换。这个结构元素与像素领域的相交方式决定了运算的结果。

*形态学滤波器服饰和膨胀图像

腐蚀和膨胀是最基本的形态学运算。数学形态学中最基本的概念是结构元素。结构元素可以简单地定义为像素的组合,在对应的像素上定义了一个原点。形态学滤波器的应用过程就包含了用这个结构元素探测图像中每个像素的操作过程。(结构元素原则上可以是任何形状,但通常它是一个简单形状,如正方形、圆形或菱形,并且把中心作为原点)

实现过程:

首先我们从百度上下载一张图:

然后通过阈值化创建二值图像

(在形态学中,习惯用高像素值白色表示前景物体,低像素值表示背景物体)

得到如下图像:

接下来就是实现腐蚀和膨胀了,在OpenCV中,实现两功能的函数分别为cv::erode和cv::dilate

先看结果:

代码:

int main()
{cv::Mat image = cv::imread("cow.jpg");cv::imshow("原图", image);cv::cvtColor(image, image, CV_BGR2GRAY);cv::threshold(image, image, 75, 255, cv::THRESH_BINARY_INV);cv::imshow("二值图像", image);cv::Mat eroded, dilated;cv::erode(image, eroded, cv::Mat());cv::dilate(image, dilated, cv::Mat());cv::imshow("腐蚀图", eroded);cv::imshow("膨胀图", dilated);cvWaitKey();
}

腐蚀就是把当前像素替换成所定义像素集合中的最小像素值,腐蚀时,如果结构元素放到某个像素位置时碰到了背景,那么这个像素就会变成背景。

膨胀是浮士德反运算,它把当前像素替换成所定义像素集合中的最大像素值,膨胀时,如果结构元素放到某个背景像素位置时碰到了前景物体,那么这个像素就会标为白色。

可见,腐蚀和膨胀的程度与结构元素的大小有关,OpenCV默认的是3*3正方形结构元素。

若是需要更改结构元素的大小,可以按照下面的代码:

cv::Mat element(7, 7, CV_8U, cv::Scalar(1));
cv::erode(image, eroded, element);

当然多次腐蚀/膨胀可以达到同样的效果

*用形态学滤波器开启和闭合图像

为了应用较高级别的形态学滤波器,需要用cv::morphologyEx函数

开启图像:

闭合图像:

代码:

cv::Mat opened, closed;
cv::Mat element5(5, 5, CV_8U, cv::Scalar(1));
cv::morphologyEx(image, opened, cv::MORPH_OPEN, element5);
cv::imshow("开启图像", opened);
cv::morphologyEx(image, closed, cv::MORPH_CLOSE, element5);
cv::imshow("闭合图像",closed);

个人感觉,OpenCV里opened image & closed image 算法思想应该是差不多的,效果很像。

继续向后阅览,也是验证了我的想法。

闭合的定义是对图像先膨胀后腐蚀,开启的定义是对图像先腐蚀后膨胀。

用途:

闭合滤波器->可用于把错误分裂成小碎片的物体连接起来

开启滤波器->可用于移除因图像噪声产生的斑点

一般使用顺序为先闭合滤波器,后开启滤波器(个人总结,因为先使用开启滤波器会消除掉部分物体碎片)

*用形态学滤波器检测边缘和角点

这里需要用到一张建筑图片,于是我请出了在一卡通上跟随我多年的老图书馆照(第一个大创项目的登陆界面也用的这个),这样:

边缘检测结果:

这里由于树木的原因,可能边缘检测效果不算非常好。

为了用形态学检测角点,定义了MorphoFeatures类。

角点检测结果:

可以看到,大部分角点能检测出来,不过也有误差(这种效果用来特征匹配用于视觉里程计应该是没问题了)

代码:

MorphoFeatures类:

class MorphoFeatures
{
private:int threshold;cv::Mat_<uchar> cross;cv::Mat_<uchar> diamond;cv::Mat_<uchar> square;cv::Mat_<uchar> x;
public:MorphoFeatures() : threshold(-1), cross(5, 5), diamond(5, 5), square(5, 5), x(5, 5){//创建十字形结构元素cross <<0, 0, 1, 0, 0,0, 0, 1, 0, 0,1, 1, 1, 1, 1,0, 0, 1, 0, 0,0, 0, 1, 0, 0;diamond <<0, 0, 1, 0, 0,0, 1, 0, 1, 0,1, 0, 0, 0, 1,0, 1, 0, 1, 0,0, 0, 1, 0, 0;square <<1, 1, 1, 1, 1,1, 0, 0, 0, 1,1, 0, 0, 0, 1,1, 0, 0, 0, 1,1, 1, 1, 1, 1;x <<1, 0, 0, 0, 1,0, 1, 0, 1, 0,0, 0, 1, 0, 0,0, 1, 0, 1, 0,1, 0, 0, 0, 1;}cv::Mat getCorners(const cv::Mat& image, int threshold){cv::Mat result;//用十字元素膨胀cv::dilate(image, result, cross);//用菱形元素腐蚀cv::erode(result, result, diamond);cv::Mat result2;//用X元素膨胀cv::dilate(image, result2, x);//用正方形元素腐蚀cv::dilate(result2, result2, square);//比较两个经过闭合运算的图像,得到角点cv::absdiff(result2, result, result);//获得二值图像cv::threshold(result, result, threshold, 255, cv::THRESH_BINARY_INV);return result;}
};

main函数:

int main()
{cv::Mat image = cv::imread("old_library.jpg");cv::imshow("原图", image);cv::cvtColor(image, image, CV_BGR2GRAY);cv::Mat result;cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());cv::threshold(result, result, 90, 255, cv::THRESH_BINARY_INV);cv::imshow("边缘", result);MorphoFeatures mpf;cv::Mat corners = mpf.getCorners(result,20);cv::Mat image2 = cv::imread("old_library.jpg");image2.copyTo(result, corners);cv::imshow("角点检测", result);cvWaitKey();
}

*分水岭算法实现图像分割

分水岭算法是一种流行的图像处理算法,用于快速将图像分割为多个同质区域。(久仰大名)

OpenCV给出的是分水岭算法的改进版(原始版本会过度分割图像),调用cv::watershed函数。该函数的输入对象是一个标记图像,图像的像素值为32位有符号整数,每个非零像素代表一个标签。

这里使用牛吃草的图,其二值化图像:

现在二值化图像包含了太多属于图像不同部分的白色像素,因此要对图像做深度腐蚀运算,只保留属于重点物体的像素,效果如下:

类似地,通过对原二值图像做大幅度膨胀运算来选中一些背景像素:

灰色的为背景天空

合并两个图像得到标记图像:

将标记图像作为分水岭函数的输入参数,得到标签图像:

也可用分水岭线条表示:

代码:

WatershedSegmenter类:

class WatershedSegmenter
{
private:cv::Mat marker;
public:void setMarkers(const cv::Mat& markerImage){markerImage.convertTo(marker,CV_32S);}void showMarkers(){cv::imshow("markers", marker);}void process(const cv::Mat& image){cv::watershed(image, marker);}cv::Mat getsegmentation(){cv::Mat tmp;marker.convertTo(tmp, CV_8U);return tmp;}cv::Mat getWatersheds(){cv::Mat tmp;marker.convertTo(tmp, CV_8U, 255, 255);return tmp;}
};

main函数:

int main()
{cv::Mat image = cv::imread("cow.jpg");cv::cvtColor(image, image, CV_BGR2GRAY);cv::threshold(image, image, 90, 255, cv::THRESH_BINARY_INV);cv::imshow("二值化图像", image);cv::Mat eroded;cv::erode(image, eroded, cv::Mat(), cv::Point(-1, -1), 4);cv::imshow("深度腐蚀图像", eroded);cv::Mat dilated;cv::dilate(image, dilated, cv::Mat(), cv::Point(-1, -1), 4);cv::threshold(dilated, dilated, 1, 128, cv::THRESH_BINARY_INV);cv::imshow("背景图像",dilated);cv::Mat markers(image.size(), CV_8U, cv::Scalar(0));markers = eroded + dilated;cv::imshow("标记图像", markers);WatershedSegmenter segmenter;segmenter.setMarkers(markers);segmenter.showMarkers();segmenter.process(image);cv::imshow("分水岭图像", segmenter.getsegmentation());cv::imshow("分水岭线条", segmenter.getWatersheds());cvWaitKey();
}

错误记录:实际程序运行到cv::watershed(image,marker)这一句时编译器会报错,但是跳过执行之后仍能得到正确的结果,比较令人困惑。

*用MSER算法提取特征区域

最大稳定极限区域(MSER)算法也用相同的水淹类比,以便从图像中提取有意义的区域,创建这些区域时也使用逐步提高水位的方法,但是MSER关注的是在水淹过程中的某个时间段内保持相对稳定的盆地,这些区域对应图像中某些物体的独特部分。

检测结果:

可以看出,这里能大致检测出窗户这些特殊部分,并用随机颜色标注。

版本更改说明:

OpenCV2的MSER基础类是cv::MSER,可直接用其实例化对象,在OpenCV3.3中报错提示MSER类含纯虚函数,即MSER为抽象类,不能实例化,后查阅OpenCV官方文档(传送门),发现其与之前版本有较大的更改

参数含义:

最新版本中,使用cv::MSER::create()方法返回一个MSER指针,而检测MSER特征则是使用的如下函数:

参数含义:

该函数同样是一个纯虚函数,所以需要用上个函数返回的指针变量来使用该纯虚函数:mser->detectRegions(image,points,bboxes);在这里,bboxes的含义我不是很清楚,但是仅就定义一下不去理会似乎也能得到想要的结果。

代码:

int main()
{cv::Mat image = cv::imread("old_library.jpg");cv::Ptr<cv::MSER> mser = cv::MSER::create(5,//检测极值区域时使用的增量200,//允许的最小面积1500);//允许的最大面积std::vector<std::vector<cv::Point>> points;std::vector<cv::Rect> bboxes;mser->detectRegions(image, points, bboxes);cv::Mat output(image.size(), CV_8UC3);output = cv::Scalar(255, 255, 255);//随机数生成器cv::RNG rng;for (std::vector<std::vector<cv::Point>>::iterator it = points.begin(); it != points.end(); ++it){//生成随机颜色cv::Vec3b color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));for (std::vector<cv::Point>::iterator itPts = it->begin(); itPts != it->end(); itPts++){if (output.at<cv::Vec3b>(*itPts)[0] == 255){output.at<cv::Vec3b>(*itPts) = color;}}}cv::imshow("detected result", output);cvWaitKey();
}

MSER检测的结果是一个包含点集的容器。由于我们通常更关心区域的整体而不是单个像素的位置,因此普遍采用含有位置和大小信息的单一的几何图形来表示MSER,常用的形状是带边缘的椭圆,画椭圆的过程封装在MSERFeatures类中。

检测结果:

代码:

MSERFeatures类:

class MSERFeatures
{
private:cv::Ptr<cv::MSER> mser = cv::MSER::create();double minAreaRatio;
public:MSERFeatures(int minArea = 60, int maxArea = 14400,double minAreaRatio = 0.5,int delta = 5,double maxVariation = 0.25,double minDiversity = 0.2) :minAreaRatio(minAreaRatio){mser = cv::MSER::create(delta, minArea, maxArea, maxVariation, minDiversity);}//得到对应每个MSER特征的旋转带边框的矩形//如果(MSER面积/矩形面积) < areaRatio,就清除这个特征void getBoundingRects(const cv::Mat& image, std::vector<cv::RotatedRect>& rects){//检测MSER特征std::vector<std::vector<cv::Point>> points;std::vector<cv::Rect> bboxes;mser->detectRegions(image, points, bboxes);//针对某个检测到的特征for (std::vector<std::vector<cv::Point>>::iterator it = points.begin(); it != points.end(); it++){cv::RotatedRect rr = cv::minAreaRect(*it);//检查面积比例if (it->size() > minAreaRatio*rr.size.area()){rects.push_back(rr);}}}cv::Mat getImageOfEllipses(const cv::Mat& image, std::vector<cv::RotatedRect>& rects, cv::Scalar color = 255){cv::Mat output = image.clone();getBoundingRects(image, rects);for (std::vector<cv::RotatedRect>::iterator it = rects.begin(); it != rects.end(); it++){cv::ellipse(output, *it, color);}return output;}
};

main函数:

int main()
{cv::Mat image = cv::imread("old_library.jpg");cv::imshow("原图", image);MSERFeatures mserf(200, 1500, 0.5);std::vector<cv::RotatedRect> rects;cv::Mat result = mserf.getImageOfEllipses(image, rects,cv::Scalar(255,255,255));cv::imshow("识别区椭圆", result);cvWaitKey();
}

*GrabCut算法提取前景物体

如果要从静态图像中提取前景物体(例如从一个图像剪切物体黏贴到另一个图像),采用GrubCut算法是最好的选择。

cv::grabCut函数的用法非常简单。只需要输入一个图像,并对一些像素做上属于背景或属于前景的标记。算法会根据这个局部的标记,计算出整个图像中前景/背景的分割线

前景检测结果:

代码:

int main()
{cv::Mat image = cv::imread("cow.jpg");cv::imshow("原图", image);cv::Rect rectangle(131, 135, 216, 140);cv::rectangle(image, rectangle, cv::Scalar(255, 255, 255));cv::imshow("image with rectangle", image);cv::Mat result;cv::Mat bgModel, fgModel;image = cv::imread("cow.jpg");cv::grabCut(image, result, rectangle,//包含前景的矩形bgModel, fgModel, //模型5,//迭代次数cv::GC_INIT_WITH_RECT);//使用矩形cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);//生成输出图像cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255));image.copyTo(foreground, result);cv::imshow("result", foreground);cvWaitKey();
}

注意点:

调用cv::grabCut时,除了需要输入图像和分割后图像,还需要定义两个矩阵,用于存放和构建模型。

输出的分割图像可以是一下的四个值之一:

cv::GC_BGD:这个值表示明确属于背景的像素

cv::GC_FGD:这个值表示明确属于前景的像素

cv::GC_PR_BGD:这个值表示可能属于背景的像素

cv::GC_PR_BGD:这个值表示可能属于前景的像素

OpenCV_用形态学运算变换图像相关推荐

  1. 用形态学运算变换图像

    数学形态学用于分析和处理离散图像,它定义了一系列的运算,用预定义的形状元素探测图像,从而实现图像的转换. 1.形态学滤波器腐蚀和膨胀图像 结构元素定义为像素的组合,在对应的像素上定义一个原点(锚点). ...

  2. 使用Julia进行图像处理--使用形态学运算进行图像调整

    使用Julia进行图像处理--使用形态学运算进行图像调整 前言 图像二值化 基本运算 图像侵蚀 使用侵蚀分离物体 准备用于文本识别的图像 图像膨胀 合并几乎连接的对象 突出显示细节 派生操作 图像开运 ...

  3. 使用色彩追踪和形态学运算得到图像中感兴趣区域

    色彩追踪简介:在RGB图像中,我们感兴趣的部分往往具有趋于一致的颜色,我们想得到感兴趣部分时,可以考虑先找到特定颜色的区域.比如说有一张风景照,我们对蓝天白云(其它部分不是蓝色)感兴趣,我们考虑使用色 ...

  4. 图像形态学运算之图像开闭运算 含python实现

    如果您觉得本文不错,帮忙点赞哦! 一. 图像形态学处理 -- 膨胀和腐蚀 图解图像腐蚀和膨胀 ↑ 二. 开运算与闭运算: 开运算:先腐蚀后膨胀,能够消除图像区域外的小白点(噪声). 闭运算:先膨胀后腐 ...

  5. 形态学上的图像顶帽运算和黑帽运算是什么?

    作者 | 杨秀璋 责编 | 夕颜 出品 | CSDN博客 数学形态学(Mathematical morphology)是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论.其 ...

  6. 数字图像处理(13): 形态学处理——图像开运算与图像闭运算

    目录 1 图像开运算(先腐蚀,后膨胀) 1.1 基本原理 1.2 代码示例 2 图像闭运算(先膨胀,后腐蚀) 2.1 基本原理 2.2 代码示例 3 图像梯度运算(膨胀 - 腐蚀) 3.1 基本原理 ...

  7. 二值形态学操作、图像的边缘检测、图像编码

    实验五 二值形态学操作 一.实验目的  了解二值形态学的基本运算  掌握基本形态学运算的Matlab实现  了解形态操作的应用 二.原理     收缩和膨胀是数学形态学最基本的变换,数学形态学的 ...

  8. 什么叫做形态学图像处理_图像形态学处理中的膨胀与腐蚀介绍

    重要:本文最后更新于2020-03-04 10:40:53,某些文章具有时效性,若有错误或已失效,请在下方留言或联系代码狗. 本文主要是对图像形态学处理中的膨胀.腐蚀运算方法介绍及在图像中实现的理论基 ...

  9. opencv形态学运算:腐蚀(erode)和膨胀(dilate)

    形态学操作就是基于形状的一系列图像处理操作.OpenCV为进行图像的形态学变换提供了快捷.方便的函数.最基本的形态学操作有二种,他们是:膨胀与腐蚀(Dilation与Erosion). 膨胀与腐蚀能实 ...

  10. 数字图像处理:第八章 形态学运算

    第八章 形态学运算 目录 引言 基本概念 开运算和闭运算 击中击不中变换HMT(Hit-Miss Transform) 边界和骨架(Boundary and Skeleton) 作业 1.引言 形态学 ...

最新文章

  1. 李开复给中国学生的第六封信:选择的智慧
  2. 使用Windows版Redis
  3. android电池充电动画,Android 开机充电图标和充电动画效果
  4. 《java入门第一季》之类面试题
  5. mysql_数据备份和迁移(Windows)
  6. python的异常处理及异常类定义
  7. windows防火墙设置_合理利用Windows 7防火墙,阻止部分功能,避免网络恶意软件攻击...
  8. linux系统初始化脚本
  9. Unity发布游戏在iOS设备上出现的字体问题
  10. Web之一只jio碰到门了
  11. 【SVL官方说明文档】
  12. mc服务器不显示皮肤,游戏中不显示皮肤的可能原因
  13. 编写php自动脚本,自己编写自动签到脚本
  14. 预测学习应用于机器人之Unsupervised Learning for Physical Interaction through Video Prediction
  15. 26个数据分析案例——第二站:基于Hive的民航客户价值分析
  16. 机器学习数据预处理----分类型文字数据的处理
  17. 吉首大学2019年程序设计竞赛 E——多喝嘤料
  18. python实现爬取东方财富网
  19. python怎么表示循环小数_循环小数表示法
  20. 做好培训或是展示PPT的几大原则

热门文章

  1. Excel自动批量发邮件
  2. qq连连看java版_Java实战_仿QQ连连看
  3. 如何批量设置 Word 文档的打开密码?
  4. 基于.net开发chrome核心浏览器【六】
  5. 全体是合同,多学学吧。当前少吃亏。
  6. java开发autocad_.NET AutoCAD二次开发之路(四、文字篇)
  7. 数据结构-快速排序 C语言源码
  8. Python掷骰子游戏
  9. 智慧城市的顶层设计与底层对接
  10. 中国航空标准件市场运行动态分析及十四五发展规划研究报告2022年版