OpenCV图像处理教程C++(十一) 形态学操作--膨胀、腐蚀、开运算、闭运算、形态学梯度、顶帽、黑帽

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

膨胀:此操作将图像(A)与任意形状的内核 (B),通常为正方形或圆形,进行卷积。内核 B 有一个可定义的 锚点, 通常定义为内核中心点。进行膨胀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最大相素值提取,并代替锚点位置的相素。显然,这一最大化操作将会导致图像中的亮区开始”扩展” (因此有了术语膨胀 dilation )。这种操作会造成图像中像素值高的区域变大,而像素值小的区域变小。(感官上图像变细,变白了)

腐蚀:跟膨胀操作造作的效果刚好相反。腐蚀在形态学操作家族里是膨胀操作的孪生姐妹。它提取的是内核覆盖下的相素最小值。进行腐蚀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最小相素值提取,并代替锚点位置的相素。以与膨胀相同的图像作为样本,我们使用腐蚀操作。从下面的结果图我们看到亮区变细,而黑色区域则变大了。(感官上图像变粗,变黑了)

膨胀与腐蚀能实现多种多样的功能,主要如下:

  • 消除噪声
  • 分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素。
  • 寻找图像中的明显的极大值区域或极小值区域
  • 列表内容
  • 求出图像的梯度

形态学膨胀——dilate函数

void dilate(
    InputArray src,//输入图像,即源图像,填Mat类的对象即可。
    OutputArray dst,//即目标图像,需要和源图片有一样的尺寸和类型
    InputArray kernel,//膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核
    Point anchor=Point(-1,-1),//锚的位置,其有默认值(-1,-1),表示锚位于中心。
    int iterations=1,//迭代使用erode()函数的次数,默认值为1
    int borderType=BORDER_CONSTANT,//用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT
    const Scalar& borderValue=morphologyDefaultBorderValue() //一般我们不用去管他
);
getStructuringElement(
//第一个参数表示内核的形状,矩形: MORPH_RECT,交叉形: MORPH_CROSS,椭圆形: MORPH_ELLIPSE
//第二和第三个参数分别是内核的尺寸以及锚点的位置,有默认值Point(-1,-1),表示锚点位于中心
)
形态学腐蚀——erode函数

void erode(
InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()
 );

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;
Mat src, dst1,dst2;
int elementsize1 = 3;//内核矩阵尺寸
int elementsize2 = 5;//内核矩阵尺寸

//膨胀
void Elementsizechange1(int,void*) {
    Mat element1 = getStructuringElement(MORPH_RECT, Size(elementsize1 * 2 + 1, elementsize1 * 2 + 1));//保证是奇数
    dilate(src, dst1, element1);//膨胀
    imshow("膨胀", dst1);
}
void Elementsizechange2(int, void*) {
    Mat element2 = getStructuringElement(MORPH_RECT, Size(elementsize2 * 2 + 1, elementsize2* 2 + 1));//保证是奇数
     erode(src, dst2, element2);//腐蚀
    imshow("腐蚀", dst2);
}
int main() {
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\5.jpg");
    imshow("原图", src);
    Elementsizechange1(0,0);
    createTrackbar("内核尺寸1:", "膨胀", &elementsize1, 21, Elementsizechange1);//21是最大尺寸了
    Elementsizechange2(0, 0);
    createTrackbar("内核尺寸2:", "腐蚀", &elementsize2, 21, Elementsizechange2);//21是最大尺寸了
    waitKey(0);
}

结果:
这里写图片描述

开操作-open
先腐蚀后膨胀
dst=open(src,element)=dilate(erode(src,element))
可以去掉小的对象,假设对象是前景色,背景是黑色。
开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

闭运算-closing operation
填充小的黑洞
先膨胀后腐蚀
dst=close(src,element)=erode(dilate(src,element))
闭运算能够排除小型黑洞。

形态学梯度-MorphologicalGradient
膨胀图与腐蚀图只差
dst=morph_grad(src,element)=dilate(src,element)-erode(src,element)
可以将团块的边缘突出出来,我们可以用形态学梯度来保留物体的边缘轮廓。

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

