C++版本OpenCv教程(三十九)图像腐蚀

图像的腐蚀过程与图像的卷积操作类似,都需要模板矩阵来控制运算的结果,在图像的腐蚀和膨胀中这个模板矩阵被称为结构元素。与图像卷积相同,结构元素可以任意指定图像的中心点,并且结构元素的尺寸和具体内容都可以根据需求自己定义。定义结构元素之后,将结构元素的中心点依次放到图像中每一个非0元素处,如果此时结构元素内所有的元素所覆盖的图像像素值均不为0,则保留结构元素中心点对应的图像像素,否则将删除结构元素中心点对应的像素。图像的腐蚀过程示意图如图6-12所示,图6-12中左侧为待腐蚀的原图像,中间为结构元素,首先将结构元素的中心与原图像中的A像素重合,此时结构元素中心点的左侧和上方元素所覆盖的图像像素值均为0,因此需要将原图像中的A像素删除;当把结构元素的中心点与B像素重合时,此时结构元素中所有的元素所覆盖的图像像素值均为1,因此保留原图像中的B像素。将结构元素中心点依次与原图像中的每个像素重合,判断每一个像素点是否保留或者删除,最终原图像腐蚀的结果如图6-12中右侧图像所示。
图6-12 图像腐蚀结果示意图

图像腐蚀可以用“Θ”表示,其数学表示形式如式(6.4)所示,通过公式可以发现,其实对图像A的腐蚀运算就是寻找图像中能够将结构元素B全部包含的像素点。
在这里插入图片描述

图像腐蚀过程中使用的结构元素可以根据需求自己生成,但是为了研究人员的使用方便,OpenCV 4提供了**getStructuringElement()**函数用于生成常用的矩形结构元素、十字结构元素和椭圆结构元素。该函数的函数原型在代码清单6-10中给出。

Mat cv::getStructuringElement(int  shape,
                              Size  ksize,
                              Point  anchor = Point(-1,-1) 
                              )
  • shape:结构元素的种类,可以选择的参数及含义在表6-5中给出。
  • ksize:结构元素的尺寸大小
  • anchor:中心点的位置,默认参数为结构元素的几何中心点。

该函数用于生成图像形态学操作中常用的矩形结构元素、十字结构元素和椭圆结构元素。函数第一个参数为生成结构元素的种类,可以选择的参数及含义在表6-5给出,函数第二个参数是结构元素的尺寸大小,能够影响到图像腐蚀的效果,一般情况下,结构元素的种类相同时,结构元素的尺寸越大腐蚀效果越明显。函数的最后一个参数是结构元素的中心点,只有十字结构元素的中心点位置会影响图像腐蚀后的轮廓形状,其他种类的结构元素的中心点位置只影响形态学操作结果的平移量。
表6-5 getStructuringElement()函数结构元素形状可选择参数
OpenCV 4提供了用于图像腐蚀的erode()函数,该函数的函数原型在代码清单6-11中给出。

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:腐蚀的次数,默认值为1。
  • borderType:像素外推法选择标志,取值范围在表3-5中给出。默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。
  • borderValue:使用边界不变外推法时的边界值。

该函数根据结构元素对输入图像进行腐蚀,在腐蚀多通道图像时每个通道独立进行腐蚀运算。
函数的第一个参数为待腐蚀的图像,图像通道数可以是任意的,但是图像的数据类型必须是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。
函数第二个参数为腐蚀后的输出图像,与输入图像具有相同的尺寸和数据类型。
函数第三个和第四个参数都是与结构元素相关的参数,第三个参数为结构元素,第四个参数为结构元素的中心位置,第四个参数的默认值为Point(-1,-1),表示结构元素的几何中心处为结构元素的中心点。
函数第五个参数是使用结构元素腐蚀的次数,腐蚀次数越多效果越明显,参数默认值为1,表示只腐蚀1次。
函数第六个参数是图像像素外推法的选择标志,
第七个参数为使用边界不变外推法时的边界值,这两个参数对图像中主要部分的腐蚀操作没有影响,因此在多数情况下使用默认值即可。

