“ 此篇介绍两种的对图像形态学操作方式(形态学腐蚀、形态学膨胀)”
在许多视觉识别的应用场合,若需要去除图像中微小噪声,分离较近的两个物体,则可对图像形态学腐蚀处理;若需要填充图像中缺失的微小区域、合并较近的两个物体,则可对图像形态学膨胀处理;以下就两种情况展开描述:
运行环境:
Ubuntu20.04
OpenCV4.5.1
Qt-Creator4.11.1
gdb-ImageWatch
>注意:以下操作均在对源图像进行二值化操作的前提下进行!
01
—
形态学腐蚀
形态学腐蚀就是求局部最小值的操作,[核]与图像卷积,即计算核B覆盖的区域的像素点的最小值,并把这个最小值赋值给参考点指定的像素。
原理:
A⨀B={z|(B)z⊆A}
解释:移动结构B,如果结构B与A图的交集完全属于结构A的区域内,则保存该位置点。
结构元素的中心像素与原图像的非零像素重合,结构元素覆盖区域有零像素,则原图像被覆盖的中心像素要被删除;结构元素覆盖区域无零像素,则原图像被覆盖的中心像素要被保留;以次X、Y并行移动,按此操作。
为了更好的理解原理,这里先引入一个概念即结构元素。以下分别为3×3十字结构元素、3×3矩形结构元素、7×7椭圆结构元素,这里是的结构元素也可以自定义。
如下图利用3×3十字结构元素对左图像腐蚀变成右图:
具体做了什么呢?当结构元素对二值化图像每个像素扫描出现的两种情况(如下图)。
中心像素要被删除
中心像素要被保留
步骤:
Step1:生成或制作结构元素
Step2:结构元素的中心像素与原图像的非零像素重合,结构元素覆盖区域有零像素,则原图像被覆盖的中心像素要被删除;结构元素覆盖区域无零像素,则原图像被覆盖的中心像素要被保留;以次X、Y并行移动,按此操作。
函数介绍:
>结构元素生成函数
getStructuringElement(int shape,Size ksize,Point anchor=Point(-1,-1))
shape:结构元素的种类;
ksize:结构元素的尺寸大小;
anchor:中心点的位置,默认参数为结构元素的几何中心;
标记参数 简记 作用 MORPH_RECT 0 矩形结构元素,所有元素都为1 MORPH_CROSS 1 十字结构元素,中间的列和行元素为1 MORPH_ELLIPSE 2 椭圆结构元素,矩形的椭圆内接元素为1 >图像腐蚀操作函数
void cv::erode(InputArray src,OutputArray dst,InputArray kernel,Point anchor=Point(-1,-1),int iterations=1,int borderType=BORDER_CONSTANT,
const Scalar & borderValue=morphologyDefaultBorderValue())
src:输入的待腐蚀图像,图像的通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一;
dst:腐蚀后的输出图像,与输入图像src具有相同的尺寸和数据类型;
kernel:用于腐蚀操作的结构元素,可以自己输入,也可以用getStructuringElement()函数生成;
anchor:中心点在结构元素中的位置,默认参数为结构元素的几何中心点;
iterations:腐蚀的次数;
borderType:像素外推法选择标志;
borderValue:边界不变的边界值;
代码展示:
#include #include using namespace cv;using namespace std;void drawState(Mat &img,int number,Mat centroids,Mat stats,String str);int main(){ //生成用于腐蚀的原图像 Mat src=(Mat_(6,6)<<0,0,0,0,255,0, 0,255,255,255,255,255, 0,255,255,255,255,0, 0,255,255,255,255,0, 0,255,255,255,255,0, 0,0,0,0,0,0); Mat struct1,struct2; struct1=getStructuringElement(0,Size(3,3));//矩形结构元素 struct2=getStructuringElement(1,Size(3,3));//十字结构元素 Mat erodeSrc; erode(src,erodeSrc,struct2); namedWindow("src",WINDOW_GUI_NORMAL); namedWindow("erodeSrc",WINDOW_GUI_NORMAL); imshow("src",src); imshow("erodeSrc",erodeSrc);//~~~~~~~~~~~~~~~~~~形态学腐蚀应用于图像中~~~~~~~~~~~~~~~~~~~~ cout<<"文字腐蚀验证"<<endl; waitKey(0); Mat black=imread("/home/zja/Pictures/Black1.png"); Mat erode_black1,erode_black2; //黑背景图像腐蚀 erode(black,erode_black1,struct1); erode(black,erode_black2,struct2); imshow("black",black); imshow("erode_black1",erode_black1); imshow("erode_black2",erode_black2);//~~~~~~~~~~~~~~~~~对小连通域的去除腐蚀去除~~~~~~~~~~~~~~~~~ cout<<"验证腐蚀对小连通域的去除"<<endl; waitKey(0); Mat star=imread("/home/zja/Pictures/star.jpg"); Mat star2; copyTo(star,star2,star);//深拷贝 克隆一个单独的图像,用于后期图像绘制 Mat wink,winkBW; //将图像转成二值化图像,用于统计连通域 cvtColor(star,wink,COLOR_BGR2GRAY); threshold(wink,winkBW,50,255,THRESH_BINARY); Mat out,stats,centroids; //统计图像中连通域的个数 int number=connectedComponentsWithStats(winkBW,out,stats,centroids,8,CV_16U); drawState(star,number,centroids,stats,"未腐蚀时统计连通域");//绘制图像 erode(winkBW,winkBW,struct1); number=connectedComponentsWithStats(winkBW,out,stats,centroids,8,CV_16U); drawState(star2,number,centroids,stats,"腐蚀后统计连通域");//绘制图像waitKey(0);return 0;}//绘制图像的子函数void drawState(Mat &img,int number,Mat centroids,Mat stats,String str){ RNG rng(10086); vector colors; for(int i=0;i { //使用均匀分布的随机数确定颜色 Vec3b vec3=Vec3b(rng.uniform(0,256),rng.uniform(0,256),rng.uniform(0,256)); colors.push_back(vec3); } for(int i=1;i { //中心位置 int center_x=centroids.at<double>(i,0); int center_y=centroids.at<double>(i,1); //矩形边框 int x=stats.at<int>(i,CC_STAT_LEFT); int y=stats.at<int>(i,CC_STAT_TOP); int w=stats.at<int>(i,CC_STAT_WIDTH); int h=stats.at<int>(i,CC_STAT_HEIGHT); //中心位置绘制 circle(img,Point(center_x,center_y),2,Scalar(0,255,0),2,8,0); //外接矩形 Rect rect(x,y,w,h); rectangle(img,rect,colors[i],1,8,0); putText(img,format("%d",i),Point(center_x,center_y),FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,0,255),1); } imshow(str,img);}
运行效果:
对黑底白字图分别用矩形、十字结构元素腐蚀操作效果图:
以下分别为原图、未腐蚀时统计连通域数量(统计了44颗星星)、腐蚀后统计连通域数量(统计了40颗星星):
由此看出如图白色箭头标注的星星太小而被腐蚀掉(等同于噪声干扰的去除)
02
—
形态学膨胀
形态学膨胀就是求局部最大值的操作,[核]与图像卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。
原理:
A⨁B={z|(B^)z⋂A≠∅}
解释:移动结构B的过程中,与A图存在重叠区域,则记录该位置。
结构元素的中心像素与原图像的非零像素重合,将没有像素值的区域填充为1,以次X、Y并行移动,以次X、Y并行移动,按此操作。
结构元素名词在上述已介绍过,在此不做赘述。
如下图利用3×3十字结构元素对左图像膨胀变成右图:
具体做了什么呢?当结构元素对二值化图像每个像素扫描时,对十字覆盖区域填充255(如下图)。
步骤:
Step1:生成或制作结构元素
Step2:结构元素的中心像素与原图像的非零像素重合,将没有像素值的区域填充为1,以次X、Y并行移动,按此操作。
函数介绍:
>结构元素生成函数
getStructuringElement(int shape,Size ksize,Point anchor=Point(-1,-1
))
>图像膨胀操作函数
void cv::dilate(InputArray src,OutputArray dst,InputArray kernel,
Point anchor=Point(-1,-1),int iterations=1,int borderType=BORDER_
CONSTANT,const Scalar & borderValue=morphologyDefaultBorderValue(
))
src:输入的待膨胀图像,图像的通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一;
dst:腐蚀后的输出图像,与输入图像src具有相同的尺寸和数据类型;
kernel:用于膨胀操作的结构元素,可以自己输入,也可以用getStructuringElement()函数生成;
anchor:中心点在结构元素中的位置,默认参数为结构元素的几何中心点;
iterations:腐蚀的次数;
borderType:像素外推法选择标志;
borderValue:边界不变的边界值;
代码展示:
#include #include using namespace cv;using namespace std;int main(){ //生成用于膨胀的原图像 Mat src=(Mat_(6,6)<<0, 0, 0, 0, 255,0, 0,255,255,255,255,255, 0,255,255,255,255,0, 0,255,255,255,255,0, 0,255,255,255,255,0, 0, 0, 0, 0, 0, 0); Mat struct1,struct2; struct1=getStructuringElement(0,Size(3,3));//矩形结构元素 struct2=getStructuringElement(1,Size(3,3));//十字结构元素 Mat dilateSrc;//存放膨胀后的图像 dilate(src,dilateSrc,struct2); /* namedWindow("src",WINDOW_GUI_NORMAL); namedWindow("dilateSrc",WINDOW_GUI_NORMAL); imshow("src",src); imshow("dilateSrc",dilateSrc); *///~~~~~~~~~~~~~~~~~~~~形态学膨胀应用于图像中~~~~~~~~~~~~~~~~~~~~ cout<<"文字膨胀"<<endl; waitKey(0); Mat black=imread("/home/zja/Pictures/Black1.png",IMREAD_ANYCOLOR); Mat dilate_black1,dilate_black2; //黑背景图像膨胀 dilate(black,dilate_black1,struct1); dilate(black,dilate_black2,struct2); /* imshow("black",black); imshow("dilate_black1",dilate_black1); imshow("dilate_black2",dilate_black2); */waitKey(0);return 0;}
运行效果:
对黑底白字图分别用矩形、十字结构元素膨胀操作效果图: