从头开始opencv(十三)——image:Eroding and Dilating


参考资料

Goal

  Apply two very common morphological operators: Erosion and Dilation. For this purpose, you will use the following OpenCV functions:

  应用两个非常常见的形态学操作:腐蚀和膨胀。为此,我们将使用如下的opencv函数:cv::erodecv::dilate

Morphological Operations

  A set of operations that process images based on shapes. Morphological operations apply a *structuring element* to an input image and generate an output image

  一组根据形状来处理图像的操作。形态学操作将structuring element应用到输入图像中,从而生成输出图像。

  最基本的形态学操作就是腐蚀和膨胀。他们可以有很多种应用,例如:

  • Removing noise.(消除噪声)
  • Isolation of individual elements and joining disparate elements in an image.(分割出独立的图像元素,在图像中连接相邻的元素)
  • Finding of intensity bumps or holes in an image(查找图像中的极大值区域或极小值区域)

参考资料

在进行腐蚀和膨胀的讲解之前,首先需要注意,腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,”领域扩张“,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,”领域被蚕食“,效果图拥有比原图更小的高亮区域。

  我们会以下图为例来对腐蚀和膨胀进行一个简短的解释。
在这里插入图片描述

Dilation(膨胀)

  This operations consists of convolving an image A with some kernel ( B), which can have any shape or size, usually a square or circle.

  此操作包括将图像A与某一个核B进行卷积,该核可以是具有任意形状和大小的(不过通常是正方形或者圆形)

  核B具有锚点,通常是核的中心。
  
  As the kernel B is scanned over the image, we compute the maximal pixel value overlapped by B and replace the image pixel in the anchor point position with that maximal value. As you can deduce, this maximizing operation causes bright regions within an image to "grow" (therefore the name *dilation*). Take the above image as an example. Applying dilation we can get:

  在核B扫描图像时,我们计算与B重叠的最大像素值并用该最大值替换锚点位置中的图像像素。可以推断,这种最大化操作会导致图像中的明亮区域“增长”(因此称为"膨胀")

  感觉这里讲的不是很清楚,我另外查阅了一些资料:

参考资料
  结构A被结构B膨胀的定义为:
A ⊕ B = { z ∣ ( B ^ ) z ∩ A ≠ ∅ } A\oplus B=\{z|(\hat{B})_z \cap A\ne \emptyset \} AB={z(B^)zA=}
  可以理解为,将结构B在结构A上进行卷积操作,如果移动结构B的过程中,与结构A存在重叠区域,则记录该位置,所有移动结构B与结构A存在交集的位置的集合为结构A在结构B作用下的膨胀结果。

  图示中红色框内的区域表示结构A在结构B的作用下膨胀的结果。
在这里插入图片描述

参考资料

  从数学上看,膨胀或者腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行卷积。核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点(anchor point)。多数情况下,核是一个小的中间带有参考点的实心正方形或者圆盘,其实我们可以把核视为模板或者掩码。

  膨胀就是求局部最大值的操作,核B与图形卷积,即计算核B覆盖的区域(体现局部)的像素点的最大值,并把这个最大值赋值给参考点指定的像素,这样就会使图像中的高亮区域逐渐增长.

参考资料

  二值膨胀就是对一个二值图进行膨胀操作。对一个二值图像进行膨胀操作需要一个SE核,SE核由一个二值矩阵组成,还需要定义一个原点,表示核的核心。

  遍历原图像的每一个像素点,将其与SE核的原点对齐,然后取当前SE中所有1的位置所覆盖下原图中对应的像素中的最大值,用这个最大值替换当前像素值。由于二值图像最大值就是1,所以就是用1替换原点。

  只有SE位于前景物体(像素值为1)边缘时,它覆盖的区域内才会同时出现0和1两种不同的像素值,这时把当前像素替换成1。因此膨胀看起来的效果就是让前景物体膨胀了一圈,使得边界向外扩散。对于前景物体中一些细小的断裂处,这些断裂的地方可能会被连接起来。

  膨胀操作使前景边界向外扩张,会增大前景区域。可能会连接不同白色区域,也可以用来填补前景中的空洞,去除黑色噪点。

  膨胀可以理解为B的中心(锚点)沿着A的外边界走了一圈。膨胀是对高亮部分而言,A区域之外的部分<A的高亮像素,所以外面被里面取代。
在这里插入图片描述

  The background (bright) dilates around the black regions of the letter.

  背景(明亮部分)在字母的黑色区域周围膨胀。
在这里插入图片描述

Erosion(腐蚀)

  This operation is the sister of dilation. It computes a local minimum over the area of given kernel.

  这个操作是膨胀的姊妹,它用来计算在核范围内的最小值。
  
  As the kernel B is scanned over the image, we compute the minimal pixel value overlapped by B and replace the image pixel under the anchor point with that minimal value.

  在使用核B扫描图像时,我们计算与B重叠的最小像素值,并用该最小值替换锚点下的图像像素。
  
  Analagously to the example for dilation, we can apply the erosion operator to the original image (shown above). You can see in the result below that the bright areas of the image (the background, apparently), get thinner, whereas the dark zones (the "writing") gets bigger.

  与膨胀示例类似,我们可以将腐蚀算子应用于原始图像,我们可以在下面的结果中看到,图像的明亮区域变少,而黑暗区域变大。
在这里插入图片描述

Code

/**
 * @file Morphology_1.cpp
 * @brief Erosion and Dilation sample code
 * @author OpenCV team
 */

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;

