# OpenCV 图像预处理—形态学:膨胀、腐蚀、开运算、闭运算 原理详解

文章目录

形态学概念

首先看这两张图片

一张图周围有大大小小的噪音和彩点,另一张图片中字母有间隙,这种效果影响了图片的质量,该如何处理图片,提高质量?

这就是形态学操作发挥作用的地方,形态学(Morphology)是图像处理中的一种技术,主要用于分析和处理图像中的结构和形状。形态学操作基于图像的形状和结构,而不是像素的具体值。它通常应用于二值图像(黑白图像),但也可以用于灰度图像。形态学操作在许多图像处理任务中发挥着重要作用,如去噪声、分割、边缘检测等。

膨胀

膨胀操作可以理解为将图像中的前景对象扩展。其基本原理是用一个结构元素扫描图像,如果结构元素至少有一个与前景(白色)部分重叠,则图像中的中心元素被设置为前景。

与卷积类似,也有一个矩阵来扫描整张图片,比如下方这个3*3矩阵,

1 \& 1 \& 1 1 \& 1 \& 1 1 \& 1 \& 1

当这个矩阵在图片扫描的时候,如果矩阵的任何元素遇到图像的像素值"1"。则与内核中心元素重叠的像素将转换为"1"。如下图所示

整个扫描的过程动态如下:会将橙色部分进行扩张,这就是膨胀的过程。

使用膨胀操作来修复裂痕

示例代码

//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
    Mat mask_image;
    threshold(image,mask_image,150,255,THRESH_BINARY_INV);  //图像进行二值化
    // 创建结构元素 扫描核
    int kernel_size =3;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
                                                cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
                                                cv::Point(-1, -1));
    // 执行膨胀操作
    cv::Mat eroded_image;
    cv::dilate(mask_image, eroded_image, element,Point(-1,-1),1);
    // 显示结果图像
    cv::imshow("Original Image", mask_image);
    cv::imshow("Eroded Image", eroded_image);
    waitKey();
}

关键解析:

threshold(image,mask_image,150,255,THRESH_BINARY);  //图像进行二值化

对图像进行二值化,让图像非黑即白,当然膨胀操作也可以用于彩色图像,但是会有一个问题,中心点像素会累加,会提高原来图片的亮度,扫描核越大,会导致图片越亮,直到白色看不见为止。

   cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
                                                cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
                                                cv::Point(-1, -1));

创建扫描矩阵,矩阵可以任意大小,这里使用奇数n*n的矩阵 Point 取(-1,-1)代表使用中心点像素,扫描矩阵的size越大,膨胀效果越强。

  cv::dilate(mask_image, eroded_image, element,Point(-1,-1),1);

膨胀操作API ,函数原型

CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
                          Point anchor = Point(-1,-1), int iterations = 1,
                          int borderType = BORDER_CONSTANT,
                          const Scalar& borderValue = morphologyDefaultBorderValue() );

一般只关心前三个参数即可,输入,输出,扫描核(扫描的矩阵),Point 取(-1,-1)代表使用中心点像素,这里迭代次数为1次,同理 迭代次数越多,膨胀效果越强

效果如图:

扫描核size为 3:有效果,但仍然有裂痕

扫描核 size 为 5:效果增强,边缘变粗,基本上添上空隙

腐蚀

与膨胀相反,删除元素,它腐蚀图像的方式就像水侵蚀河岸一样。在腐蚀操作中,它将结构元素从输入图像的左向右和从上到下滑动。如果结构元素内的所有像素都大于 0,则保留原始像素值。否则,像素设置为 0。腐蚀用于去除被视为噪声的小斑点。

同样的使用膨胀操作的扫描核 3*3 全为1 的矩阵

1 \& 1 \& 1 1 \& 1 \& 1 1 \& 1 \& 1

此内核遍历图像的每个像素。如果与内核重叠的所有像素恰好是"1",则不会发生任何更改。但是,如果任何重叠的像素恰好为"0",则与内核的 中心 元素重叠的像素将设置为"0"。

腐蚀操作可视化图:

随着迭代次数的增加,图像像素点慢慢被腐蚀。因此,如果您需要提取粗体且周围有很多噪点的时候,可以通过侵蚀图像来消除噪点。

使用腐蚀操作消除噪点

示例代码:

//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
    Mat mask_image;
    threshold(image,mask_image,150,255,THRESH_BINARY_INV);  //图像进行二值化
    // 创建结构元素 扫描核
    int kernel_size =1;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
                                                cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
                                                cv::Point(-1, -1));
    // 执行腐蚀操作
    cv::Mat eroded_image;
    cv::erode(mask_image, eroded_image, element,Point(-1,-1),1);
    // 显示结果图像
    cv::imshow("Original Image", mask_image);
    cv::imshow("Eroded Image", eroded_image);
    waitKey();
}
int main()
{
    string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\eroding_test.jpg";
    string mix_image_path = "C:\\Users\\Marxist\\Pictures\\coco\\Linux.jpg";
    Mat image = imread(imagePath,IMREAD_GRAYSCALE);//读取灰度图像
    morphology_op_demo(image);
    return 0;
}

关键解析:

cv::erode(mask_image, eroded_image, element,Point(-1,-1),1);

函数原型:

CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel,
                         Point anchor = Point(-1,-1), int iterations = 1,
                         int borderType = BORDER_CONSTANT,
                         const Scalar& borderValue = morphologyDefaultBorderValue() );

与膨胀API参数一致,但效果相反

腐蚀操作---内核大小3*3 迭代1次,噪点明显消除

腐蚀操作---内核大小5*5 迭代1次,噪点完全消除,但是图像细节也跟着丢失了

面对图像丢失的情况,可以进行开闭运算了

开运算---先腐蚀后膨胀

开运算是先进行腐蚀操作,再进行膨胀操作。它主要用于去除小的噪点,并保持前景物体的整体形状。

关键函数:

  morphologyEx(mask_image, opening_image, MORPH_OPEN, element);

本质上就是调用了腐蚀API和膨胀API,OpenCV为了代码简洁,将二个API 合成了一个

示例代码

//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
    Mat mask_image;
    threshold(image,mask_image,150,255,THRESH_BINARY_INV);  //图像进行二值化
    // 创建结构元素 扫描核
    int kernel_size =2;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
                                                cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
                                                cv::Point(-1, -1));
    // 执行腐蚀操作
    cv::Mat eroded_image;
    cv::erode(mask_image, eroded_image, element,Point(-1,-1),1);
    Mat final;
    // 先执行 腐蚀 然后执行膨胀
    morphologyEx(mask_image, final, MORPH_OPEN, element);
    cv::imshow("Original Image", mask_image);
    cv::imshow("Eroded Image", eroded_image);
    cv::imshow("final Image", final);
    waitKey();
}
int main()
{
    string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\eroding_test.jpg";
    string mix_image_path = "C:\\Users\\Marxist\\Pictures\\coco\\Linux.jpg";
    Mat image = imread(imagePath,IMREAD_GRAYSCALE);//读取灰度图像
    morphology_op_demo(image);
    return 0;
}

开运算效果:对比腐蚀过后的图像,进行稍微膨胀 补充连接细节

闭运算---先膨胀后腐蚀

//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
    Mat mask_image;
    threshold(image,mask_image,150,255,THRESH_BINARY_INV);  //图像进行二值化
    // 创建结构元素 扫描核
    int kernel_size =2;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
                                                cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
                                                cv::Point(-1, -1));
    // 执行膨胀操作
    cv::Mat eroded_image;

    cv::dilate(mask_image, eroded_image, element,Point(-1,-1),1);

    Mat final;
    // 先执行 腐蚀 然后执行膨胀
    morphologyEx(mask_image, final, MORPH_CLOSE, element);

    cv::imshow("Original Image", mask_image);
    cv::imshow("Eroded Image", eroded_image);
    cv::imshow("final Image", final);
    waitKey();
}
int main()
{
    string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\dilation_test.jpg";
    string mix_image_path = "C:\\Users\\Marxist\\Pictures\\coco\\Linux.jpg";
    Mat image = imread(imagePath,IMREAD_GRAYSCALE);//读取灰度图像
    morphology_op_demo(image);
    return 0;
}

闭运算与开运算相反,先膨胀后腐蚀,它主要用于填补前景物体中的小孔和连接断开的物体。

闭运算效果:对比膨胀后的图像,边缘稍微细了些

相关推荐
TaoYuan__30 分钟前
机器学习【激活函数】
人工智能·机器学习
TaoYuan__31 分钟前
机器学习的常用算法
人工智能·算法·机器学习
正义的彬彬侠35 分钟前
协方差矩阵及其计算方法
人工智能·机器学习·协方差·协方差矩阵
致Great42 分钟前
Invar-RAG:基于不变性对齐的LLM检索方法提升生成质量
人工智能·大模型·rag
华奥系科技1 小时前
智慧安防丨以科技之力,筑起防范人贩的铜墙铁壁
人工智能·科技·安全·生活
ZPC82101 小时前
OpenCV—颜色识别
人工智能·opencv·计算机视觉
Mr.简锋1 小时前
vs2022搭建opencv开发环境
人工智能·opencv·计算机视觉
m0_743106461 小时前
论文笔记:no pose,no problem-基于dust3r输出GS参数实现unpose稀疏重建
论文阅读·深度学习·计算机视觉·3d·几何学
weixin_443290691 小时前
【论文阅读】InstructPix2Pix: Learning to Follow Image Editing Instructions
论文阅读·人工智能·计算机视觉
ai产品老杨2 小时前
部署神经网络时计算图的优化方法
人工智能·深度学习·神经网络·安全·机器学习·开源