图像处理 膨胀操作_数字图像处理膨胀操作 - CSDN
  • 数字图像处理---通俗理解腐蚀与膨胀

    千次阅读 多人点赞 2018-11-08 21:35:20
    腐蚀与膨胀是数字形态学里的两个基本操作,一般用于二值图像(当然RGB图也可以用)。腐蚀的作用说白了就是让暗的区域变大,而膨胀的作用就是让亮的区域变大(可以想象成巴啦啦能量和古娜拉黑暗之神在神仙打架。。巴...

    0.腐蚀与膨胀有什么卵用?

    腐蚀与膨胀是数字形态学里的两个基本操作,一般用于二值图像(当然RGB图也可以用)。腐蚀的作用说白了就是让暗的区域变大,而膨胀的作用就是让亮的区域变大(可以想象成巴啦啦能量和古娜拉黑暗之神在神仙打架。。巴啦啦能量赢了就是膨胀,古娜拉黑暗之神赢了就是腐蚀。。。)
    在这里插入图片描述

    最典型的一个应用场景就是在你二值化后,你的目标和背景抠的不是很干净的时候可以试试两个操作,有时候效果出奇的好。就比如我要把一张验证码的图二值化后的结果更精确的话,就可以试试腐蚀与膨胀(第二幅图是膨胀+腐蚀之后的效果,可以看得出来已经把干扰点和干扰线去掉了)。
    在这里插入图片描述
    在这里插入图片描述

    1.腐蚀

    腐蚀的原料有:原图、核(腐蚀的灵魂)、结果图(这货是从原图深拷贝出来的)。下面举栗子来说明腐蚀的工作流程。
    假设现在我们想对这样一幅单通道图做腐蚀操作(被IG关爱过视力的童鞋肯定能看出图里画的是个1)
    在这里插入图片描述
    然后我们就先要把原图拷贝一份出来当结果图,现在的结果图和原图是一毛一样的。
    在这里插入图片描述
    有了原图和结果图之后,我们就需要灵魂了(设计核)。核一般有方框形的,X形的,菱形的,等等等等(当然也可以自己DIY,比如我就DIY了个十字架形的)。并且腐蚀的效果和你设计的核有很大关系,什么样的核适合什么样的图这个全靠自己的脑洞和经验。反正我喜欢多试试几种核,看看哪个核效果好就用哪个。我这里DIY的核是3行3列的矩阵,样子如下:
    在这里插入图片描述
    现在有了灵魂之后,我们就要注入灵魂了。怎么注入呢?非常简单,把我们的核扔到原图和结果图的左上角(PS:我这里没做padding,因为我懒)。
    在这里插入图片描述

    在这里插入图片描述
    扔上去之后,我们就要用到核里面的值了。我们核里面有0和1(非零值),0代表我不care,1(非零值)代表你成功的而引起了我的注意。当我们的核盖在原图上之后就表明我只关心我核是1(非零值)在原图上对应的像素,也就是只关心我红框框里中间呈十字架形的5个255,其他的4个像素我并不关心。锁定完目标后,我们只要看我这5个像素中有没有0,如果有我们就把结果图中红框中心的像素值改成0,否则我就什么都不干。我们看一哈,5个255,并没有0所以什么都不干。

    我们费了半天功夫发现什么都没干,你说气不气,所以捏,我们就把原图和结果图上的红框往右边移一步(其实不是气不气的问题啦,是腐蚀和膨胀是要遍历整个图,所以要迭代啦)。所以挪一步之后成了酱紫。
    在这里插入图片描述
    在这里插入图片描述
    新的地方,新的开始,所以我们要继续刚刚的套路,看看原图红框中十字架部分的5个像素有没有0,发现有0!。所以我们的结果图红框中心点的像素值就被改成0了。
    在这里插入图片描述
    然后继续刚刚的套路,往右挪,看关心的地方有没有0,右边挪不动了就往下挪。直到整个红框雨露均沾后,整个腐蚀算法就做完了。
    完事之后我们会发现结果图变成了酱紫。
    在这里插入图片描述
    嗯,很正常,因为腐蚀就是让黑的区域变大,所以1变胖了,而且原图的1中间有隔断,经过腐蚀之后隔断处也连了起来。

    2.膨胀

    其实理解了腐蚀之后,理解膨胀简直简单的而一批。腐蚀之所以叫腐蚀是因为他只看感兴趣区域里面有没有0,有0我就把结果赋成0,所以黑的区域能变大。而膨胀只是和腐蚀相反,它只看感兴趣区域里面有没有255,有255我就把结果赋成255,所以亮的区域能变大。
    假如原图(0可以看成是噪点)是酱紫
    在这里插入图片描述
    那结果图一开始也是酱紫
    在这里插入图片描述
    假设膨胀的灵魂也是十字架的核
    在这里插入图片描述
    那么膨胀第一步也是在原图和结果图上套个红框
    在这里插入图片描述
    在这里插入图片描述
    然后就看十字架区域有没有255,发现有我就把结果图上的对应位置的像素改成255(当然,这时的结果图对应像素本来就是255,所以相当于什么都没干)。原图和结果图上的红框都往右挪一步
    在这里插入图片描述
    在这里插入图片描述
    这个时候发现十字架区域有255,发现有我就把结果图上的对应位置的像素改成255(当然,这时的结果图对应像素本来就是255,所以相当于什么都没干)。原图和结果图上的红框都往右挪一步。
    在这里插入图片描述
    在这里插入图片描述
    这个时候十字架区域有255,所以把结果图对应的像素改成255,改完之后结果图是酱紫。
    在这里插入图片描述
    然后继续挪,挪到右下角,整个算法就停止了。膨胀做完之后,结果图是酱紫。
    在这里插入图片描述
    可以看得出来,原图的0我看成是噪点,经过膨胀之后噪点全部被擦除。

    3.结束语

    可以看得出来腐蚀和膨胀这两个算法流程非常简单,无非就是摩擦摩擦。。似魔鬼的步伐。。在图像上摩擦。。摩擦。。虽然算法简单,但如果核设计的好,使用恰当的话,能还是够得到比较满意的效果的。

    展开全文
  • 图像处理技术中,有一些的操作会对图像的形态发生改变,这些操作一般称之为形态学操作(phology)。数学形态学是基于集合论的图像处理方法,最早出现在生物学的形态与结构中,图像处理中的形态学操作用于图像与...
    在图像处理技术中,有一些的操作会对图像的形态发生改变,这些操作一般称之为形态学操作(phology)。数学形态学是基于集合论的图像处理方法,最早出现在生物学的形态与结构中,图像处理中的形态学操作用于图像与处理操作(去噪,形状简化)图像增强(骨架提取,细化,凸包及物体标记)、物体背景分割及物体形态量化等场景中,形态学操作的对象是二值化图像。
    有名的形态学操作中包括腐蚀,膨胀,开操作,闭操作等。其中腐蚀,膨胀是许多形态学操作的基础。
      
    腐蚀操作:
      顾名思义,是将物体的边缘加以腐蚀。具体的操作方法是拿一个宽m,高n的矩形作为模板,对图像中的每一个像素x做如下处理:像素x至于模板的中心,根据模版的大小,遍历所有被模板覆盖的其他像素,修改像素x的值为所有像素中最小的值。这样操作的结果是会将图像外围的突出点加以腐蚀。如下图的操作过程:

                                                                                 图5 腐蚀操作原理

    上图演示的过程是背景为黑色,物体为白色的情况。腐蚀将白色物体的表面加以“腐蚀”。在opencv的官方教程中,是以如下的图示说明腐蚀过程的,与我上面图的区别在于:背景是白色,而物体为黑色(这个不太符合一般的情况,所以我没有拿这张图作为通用的例子)。读者只需要了解背景为不同颜色时腐蚀也是不同的效果就可以了。


                                                            图6 腐蚀操作原理2

    简单来说在背景为白(1),情景为黑的图像(0)中,核与其覆盖的图像部分做“与”操作,如果全为1,则该像素点为1,否则为0;也就是0容易得到,图像更多的地方变黑了,白色部分被腐蚀了。


    膨胀操作:

      膨胀操作与腐蚀操作相反,是将图像的轮廓加以膨胀。操作方法与腐蚀操作类似,也是拿一个矩形模板,对图像的每个像素做遍历处理。不同之处在于修改像素的值不是所有像素中最小的值,而是最大的值。这样操作的结果会将图像外围的突出点连接并向外延伸。如下图的操作过程:


                                                                               图7 膨胀操作原理

    下面是在opencv的官方教程中,膨胀过程的图示:


                                                           图8 膨胀操作原理2

    简单来说在背景为白(1),前景为黑(0)的图像中,核与其覆盖的图像部分做“与”操作,如果全为0,则该像素点为0,否则为1;也就是1容易得到,图像更多的地方变白了,白色部分膨胀了。


    开操作:

    作用:放大裂缝和低密度区域,消除小物体,在平滑较大物体的边界时,不改变其面积。消除物体表面的突起。

    开操作就是对图像先腐蚀,再膨胀。其中腐蚀与膨胀使用的模板是一样大小的。为了说明开操作的效果,请看下图的操作过程:


                                                                              图9 开操作原理

    由于开操作是先腐蚀,再膨胀。因此可以结合图5和图7得出图9,其中图5的输出是图7的输入,所以开操作的结果也就是图7的结果。


    闭操作:

    作用:排除小型黑洞,突触了比原图轮廓区域更暗的区域,将两个区域连接起来,形成连通域。

      闭操作就是对图像先膨胀,再腐蚀。闭操作的结果一般是可以将许多靠近的图块相连称为一个无突起的连通域。在我们的图像定位中,使用了闭操作去连接所有的字符小图块,然后形成一个车牌的大致轮廓。闭操作的过程我会讲的细致一点。为了说明字符图块连接的过程。在这里选取的原图跟上面三个操作的原图不大一样,是一个由两个分开的图块组成的图。原图首先经过膨胀操作,将两个分开的图块结合起来(注意我用偏白的灰色图块表示由于膨胀操作而产生的新的白色)。接着通过腐蚀操作,将连通域的边缘和突起进行削平(注意我用偏黑的灰色图块表示由于腐蚀被侵蚀成黑色图块)。最后得到的是一个无突起的连通域(纯白的部分)。


                                                                             图10 闭操作原理

    4.代码

      在opencv中,调用闭操作的方法是首先建立矩形模板,矩形的大小是可以设置的,由于矩形是用来覆盖以中心像素的所有其他像素,因此矩形的宽和高最好是奇数。

      通过以下代码设置矩形的宽和高。

    Mat element = getStructuringElement(MORPH_RECT, Size(m_MorphSizeWidth, m_MorphSizeHeight) );
    在这里,我们使用了类成员变量,这两个类成员变量在构造函数中被赋予了初始值。宽是17,高是3.
      设置完矩形的宽和高以后,就可以调用形态学操作了。opencv中所有形态学操作有一个统一的函数,通过参数来区分不同的具体操作。例如MOP_CLOSE代表闭操作,MOP_OPEN代表开操作。
    morphologyEx(img_threshold, img_threshold, MORPH_CLOSE, element);


    展开全文
  • 用3x3的结构元素,扫描图像的每一个像素,用结构元素与其覆盖的二值图像做“与”操作:如果都为1,结果图像的该像素为1。否则为0。 结果:使二值图像减小一圈 B}Í S = { x,y | SxyÄ定义:E = B   膨胀的算法...

    腐蚀的算法:

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

    结果:使二值图像减小一圈

    B}Í S = { x,y | SxyÄ定义:E = B

     

    膨胀的算法:

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

    结果:使二值图像扩大一圈

     S = { x,y | Sxy∩B ≠Ф}Å定义:E = B
    void erode_image(IplImage * src,IplImage * dst)
    {	
    	if(src == NULL || dst == NULL)
    		return;
    	
    	int width = src->width;
    	int height = src->height;
    
    	//水平方向的腐蚀
    	for (int i=0;i < src->height;i++)
    	{
    		for (int j=1;j < src->width - 1;j++)
    		{
    		//	data = ((uchar *)(src->imageData + src->widthStep * i))[j];
    			if(((uchar *)(src->imageData + src->widthStep * i))[j] == 0)
    			{
    				((uchar *)(dst->imageData + dst->widthStep * i))[j] = 0;
    				for (int k=0;k < 3;k++)
    				{
    				//	data = ((uchar *)(src->imageData + src->widthStep * i))[j + k -1];
    					if(((uchar *)(src->imageData + src->widthStep * i))[j + k -1] == 255)
    					{
    						((uchar *)(dst->imageData + dst->widthStep * i))[j] = 255;
    						break;
    					}
    				}
    			}
    			else
    				((uchar *)(dst->imageData + dst->widthStep * i))[j] = 255;
    		}
    	}
    	//垂直方向的腐蚀
    	for (int i=0;i < dst->width;i++)
    	{
    		for (int j=1;j < dst->height - 1;j++)
    		{
    			//	data = ((uchar *)(src->imageData + src->widthStep * i))[j];
    			if(((uchar *)(dst->imageData + dst->widthStep * j))[i] == 0)
    			{
    				((uchar *)(src->imageData + src->widthStep * j))[i] = 0;
    				for (int k=0;k < 3;k++)
    				{
    					//	data = ((uchar *)(src->imageData + src->widthStep * i))[j + k -1];
    					if(((uchar *)(dst->imageData + dst->widthStep * (j + k -1)))[i] == 255)
    					{
    						((uchar *)(src->imageData + src->widthStep * j))[i] = 255;
    						break;
    					}
    				}
    			}
    			else
    				((uchar *)(src->imageData + src->widthStep * j))[i] = 255;
    		}
    	}
    }	
    void dilate_image(IplImage * src,IplImage * dst)
    {	
    	if(src == NULL || dst == NULL)
    		return;
    
    	int width = src->width;
    	int height = src->height;
    
    	//水平方向的膨胀
    	for (int i=0;i < src->height;i++)
    	{
    		for (int j=1;j < src->width - 1;j++)
    		{
    			if(((uchar *)(src->imageData + src->widthStep * i))[j] == 255)
    			{
    				((uchar *)(dst->imageData + dst->widthStep * i))[j] = 255;
    				for (int k=0;k < 3;k++)
    				{
    					if(((uchar *)(src->imageData + src->widthStep * i))[j + k -1] == 0)
    					{
    						((uchar *)(dst->imageData + dst->widthStep * i))[j] = 0;
    						break;
    					}
    				}
    			}
    			else
    				((uchar *)(dst->imageData + dst->widthStep * i))[j] = 0;
    		}
    	}
    	//垂直方向的膨胀
    	for (int i=0;i < dst->width;i++)
    	{
    		for (int j=1;j < dst->height - 1;j++)
    		{
    			//	data = ((uchar *)(src->imageData + src->widthStep * i))[j];
    			if(((uchar *)(dst->imageData + dst->widthStep * j))[i] == 255)
    			{
    				((uchar *)(src->imageData + src->widthStep * j))[i] = 255;
    				for (int k=0;k < 3;k++)
    				{
    					//	data = ((uchar *)(src->imageData + src->widthStep * i))[j + k -1];
    					if(((uchar *)(dst->imageData + dst->widthStep * (j + k -1)))[i] == 0)
    					{
    						((uchar *)(src->imageData + src->widthStep * j))[i] = 0;
    						break;
    					}
    				}
    			}
    			else
    				((uchar *)(src->imageData + src->widthStep * j))[i] = 0;
    		}
    	}
    }
    image = cvLoadImage(filename,CV_LOAD_IMAGE_GRAYSCALE);
    		dst1 = cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels);
    		
    		dilate_image(image,dst1);
    		erode_image(image,dst1);
    
    		cvSaveImage(str,image);


    原图


    结果




    展开全文
  • 二值图像形态学运算时图像形态学运算的基础。二值图像形态学运算的过程就是在图像...二值图像的形态学处理的基本运算有腐蚀、膨胀、开运算、闭运算,击中与击不中、骨架抽取等。注意,本文所有例子都是24位真彩色图...

    本篇博客是自己的理解,如有错误,欢迎指正

    注意,本文所有例子都是24位真彩色图,并不是真正意义上的二值单通道图像。图像下载地址

    https://download.csdn.net/download/hjxu2016/10436834

    二值图像形态学运算时图像形态学运算的基础。二值图像形态学运算的过程就是在图像中移动结构元素,将结构元素与其下面重叠部分的图像进行交、并等集合运算。为了确定元素中的参照位置,一般把进行形态学运算时的结构元素的参考点称为原点,且远点可以选择在结构元素之中,也可以选择在结构元素之外。

    二值图像的形态学处理的基本运算有腐蚀、膨胀、开运算、闭运算,击中与击不中、骨架抽取等。

    一. 图像腐蚀

    就像土壤侵蚀一样,这个操作会把前景物体的边界腐蚀掉(但是前景仍然是白色)

    简单的讲,就是黑的吃白的。至于怎么吃,涉及结构元素以及原点的设计。先看解释


    再看结构元素的定义


    记录一下步骤

    为了防止越界,不处理最左边、最右边、最上和最下边的元素。从第2行、第2列开始检查源图像中的像素点,如果当点对应结构元素中为白色的那些点钟有一个不是白色,则将目标图像中对应的像素点赋值成白色。

    将目标图像中的当前点先赋值成黑色,如果源图像中对应结构元素中为白色的那些点中只要有一个不是白色,则将目标图像中的当前点赋值成白色。

    在这里,我仅将原点赋值,结果也差不多

    先看3*3的结构元素,且所有值都为1,也就是说,只要结构元素对应的像素点有一个值不是白色,为0,那么原点就赋值成黑色0.

    第一步:在ResourceView资源视图中,添加Menu子菜单:(注意ID号)

    第二步:打开类向导(Ctrl+W),为点运算每个ID菜单添加相应的功能处理函数,如下图所示:选择类CImageProcessingView,选择ID号。

    void CImageProcessingView::OnXtxFs()
    {
    	// TODO: 在此添加命令处理程序代码
    	if (numPicture == 0)
    	{
    		AfxMessageBox("请输入一张图像", MB_OK, 0);
    		return;
    	}
    
    	if (m_nBitCount != 24)
    	{
    		AfxMessageBox("输入图片不是24位", MB_OK, 0);
    		return;
    	}
    	AfxMessageBox("形态学-腐蚀1!", MB_OK, 0);
    	//int kelnel[3][3];//定义3*3的结构元素
    	/*结构元素
    	{	{1,1,1}
    		{1,1,1}
    		{1,1,1}
    	}
    	*/
    	int num;//记录每一行需要填充的字节
    	if (m_nWidth * 3 % 4 != 0)
    	{
    		num = 4 - m_nWidth * 3 % 4;
    	}
    	else
    	{
    		num = 0;
    	}
    
    	//打开临时的图片  
    	FILE *fpo = fopen(BmpName, "rb");
    	FILE *fpw = fopen(BmpNameLin, "wb+");
    	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
    	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
    	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
    	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
    	fread(m_pImage, m_nImage, 1, fpo);
    	unsigned char *ImageSize;
    	ImageSize = new unsigned char[m_nImage];
    
    	int x, y, i, j, val,xx,yy;
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 255;//这里我给val赋值为255,如果发现结构元素对应的像素有是0的点,就赋值成0
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)
    					{
    						val = 0;
    						break;
    					}
    				}
    				if (val == 0) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num +1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    
    	fwrite(ImageSize, m_nImage, 1, fpw);
    
    	fclose(fpo);
    	fclose(fpw);
    	numPicture = 2;
    	level = 400;
    	Invalidate();
    }

    结果如下所示,是不是感觉腐蚀过头了。

    我们再换一个2*2的核,且所有值为1看看结果

    void CImageProcessingView::OnXtxFs2()
    {
    	// TODO: 在此添加命令处理程序代码
    	if (numPicture == 0)
    	{
    		AfxMessageBox("请输入一张图像", MB_OK, 0);
    		return;
    	}
    
    	if (m_nBitCount != 24)
    	{
    		AfxMessageBox("输入图片不是24位", MB_OK, 0);
    		return;
    	}
    	AfxMessageBox("形态学-腐蚀1!", MB_OK, 0);
    	//int kelnel[2][2];//定义3*3的结构元素
    	/*结构元素
    	{	{1,1,1}
    	{1,1,1}
    	}
    	*/
    	int num;//记录每一行需要填充的字节
    	if (m_nWidth * 3 % 4 != 0)
    	{
    		num = 4 - m_nWidth * 3 % 4;
    	}
    	else
    	{
    		num = 0;
    	}
    
    	//打开临时的图片  
    	FILE *fpo = fopen(BmpName, "rb");
    	FILE *fpw = fopen(BmpNameLin, "wb+");
    	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
    	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
    	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
    	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
    	fread(m_pImage, m_nImage, 1, fpo);
    	unsigned char *ImageSize;
    	ImageSize = new unsigned char[m_nImage];
    
    	int x, y, i, j, val, xx, yy;
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 255;
    			for (j = 0; j < 2; j++)//这边是2*2
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 2; i++)
    				{
    					xx = x + i - 1;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)
    					{
    						val = 0;
    						break;
    					}
    				}
    				if (val == 0) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    
    	fwrite(ImageSize, m_nImage, 1, fpw);
    
    	fclose(fpo);
    	fclose(fpw);
    	numPicture = 2;
    	level = 400;
    	Invalidate();
    }

    结果如下,粗细还可以


    在看一下特殊结构元素

    void CImageProcessingView::OnXtxFs3()
    {
    	// TODO: 在此添加命令处理程序代码
    	if (numPicture == 0)
    	{
    		AfxMessageBox("请输入一张图像", MB_OK, 0);
    		return;
    	}
    
    	if (m_nBitCount != 24)
    	{
    		AfxMessageBox("输入图片不是24位", MB_OK, 0);
    		return;
    	}
    	AfxMessageBox("形态学-腐蚀1!", MB_OK, 0);
    	//int kelnel[3][3] = {	{0,1,0},
    	//						{1,1,1},
    	//						{0,1,0} };//定义3*3的结构元素
    	int kelnel[3][3] = {{ 1,0,1 },
    						{ 0,0,0 },
    						{ 1,0,1 } };//定义3*3的结构元素
    
    	int num;//记录每一行需要填充的字节
    	if (m_nWidth * 3 % 4 != 0)
    	{
    		num = 4 - m_nWidth * 3 % 4;
    	}
    	else
    	{
    		num = 0;
    	}
    
    	//打开临时的图片  
    	FILE *fpo = fopen(BmpName, "rb");
    	FILE *fpw = fopen(BmpNameLin, "wb+");
    	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
    	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
    	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
    	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
    	fread(m_pImage, m_nImage, 1, fpo);
    	unsigned char *ImageSize;
    	ImageSize = new unsigned char[m_nImage];
    
    	int x, y, i, j, val, xx, yy;
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 255;
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (kelnel[j][i]) continue;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)
    					{
    						val = 0;
    						break;
    					}
    				}
    				if (val == 0) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    
    	fwrite(ImageSize, m_nImage, 1, fpw);
    
    	fclose(fpo);
    	fclose(fpw);
    	numPicture = 2;
    	level = 400;
    	Invalidate();
    }

    所以,结构元素的设计是会影响腐蚀结果的。

    一. 图像膨胀

    我认为膨胀和腐蚀正好相反,简单的讲,就是白的吃黑的。至于怎么吃,涉及结构元素以及原点的设计。


    记录一下步骤

    为了防止越界,不处理最左边、最右边、最上和最下边的元素。从第2行、第2列开始检查源图像中的像素点,如果当点对应结构元素中为白色的那些点中只要有一个是白色,则将目标图像中的当前点赋值成白色。

    代码,建立类向导

    void CImageProcessingView::OnXtxPz()
    {
    	// TODO: 在此添加命令处理程序代码
    	// TODO: 在此添加命令处理程序代码
    	if (numPicture == 0)
    	{
    		AfxMessageBox("请输入一张图像", MB_OK, 0);
    		return;
    	}
    
    	if (m_nBitCount != 24)
    	{
    		AfxMessageBox("输入图片不是24位", MB_OK, 0);
    		return;
    	}
    	AfxMessageBox("形态学-膨胀1!", MB_OK, 0);
    	//int kelnel[3][3] = {	{0,1,0},
    	//						{1,1,1},
    	//						{0,1,0} };//定义3*3的结构元素
    	int kelnel[3][3] = { { 0,1,0 },
    						{ 1,1,1 },
    						{ 0,1,0 } };//定义3*3的结构元素
    
    	int num;//记录每一行需要填充的字节
    	if (m_nWidth * 3 % 4 != 0)
    	{
    		num = 4 - m_nWidth * 3 % 4;
    	}
    	else
    	{
    		num = 0;
    	}
    
    	//打开临时的图片  
    	FILE *fpo = fopen(BmpName, "rb");
    	FILE *fpw = fopen(BmpNameLin, "wb+");
    	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
    	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
    	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
    	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
    	fread(m_pImage, m_nImage, 1, fpo);
    	unsigned char *ImageSize;
    	ImageSize = new unsigned char[m_nImage];
    
    	int x, y, i, j, val, xx, yy;
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 0;//先赋值黑色,再赋值白色
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (kelnel[j][i] == 0) continue;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] != 0)
    					{
    						val = 255;
    						break;
    					}
    				}
    				if (val == 255) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    
    	fwrite(ImageSize, m_nImage, 1, fpw);
    
    	fclose(fpo);
    	fclose(fpw);
    	numPicture = 2;
    	level = 400;
    	Invalidate();
    }

    白的把黑的吃掉了


    三. 图像开运算

    开运算就是使用用一个结构元素,对目标图像先腐蚀,再膨胀。先黑的吃白的,再白的吃黑的,可以消除小的白色的点

    void CImageProcessingView::OnXtxKys()
    {
    	// TODO: 在此添加命令处理程序代码
    	if (numPicture == 0)
    	{
    		AfxMessageBox("请输入一张图像", MB_OK, 0);
    		return;
    	}
    
    	if (m_nBitCount != 24)
    	{
    		AfxMessageBox("输入图片不是24位", MB_OK, 0);
    		return;
    	}
    	AfxMessageBox("形态学-开运算!", MB_OK, 0);
    	//int kelnel[3][3] = {	{0,1,0},
    	//						{1,1,1},
    	//						{0,1,0} };//定义3*3的结构元素
    	int kelnel[3][3] = { { 0,1,0 },
    	{ 1,1,1 },
    	{ 0,1,0 } };//定义3*3的结构元素
    
    	int num;//记录每一行需要填充的字节
    	if (m_nWidth * 3 % 4 != 0)
    	{
    		num = 4 - m_nWidth * 3 % 4;
    	}
    	else
    	{
    		num = 0;
    	}
    
    	//打开临时的图片  
    	FILE *fpo = fopen(BmpName, "rb");
    	FILE *fpw = fopen(BmpNameLin, "wb+");
    	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
    	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
    	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
    	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
    	fread(m_pImage, m_nImage, 1, fpo);
    	unsigned char *ImageSize;
    	ImageSize = new unsigned char[m_nImage];
    
    	int x, y, i, j, val, xx, yy;
    	//先腐蚀
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 255;//先赋值成白色
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (kelnel[j][i]) continue;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)//只要有一个不是白色,赋值成白色
    					{
    						val = 0;
    						break;
    					}
    				}
    				if (val == 0) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    	//再膨胀
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 0;//先赋值成黑色
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (kelnel[j][i] == 0) continue;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] != 0)//只要有一个是白色,赋值成白色
    					{
    						val = 255;
    						break;
    					}
    				}
    				if (val == 255) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    
    	fwrite(ImageSize, m_nImage, 1, fpw);
    
    	fclose(fpo);
    	fclose(fpw);
    	numPicture = 2;
    	level = 400;
    	Invalidate();
    }

    四. 图像闭运算

    开运算就是使用用一个结构元素,对目标图像先膨胀,再腐蚀。先白的吃黑的,再黑的吃白的,可以消除小的黑色的点

    void CImageProcessingView::OnXtxBys()
    {
    	// TODO: 在此添加命令处理程序代码
    	if (numPicture == 0)
    	{
    		AfxMessageBox("请输入一张图像", MB_OK, 0);
    		return;
    	}
    
    	if (m_nBitCount != 24)
    	{
    		AfxMessageBox("输入图片不是24位", MB_OK, 0);
    		return;
    	}
    	AfxMessageBox("形态学-闭运算!", MB_OK, 0);
    	//int kelnel[3][3] = {	{0,1,0},
    	//						{1,1,1},
    	//						{0,1,0} };//定义3*3的结构元素
    	int kelnel[3][3] = { { 0,1,0 },
    	{ 1,1,1 },
    	{ 0,1,0 } };//定义3*3的结构元素
    
    	int num;//记录每一行需要填充的字节
    	if (m_nWidth * 3 % 4 != 0)
    	{
    		num = 4 - m_nWidth * 3 % 4;
    	}
    	else
    	{
    		num = 0;
    	}
    
    	//打开临时的图片  
    	FILE *fpo = fopen(BmpName, "rb");
    	FILE *fpw = fopen(BmpNameLin, "wb+");
    	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
    	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
    	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
    	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
    	fread(m_pImage, m_nImage, 1, fpo);
    	unsigned char *ImageSize;
    	ImageSize = new unsigned char[m_nImage];
    
    	int x, y, i, j, val, xx, yy;
    	//先膨胀
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 0;//先赋值成黑色
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (kelnel[j][i]) continue;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] != 0)//只要有一个是白色,则赋值成白色
    					{
    						val = 255;
    						break;
    					}
    				}
    				if (val == 255) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    	//再腐蚀
    	for (y = 1; y < m_nHeight - 1; y++)
    	{
    		for (x = 1; x < m_nWidth - 1; x++)
    		{
    			val = 255;//先赋值成白色
    			for (j = 0; j < 3; j++)
    			{
    				yy = y + j - 1;
    				for (i = 0; i < 3; i++)
    				{
    					xx = x + i - 1;
    					if (kelnel[j][i] == 0) continue;
    					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)//只要有一个不是白色,赋值成黑色
    					{
    						val = 0;
    						break;
    					}
    				}
    				if (val == 0) break;
    			}
    			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
    			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
    		}
    	}
    
    	fwrite(ImageSize, m_nImage, 1, fpw);
    
    	fclose(fpo);
    	fclose(fpw);
    	numPicture = 2;
    	level = 400;
    	Invalidate();
    }
    


    展开全文
  • 参考:【数字图像处理学习笔记之四】图像腐蚀、膨胀:https://blog.csdn.net/zhougynui/article/details/51834725 1 背景知识 结构元素:二维结构元素可以理解成一个二维矩阵,矩阵元素的值为0或者1;通常结构元素...
  •  数字图像处理中的形态学处理是指将数字形态学作为工具从图像中提取对于表达和描绘区域形状有用处的图像分量,比如边界、骨架以及凸壳,还包括用于预处理或后处理的形态学过滤、细化和修剪等。图像形态学处理中我们...
  • 目录 1 形态学操作 2 图像腐蚀 3 图像膨胀 参考资料 1 形态学操作 ...形态学(morphology)一词通常表示生物学的一个分支,该分支...形态学处理主要针对的是二值图像(0或1)。 形态学通常使用图像腐蚀和图像膨胀...
  • 腐蚀与膨胀基本原理:就是用一个特定的结构元素来与待处理图像按像素做逻辑操作;可以理解成拿一个带孔的网格板(结构元素矩阵中元素为1的为孔)盖住图像的某一部分,然后按照各种不同的观察方式来确定操作类型。 ...
  • [Python图像处理] 八.图像腐蚀与图像膨胀

    万次阅读 多人点赞 2020-08-20 12:48:51
    图像膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,主要用来寻找图像中的极大区域和极小区域。其中膨胀类似于“领域扩张”,将图像中的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更...
  • 二值图像腐蚀与膨胀操作样例

    万次阅读 多人点赞 2020-07-21 09:09:50
    目录 操作方法简要说明 腐蚀操作 结构元素原点在结构元素内部 原点在结构元素外部 二值图像膨胀操作 点在结构元素内部 原点在结构元素外部
  • 图像膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,主要用来寻找图像中的极大区域和极小区域。其中膨胀类似于“领域扩张”,将图像中的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更...
  • 而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。 数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本...
  • 图像的形态学操作 ...网上关于简单的图像处理的文章有很多很多,后面简单的图像处理系列博客中,或多或少的参考了下述这些大佬的分享: https://blog.csdn.net/sn_gis/article/details/57414029 https://...
  • 膨胀:用结构单元对图像进行膨胀可以达到增长和粗化二值图像的效果,结构元大小选择的不同可以使图像变成不同的形状 特点:增长 粗化 应用:桥接裂缝 最近在matlab中画圆柱体,然后绕中...
  • 图像形态学即数学形态学(Mathematical morphology)是一门建立在格伦和拓扑学基础上的图像分析学科,是数学形态学图像处理的基本理论; 常见图像形态学运算:腐蚀、膨胀、开运算、闭运算、骨架抽取、极线腐蚀、击中...
  • 图像膨胀与腐蚀 作用区域:腐蚀与膨胀是对白色部分(高亮部分)而言,不是黑色部分。膨胀类似于“领域扩张”,腐蚀类似于“领域被蚕食”。 元素:设X是一个图像,B是一个矩阵。X是被处理的对象,而B是用来处理X的...
  • 图像处理之——膨胀、腐蚀算法详解

    万次阅读 多人点赞 2015-12-30 16:48:39
    结构元素:膨胀和腐蚀操作的最基本组成部分,用于测试输出图像,通常要比待处理图像小还很多。二维平面结构元素由一个数值为0或1的矩阵组成。结构元素的原点指定了图像中需要处理的像素范围,
  • OpenCV-图像处理(10、膨胀与腐蚀)

    千次阅读 2019-01-09 20:22:10
    形态学操作(morphology operators)-膨胀与腐蚀(Dilation与...膨胀与腐蚀是图像处理中最常用的形态学操作手段 腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领...
  • 而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。 数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本...
1 2 3 4 5 ... 20
收藏数 10,359
精华内容 4,143
关键字:

图像处理 膨胀操作