OpenCV图像处理篇之腐蚀与膨胀

转载请注明出处:http://xiahouzuoxin.github.io/notes


腐蚀与膨胀

腐蚀和膨胀是图像的形态学处理中最基本的操作,之后遇见的开操作和闭操作都是腐蚀和膨胀操作的结合运算。腐蚀和膨胀的应用非常广泛,而且效果还很好:

  1. 腐蚀可以分割(isolate)独立的图像元素,膨胀用于连接(join)相邻的元素,这也是腐蚀和膨胀后图像最直观的展现
  2. 去噪:通过低尺寸结构元素的腐蚀操作很容易去掉分散的椒盐噪声点
  3. 图像轮廓提取:腐蚀操作
  4. 图像分割
  5. 等等...(在文后给出一则简单实用膨胀操作提取车牌数字区域的例子)

结构元素是形态学操作中最重要的概念,

 

如上图,B为结构元素。

腐蚀操作描述为:扫描图像的每一个像素,用结构元素与其覆盖的二值图像做“与”操作:如果都为1,结果图像的该像素为1,否则为0。

膨胀操作描述为:扫描图像的每一个像素,用结构元素与其覆盖的二值图像做“与”操作:如果都为0,结果图像的该像素为0,否则为1。

以上都是关于二值图像的形态学操作,对于灰度图像:

  1. 腐蚀操作

    其中,g(x,y)为腐蚀后的灰度图像,f(x,y)为原灰度图像,B为结构元素。腐蚀运算是由结构元素确定的邻域块中选取图像值与结构元素值的差的最小值。

  2. 膨胀操作

    其中,g(x,y)为腐蚀后的灰度图像,f(x,y)为原灰度图像,B为结构元素。 膨胀运算是由结构元素确定的邻域块中选取图像值与结构元素值的和的最大值。

在灰度图的形态学操作中,一般选择“平摊”的结构元素,即结构元素B的值为0,则上面对灰度图的形态学操作可简化如下:

好了,这就是基本的形态学操作——腐蚀和膨胀,下面是使用OpenCV对图像进行腐蚀和膨胀的程序,还是秉承我们一贯的原则:搁下理论,先直观地感觉图像处理算法的效果,实际项目需要时再深入挖掘!

程序分析

/*
 * FileName : eroding_and_dilating.cpp
 * Author   : xiahouzuoxin @163.com
 * Version  : v1.0
 * Date     : Fri 19 Sep 2014 07:42:12 PM CST
 * Brief    :
 *
 * Copyright (C) MICL,USTB
 */
#include "cv.h"
#include "highgui.h"
#include "opencv2/imgproc/imgproc.hpp"

using namespace std;
using namespace cv;

#define TYPE_MORPH_RECT      (0)
#define TYPE_MORPH_CROSS     (1)
#define TYPE_MORPH_ELLIPSE   (2)

#define MAX_ELE_TYPE         (2)
#define MAX_ELE_SIZE         (20)

Mat src, erode_dst, dilate_dst;

const char *erode_wn  = "eroding demo";
const char *dilate_wn = "dilating demo";

int erode_ele_type;
int dilate_ele_type;
int erode_ele_size;
int dilate_ele_size;

static void Erosion(int, void *);
static void Dilation(int, void *);

/*
 * @brief
 * @inputs
 * @outputs
 * @retval
 */
int main(int argc, char *argv[])
{
    if (argc < 2) {
        cout<<"Usage: ./eroding_and_dilating [file name]"<<endl;
        return -1;
    }

    src = imread(argv[1]);
    if (!src.data) {
        cout<<"Read image failure."<<endl;
        return -1;
    }

    // Windows
    namedWindow(erode_wn, WINDOW_AUTOSIZE);
    namedWindow(dilate_wn, WINDOW_AUTOSIZE);

    // Track Bar for Erosion
    createTrackbar("Element Type\n0:Rect\n1:Cross\n2:Ellipse", erode_wn,
            &erode_ele_type, MAX_ELE_TYPE, Erosion);  // callback @Erosion
    createTrackbar("Element Size: 2n+1", erode_wn,
            &erode_ele_size, MAX_ELE_SIZE, Erosion);

    // Track Bar for Dilation
    createTrackbar("Element Type\n0:Rect\n1:Cross\n2:Ellipse", dilate_wn,
            &dilate_ele_type, MAX_ELE_TYPE, Dilation);  // callback @Erosion
    createTrackbar("Element Size: 2n+1", dilate_wn,
            &dilate_ele_size, MAX_ELE_SIZE, Dilation);

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

    waitKey(0);

    return 0;
}

/*
 * @brief   腐蚀操作的回调函数
 * @inputs
 * @outputs
 * @retval
 */
