2016-10-18 15:54:45 lcb1992 阅读数 620

开操作是先腐蚀再膨胀,闭操作是先膨胀再腐蚀。灰度图像是求最大最小值,二值图像是进行与运算。

二值图像:

1.二值腐蚀的具体操作是:用一个结构元素(一般是3×3的大小)扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为1,则该像素为1,否则为0。中心及领域有一个点不是黑点,该点就被腐蚀成白点

2.二值膨胀的具体操作是:用一个结构元素(一般是3×3的大小)扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为0,则该像素为0,否则为1。

目的:开操作可以平滑物体轮廓,断开较窄的狭颈和消除细小的突出物。
   闭操作同样可以平滑轮廓的一部分,但与开操作相反,它通常会弥合较窄的间断和细长的沟壑,消除小的孔洞,填补轮廓线中的断裂。

灰度图像:
1. 灰度形态学膨胀:膨胀就是求局部最大值的操作,结构元素B扫描图像的每一个像素,即计算核B覆盖的图像区域的像素点的最大值,并把这个最大值赋值给参考点(核B的中心点)指定的像素,使图像变明亮。
2. 灰度形态学腐蚀:求局部最小值的操作,结构元素B扫描图像的每一个像素,即计算核B覆盖的图像区域的像素点的最小值,并把这个最小值赋值给参考点(核B的中心点)指定的像素,使图像变灰暗。



2019-08-02 21:27:33 qq_41684833 阅读数 102

图像的腐蚀膨胀

在这里插入图片描述

作者:Cabin_V

作为学习图像处理的学生,需要不断学习相关知识,我在课余时间将一些分析总结和学习的笔记写成博客来记录自己的学习过程,也希望能与大家一起交流。

关于图像处理入门和进阶分类问题,我根据《数字图像处理》这本书(本科期间上课使用),将出现的内容划分为入门,其他常用的知识划分为进阶。

转载务必说明出处!


图像腐蚀与膨胀

OpenCV为进行图像的形态学变换提供了快捷、方便的函数。最基本的形态学操作有两种,分别是:腐蚀(erode)与膨胀(dilate)。

腐蚀算法视频演示

膨胀算法视频演示

区别

腐蚀和膨胀是对白色部分而言的。膨胀是对图像中高亮的部分进行膨胀,处理后的图片高亮区域扩大;而腐蚀是对高亮部分进行腐蚀,处理后的图片高亮区域减少。

膨胀就是求局部最大值的操作。膨胀与膨胀操作就是将图像与核进行卷积。

作用

膨胀与腐蚀配合使用能实现各种各样的功能,主要如下:

  • 消除噪声

  • 分割出独立的图像元素,在图像中连接相邻的元素

  • 寻找图像中明显的极大值区域或极小值区域

  • 求出图像梯度

API说明

如果想进一步了解各个参数对图像最终结果的影响,可以跳到文章最下方代码

我们先来看下简单的图像腐蚀膨胀的代码

#include <opencv2/opencv.cpp>
using namespace cv;

int main()
{
	Mat srcImage = imread("1.jpg");
	imshow("原图",srcImage);
    
    //返回指定形状和尺寸的结构元素
	Mat element = getStructuringElement(MORPH_CROSS,Size(3,3));  
	Mat eroImage, dilImage;
    
	//图像腐蚀
	erode(srcImage, eroImage, element);                           
	imshow("腐蚀", eroImage);
	//图像膨胀
	dilate(srcImage, dilImage, element);                          
	imshow("膨胀", dilImage);
    
	waitKey(0);
	return 0;
}

首先看一下程序运行结果

原图

腐蚀和膨胀

可以看到经过原图经过腐蚀后,中间的白框不见了,而经过膨胀处理,原图中白框明显变厚。

大家可以将代码拷贝,在自己的电脑运行一下,通过调整核大小形状以及形态学操作,观察效果

注意!图片要放在.cpp文件相同路径下!

以上程序分别用到了 getStructuringElement()erode()dilate() 函数。

getStructuringElement()介绍

cv::Mat cv::getStructuringElement(int shape,Size ksize,Point anchor = cv::Point(-1,-1));

用法:可用于构造一个特定大小和形状的结构元素,用于图像形态学处理。

参数说明:

  • shape:结构元素的形状(MorphShapes的其中一种)

    • MORPH_RECT——矩形
    • MORPH_CROSS——交叉形
    • MORPH_ELLIPSE——椭圆形
  • ksize:结构元素的大小

  • anchor:锚点,表示结构元素中的一个点,默认值Point(-1,-1)表示位于结构元素中心,只有交错形状的(MORPH_CROSS)元素依赖锚点位置,对于其它形状的元素,锚点规定了形态学处理结果的偏移量。

erode()、dilate()介绍