黑帽-Black Hat
闭运算的图-原图像
dst=blackhat(src,element)=close(src,element)-src
黑帽运算后的图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核大小相关
所以,黑帽运算用来分离比邻近点暗一些的斑块。

morphologyEx函数

void morphologyEx(
InputArray src,//即源图像,填Mat类的对象即可。
OutputArray dst,//即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
int op,//,表示形态学运算的类型,可以是如下之一的标识符:
      //MORPH_OPEN – 开运算(Opening operation)
     //MORPH_CLOSE – 闭运算(Closing operation)
    //MORPH_GRADIENT -形态学梯度(Morphological gradient)
   //MORPH_TOPHAT - “顶帽”(“Top hat”)
  //MORPH_BLACKHAT - “黑帽”(“Black hat“)
InputArraykernel,//形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。
                 //我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)
Pointanchor=Point(-1,-1),//锚的位置,其有默认值(-1,-1),表示锚位于中心。
intiterations=1,//迭代使用函数的次数,默认值为1
intborderType=BORDER_CONSTANT,//用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
constScalar& borderValue=morphologyDefaultBorderValue() );

代码:

Mat src, dst1,dst2,dst3,dst4,dst5;
int elementsize = 3;//内核矩阵尺寸

void ElementsizechangeOPEN(int, void*) {//开操作实例
    Mat kernel = getStructuringElement(MORPH_RECT, Size(elementsize * 2 + 1, elementsize * 2 + 1));//保证是奇数
    morphologyEx(src, dst1, MORPH_OPEN, kernel);
    imshow("OPEN", dst1);
}

void ElementsizechangeCLOSE(int, void*) {//闭运算实例
    Mat kernel = getStructuringElement(MORPH_RECT, Size(elementsize * 2 + 1, elementsize * 2 + 1));//保证是奇数
    morphologyEx(src, dst2, MORPH_CLOSE, kernel);
    imshow("CLOSE", dst2);
}

void ElementsizechangeGRADIENT(int, void*) {//形态学梯度实例
    Mat kernel = getStructuringElement(MORPH_RECT, Size(elementsize * 2 + 1, elementsize * 2 + 1));//保证是奇数
    morphologyEx(src, dst3, MORPH_GRADIENT, kernel);
    imshow("GRADIENT", dst3);
}

void ElementsizechangeTOPHAT(int, void*) {//顶帽实例
    Mat kernel = getStructuringElement(MORPH_RECT, Size(elementsize * 2 + 1, elementsize * 2 + 1));//保证是奇数
    morphologyEx(src, dst4, MORPH_TOPHAT, kernel);
    imshow("TOPHAT", dst4);
}

void ElementsizechangeBLACKHAT(int, void*) {//黑帽作实例
    Mat kernel = getStructuringElement(MORPH_RECT, Size(elementsize * 2 + 1, elementsize * 2 + 1));//保证是奇数
    morphologyEx(src, dst5, MORPH_BLACKHAT, kernel);
    imshow("BLACKHAT", dst5);
}

int main() {
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\5.jpg");
    imshow("原图", src);

    ElementsizechangeOPEN(0, 0);
    createTrackbar("内核尺寸:", "OPEN", &elementsize, 21, ElementsizechangeOPEN);//21是最大尺寸了

    ElementsizechangeCLOSE(0, 0);
    createTrackbar("内核尺寸:", "CLOSE", &elementsize, 21, ElementsizechangeCLOSE);//21是最大尺寸了

    ElementsizechangeGRADIENT(0, 0);
    createTrackbar("内核尺寸:", "GRADIENT", &elementsize, 21, ElementsizechangeGRADIENT);//21是最大尺寸了

    ElementsizechangeTOPHAT(0, 0);
    createTrackbar("内核尺寸:", "TOPHAT", &elementsize, 21, ElementsizechangeTOPHAT);//21是最大尺寸了

    ElementsizechangeBLACKHAT(0, 0);
    createTrackbar("内核尺寸:", "BLACKHAT", &elementsize, 21, ElementsizechangeBLACKHAT);//21是最大尺寸了

    waitKey(0);
}

结果:
这里写图片描述这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_26907755/article/details/81703093