static void Erosion(int, void *)
{
    int erode_type;

    switch (erode_ele_type) {
    case TYPE_MORPH_RECT:
       erode_type = MORPH_RECT;
       break;
    case TYPE_MORPH_CROSS:
       erode_type = MORPH_CROSS;
       break;
    case TYPE_MORPH_ELLIPSE:
       erode_type = MORPH_ELLIPSE;
       break;
    default:
       erode_type = MORPH_RECT;
       break;
    }

    Mat ele = getStructuringElement(erode_type, Size(2*erode_ele_size+1, 2*erode_ele_size+1),
            Point(erode_ele_size, erode_ele_size));

    erode(src, erode_dst, ele);

    imshow(erode_wn, erode_dst);
}

/*
 * @brief   膨胀操作的回调函数
 * @inputs
 * @outputs
 * @retval
 */
static void Dilation(int, void *)
{
    int dilate_type;

    switch (dilate_ele_type) {
    case TYPE_MORPH_RECT:
       dilate_type = MORPH_RECT;
       break;
    case TYPE_MORPH_CROSS:
       dilate_type = MORPH_CROSS;
       break;
    case TYPE_MORPH_ELLIPSE:
       dilate_type = MORPH_ELLIPSE;
       break;
    default:
       dilate_type = MORPH_RECT;
       break;
    }

    Mat ele = getStructuringElement(dilate_type, Size(2*dilate_ele_size+1, 2*dilate_ele_size+1),
            Point(dilate_ele_size, dilate_ele_size));

    dilate(src, dilate_dst, ele);

    imshow(dilate_wn, dilate_dst);
}
  1. 膨胀和腐蚀操作的函数分别是erodedilate,传递给他们的参数也都依次是原图像、形态学操作后的图像、结构元素ele。本程序中给出了3种结构元素类型,分别是

    #define TYPE_MORPH_RECT      (0)  // 矩形
    #define TYPE_MORPH_CROSS     (1)  // 十字交叉型
    #define TYPE_MORPH_ELLIPSE   (2)  // 椭圆型

    再通过OpenCV提供的getStructuringElement函数创建Mat类型的结构元素。

    getStructuringElement的参数依次是结构元素类型(OpenCV中提供了宏定义MORPH_RECT、MORPH_CROSS和MORPH_ELLIPSE表示)、结构元素大小。

  2. 这里我们首次接触了createTrackbar函数(声明在highgui.hpp中),该函数的功能是给窗口添加滑动条。其原型是:
    CV_EXPORTS int createTrackbar( const string& trackbarname, const string& winname,
                             int* value, int count,
                             TrackbarCallback onChange=0,
                             void* userdata=0);

    trackbarname为滑动条的名称,将会显示在滑动条的前面,参见结果中的图片显示; winname为窗口名; value为滑动条关联的变量,如上面程序中第一个滑动条关联到erode_ele_type,表示——当滑动条滑动变化时,erode_ele_type的值发生响应的变化; count表示滑动条能滑动到的最大值; TrackbarCallback onChange其实是这个函数的关键,是滑动条变化时调用的回调函数。当滑动条滑动时,value值发生变化,系统立刻调用onChange函数,执行相关的操作,回调函数的定义形式是固定的:

    void onChange(int, void *)

    程序中的回调函数ErosionDilation函数的定义都遵循该形式:

    static void Erosion(int, void *);
    static void Dilation(int, void *);

结果及实际应用

对“黑白小猪”进行膨胀操作的变化(随着结构元素大小的变化)如下图:

对“黑白小猪”进行腐蚀操作的变化(随着结构元素大小的变化)如下图:

膨胀与腐蚀在图像处理中具有广泛的用途,比如提取车牌过程中,可以通过膨胀运算确定车牌的区域。如下图为通过sobel算子提取边缘后的车牌,

为去掉边界,确定车牌在图中的位置,可以通过膨胀操作,结果如下:

上图中的红线区域就是膨胀后能用于确定车牌的连通区域,再通过对连通区域的搜索及“车牌的矩形特性”即可确定含有车牌数字在图片中的位置。

时间: 2024-11-06 11:52:17

OpenCV图像处理篇之腐蚀与膨胀的相关文章

opencv —— erode、dilate 腐蚀与膨胀