void cv::erode( InputArray src, OutputArray dst, InputArray kernel,
                Point anchor, int iterations,
                int borderType, const Scalar& borderValue );

用法:实现图像腐蚀功能的函数。

void cv::dilate( InputArray src, OutputArray dst, InputArray kernel,
                 Point anchor, int iterations,
                 int borderType, const Scalar& borderValue );

用法:实现图像膨胀功能的函数。

两个函数的参数个数和名称是一模一样的,不同的只是函数功能,所以参数方面一起介绍。

参数说明:一般只需使用前三个参数,后四个参数都有默认值。

  • srcInputArray类型,输入图像,Mat类型的待处理图像。图像通道的数量可以是任意的,但图像深度应为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F其中之一。

  • dstOutputArray类型,目标图像,和输入图像有一样的尺寸和类型。

  • kerneInputArray类型,形态学运算的内核。当为NULL时,表示的是使用参考点位于中心3x3的核。一般配合**getStructuringElement()**函数使用。

  • anchorPoint类型,锚的位置。有默认值(-1,-1),表示锚位于单位的中心。一般设置为默认值。

  • iterationint类型,迭代使用**erode()**函数的次数,默认值为1。

  • borderTypeint类型,用于推断图像外部像素的某种边界模式。默认值为BORDER_DEFAULT。

  • borderValueconst Scalar&类型,当边界为常数时的边界值。默认值为morphologyDefaultBorderValue(),一般设置为默认值。

综合代码

这个程序在前面代码基础上加入了滑动条的功能

可以选择“腐蚀/膨胀”、内核形状、内核尺寸。

运行界面如下:

运行界面

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

//全局变量声明
Mat g_srcImage, g_dstImage;
//Trackbar接收的参数
int g_nTrackbarNumer = 0;               //0-腐蚀,1-膨胀
int g_nStructElementShape = MORPH_RECT; //结构元素的形状 0-rect 1-cross 2-ellipse
int g_nStructElementSize = 0;           //结构元素的尺寸


//函数声明
void Process();
void on_ElementShapeChange(int, void *);
void on_TrackbarNumChange(int, void *);
void on_ElementSizeChange(int, void *);

int main()
{
	g_srcImage = imread("1.jpg");
	if (!g_srcImage.data)
	{
		printf("error!\n");
		return false;
	}

	namedWindow("Erode/Dilate");
	imshow("Erode/Dilate",g_srcImage);

	//创建滑动条
	createTrackbar("Ero/Dil", "Erode/Dilate", &g_nTrackbarNumer, 1, on_TrackbarNumChange);
	createTrackbar("shape", "Erode/Dilate", &g_nStructElementShape, 2, on_ElementShapeChange);
	createTrackbar("size", "Erode/Dilate", &g_nStructElementSize, 11, on_ElementSizeChange);

	//按q退出程序
	if (waitKey(0) != 'q')
	{
	}

	return 0;
}

void Process()
{
	//根据g_nStructElementShape和g_nStructElementSize自定义核
	Mat element = getStructuringElement(g_nStructElementShape, Size(2*g_nStructElementSize+1, 2*g_nStructElementSize+1), Point(g_nStructElementSize, g_nStructElementSize));

	//选择腐蚀或膨胀操作
	if (g_nTrackbarNumer == 0)
	{
		erode(g_srcImage, g_dstImage, element);
	}
	else
	{
		dilate(g_srcImage, g_dstImage, element);
	}

	imshow("Erode/Dilate", g_dstImage);
}

//Trackbar-shape响应函数
void on_ElementShapeChange(int, void *)
{
	//g_nStructElementShape数值已改变
	Process();
}

//Trackbar-erode/dilate响应函数
void on_TrackbarNumChange(int, void *)
{
	//g_nTrackbarNumer数值已改变
	Process();
}

//Trackbar-size响应函数
void on_ElementSizeChange(int, void *)
{
	//g_nStructElementSize数值已改变
	Process();
}

本篇文章对你们有用的话,点个赞再走~

2019-11-20 10:46:49 qq_40041064 阅读数 12

形态学图像处理的两个最基本的概念为腐蚀和膨胀。

             

                             腐蚀                                                                                    膨胀

相关实现代码及编译输出

	Mat erodeImage,dilateImage;
	Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3));
	erode(srcImage, erodeImage, element);
	dilate(srcImage, dilateImage, element);

	cout << "element = " << endl << element << endl;
	namedWindow("结构元", WINDOW_NORMAL);
	imshow("结构元", element);

	cout << "erodeImage = " << endl << erodeImage << endl;
	namedWindow("腐蚀图", WINDOW_NORMAL);
	imshow("腐蚀图", erodeImage);

	cout << "dilateImage = " << endl << dilateImage << endl;
	namedWindow("膨胀图", WINDOW_NORMAL);
	imshow("膨胀图", dilateImage);

