腐蚀和膨胀

腐蚀和膨胀是形态学的基本运算,通过这些基本运算可以去除图像中的噪声,分割出独立的区域或者将两个连通域连接在一起等

将图像二值化后通过计算图像的连通域的数目实现对图像中的计数,但是我们发现,图像两个不为0的像素也是独立的连通域

从而影响计数结果,这种面积较小的连通域可以通过腐蚀操作消除,从而减少因噪声导致的计数错误

因此腐蚀和膨胀在实际的图像处理项目中具有重要的作用

图像腐蚀erode()

图像的腐蚀过程与图像的卷积操作类似,都需要模板矩阵来控制运算的结果,在图像的腐蚀和膨胀中。这个模板矩阵称为结构元素

与图像卷积相同,结构元素可以任意指定图像的中心点,并且结构元素的尺寸和具体内容都可以根据需求自己定义

在定义结构元素之后,将结构元素绕着中心点旋转180°,之后将结构元素的中心点依次放到图像这种每一个非零元素处

如果此时结构元素内所有元素覆盖的图像像素值均不为0,那么保留结构元素中心点对应的图像像素,否则删除中心点对应像素

图像腐蚀过程中使用的结构元素可以根据需求自己生成

OpenCV4提供了getStructuringElement()函数用于生成常用的矩形结构元素、十字结构元素和椭圆结构元素

getStructuringElement()函数原型:

Mat getStructuringElement(int shape,
                          Size ksize,
                          Point anchor = Point(-1,-1)
                          )
  • shape:生成结构元素的种类
  • ksize:结构元素的尺寸
  • anchor:中心点的位置,默认为结构元素的几何中心

该函数生成常用的矩形结构元素、十字结构元素和椭圆结构元素

第一个参数为生成结构元素的种类

第二个参数是结构元素的尺寸,能够影响图像腐蚀的效果,一般情况下,当结构元素的种类相同时,结构元素的尺寸越大,腐蚀效果越

明显

最后一个参数是结构元素的中心点位置,只有十字结构元素的中心点位置会影响图像腐蚀后的轮廓形状

其他种类的结构元素只影响形态学操作结构的平移量

getStructuringElement()函数结构元素的种类元素的可选择参数

标记参数 简记 作用
MORPH_RECT 0 矩形结构元素,所有元素都为1
MORPH_CROSS 1 十字结构元素,中间的列和行元素都为1
MORPH_ELLIPSE 2 椭圆结构元素,矩形的内接椭圆元素为1

OpenCV4提供了用于图像腐蚀erode()函数

erode()图像腐蚀:

void 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:像素外推选择标志,默认为BORDER_DEFAULT,表示不包含边界值的倒序填充(2.3.4仿射变换)
  • borderValue:使用边界不变外推法时的边界值

该函数根据结构元素对输入图像进行腐蚀,在腐蚀多通道图像时,每个通道独立进行腐蚀运算

第三个参数为结构元素

第四个参数为结构元素的中心位置,默认值Point(-1,-1),表示几何中心

第五个参数为腐蚀次数,默认值为1

最后两个参数对图像主要腐蚀操作没有影响,因此多数情况下使用默认参数

该函数的腐蚀过程只针对图像中的非零像素,因此,如果图像背景为0,那么腐蚀过后图像中内容变得更小、瘦

如果图像以255为背景,那么腐蚀操作后内容变得更粗、大

示例:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
//绘制包含区域函数
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);
        int area = stats.at<int>(i, CC_STAT_AREA);
        //中心位置绘制
        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);
        cout << "number:" << i << ",area:" << area << endl; //format()函数用于格式化字符串。可以接受无限个参数,可以指定顺序。返回结果为字符串。
    }
    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,0,0);
    Mat struct1,struct2;
    struct1 = getStructuringElement(0,Size(9,9)); //矩形结构元素
    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("E:\\CLion\\opencv_xin\\SG\\SG_black.png",IMREAD_ANYCOLOR);
    Mat LearnCV_write = imread("E:\\CLion\\opencv_xin\\SG\\SG_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_black2",erode_black2);
    //白色背景图像腐蚀
    erode(LearnCV_write,erode_write1,struct1);
    erode(LearnCV_write,erode_write2,struct2);
    imshow("LearnCV_write",LearnCV_write);
    imshow("erode_write1",erode_write1);
    imshow("erode_write2",erode_write2);
    //验证腐蚀对小连通域的去除
    Mat img = imread("E:\\CLion\\opencv_xin\\SG\\SG_connect.png");
    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;
}

\

图像膨胀dilate()

相比于图像腐蚀,图像膨胀是其相反的过程,与图像腐蚀相似,图像膨胀同样需要结果元素用于控制图像膨胀的效果

结构元素可以任意指定图像的中心点,并且结构元素的尺寸和具体内容都可以根据需求自己定义

如果原图中某个元素被结构元素覆盖,但是该像素的像素值不与中心点对应像素点的像素值相同,那么将原图中该像素改为中心点像素值

OpenCV4提供了用于图像膨胀的dilate()函数

dilate()函数原型:

void 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:膨胀的次数,默认值为1
  • borderType:像素外推选择标志,默认为BORDER_DEFAULT,表示不包含边界值的倒序填充(2.3.4仿射变换)
  • borderValue:使用边界不变外推法时的边界值

该函数参数与腐蚀意义相同

该函数的膨胀过程只针对图像中的非零像素,因此,如果图像背景为255,那么膨胀过后图像中内容变得更小、瘦

如果图像以0为背景,那么膨胀操作后内容变得更粗、大

示例:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
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,0,0);
    Mat struct1,struct2;
    struct1 = getStructuringElement(0,Size(9,9)); //矩形结构元素
    struct2 = getStructuringElement(1,Size(3,3)); //十字结构元素
    Mat erodeSrc; //存放膨胀后的图像
    dilate(src,erodeSrc,struct2);
    namedWindow("src",WINDOW_GUI_NORMAL);
    namedWindow("dilateSrc",WINDOW_GUI_NORMAL);
    imshow("src",src);
    imshow("dilateSrc",erodeSrc);

    Mat LearnCV_black = imread("E:\\CLion\\opencv_xin\\SG\\SG_black.png",IMREAD_ANYCOLOR);
    Mat LearnCV_write = imread("E:\\CLion\\opencv_xin\\SG\\SG_write.png",IMREAD_ANYCOLOR);
    Mat dilate_black1,dilate_black2,dilate_write1,dilate_write2;
    //黑色背景图像膨胀
    dilate(LearnCV_black,dilate_black1,struct1);
    dilate(LearnCV_black,dilate_black2,struct2);
    imshow("LearnCV_black",LearnCV_black);
    imshow("dilate_black1",dilate_black1);
    imshow("dilate_black2",dilate_black2);
    //白色背景图像膨胀
    dilate(LearnCV_write,dilate_write1,struct1);
    dilate(LearnCV_write,dilate_write2,struct2);
    imshow("LearnCV_write",LearnCV_write);
    imshow("dilate_write1",dilate_write1);
    imshow("dilate_write2",dilate_write2);
    waitKey(0);
    return 0;
}

results matching ""

    No results matching ""