腐蚀与膨胀是形态学滤波.其中,腐蚀是最小值滤波,膨胀是最大值滤波,即分别选取内核中的最小值与最大值赋值给锚点.若内核为 N×1 或 1×N 形状,可用于横纵方向直线检测. 膨胀:dilate 函数 void dilate (InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_CONSTANT, co

Opencv对图像做腐蚀和膨胀处理的结果

在数字图像处理中,图像的形态学变换占有很重要的地位.基本的形态转换就是膨胀和腐蚀,它们能实现多种功能:例如消除噪声.分割出独立的图像元素以及在图像中连接相邻的元素.当然形态学也常备用于求出图像的梯度. 一. 关键函数介绍 下面就介绍OpenCV中对图像进行二值化的关键函数 cvErode() 函数功能:对图像进行腐蚀 函数原型:void cvErode( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iteratio

opencv:形态学操作-腐蚀与膨胀

#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main(int argc, char** argv) { Mat src = imread("f:/images/shuang001.jpg"); Mat gray, binary; cvtColor(src, gray, COLOR_BGR2GRAY); threshol

OpenCV图像处理篇之图像平滑

图像平滑算法 图像平滑与图像模糊是同一概念,主要用于图像的去噪.平滑要使用滤波器,为不改变图像的相位信息,一般使用线性滤波器,其统一形式如下: 其中h称为滤波器的核函数,说白了就是权值.不同的核函数代表不同的滤波器,有不同的用途. 在图像处理中,常见的滤波器包括: 归一化滤波器(Homogeneous blur) 也是均值滤波器,用输出像素点核窗口内的像素均值代替输出点像素值. 高斯滤波器(Guassian blur) 是实际中最常用的滤波器,高斯滤波是将输入数组的每一个像素点与 高斯内核 卷积

OpenCV图像处理篇之Hough变换

图像空间到参数空间的转换 对于图像中共线的点集{(x0,y0), (x1,y1), ...}都经过直线y=kx+b,先在我们换一个说法,"斜率为k,截距为b的直线y=kx+b包含了所有在该直线上的点".一种强调的是图像中的点集,另一种强调的是直线的参数k和b,通过直线的点集去描述这条直线明显没有直接通过k,b两个参数去描述那样直接方便.而Hough变换就是将我们"点共线"的思维转化到参数空间{k,b}进行描述,图像空间中所有经过y=kx+b的点经过Hough变换后在

OpenCV图像处理篇之边缘检测算子

3种边缘检测算子 灰度或结构等信息的突变位置是图像的边缘,图像的边缘有幅度和方向属性,沿边缘方向像素变化缓慢,垂直边缘方向像素变化剧烈.因此,边缘上的变化能通过梯度计算出来. 一阶导数的梯度算子 对于二维的图像,梯度定义为一个向量, Gx对于x方向的梯度,Gy对应y方向的梯度,向量的幅值本来是 mag(f)?=?(Gx2?+?Gy2)1/2,为简化计算,一般用mag(f)=|Gx|+|Gy|近似,幅值同时包含了x而后y方向的梯度信息.梯度的方向为 α?=?arctan(Gx/Gy) . 由于图像

OpenCV图像处理篇之阈值操作函数

阈值操作类型 这5种阈值操作类型保留opencv tutorials中的英文名称,依次为: Threshold Binary:即二值化,将大于阈值的灰度值设为最大灰度值,小于阈值的值设为0. Threshold Binary, Inverted:将大于阈值的灰度值设为0,大于阈值的值设为最大灰度值. Truncate:将大于阈值的灰度值设为阈值,小于阈值的值保持不变. Threshold to Zero:将小于阈值的灰度值设为0,大于阈值的值保持不变. Threshold to Zero, In

OpenCV图像处理篇之边缘检測算子

3种边缘检測算子 灰度或结构等信息的突变位置是图像的边缘,图像的边缘有幅度和方向属性.沿边缘方向像素变化缓慢,垂直边缘方向像素变化剧烈.因此,边缘上的变化能通过梯度计算出来. 一阶导数的梯度算子 对于二维的图像.梯度定义为一个向量. Gx对于x方向的梯度,Gy相应y方向的梯度,向量的幅值本来是 mag(f)?=?(Gx2?+?Gy2)1/2,为简化计算,一般用mag(f)=|Gx|+|Gy|近似,幅值同一时候包括了x而后y方向的梯度信息.梯度的方向为 α?=?arctan(Gx/Gy) . 因为

OpenCV图像处理篇之采样金字塔

转载请注明出处:http://xiahouzuoxin.github.io/notes 图像金字塔 图像金字塔是通过将原始图像经过平滑.下采样所生成一系列具有不同分辨率的图像的集合.金字塔结构(Pyramid)适于多分辨率处理的一种图像存储数据结构. 最常用的生成图像金字塔的方法是采用高斯函数平滑图像,每次将分辨率降低为原来的一半,由此得到一个图像序列{ML,ML-1,--,M0},图像金字塔的存储量为N^2*(1+1/4+1/16+...)=(4*N^2)/3. 如上图:最右边为原始图像,从右