若再对dilateImage膨胀过后的图像进行腐蚀,得到的结果为

存在的问题是:若超出图片范围的像素默认为255。

提醒大家注意:腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。

其他高级的形态学图像处理,见下一个博文。

转载请标注出处

2015-05-02 11:20:31 fztfztfzt 阅读数 850
腐蚀:对一幅图像取位移再求交得到,用于从一幅图像中去除小而无用的目标
膨胀:对一幅图像取位移再求并得到,连接图像中的断续点和填补图像中的空洞。
腐蚀与膨胀不是逆运算,因为由腐蚀得到的图像不是都能通过膨胀复原
开启:先腐蚀在膨胀  可看作是腐蚀图像的膨胀恢复
闭合:先膨胀在腐蚀  可看作是膨胀图像的腐蚀恢复
  1. //腐蚀OR膨胀数组
  2. const KERNEL kernel_erosion[]=
  3. {
  4. //垂直方向
  5. {0,1,0,
  6. 0,1,0,
  7. 0,1,0,
  8. 1,3
  9. },
  10. //水平方向
  11. {0,0,0,
  12. 1,1,1,
  13. 0,0,0,
  14. 1,3
  15. },
  16. //十方向
  17. {0,1,0,
  18. 1,1,1,
  19. 0,1,0,
  20. 1,3
  21. },
  22. //8方向
  23. {1,1,1,
  24. 1,1,1,
  25. 1,1,1,
  26. 1,3
  27. }
  28. };

腐蚀代码:
  1. /*************************************************************************
  2. * 函数名称:Erosion()
  3. * 参数:BYTE* bmp,LONG width,LONG height ------图像参数
  4. KERNEL array 腐蚀方向数组
  5. * 返回值:无
  6. * 说明:对图像进行不同方向的腐蚀处理
  7. ************************************************************************/
  8. voidErosion(BYTE *bmp,LONG width,LONG height,KERNEL array);
  1. voidMyProcess::Erosion(BYTE *bmp,LONG width,LONG height,KERNEL kernel)
  2. {
  3. LONG i,j,k,m;//循环变量
  4. LONG door=128;//像素阈值
  5. BYTE *temp_bmp =new BYTE[(width+6)*(height+2)];
  6. BmpFilter(temp_bmp,bmp,width,height);
  7. //腐蚀处理,取各方向最大值
  8. for(i=1;i<height+1;i++)
  9. for(j=3;j<width+3;j++)
  10. {
  11. for(k=-1;k<kernel.Dimention-1;k++)
  12. for(m=-1;m<kernel.Dimention-1;m++)
  13. {
  14. int a = temp_bmp[(i+k)*(width+6)+j+m*3];
  15. int b = kernel.Element[k+1][m+1];
  16. if(a*b>door) bmp[(i-1)*width+j-3]=255;
  17. }
  18. }
  19. delete[] temp_bmp;
  20. }
膨胀代码:
  1. /*************************************************************************
  2. * 函数名称:Dilation()
  3. * 参数:BYTE* bmp,LONG width,LONG height ------图像参数
  4. KERNEL array 膨胀方向数组
  5. * 返回值:无
  6. * 说明:对图像进行不同方向的膨胀处理
  7. ************************************************************************/
  8. voidDilation(BYTE *bmp,LONG width,LONG height,KERNEL array);
  1. voidMyProcess::Dilation(BYTE *bmp,LONG width,LONG height,KERNEL kernel)
  2. {
  3. LONG i,j,k,m;//循环变量
  4. LONG door=128;//像素阈值
  5. BYTE *temp_bmp =new BYTE[(width+6)*(height+2)];
  6. BmpFilter(temp_bmp,bmp,width,height);
  7. //膨胀处理,取各方向最小值
  8. for(i=1;i<height+1;i++)
  9. for(j=3;j<width+3;j++)
  10. {
  11. for(k=-1;k<kernel.Dimention-1;k++)
  12. for(m=-1;m<kernel.Dimention-1;m++)
  13. {
  14. int a = temp_bmp[(i+k)*(width+6)+j+m*3];
  15. int b = kernel.Element[k+1][m+1];
  16. if(a*b<=door) bmp[(i-1)*width+j-3]=0;
  17. }
  18. }
  19. delete[] temp_bmp;
  20. }

2014-09-19 22:18:31 xiahouzuoxin 阅读数 36558

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


腐蚀与膨胀

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

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

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

erode_show dilate_show

如上图,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 *);

结果及实际应用

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

dilating_demo

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

eroding_demo

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

car_plate

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

car_plate_dilate

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

图像腐蚀和膨胀

阅读数 2394

没有更多推荐了,返回首页