/// Global variables
Mat src, erosion_dst, dilation_dst;

int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;

/** Function Headers */
void Erosion(int, void*);
void Dilation(int, void*);

/**
 * @function main
 */
int main(int argc, char** argv)
{
    /// Load an image
    src = imread("D:/0PKU/opencv/test.jpg", IMREAD_COLOR);
    if (src.empty())
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }

    /// Create windows
    namedWindow("Erosion Demo", WINDOW_AUTOSIZE);
    namedWindow("Dilation Demo", WINDOW_AUTOSIZE);
    moveWindow("Erosion Demo", 0, 0);
    moveWindow("Dilation Demo", src.cols, 0);

    /// Create Erosion Trackbar
    createTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
        &erosion_elem, max_elem,
        Erosion);

    createTrackbar("Kernel size:\n 2n +1", "Erosion Demo",
        &erosion_size, max_kernel_size,
        Erosion);

    /// Create Dilation Trackbar
    createTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
        &dilation_elem, max_elem,
        Dilation);

    createTrackbar("Kernel size:\n 2n +1", "Dilation Demo",
        &dilation_size, max_kernel_size,
        Dilation);

    /// Default start
    Erosion(0, 0);
    Dilation(0, 0);

    waitKey(0);
    return 0;
}

//![erosion]
/**
 * @function Erosion
 */
void Erosion(int, void*)
{
    int erosion_type = 0;
    if (erosion_elem == 0) { erosion_type = MORPH_RECT; }
    else if (erosion_elem == 1) { erosion_type = MORPH_CROSS; }
    else if (erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }

    //![kernel]
    Mat element = getStructuringElement(erosion_type,
        Size(2 * erosion_size + 1, 2 * erosion_size + 1),
        Point(erosion_size, erosion_size));
    //![kernel]

    /// Apply the erosion operation
    erode(src, erosion_dst, element);
    imshow("Erosion Demo", erosion_dst);
}
//![erosion]

//![dilation]
/**
 * @function Dilation
 */
void Dilation(int, void*)
{
    int dilation_type = 0;
    if (dilation_elem == 0) { dilation_type = MORPH_RECT; }
    else if (dilation_elem == 1) { dilation_type = MORPH_CROSS; }
    else if (dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }

    Mat element = getStructuringElement(dilation_type,
        Size(2 * dilation_size + 1, 2 * dilation_size + 1),
        Point(dilation_size, dilation_size));

    /// Apply the dilation operation
    dilate(src, dilation_dst, element);
    imshow("Dilation Demo", dilation_dst);
}
//![dilation]

Explanation

  程序结构:

  • 加载图片
  • 创建两个窗口
  • 对每种操作都创建了两个对应的trackbars
    • 第一个trackbar element返回erosion_elem或者dilation_elem
    • 第二个trackbar kernel 返回erosion_size或者dilation_size

  Every time we move any slider, the user's function Erosion or Dilation will be called and it will update the output image based on the current trackbar values.

  每当我们移动任何滑块时,都会调用用户的”侵蚀“或”膨胀“功能,他将基于当前的trackbar栏的内容更新输出图像。

erosion

void Erosion( int, void* )
{
  int erosion_type = 0;
  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
  Mat element = getStructuringElement( erosion_type,
                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                       Point( erosion_size, erosion_size ) );
  erode( src, erosion_dst, element );
  imshow( "Erosion Demo", erosion_dst );
}

  核心代码在cv::erode上,它有三个参数:

  • src:源图像

  • erosion_dst:输出图像

  • element:这是我们用来执行操作的核,如果我们没有指定element的值,默认为简单的 3 × 3 3\times 3 3×3矩阵。否则,我们可以指定其他的形状(使用cv::getStructuringElement()函数)

      Mat element = getStructuringElement( erosion_type,
                           Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                           Point( erosion_size, erosion_size ) );
    //erosion_type:
    //Rectangle——MORPH_RECT
    //Cross——MORPH_CROSS
    //Ellipse——MORPH_ELLIPSE
    

dilation

void Dilation( int, void* )
{
  int dilation_type = 0;
  if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
  else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
  else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
  Mat element = getStructuringElement( dilation_type,
                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                       Point( dilation_size, dilation_size ) );
  dilate( src, dilation_dst, element );
  imshow( "Dilation Demo", dilation_dst );
}

  代码和”腐蚀“的代码极为类似。

CreateTrackbar()

int cv::createTrackbar	(	
const String & 	trackbarname,//创建的轨迹栏(trackbar)的名字
const String & 	winname,//用来创建轨迹栏的窗口的名称
int * 	value,//指向整型变量的可选指针,该变量的值反映滑块的位置。创建后,滑块位置由该变量定义
int 	count,//滑块的最大大小。最小位置始终为0
TrackbarCallback 	onChange = 0,//回调函数
void * 	userdata = 0 //传递给回调的用户数据
)	

  Creates a trackbar and attaches it to the specified window.

  创建一个轨迹栏并将其附加到指定的窗口。
  
  The function createTrackbar creates a trackbar (a slider or range control) with the specified name and range, assigns a variable value to be a position synchronized with the trackbar and specifies the callback function onChange to be called on the trackbar position change. The created trackbar is displayed in the specified window winname.

  函数createTrackbar创建具有指定名称和范围的trackbar,在拖动轨迹的时候,会调用回调函数onChange,创建的trackbar会显示在指定窗口上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值