图像的形态学操作

OpenCV中的形态学操作

图像的形态学操作(Morphological Operations)是一种基于图像形状的处理方法,通常用于二值图像的分析和处理。形态学操作通过对图像中各个区域的结构进行改变或分析,来提取或增强图像中的形态特征(如边缘、物体、空洞等)。

这些操作在许多计算机视觉任务中非常常见,例如噪声去除、边缘检测、图像分割、物体识别等。它们主要基于图像的几何形状进行分析,通过设置形态学核(通常是小的二值结构元素)来操作图像。

基本的形态学操作

常见的形态学操作有以下几种:

  • 膨胀(Dilation)腐蚀(Erosion)
  • 开运算(Opening)闭运算(Closing)
  • 顶帽运算(Top-hat)黑帽运算(Black-hat)

1.腐蚀和膨胀

膨胀:膨胀操作会使图像中的白色区域变大,黑色区域缩小。它将卷积核(通常是一个小的矩形或圆形区域)放置在图像的每个像素上,然后对应位置相乘,并选取所得结果中的最小值。

腐蚀:腐蚀操作与膨胀相反,它会使图像中的白色区域缩小,黑色区域变大。它会将所得结果的最小值作为输出。

示例:

假设有一个简单的二值图像,其中前景是白色,背景是黑色,卷积核为3*3的全1矩阵。

原图:
  1 1 1 1 1
  1 0 0 0 1
  1 0 0 0 1
  1 1 1 1 1
  • 腐蚀:

    • 进行腐蚀后,白色区域会变小:
    腐蚀后:
      0 0 0 0 0
      0 0 0 0 0
      0 0 0 0 0
      0 0 0 0 0
    
  • 膨胀:

    • 进行膨胀后,白色区域会变大:
    膨胀后:
      1 1 1 1 1
      1 1 1 1 1
      1 1 1 1 1
      1 1 1 1 1
    

OpenCV中的实现

生成结构元素

在进行图像腐蚀等操作前,我们得先生成结构元素。

Mat cv::getStructuringElement(int shape,							//生成结构元素的种类,比如MORPH_CROSS
							  cv::Size ksize,						//尺寸,一般有3*3,5*5,7*7
							  cv::Point anchor = cv::Point(-1,-1) 	//中心的位置,默认为几何中心
)

其中shape可以为:

  • cv::MORPH_ELLIPSE:圆形结构元素
  • cv::MORPH_CROSS:十字形结构元素
  • cv::MORPH_RECT:矩形结构元素

结构元素决定了操作时如何对每个像素的邻域进行处理,进而影响图像处理的形态(即结果的边界形状)。

例如:

  1. 矩形结构元素
    • 使用矩形结构元素,图像的物体边缘会以矩形的方式扩展或收缩,因为矩形结构元素会考虑到水平方向和垂直方向的邻域。腐蚀操作时,图像中的白色区域会以矩形形状收缩
    • 适用:对于一个有明显直线和角落的物体,矩形结构元素会使物体的边缘保持直线或者矩形的形状。
  2. 圆形结构元素
    • 使用圆形结构元素,图像中的物体边缘会呈现圆形的扩展或收缩,因为圆形结构元素会考虑到周围像素的圆形邻域。腐蚀操作时,图像中的白色区域会以圆形形状收缩
    • 适用:对于一个不规则形状的物体,使用圆形结构元素会使得物体的边缘变得更加平滑,尤其是弯曲的边缘会变得更加圆滑。圆形结构元素能够更自然地连接物体的曲线。
  3. **十字形结构元素:
    • 使用十字形结构元素,膨胀和腐蚀操作的结果会沿着水平和垂直方向扩展或收缩。这意味着边缘在这些方向上会受到影响,但在对角线方向上不会被影响
    • 适用:如果物体的边缘是直线,十字形结构元素的膨胀操作会沿着这些直线方向扩展边缘,但不会影响到对角线上的物体形状。

总结一下,矩形结构元素会让边缘保持直线形状(矩形或方形)。圆形结构元素会让边缘变得圆滑,适合对不规则形状的物体进行处理。十字形结构元素则在水平和垂直方向上扩展边界,适用于对有明确水平或垂直边缘的图像进行处理。

cv::dilate() 函数与cv::erode() 函数
1.cv::dilate()函数

cv::dilate()函数用于执行膨胀操作(dilation),其函数原型为:

void cv::dilate(
    InputArray src,       					// 输入图像(通常是二值图像)
    OutputArray dst,      					// 输出图像
    InputArray kernel,    					// 结构元素(卷积核)
    Point anchor = Point(-1, -1),  			// 锚点,默认为结构元素的中心
    int iterations = 1,   					// 膨胀操作的迭代次数
    int borderType = BORDER_CONSTANT, 		// 边界类型,默认为常数边界
    const Scalar& borderValue = Scalar()  	// 边界值
);
2.cv::erode()函数

cv::erode()函数用于执行腐蚀操作(erosion),其函数原型为:

void cv::erode(
    InputArray src,      					// 输入图像(通常是二值图像)
    OutputArray dst,      					// 输出图像
    InputArray kernel,    					// 结构元素(卷积核)
    Point anchor = Point(-1, -1), 			// 锚点,默认为结构元素的中心
    int iterations = 1,   					// 腐蚀操作的迭代次数,默认为1
    int borderType = BORDER_CONSTANT, 		// 边界类型,默认为常数边界
    const Scalar& borderValue = Scalar()  	// 边界值
);

iterations 参数控制了膨胀和腐蚀操作的次数。迭代次数越多,效果越显著。

  • 多次腐蚀:会使物体边界收缩得更厉害,甚至完全去除细小物体。

  • 多次膨胀:会使物体边界扩展得更大,填补更多的空洞,连接更多的区域。

完整代码如下
膨胀示例代码
#include 
#include 

int main() {
    // 读取输入图像(假设是二值图像)
    cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
    
    // 检查图像是否成功读取
    if (inputImage.empty()) {
        std::cerr << "Image not loaded!" << std::endl;
        return -1;
    }

    // 创建一个3x3的矩形结构元素
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

    // 执行膨胀操作
    cv::Mat dilatedImage;
    cv::dilate(inputImage, dilatedImage, kernel, cv::Point(-1, -1), 1); // 迭代次数为1

    // 显示原图和膨胀后的图像
    cv::imshow("Original Image", inputImage);
    cv::imshow("Dilated Image", dilatedImage);

    cv::waitKey(0);
    return 0;
}
腐蚀示例代码:
#include 
#include 

int main() {
    // 读取输入图像(假设是二值图像)
    cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);

    // 检查图像是否成功读取
    if (inputImage.empty()) {
        std::cerr << "Image not loaded!" << std::endl;
        return -1;
    }

    // 创建一个3x3的矩形结构元素
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

    // 执行腐蚀操作
    cv::Mat erodedImage;
    cv::erode(inputImage, erodedImage, kernel, cv::Point(-1, -1), 1); // 迭代次数为1

    // 显示原图和腐蚀后的图像
    cv::imshow("Original Image", inputImage);
    cv::imshow("Eroded Image", erodedImage);

    cv::waitKey(0);
    return 0;
}

膨胀和腐蚀的实际应用

  1. 去噪
    • 腐蚀:用于去除二值图像中的小白点或噪声。
    • 膨胀:用于填补物体边缘的小黑点或空洞。
  2. 物体边界处理
    • 膨胀:用来增强物体的边界或将小物体连接起来。
    • 腐蚀:用来减少物体边界,移除图像中的小物体。
  3. 形状提取
    • 通过连续的腐蚀和膨胀操作,可以提取图像中的物体形状、去除不必要的部分。
  4. 连接和分离物体
    • 通过膨胀,可以将分离的物体连接在一起;通过腐蚀,可以将连接在一起的物体分开。

2.开运算和闭运算

开运算(Opening)和闭运算(Closing)是形态学图像处理中的两种重要操作,它们都是基于膨胀和腐蚀操作的组合。这两种运算能够有效地去除噪声、平滑物体的边界、填补小的空洞等。开运算和闭运算在许多图像处理任务中被广泛使用,尤其是在二值图像的处理和分析中。

morphologyEx()函数

在介绍开运算和闭运算之前,首先我们要介绍一下morphologyEx()函数,因为在OpenCV中开闭运算通过此函数实现。而且,上面提到的腐蚀和膨胀操作也可以通过这个函数完成。

函数原型:
void morphologyEx(
    InputArray src, 
    OutputArray dst, 
    int op, 
    InputArray kernel, 
    Point anchor = Point(-1, -1), 
    int iterations = 1, 
    BorderTypes borderType = BORDER_CONSTANT, 
    const Scalar& borderValue = morphologyDefaultBorderValue()
);
参数说明:
  1. src: 输入图像,必须是单通道的图像,通常为灰度图。它是需要应用形态学操作的源图像。
  2. dst: 输出图像,即形态学操作后的结果。
  3. op: 操作类型。这个参数指定了执行哪种形态学操作,它是一个整数值。OpenCV 提供了几种不同的操作类型,通过以下常量来指定:
    • MORPH_ERODE:腐蚀操作(erosion)
    • MORPH_DILATE:膨胀操作(dilation)
    • MORPH_OPEN:开运算(opening),先腐蚀再膨胀
    • MORPH_CLOSE:闭运算(closing),先膨胀再腐蚀
    • MORPH_GRADIENT:形态学梯度(morphological gradient),膨胀和腐蚀的差
    • MORPH_TOPHAT:顶帽运算(top hat),原图减去开运算结果
    • MORPH_BLACKHAT:黑帽运算(black hat),闭运算结果减去原图
  4. kernel: 结构元素(kernel),和上文介绍的结构元素一样:
    • MORPH_RECT:矩形结构元素
    • MORPH_ELLIPSE:椭圆形结构元素
    • MORPH_CROSS:十字形结构元素
  5. anchor: 锚点位置,指定结构元素的中心点,默认值为 Point(-1, -1),表示结构元素的中心点自动设置为结构元素的几何中心。
  6. iterations: 迭代次数,指定形态学操作执行的次数。通常,我们只需要执行一次操作,但有时需要多次迭代来得到期望的效果。
  7. borderType: 边界类型,定义图像边界外的区域如何处理。常用的边界类型有:
    • BORDER_CONSTANT:常量边界(默认值)
    • BORDER_REFLECT:边界反射
    • BORDER_REPLICATE:边界复制
    • BORDER_WRAP:边界环绕
    • BORDER_REFLECT_101:反射类型 101
    • BORDER_TRANSPARENT:透明边界
  8. borderValue: 边界值,指定边界类型时,设置常量边界的值。默认使用 morphologyDefaultBorderValue()

1. 开运算(Opening)

1.1 定义

开运算是腐蚀和膨胀操作的组合。具体来说,开运算对图像进行腐蚀操作,进行膨胀操作。开运算可以用来去除小的噪点,并平滑物体的边界。

1.2 过程
  • 腐蚀:首先使用腐蚀操作减少图像中前景(白色)部分的大小,移除小的白色噪点。
  • 膨胀:然后对腐蚀后的图像进行膨胀,恢复物体的边界,但不会恢复之前腐蚀过程中去除的小噪点。

这种操作可以去除图像中的小的白色噪点或小的连接区域,同时保留物体的主要结构。

1.3 应用
  • 去噪:去除二值图像中的小的白色噪点。
  • 平滑物体边界:去除物体边缘的小缺陷,使边界更加平滑。
  • 物体分割:去除连接在一起的小物体,使它们独立出来。
1.4 代码示例(开运算)
#include 
#include 

int main() {
    // 读取输入图像(假设是二值图像)
    cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);

    // 检查图像是否成功读取
    if (inputImage.empty()) {
        std::cerr << "Image not loaded!" << std::endl;
        return -1;
    }

    // 创建一个3x3的矩形结构元素
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

    // 执行开运算(腐蚀 + 膨胀)
    cv::Mat openedImage;
    cv::morphologyEx(inputImage, openedImage, cv::MORPH_OPEN, kernel);

    // 显示原图和开运算后的图像
    cv::imshow("Original Image", inputImage);
    cv::imshow("Opened Image", openedImage);

    cv::waitKey(0);
    return 0;
}

2. 闭运算(Closing)

2.1 定义

闭运算是膨胀和腐蚀操作的组合。具体来说,闭运算对图像进行膨胀操作,进行腐蚀操作。闭运算可以用来填补物体中的小孔洞、连接邻近的物体,并平滑物体的边界。

2.2 过程
  • 膨胀:首先使用膨胀操作扩展图像中前景(白色)部分的大小,填补物体中的小黑洞或缝隙。
  • 腐蚀:然后对膨胀后的图像进行腐蚀,恢复物体的边界,但不会影响已经填补的孔洞或连接的区域。

闭运算通过膨胀填补空洞,通过腐蚀恢复边界的细节,常用于连接分离的物体或填补小的缺失区域。

2.3 应用
  • 填补小孔洞:闭运算可以填补图像中的小黑洞或小裂缝。
  • 物体连接:当图像中的物体因为噪声或其他原因被分割成小块时,闭运算可以将它们连接起来。
  • 平滑边界:闭运算也可以平滑物体边界,使物体边界更加清晰。
2.4 代码示例(闭运算)
#include 
#include 

int main() {
    // 读取输入图像(假设是二值图像)
    cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);

    // 检查图像是否成功读取
    if (inputImage.empty()) {
        std::cerr << "Image not loaded!" << std::endl;
        return -1;
    }

    // 创建一个3x3的矩形结构元素
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

    // 执行闭运算(膨胀 + 腐蚀)
    cv::Mat closedImage;
    cv::morphologyEx(inputImage, closedImage, cv::MORPH_CLOSE, kernel);

    // 显示原图和闭运算后的图像
    cv::imshow("Original Image", inputImage);
    cv::imshow("Closed Image", closedImage);

    cv::waitKey(0);
    return 0;
}

3. 开运算与闭运算的区别

特性 开运算 闭运算
操作顺序 先腐蚀,再膨胀 先膨胀,再腐蚀
作用 去除小的白色噪点,平滑物体的边界 填补小的黑色空洞,连接物体
主要应用 去噪、物体分割、平滑边界 填补小孔洞、物体连接、平滑边界
影响效果 物体边界收缩,细节被消除 物体边界扩展,细节被连接

3.顶帽运算与黑帽运算

顶帽运算(Top Hat)和黑帽运算(Black Hat)是形态学操作中的两种常见运算,它们都是通过开运算和闭运算的差值来处理图像的。它们能够帮助提取图像中某些特定区域的细节,常用于图像的特征提取、背景建模和物体检测等任务。

1. 顶帽运算(Top Hat)

顶帽运算是原图像与其开运算结果的差值。开运算(Opening)是先腐蚀后膨胀,它通常用于去除小的噪声或细小的物体。顶帽运算则帮助我们提取出原图像中比背景亮的区域,也就是去除掉了背景后剩下的细节部分。

数学公式:

TopHat ( I ) = I − Opening ( I ) \text{TopHat}(I) = I - \text{Opening}(I) TopHat(I)=IOpening(I)

  • I是原图像。
  • Opening 是开运算:先腐蚀再膨胀。
作用:
  • 提取图像中比背景亮的部分。
  • 强调细节部分,如物体表面的细小特征、噪声等。
应用场景:
  • 细节增强:在图像中提取明亮区域,可以用于增强物体的边缘或小区域。
  • 噪声去除:去除背景的平坦区域,只保留小的亮区域。

2. 黑帽运算(Black Hat)

黑帽运算是闭运算结果与原图像的差值。闭运算(Closing)是先膨胀后腐蚀,它通常用于填补图像中的小黑洞或噪声。黑帽运算则帮助我们提取出原图像中比背景暗的区域,也就是去除掉物体后的背景部分。

数学公式:

BlackHat ( I ) = Closing ( I ) − I \text{BlackHat}(I) = \text{Closing}(I) - I BlackHat(I)=Closing(I)I

其中:

  • I是原图像。
  • Closing 是闭运算:先膨胀再腐蚀。
作用:
  • 提取图像中比背景暗的部分。
  • 强调背景中的细节或小物体。
应用场景:
  • 阴影提取:提取图像中的暗区域,可以用于检测背景中的阴影或其他暗物体。
  • 背景增强:通过去除物体,提取出背景中较暗的区域。

3.在OpenCV中的实现

#include 
#include 

using namespace cv;
using namespace std;

int main()
{
    // 1. 读取图像
    Mat image = imread("example.jpg", IMREAD_GRAYSCALE); // 以灰度模式读取图像
    if (image.empty()) {
        cout << "Could not open or find the image!" << endl;
        return -1;
    }

    // 2. 显示原始图像
    imshow("Original Image", image);
    waitKey(0); // 等待按键事件

    // 3. 创建结构元素(Kernel)
    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); // 使用3x3矩形结构元素

    // 4. 顶帽运算(原图像 - 开运算结果)
    Mat tophat;
    morphologyEx(image, tophat, MORPH_TOPHAT, kernel);

    // 5. 黑帽运算(闭运算结果 - 原图像)
    Mat blackhat;
    morphologyEx(image, blackhat, MORPH_BLACKHAT, kernel);

    // 6. 显示结果
    imshow("Top Hat Image", tophat); // 显示顶帽运算结果
    imshow("Black Hat Image", blackhat); // 显示黑帽运算结果
    waitKey(0); // 等待按键事件

    // 释放窗口
    destroyAllWindows();

    return 0;
}

相信读者也能看出来,morphologyEx() 是一个非常灵活的函数。它可以执行各种形态学操作,帮助我们处理图像中的形状、边界、噪声等。通过调整结构元素的大小和形状,morphologyEx() 可以应用于许多不同的图像处理任务,如去噪、物体分割、边缘检测、细节提取等。

你可能感兴趣的:(OpenCV入门,opencv,计算机视觉,c++)