OpenCV 二值图像处理——形态学的基本操作(C++)

形态学的基本操作

目录

形态学的基本操作

膨胀

腐蚀

开运算

闭运算

梯度

顶帽

黑帽


膨胀

  跟卷积操作类似,假设有图像A和结构元素B,结构元素B在A上面移动,其中B定义其中心为锚点,计算B覆盖下A的最大像素值用来替换锚点的像素,其中B作为结构体可以是任意形状。我们回忆一下中值平滑操作——取每一个位置的矩形领域内值的中值作为该位置的输出灰度值,图像的膨胀操作与中值平滑操作类似,它是取每一个位置的矩形领域内值的最大值作为该位置的输出灰度值。不同的是,这里的领域不再单纯是矩形结构的,也可以是椭圆形结构的、十字交叉形结构的等,其中红色是参考点,也称为锚点(anchor point),如下所示:

    因此取每个位置领域内最大值,所以膨胀后输出图像的总体亮度的平均值比起原图会有所升高,图像中比较亮的区域的面积会变大,而较暗物体的尺寸会减小甚至消失

// 形态学构造函数  
getStructuringElement( int shape, Size ksize, Point anchor )

    shape:1)MORPH_RECT 表示产生矩形的结构元

                2)MORPH_ELLIPSEM 表示产生椭圆形的结构元

                3)MORPH_CROSS 表示产生十字交叉形的结构元

    ksize:表示结构元的尺寸,即(宽,高),必须是奇数
    anchor:表示结构元的锚点,即参考点。默认值Point(-1, -1)代表中心像素为锚点
 

// 膨胀操作可以对输入图像用特定结构元素进行膨胀操作,该结构元素确定膨胀操作过程中的邻域的形状,各点像素值将被替换为对应邻域上的最大值

dilate(src,dst,kernel);
// src    原始图像:通道数不限,depth必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
// dst    输出图像:size与type与原始图像相同
// kernel 用于膨胀操作的结构元素,如果取值为Mat(),那么默认使用一个3 x 3 的方形结构元素,可以使用getStructuringElement()来创建结构元素

// Trackbar 函数
int createTrackbar(const string& trackbarname, const string& winname,
     int* value, int count, TrackbarCallback onChange=0, void* userdata=0)
//参数:
/*
trackbarname:滑动条名,滑动条的标识符,不同滑动条拥有不同的滑动条名

winname:窗口名,滑动条想要绑定的窗口的名字

value:可选,用于反映滑块位置。创建滑动条时可用此值指定滑块位置

count:滑块最大值。最小值通常是 0

onChange:回调函数,函数原型为 void Foo(int,void*)
*/

下面是一个示例,进行形态学膨胀操作,改变迭代次数:

#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;

// 滑动条名
const string trackbarname = "expand";
// 窗口名
const string winname = "TrackbarDemo";
// 最大值
const int maxNum = 10;

// 预设值
int value = 2;

Mat img;
Mat res;

int dilation_type = MORPH_RECT;
int dilation_size = 1;
Mat element = getStructuringElement(dilation_type,
    Size(2 * dilation_size + 1, 2 * dilation_size + 1),
    Point(dilation_size, dilation_size));

void onExpand(int, void*) {
    printf("value: %d\n", value);
    dilate(img, res, element, Point(-1, -1), value);

    imshow(winname, res);
}

int main(int argc, char* argv[]) {
    img = imread("tahiti.jpg", -1);
    if (img.empty()) {
        exit(0);
    }
    res = img.clone();

    namedWindow(winname);
    createTrackbar(trackbarname, winname, &value, maxNum, onExpand, NULL);

    onExpand(0, NULL);

    waitKey(0);

    return 0;
}

当预设值value = 1:

 当预设值value = 7:

腐蚀

 腐蚀操作与膨胀操作类似,只是它取结构元所指定的领域内值的最小值作为该位置的输出灰度值。因为取每个位置领域内最小值,所以腐蚀后输出图像的总体亮度的平均值比起原图会有所降低,图像中比较亮的区域的面积会变小甚至消失,而较暗物体的尺寸会扩大

erode(src,dst,kernel);
// src    原始图像:通道数不限,depth必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
// dst    输出图像:size与type与原始图像相同
// kernel 用于腐蚀操作的结构元素,如果取值为Mat(),那么默认使用一个3 x 3 的方形结构元素,可以使用getStructuringElement()来创建结构元素
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;

// 滑动条名
const string trackbarname = "expand";
// 窗口名
const string winname = "TrackbarDemo";
// 最大值
const int maxNum = 10;

// 预设值
int value = 2;

Mat img;
Mat res;

int dilation_type = MORPH_RECT;
int dilation_size = 1;
Mat element = getStructuringElement(dilation_type,
    Size(2 * dilation_size + 1, 2 * dilation_size + 1),
    Point(dilation_size, dilation_size));

void onExpand(int, void*) {
    printf("value: %d\n", value);
    erode(img, res, element, Point(-1, -1), value);

    imshow(winname, res);
}

int main(int argc, char* argv[]) {
    img = imread("tahiti.jpg", -1);
    if (img.empty()) {
        exit(0);
    }
    res = img.clone();

    namedWindow(winname);
    createTrackbar(trackbarname, winname, &value, maxNum, onExpand, NULL);

    onExpand(0, NULL);

    waitKey(0);

    return 0;
}

开运算

先腐蚀后膨胀,可以去掉较小的对象,假设对象是前景色,背景是黑色.

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

// 形态学变换函数
morphologyEx(src,dest,OPT,kernel);

// int OPT,形态学操作类型:  CV_MOP_OPEN/CLOSE/GRADIENT/TOPHAT/BLACKHAT
dst=open(src,element)=dilate(erode(src,element))

闭运算

填充小的黑洞
先膨胀后腐蚀

闭运算能够排除小型黑洞。

dst=close(src,element)=erode(dilate(src,element))

梯度


 膨胀图与腐蚀图只差

可以将团块的边缘突出出来,我们可以用形态学梯度来保留物体的边缘轮廓

dst=morph_grad(src,element)=dilate(src,element)-erode(src,element)

顶帽

原图像-开运算的图

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

dst=tophat(src,element)=src-open(src,element)

黑帽

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

dst=blackhat(src,element)=close(src,element)-src

下面是 开/ 闭运算,黑帽/ 顶帽 , 梯度 的示例:

#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;
    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("tahiti.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);


    return 0;
}

原图:

开运算:

 

闭运算:

 梯度:

顶帽:

黑帽:

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DDsoup

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值