opencv中包含的形态学操作共有腐蚀、膨胀、闭运算、开运算、形态学梯度、顶帽运算、黑帽运算、hitmiss运算8中。其中,腐蚀、膨胀运算是所有形态学运算基础。本文将对这些形态学操作的效果进行展示。
腐蚀,即用模板对目标像素的领域进行匹配,如果邻域信息与模板完全匹配,则将像素点置255,否则置0。因为图形边缘区域的像素点是较难完全匹配模块的,所以通过该操作后图形边缘的像素点被删除,因此称该操作为腐蚀。
膨胀,即用模板对目标像素的领域进行匹配,如果邻域信息与模板中任意一个像素匹配,则将像素点置255,否则置0。因为该操作,只要求部分匹配,一些黑点(像素值为0)特定邻域内存在白点(像素值为255),则被置为白点。所以通过该操作后图形的轮廓变大,因此称该操作为膨胀。

通过对腐蚀膨胀运算的组合运算,因此得到了闭运算、开运算、形态学梯度。将原图与开运行、闭运算的结果进行异或运算,则就得到了顶帽运算和黑帽运算。取原图在进行开运行、闭运算后的不变区域则得到了hitmiss。注:hitmiss并不等于开运算与闭运算的并集,博主这里的描述只是为了近似理解。

1、多图展示代码

代码中的imshows函数用于多图展示,博主为了省事插入了两行与多图展示不相干的代码,即将图1与图2的异或结果添加到vector中。

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

//修改自以下链接,博主添加了pad,使两列图像边多了空格
//https ://blog.csdn.net/Beking17113/article/details/122304671
void multipleImage(vector<Mat> imgVector, Mat& dst, int imgCols, int MAX_PIXEL = 400)
{
    //两列图像间的空白区域
    int pad = 10;
    int imgNum = imgVector.size();
    //选择图片最大的一边 将最大的边按比例变为300像素
    Size imgOriSize = imgVector[0].size();
    int imgMaxPixel = max(imgOriSize.height, imgOriSize.width);
    //获取最大像素变为MAX_PIXEL的比例因子
    double prop = imgMaxPixel < MAX_PIXEL ? (double)imgMaxPixel / MAX_PIXEL : MAX_PIXEL / (double)imgMaxPixel;
    Size imgStdSize(imgOriSize.width * prop, imgOriSize.height * prop); //窗口显示的标准图像的Size

    Mat imgStd; //标准图片
    Point2i location(0, 0); //坐标点(从0,0开始)
    //Mat imgWindow(imgStdSize.height * ((imgNum - 1) / imgCols + 1), imgStdSize.width * imgCols+ pad * imgCols-pad, imgVector[0].type());
    int imgRows = (imgNum - 1) / imgCols + 1;
    Mat imgWindow = Mat::zeros(imgStdSize.height * imgRows + pad * imgRows - pad, imgStdSize.width * imgCols + pad * imgCols - pad, imgVector[0].type());
    for (int i = 0; i < imgNum; i++)
    {
        location.x = (i % imgCols) * (imgStdSize.width+pad);
        location.y = (i / imgCols) * imgStdSize.height;
        resize(imgVector[i], imgStd, imgStdSize, prop, prop, INTER_LINEAR); //设置为标准大小

        //将imgStd复制到imgWindow的指定区域中
        imgStd.copyTo(imgWindow(Rect(location, imgStdSize)));
    }
    dst = imgWindow;
}

void imshows(vector<Mat> imgVector,string title, int imgCols=-1) {
    Mat dst,xor;
    //将图1与图2的异或结果也添加到输出中
    bitwise_xor(imgVector[0], imgVector[1], xor);
    imgVector.push_back(xor);

    if (imgCols == -1) {
        imgCols = imgVector.size();
    }
    multipleImage(imgVector,dst, imgCols);
    namedWindow(title);
    imshow(title, dst);
    imwrite(title+".png", dst);
}

2、进行形态学操作

