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);
使图形的轮廓变细
膨胀
morphologyEx(img, dilate_mat, MORPH_DILATE, element);
使图形的轮廓变粗
闭运算
morphologyEx(img, close_mat, MORPH_CLOSE, element);
先做膨胀,在做腐蚀==》闭合小孔洞,消除毛刺
开运算
morphologyEx(img, open_mat, MORPH_OPEN, element);
先做腐蚀,在做膨胀==》移除小区域,使连通域断开
形态学梯度
morphologyEx(img, close_mat, MORPH_CLOSE, element);
膨胀-腐蚀==》得到物体的大概轮廓
顶帽运算
morphologyEx(img, tophat_mat, MORPH_TOPHAT, element);
原图形-开运算==》用于获取图形中的的尖角、毛刺等细小的白色区域
黑帽运算
morphologyEx(img, blackhat_mat, MORPH_BLACKHAT, element);
闭运算-原图像==》用于获取图形中的的缺口、孔洞区域等细小的黑色区域
hitmiss
morphologyEx(img, hitmiss_mat, MORPH_HITMISS, element);
hitmiss== 腐蚀图 并 原图补集的腐蚀图,简单解释为图像进行腐蚀膨胀时未发生变化的区域,也就是腐蚀膨胀都未击中的像素,从效果上看就是取图像的主体。 hitmiss变换详情参考:https://www.pythonf.cn/read/168479