需要注意的是该函数的腐蚀过程只针对图像中的非0像素,因此如果图像是以0像素为背景,那么腐蚀操作后会看到图像中的内容变得更瘦更小;如果图像是以255像素为背景,那么腐蚀操作后会看到图像中的内容变得更粗更大。

为了更加了解图像腐蚀的效果以及erode()函数的使用方法,在代码清单6-12中给出了对图6-12中的原图像进行腐蚀的示例程序,程序运行结果如图6-13所示。在程序中分别利用矩形结构元素和十字结构元素对像素值为0做背景的图像和像素值为255做背景的图像进行腐蚀,结果在图6-14、图6-15给出。最后利用图像腐蚀操作对代码清单6-6中二值化后的图像进行滤波,之后统计连通域个数,实现对原图像中的米粒进行计数,程序运行结果在图6-16给出。通过结果可以发现,腐蚀操作可以去除由噪声引起的较小的连通域,得到了正确的米粒数。

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;

//绘制包含区域函数
void drawState(Mat &img,int number,Mat centroids,Mat stats,String str){
    
    
    RNG rng(10086);
    vector<Vec3b>colors;
    for(int i=0;i<number;++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<number;++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,0,0);
        //外接矩形
        Rect rect(x,y,w,h);
        rectangle(img,rect,colors[i],1,0,0);
        putText(img,format("%d",i),Point(center_x,center_y),FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,0,255),1)
    }
    imshow(str,img);
}

int main(){
    
    
    //生成用于腐蚀的原图像
    Mat src=(Mat_<uchar>(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,255,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);

    Mat LearnCV_black=imread("LearnCV_black.png",IMREAD_ANYCOLOR);
    Mat LearnCV_write=imread("LearnCV_write.png",IMREAD_ANYCOLOR);
    Mat erode_black1,erode_black2,erode_write1,erode_write2;
    //黑背景图像腐蚀
    erode(LearnCV_black,erode_black1,struct1);
    erode(LearnCV_black,erode_black2,struct2);
    imshow("LearnCV_black",LearnCV_black);
    imshow("erode_black1",erode_black1);
    imshow("erode_back2",erode_black2);

    //白色背景腐蚀
    erode(LearnCV_write,erode_write1,struct1);
    erode(LearnCV_write,erode_write2,struct2);
    imshow("Learn_write",LearnCV_write);
    imshow("erode_write1",erode_write1);
    imshow("erode_write2",erode_write2);

    //验证腐蚀对小连通域的去除
    Mat img=imread("rice.jpeg");
    Mat img2;
    copyTo(img,img2,img);//克隆一个单独的图像,用于后期图像绘制
    Mat rice,riceBW;

    //将图像转成二值图像,用于统计连通域
    cvtColor(img,rice,COLOR_BGR2GRAY);
    threshold(rice,riceBW,50,255,THRESH_BINARY);

    Mat out,stats,centroids;
    //统计图像中连通域的个数
    int number=connectedComponentsWithStats(riceBW,out,stats,centroids,8,CV_16U);
    drawState(img,number,centroids,stats,"未腐蚀时统计连通域");//绘制图像

    erode(riceBW,riceBW,struct1);//对图像进行腐蚀
    number=connectedComponentsWithStats(riceBW,out,stats,centroids,8,CV_16U);
    drawState(img2,number,centroids,stats,"腐蚀后统计连通域");//绘制图像

    waitKey(0);
    return 0;
}

图6-13 用十字结构元素腐蚀示例
图6-14 myErode.cpp程序中黑背景图像腐蚀结果
图6-15 myErode.cpp程序中白背景图像腐蚀结果图6-15 myErode.cpp程序中白背景图像腐蚀结果

猜你喜欢

转载自blog.csdn.net/qq_33287871/article/details/112700635