int main(int argc, char *argv[])
{
    Mat img = imread("D:/008945.png",0);//左侧:图片路径
    resize(img, img, { 512,512 });
    int thres_ = 0;
    cv::threshold(img, img, thres_, 255, THRESH_BINARY);//大于阈值返回 最大值 255 小于返回0

    Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));

    Mat dilate_mat,erode_mat,close_mat, open_mat, grad_mat, tophat_mat, blackhat_mat, hitmiss_mat;

    //腐蚀==》使图形的轮廓变细
    //erode(img, erode_mat, element);
    morphologyEx(img, erode_mat, MORPH_ERODE, element);

    //膨胀==》使图形的轮廓变粗
    //dilate(img, dilate_mat, element);
    morphologyEx(img, dilate_mat, MORPH_DILATE, element);

    //闭运算==先做膨胀,在做腐蚀==》闭合小孔洞,消除毛刺
    morphologyEx(img, close_mat, MORPH_CLOSE, element);

    //开运算==先做腐蚀,在做膨胀==》移除小区域,使连通域断开
    morphologyEx(img, open_mat, MORPH_OPEN, element);

    //形态学梯度==膨胀-腐蚀==》得到物体的大概轮廓
    morphologyEx(img, grad_mat, MORPH_GRADIENT, element);

    //顶帽运算==原图形-开运算==》用于获取图形中的的尖角、毛刺等细小的白色区域 
    morphologyEx(img, tophat_mat, MORPH_TOPHAT, element);

    //黑帽运算==闭运算-原图像==》用于获取图形中的的缺口、孔洞区域等细小的黑色区域
    morphologyEx(img, blackhat_mat, MORPH_BLACKHAT, element);

    //hitmiss== 腐蚀图 并 原图补集的腐蚀图,简单解释为图像进行腐蚀膨胀时未发生变化的区域,也就是腐蚀膨胀都未击中的像素,从效果上看就是取图像的主体
    //hitmiss变换详情参考:https://www.pythonf.cn/read/168479
    morphologyEx(img, hitmiss_mat, MORPH_HITMISS, element);

    imshows({ img, erode_mat },"erode");
    imshows({ img, dilate_mat }, "dilate");
    imshows({ img, close_mat }, "close");
    imshows({ img, open_mat }, "open");
    imshows({ img, grad_mat }, "grad");
    imshows({ img, tophat_mat }, "tophat");
    imshows({ img, blackhat_mat }, "blackhat");
    imshows({ img, hitmiss_mat }, "hitmiss");
    waitKey();
}

3、形态学运算效果

下面图片中第一列是原图,第二列是效果图,第三列是原图与效果图的异或图。

腐蚀

morphologyEx(img, erode_mat, MORPH_ERODE, element);
使图形的轮廓变细
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

膨胀

morphologyEx(img, dilate_mat, MORPH_DILATE, element);
使图形的轮廓变粗
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

闭运算

morphologyEx(img, close_mat, MORPH_CLOSE, element);
先做膨胀,在做腐蚀==》闭合小孔洞,消除毛刺
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

开运算

morphologyEx(img, open_mat, MORPH_OPEN, element);
先做腐蚀,在做膨胀==》移除小区域,使连通域断开
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

形态学梯度

morphologyEx(img, close_mat, MORPH_CLOSE, element);
膨胀-腐蚀==》得到物体的大概轮廓
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

顶帽运算

morphologyEx(img, tophat_mat, MORPH_TOPHAT, element);
原图形-开运算==》用于获取图形中的的尖角、毛刺等细小的白色区域
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

黑帽运算

morphologyEx(img, blackhat_mat, MORPH_BLACKHAT, element);
闭运算-原图像==》用于获取图形中的的缺口、孔洞区域等细小的黑色区域
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

hitmiss

morphologyEx(img, hitmiss_mat, MORPH_HITMISS, element);
hitmiss== 腐蚀图 并 原图补集的腐蚀图,简单解释为图像进行腐蚀膨胀时未发生变化的区域,也就是腐蚀膨胀都未击中的像素,从效果上看就是取图像的主体。 hitmiss变换详情参考:https://www.pythonf.cn/read/168479
Opencv 基本操作三 实现各个形态学处理并实现多图展示-LMLPHP

08-20 15:23