图像处理开运算公式

2012-11-17 09:04:27 Vast_Sea 阅读数 24467
  • (二)矩阵基本运算

    本课程由专业数学系老师讲解,从数学背景和现实应用中讲解线性代数的相关知识,摆脱传统的讲概念、记公式、解体的数学学习模式,让听众对线性代数有一个全新的、深刻的认识。使听众能够了解矩阵和空间的概念、性质。...

    825人学习 AI100讲师
    免费试看


From:  http://www.dakaren.com/index.php/archives/196.htm


一、腐蚀和膨胀

  形态学是一门新兴科学,它的用途主要是获取物体拓扑和结果信息,它通过物体和结构元素相互作用的某些运算,得到物体更本质的形态。它在图像处理中的应用主要是:

  1.利用形态学的基本运算,对图像进行观察和处理,从而达到改善图像质量的目的;

  2.描述和定义图像的各种几何参数和特征,如面积,周长,连通度,颗粒度,骨架和方向性。

  限于篇幅,我们只介绍简单二值图像的形态学运算,对于灰度图像的形态学运算,有兴趣的读者可以看有关的参考书。二值图像基本的形态学运算是腐蚀和膨胀,简单的腐蚀是消除物体的所有边界点的一种过程,其结果是使剩下的物体沿其周边比原物体小一个像素的面积。如果物体是圆的,它的直径在每次腐蚀后将减少两个像素,如果物体在某一点处任意方向上连通的像素小于三个,那么该物体经过一次腐蚀后将在该点处分裂为二个物体。简单的膨胀运算是将与某物体接触的所有背景点合并到该物体中的过程。过程的结果是使物体的面积增大了相应数量的点,如果物体是圆的,它的直径在每次膨胀后将增大两个像素。如果两个物体在某一点的任意方向相隔少于三个像素,它们将在该点连通起来。

  下面给出具体的实现腐蚀和膨胀的函数代码:

////////////////////////////////二值图像腐蚀操作函数
BOOL ImageErosion(BYTE *pData,int Width,int Height)
{//pData为图像数据的指针,Width和Height为图像的宽和高;
BYTE* pData1;
int m,n,i,j,sum,k,sum1;
BOOL bErosion;
if(pData==NULL)
{
AfxMessageBox(“图像数据为空,请读取图像数据”);
return FALSE;
}
//申请空间,pData1存放处理后的数据;
pData1=(BYTE*)new char[WIDTHBYTES(Width*8)*Height];
if(pData1==NULL)
{
AfxMessageBox(“图像缓冲数据区申请失败,请重新申请图像数据缓冲区”);
return FALSE ;
}
memcpy(pData1,pData,WIDTHBYTES(Width*8)*Height);
for(i=10;i<Height-10;i++)
for(j=32;j<Width-32;j++)
{
bErosion=FALSE;
sum=*(pData+WIDTHBYTES(Width*8)*i+j);
if(sum==255)
{
//求像素点八邻域的灰度均值;
for(m=-1;m<2;m++)
{
for(n=-1;n<2;n++)
{
sum1=*(pData+WIDTHBYTES(Width*8)*(i+m)+j+n);
if(sum1==0)
{
*(pData1+WIDTHBYTES(Width*8)*i+j)=0;
bErosion=TRUE;
break;
}
}
if(bErosion)
{
bErosion=FALSE;
break;
}
}
}
}
memcpy(pData,pData1,WIDTHBYTES(Width*8)*Height);
return TRUE;
}
////////////////////////////////////二值图像的膨胀操作
BOOL ImageDilation(BYTE *pData,int Width,int Height)
{
BYTE* pData1;
int m,n,i,j,sum,k,sum1;
BOOL bDilation;
if(pData==NULL)
{
AfxMessageBox(“图像数据为空,请读取图像数据”);
return FALSE;
}
//申请空间,pData1存放处理后的数据;
pData1=(BYTE*)new char[WIDTHBYTES(Width*8)*Height];
if(pData1==NULL)
{
AfxMessageBox(“图像缓冲数据区申请失败,请重新申请图像数据缓冲区”);
return FALSE ;
}
memcpy(pData1,pData,WIDTHBYTES(Width*8)*Height);
for(i=10;i<Height-10;i++)
for(j=32;j<Width-32;j++)
{
bDilation=FALSE;
sum=*(pData+WIDTHBYTES(Width*8)*i+j);
if(sum==0)
{
//求像素点八邻域的灰度值;
for(m=-1;m<2;m++)
{
for(n=-1;n<2;n++)
{
sum1=*(pData+WIDTHBYTES(Width*8)*(i+m)+j+n);
if(sum1==255)
{
*(pData1+WIDTHBYTES(Width*8)*i+j)=255;
bDilation=TRUE;
break;
}
}
if(bDilation)
{
bDilation=FALSE;
break;
}
}
}
}
memcpy(pData,pData1,WIDTHBYTES(Width*8)*Height);
return TRUE;
}

  从上面的说明可以看出,腐蚀可以消除图像中小的噪声区域,膨胀可以填补物体中的空洞。对一个图像先进行腐蚀运算然后再膨胀的操作过程称为开运算,它可以消除细小的物体、在纤细点处分离物体、平滑较大物体的边界时不明显的改变其面积。如果对一个图像先膨胀然后再收缩,我们称之为闭运算,它具有填充物体内细小的空洞、连接邻近物体、在不明显改变物体面积的情况下平滑其边界的作用。通常情况下,当有噪声的图像用阈值二值化后,所得到的边界是很不平滑的,物体区域具有一些错判的孔洞,背景区域散布着一些小的噪声物体,连续的开和闭运算可以显著的改善这种情况,这时候需要在连接几次腐蚀迭代之后,再加上相同次数的膨胀,才可以产生所期望的效果。为了更好的显示出二值图像的处理效果,我们仍旧以图像采集卡获取的汽车图像为处理源图像,下图为处理后的效果:


(a)噪声图

(b)开运算处理

    图一 开运算效果图

  上图中,a图为包含噪声的图像,b图为经过腐蚀膨胀处理后的图像,可以看出,经过上述处理,成功的消除了图像中的噪声点,同时又起到了平滑边缘的作用。


2014-04-27 21:24:32 zhmxy555 阅读数 103519
  • (二)矩阵基本运算

    本课程由专业数学系老师讲解,从数学背景和现实应用中讲解线性代数的相关知识,摆脱传统的讲概念、记公式、解体的数学学习模式,让听众对线性代数有一个全新的、深刻的认识。使听众能够了解矩阵和空间的概念、性质。...

    825人学习 AI100讲师
    免费试看


本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

文章链接: http://blog.csdn.net/poem_qianmo/article/details/23184547

作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

知乎:http://www.zhihu.com/people/mao-xing-yun

邮箱: happylifemxy@163.com

写作当前博文时配套使用的OpenCV版本: 2.4.8

 

上篇文章中,我们重点了解了腐蚀和膨胀这两种最基本的形态学操作,而运用这两个基本操作,我们可以实现更高级的形态学变换。

所以,本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算、形态学梯度、“顶帽”、“黑帽”等等。

 先上几张示例程序的截图吧:


有没有很熟悉这张图?没错,这就是最近热映的电影Captain America~

下面这张图的效果就有些凶残了:

OK,截图先看到这里。在正文之前先来唠唠和主题相关的事情。


第一件事,OpenCV最新版本更新到了2.4.9。


在写这篇博文的两天之前(4月25日上午),OpenCV官网页面显示最新版本还是2.4.8,但是通过浅墨细心地发现,文档页面的标题已经悄悄而低调地改成了2.4.9.所以我们当时应该可以去断定,OpenCV2.4.9应该马上就要和我们见面了。

 

果然,OpenCV2.4.9就在两天后(4月27日),正式在OpenCV官方网站上上线了。现在转到OpenCV的官方主页,赫然发现最新版本已然显示为2.4.9:

这是OpenCV的官方主页传送门:http://opencv.org/

大家可以自己前去看看以及下载最新版本的OpenCV。如果不出意外的话呢,下次文章我们就将紧跟时代,用上最新版本的OpenCV2.4.9进行讲解和程序的书写,所以,大家在看这篇文章之后呢,可以去下载当前最新的2.4.9版本并装上配置好。




第二件事,是浅墨想跟大家做一个关于OpenCV系列文章的书写内容和风格的思想汇报。


是这样的,浅墨发现最近几期写出来的文章有些偏离自己开始开这个专栏的最初的愿望——原理和概念部分占的比重有些大,有些弱化OpenCV实际的使用。

写这些博文的初心是教大家如何使用OpenCV来写代码,原理部分我想很多朋友应该多少都懂,就算某些同学对某些概念有些模糊,大家也完全可以带着关键词句去google或者百度。

浅墨的想法是,以后的专栏文章原理部分尽量从简,“深入”的源码剖析部分也是从简,重点突出“浅出”部分,让大家快速上手OpenCV函数的使用,这样浅墨的工作量也会小很多,更新也会更勤。

PS:浅墨其实每次在写图像处理原理部分的时候都特纠结,因为浅墨其实感兴趣的和大家一样,也是如何写代码,而不是那些多多少少让人提不起兴趣来的图像处理公式和概念。这往往就照成了博文更新的拖延症。

所以呢,在浅墨以后写的OpenCV文章中,原理和深入部分我们就点到为止,文章的拳头内容是“浅出”部分,重点教大家如何快速上手OpenCV API。我想这也是大家一直期待和想要看到的浅墨出品的文章的样子吧。:)

OK,大概就是这些。我们开始今天的正题。






一、理论与概念讲解——从现象到本质



首先呢,要知道形态学的高级形态,往往都是建立在腐蚀和膨胀这两个基本操作之上的。而关于腐蚀和膨胀,概念和细节以及相关代码可以看浅墨之前写的这篇文章:


【OpenCV入门教程之十】 形态学图像处理(一):膨胀与腐蚀


对膨胀和腐蚀心中有数了,接下来的高级形态学操作,应该就不难理解。

另外,为了下面对比和演示以及理解的方便,浅墨自己制作了一张毛笔字图,这里先上原图:

 

 OK,我们开始讲解。





1.1 开运算(Opening Operation)

 



开运算(Opening Operation),其实就是先腐蚀后膨胀的过程。其数学表达式如下:




开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。效果图是这样的:

实际效果图:

 




1.2 闭运算(Closing Operation)


先膨胀后腐蚀的过程称为闭运算(Closing Operation),其数学表达式如下:

 

闭运算能够排除小型黑洞(黑色区域)。效果图如下所示:

 

实际效果图:





1.3 形态学梯度(MorphologicalGradient)


形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,数学表达式如下:

 

对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来保留物体的边缘轮廓,如下所示:

 

实际素材效果图:





1.4 顶帽(Top Hat)


顶帽运算(Top Hat)又常常被译为”礼帽“运算。为原图像与上文刚刚介绍的“开运算“的结果图之差,数学表达式如下:

因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。

顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

如下所示:

素材效果图:

 

 



1.5 黑帽(Black Hat)



黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。数学表达式为:


黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。

所以,黑帽运算用来分离比邻近点暗一些的斑块。非常完美的轮廓效果图:

 

实际素材效果图:





 

二、深入——OpenCV源码分析溯源




本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来一起看一下morphologyEx函数的源代码。

//-----------------------------------【erode()函数中文注释版源代码】----------------------------  
//   说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码  
//   OpenCV源代码版本:2.4.8  
//   源码路径:…\opencv\sources\modules\imgproc\src\morph.cpp  
//   源文件中如下代码的起始行数:1369行  
//   中文注释by浅墨  
//--------------------------------------------------------------------------------------------------------   
void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,
                       InputArray kernel, Pointanchor, int iterations,
                       int borderType, constScalar& borderValue )
{
//拷贝Mat数据到临时变量
   Mat src = _src.getMat(), temp;
   _dst.create(src.size(), src.type());
   Mat dst = _dst.getMat();
 
//一个大switch,根据不同的标识符取不同的操作
   switch( op )
    {
   case MORPH_ERODE:
       erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
       break;
   case MORPH_DILATE:
       dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
       break;
   case MORPH_OPEN:
       erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
       dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
       break;
   case CV_MOP_CLOSE:
       dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
       erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
       break;
   case CV_MOP_GRADIENT:
       erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
       dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
       dst -= temp;
       break;
   case CV_MOP_TOPHAT:
       if( src.data != dst.data )
           temp = dst;
       erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( temp, temp, kernel, anchor,iterations, borderType, borderValue );
       dst = src - temp;
       break;
   case CV_MOP_BLACKHAT:
       if( src.data != dst.data )
           temp = dst;
       dilate( src, temp, kernel, anchor, iterations, borderType, borderValue);
       erode( temp, temp, kernel, anchor, iterations, borderType, borderValue);
       dst = temp - src;
       break;
   default:
       CV_Error( CV_StsBadArg, "unknown morphological operation" );
    }
}

看上面的源码可以发现,其实morphologyEx函数其实就是内部一个大switch而已。根据不同的标识符取不同的操作。比如开运算MORPH_OPEN,按我们上文中讲解的数学表达式,就是先腐蚀后膨胀,即依次调用erode和dilate函数,为非常简明干净的代码。

 




 


三、浅出——API函数快速上手

 



3.1 morphologyEx函数详解



上面我们已经讲到,morphologyEx函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来了解它的参数意义和使用方法。

C++: void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArraykernel,
Pointanchor=Point(-1,-1),
intiterations=1,
intborderType=BORDER_CONSTANT,
constScalar& borderValue=morphologyDefaultBorderValue() );

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
  • 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
  • 第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
    • MORPH_OPEN – 开运算(Opening operation)
    • MORPH_CLOSE – 闭运算(Closing operation)
    • MORPH_GRADIENT -形态学梯度(Morphological gradient)
    • MORPH_TOPHAT - “顶帽”(“Top hat”)
    • MORPH_BLACKHAT - “黑帽”(“Black hat“)

另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。

 

  • 第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:

其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:

    • 矩形: MORPH_RECT
    • 交叉形: MORPH_CROSS
    • 椭圆形: MORPH_ELLIPSE

而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。

我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。

getStructuringElement函数相关的调用示例代码如下:

int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
 
//获取自定义核
Mat element =getStructuringElement(MORPH_RECT,
       Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
       Point(g_nStructElementSize, g_nStructElementSize ));


调用这样之后,我们便可以在接下来调用erode、dilate或morphologyEx函数时,kernel参数填保存getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。

  • 第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
  • 第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
  • 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
  • 第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

其中的这些操作都可以进行就地(in-place)操作。且对于多通道图像,每一个通道都是单独进行操作。

 OK,讲解完毕,下面就是使用的范例。



高能预警!高能预警!高能预警!

一大波示例代码正在逼近。

为了方便大家需要的时候随时取用。下面我们依次列举出开运算,闭运算,形态学梯度,顶帽,黑帽,腐蚀,膨胀的效果实现简化版完整代码。

其实说白了,这些代码基本上内容一致,其实就是改一下morphologyEx里面的第三个标识符参数而已。核都是选的MORPH_RECT,矩形元素结构。

另外,通过看源码我们发现,最基本的腐蚀和膨胀操作也可以用morphologyEx函数来实现,他们由morphologyEx函数源码中switch的前两个case来实现(虽然在case体内就是简单地各自调用了一下erode和dilation函数,但还是有写出来的必要)。所以在这里,我们也用morphologyEx再重新来实现一遍他们。

按着顺序来列出吧,就直接列详细注释好的代码和运行结果了。

 




3.2 开运算示例程序


OpenCV中调用morphologyEx函数进行开运算操作的示例程序如下:

//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】开运算"); 
       namedWindow("【效果图】开运算"); 
       //显示原始图 
       imshow("【原始图】开运算", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_OPEN, element);
       //显示效果图 
       imshow("【效果图】开运算", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:







3.3 闭运算示例程序



OpenCV中调用morphologyEx函数进行闭运算操作的示例程序如下:


//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】闭运算"); 
       namedWindow("【效果图】闭运算"); 
       //显示原始图 
       imshow("【原始图】闭运算", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_CLOSE, element);
       //显示效果图 
       imshow("【效果图】闭运算", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:





3.4 形态学梯度示例程序



OpenCV中调用morphologyEx函数进行形态学梯度操作的示例程序如下:

//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】形态学梯度"); 
       namedWindow("【效果图】形态学梯度"); 
       //显示原始图 
       imshow("【原始图】形态学梯度", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_GRADIENT, element);
       //显示效果图 
       imshow("【效果图】形态学梯度", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:






3.5 顶帽运算(Top Hat)示例程序



OpenCV中调用morphologyEx函数进行顶帽运算操作的示例程序如下:

//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】顶帽运算"); 
       namedWindow("【效果图】顶帽运算"); 
       //显示原始图 
       imshow("【原始图】顶帽运算", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_TOPHAT, element);
       //显示效果图 
       imshow("【效果图】顶帽运算", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:




3.6 黑帽运算(BlackHat)示例程序


OpenCV中调用morphologyEx函数进行黑帽运算操作的示例程序如下:

//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】黑帽运算"); 
       namedWindow("【效果图】黑帽运算"); 
       //显示原始图 
       imshow("【原始图】黑帽运算", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_BLACKHAT, element);
       //显示效果图 
       imshow("【效果图】黑帽运算", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:



 



3.7 腐蚀(morphologyEx调用版)示例程序



OpenCV中调用morphologyEx函数进行腐蚀操作的示例程序如下:

//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】腐蚀"); 
       namedWindow("【效果图】腐蚀"); 
       //显示原始图 
       imshow("【原始图】腐蚀", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_ERODE, element);
       //显示效果图 
       imshow("【效果图】腐蚀", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:



 



3.8 膨胀(morphologyEx调用版)示例程序



OpenCV中调用morphologyEx函数进行膨胀操作的示例程序如下:

//-----------------------------------【头文件包含部分】---------------------------------------
//            描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空间声明部分】---------------------------------------
//            描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
//            描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //载入原始图  
       Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
       //创建窗口  
       namedWindow("【原始图】膨胀"); 
       namedWindow("【效果图】膨胀"); 
       //显示原始图 
       imshow("【原始图】膨胀", image); 
       //定义核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //进行形态学操作
       morphologyEx(image,image, MORPH_DILATE, element);
       //显示效果图 
       imshow("【效果图】膨胀", image); 
 
       waitKey(0); 
 
       return 0; 
}

运行效果图:



 








四、综合示例——在实战中熟稔

 



依然是每篇文章都会配给大家的一个详细注释的博文配套示例程序,把这篇文章中介绍的知识点以代码为载体,展现给大家。

这个示例程序中,一共有四个显示图像的窗口。

原始图一个,开/闭运算为一个,腐蚀/膨胀为一个,顶帽/黑帽运算为一个。

分别使用滚动条,来控制得到的形态学效果。且迭代值为10的时候,为中间。

另外,还可以通过键盘按键1,2,3以及空格,来调节成不同的元素结构(矩形、椭圆、十字形)。说明页面如下:





废话不多说,上代码吧:


//-----------------------------------【程序说明】----------------------------------------------
//		程序名称::《【OpenCV入门教程之十一】形态学图像处理(一):膨胀与腐蚀  》 博文配套源码 
//		开发所用IDE版本:Visual Studio 2010
//   		开发所用OpenCV版本:	2.4.8
//		2014年4月25日 Create by 浅墨
//----------------------------------------------------------------------------------------------

//-----------------------------------【头文件包含部分】---------------------------------------
//		描述:包含程序所依赖的头文件
//---------------------------------------------------------------------------------------------- 
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

//-----------------------------------【命名空间声明部分】--------------------------------------
//		描述:包含程序所使用的命名空间
//----------------------------------------------------------------------------------------------- 
using namespace std;
using namespace cv;


//-----------------------------------【全局变量声明部分】--------------------------------------
//		描述:全局变量声明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage;//原始图和效果图
int g_nElementShape = MORPH_RECT;//元素结构的形状

//变量接收的TrackBar位置参数
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
int g_nErodeDilateNum = 0;
int g_nTopBlackHatNum = 0;



//-----------------------------------【全局函数声明部分】--------------------------------------
//		描述:全局函数声明
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*);//回调函数
static void on_ErodeDilate(int, void*);//回调函数
static void on_TopBlackHat(int, void*);//回调函数
static void ShowHelpText();//帮助文字显示


//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
	//改变console字体颜色
	system("color 2F");  

	ShowHelpText();

	//载入原图
	g_srcImage = imread("1.jpg");//工程目录下需要有一张名为1.jpg的素材图
	if( !g_srcImage.data ) { printf("Oh,no,读取srcImage错误~! \n"); return false; }

	//显示原始图
	namedWindow("【原始图】");
	imshow("【原始图】", g_srcImage);

	//创建三个窗口
	namedWindow("【开运算/闭运算】",1);
	namedWindow("【腐蚀/膨胀】",1);
	namedWindow("【顶帽/黑帽】",1);

	//参数赋值
	g_nOpenCloseNum=9;
	g_nErodeDilateNum=9;
	g_nTopBlackHatNum=2;

	//分别为三个窗口创建滚动条
	createTrackbar("迭代值", "【开运算/闭运算】",&g_nOpenCloseNum,g_nMaxIterationNum*2+1,on_OpenClose);
	createTrackbar("迭代值", "【腐蚀/膨胀】",&g_nErodeDilateNum,g_nMaxIterationNum*2+1,on_ErodeDilate);
	createTrackbar("迭代值", "【顶帽/黑帽】",&g_nTopBlackHatNum,g_nMaxIterationNum*2+1,on_TopBlackHat);

	//轮询获取按键信息
	while(1)
	{
		int c;

		//执行回调函数
		on_OpenClose(g_nOpenCloseNum, 0);
		on_ErodeDilate(g_nErodeDilateNum, 0);
		on_TopBlackHat(g_nTopBlackHatNum,0);

		//获取按键
		c = waitKey(0);

		//按下键盘按键Q或者ESC,程序退出
		if( (char)c == 'q'||(char)c == 27 )
			break;
		//按下键盘按键1,使用椭圆(Elliptic)结构元素结构元素MORPH_ELLIPSE
		if( (char)c == 49 )//键盘按键1的ASII码为49
			g_nElementShape = MORPH_ELLIPSE;
		//按下键盘按键2,使用矩形(Rectangle)结构元素MORPH_RECT
		else if( (char)c == 50 )//键盘按键2的ASII码为50
			g_nElementShape = MORPH_RECT;
		//按下键盘按键3,使用十字形(Cross-shaped)结构元素MORPH_CROSS
		else if( (char)c == 51 )//键盘按键3的ASII码为51
			g_nElementShape = MORPH_CROSS;
		//按下键盘按键space,在矩形、椭圆、十字形结构元素中循环
		else if( (char)c == ' ' )
			g_nElementShape = (g_nElementShape + 1) % 3;
	}

	return 0;
}


//-----------------------------------【on_OpenClose( )函数】----------------------------------
//		描述:【开运算/闭运算】窗口的回调函数
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*)
{
	//偏移量的定义
	int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
	//自定义核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
	//进行操作
	if( offset < 0 )
		morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
	else
		morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element);
	//显示图像
	imshow("【开运算/闭运算】",g_dstImage);
}


//-----------------------------------【on_ErodeDilate( )函数】----------------------------------
//		描述:【腐蚀/膨胀】窗口的回调函数
//-----------------------------------------------------------------------------------------------
static void on_ErodeDilate(int, void*)
{
	//偏移量的定义
	int offset = g_nErodeDilateNum - g_nMaxIterationNum;	//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
	//自定义核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
	//进行操作
	if( offset < 0 )
		erode(g_srcImage, g_dstImage, element);
	else
		dilate(g_srcImage, g_dstImage, element);
	//显示图像
	imshow("【腐蚀/膨胀】",g_dstImage);
}


//-----------------------------------【on_TopBlackHat( )函数】--------------------------------
//		描述:【顶帽运算/黑帽运算】窗口的回调函数
//----------------------------------------------------------------------------------------------
static void on_TopBlackHat(int, void*)
{
	//偏移量的定义
	int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
	//自定义核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
	//进行操作
	if( offset < 0 )
		morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT , element);
	else
		morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
	//显示图像
	imshow("【顶帽/黑帽】",g_dstImage);
}

//-----------------------------------【ShowHelpText( )函数】----------------------------------
//		描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
//输出一些帮助信息
	printf("\n\n\n\t请调整滚动条观察图像效果~\n\n");
	printf( "\n\n\t按键操作说明: \n\n"
		"\t\t键盘按键【ESC】或者【Q】- 退出程序\n"
		"\t\t键盘按键【1】- 使用椭圆(Elliptic)结构元素\n"
		"\t\t键盘按键【2】- 使用矩形(Rectangle )结构元素\n"
		"\t\t键盘按键【3】- 使用十字型(Cross-shaped)结构元素\n"
		"\t\t键盘按键【空格SPACE】- 在矩形、椭圆、十字形结构元素中循环\n"
		"\n\n\t\t\t\t\t\t\t\t by浅墨"
		);
}

放出一些效果图:

首先是原图:


非常帅气的Captain America有木有!

腐蚀效果图:


膨胀效果图:



开运算效果图:



闭运算效果图:



顶帽运算效果图:


黑帽运算效果图:



好的,就放出这些效果图吧,具体更多的运行效果大家就自己下载示例程序回去玩~

 

本篇文章的配套源代码请点击这里下载:


【浅墨OpenCV入门教程之十一】配套源代码下载

 


OK,今天的内容大概就是这些,我们下篇文章见:)




2017-02-22 19:45:23 jacke121 阅读数 6846
  • (二)矩阵基本运算

    本课程由专业数学系老师讲解,从数学背景和现实应用中讲解线性代数的相关知识,摆脱传统的讲概念、记公式、解体的数学学习模式,让听众对线性代数有一个全新的、深刻的认识。使听众能够了解矩阵和空间的概念、性质。...

    825人学习 AI100讲师
    免费试看

转自:http://blog.sina.com.cn/s/blog_4bdb170b01019atv.html

图像处理-线性滤波-1 基础(相关算子、卷积算子、边缘效应)

这里讨论利用输入图像中像素的小邻域来产生输出图像的方法,在信号处理中这种方法称为滤波(filtering)。其中,最常用的是线性滤波:输出像素是输入邻域像素的加权和。

 

1.相关算子(Correlation Operator)

       定义:image,  即image ,其中h称为相关核(Kernel).

        

  步骤:

        1)滑动核,使其中心位于输入图像g的(i,j)像素上

        2)利用上式求和,得到输出图像的(i,j)像素值

        3)充分上面操纵,直到求出输出图像的所有像素值

 

  例:

A = [17  24   1   8  15            h = [8   1   6
     23   5   7  14  16                     3   5   7
      4   6  13  20  22                     4   9   2]
     10  12  19  21   3           
     11  18  25   2   9]

计算输出图像的(2,4)元素=image

image

Matlab 函数:imfilter(A,h)

 

2.卷积算子(Convolution)

定义:image image ,其中

   步骤:

        1)将核围绕中心旋转180度

        2)滑动核,使其中心位于输入图像g的(i,j)像素上

        3)利用上式求和,得到输出图像的(i,j)像素值

        4)充分上面操纵,直到求出输出图像的所有像素值

       例:计算输出图像的(2,4)元素=image

       image

Matlab 函数:Matlab 函数:imfilter(A,h,'conv')% imfilter默认是相关算子,因此当进行卷积计算时需要传入参数'conv'

3.边缘效应

当对图像边缘的进行滤波时,核的一部分会位于图像边缘外面。

image

常用的策略包括:

1)使用常数填充:imfilter默认用0填充,这会造成处理后的图像边缘是黑色的。

2)复制边缘像素:I3 = imfilter(I,h,'replicate');

image

   

4.常用滤波

fspecial函数可以生成几种定义好的滤波器的相关算子的核。

例:unsharp masking 滤波

1
2
3
4
5
I = imread('moon.tif');
h = fspecial('unsharp');
I2 = imfilter(I,h);
imshow(I), title('Original Image')
figure, imshow(I2), title('Filtered Image')
 
 

图像处理-线性滤波-2 图像微分(1、2阶导数和拉普拉斯算子)

更复杂些的滤波算子一般是先利用高斯滤波来平滑,然后计算其1阶和2阶微分。由于它们滤除高频和低频,因此称为带通滤波器(band-pass filters)。

在介绍具体的带通滤波器前,先介绍必备的图像微分知识。

1 一阶导数

连续函数,其微分可表达为image ,或image                         (1.1)

对于离散情况(图像),其导数必须用差分方差来近似,有

                                   image,前向差分 forward differencing                  (1.2)

                                   image ,中心差分 central differencing                     (1.3)

1)前向差分的Matlab实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function dimg = mipforwarddiff(img,direction)
% MIPFORWARDDIFF     Finite difference calculations 
%
%   DIMG = MIPFORWARDDIFF(IMG,DIRECTION)
%
%  Calculates the forward-difference for a given direction
%  IMG       : input image
%  DIRECTION : 'dx' or 'dy'
%  DIMG      : resultant image
%
%   See also MIPCENTRALDIFF MIPBACKWARDDIFF MIPSECONDDERIV
%   MIPSECONDPARTIALDERIV
  
%   Omer Demirkaya, Musa Asyali, Prasana Shaoo, ... 9/1/06
%   Medical Image Processing Toolbox
  
imgPad = padarray(img,[1 1],'symmetric','both');%将原图像的边界扩展
[row,col] = size(imgPad);
dimg = zeros(row,col);
switch (direction)   
case 'dx',
   dimg(:,1:col-1) = imgPad(:,2:col)-imgPad(:,1:col-1);%x方向差分计算,
case 'dy',
   dimg(1:row-1,:) = imgPad(2:row,:)-imgPad(1:row-1,:); 
otherwise, disp('Direction is unknown');
end;
dimg = dimg(2:end-1,2:end-1);

2)中心差分的Matlab实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function dimg = mipcentraldiff(img,direction)
% MIPCENTRALDIFF     Finite difference calculations 
%
%   DIMG = MIPCENTRALDIFF(IMG,DIRECTION)
%
%  Calculates the central-difference for a given direction
%  IMG       : input image
%  DIRECTION : 'dx' or 'dy'
%  DIMG      : resultant image
%
%   See also MIPFORWARDDIFF MIPBACKWARDDIFF MIPSECONDDERIV
%   MIPSECONDPARTIALDERIV
  
%   Omer Demirkaya, Musa Asyali, Prasana Shaoo, ... 9/1/06
%   Medical Image Processing Toolbox
  
img = padarray(img,[1 1],'symmetric','both');
[row,col] = size(img);
dimg = zeros(row,col);
switch (direction)
    case 'dx',
        dimg(:,2:col-1) = (img(:,3:col)-img(:,1:col-2))/2;
    case 'dy',
        dimg(2:row-1,:) = (img(3:row,:)-img(1:row-2,:))/2;
    otherwise,
        disp('Direction is unknown');
end
dimg = dimg(2:end-1,2:end-1);
1
  

实例:技术图像x方向导数

1
2
I = imread('coins.png'); figure; imshow(I);
Id = mipforwarddiff(I,'dx'); figure, imshow(Id);

      image image

    原图像                                                   x方向1阶导数

 

2 图像梯度(Image Gradient)

图像I的梯度定义为image  ,其幅值为image 。出于计算性能考虑,幅值也可用image 来近似。

Matlab函数

1)gradient:梯度计算

2)quiver:以箭头形状绘制梯度。注意放大下面最右侧图可看到箭头,由于这里计算横竖两个方向的梯度,因此箭头方向都是水平或垂直的。

实例:仍采用上面的原始图像

1
2
3
4
5
I = double(imread('coins.png'));
[dx,dy]=gradient(I);
magnitudeI=sqrt(dx.^2+dy.^2);
figure;imagesc(magnitudeI);colormap(gray);%梯度幅值
hold on;quiver(dx,dy);%叠加梯度方向

        image image

                         梯度幅值                                   梯度幅值+梯度方向

 

3 二阶导数

对于一维函数,其二阶导数image ,即image 。它的差分函数为

                                 image                  (3.1)

 

3.1 普拉斯算子(laplacian operator)

3.1.2 概念

拉普拉斯算子是n维欧式空间的一个二阶微分算子。它定义为两个梯度向量算子的内积

                          image       (3.2)

其在二维空间上的公式为:    image                (3.3)

 

对于1维离散情况,其二阶导数变为二阶差分

1)首先,其一阶差分为image

2)因此,二阶差分为

          image

3)因此,1维拉普拉斯运算可以通过1维卷积核image 实现

 

对于2维离散情况(图像),拉普拉斯算子是2个维上二阶差分的和(见式3.3),其公式为:

image   (3.4)

上式对应的卷积核为

                       image

常用的拉普拉斯核有:

                      image

3.1.2 应用

拉普拉斯算子会突出像素值快速变化的区域,因此常用于边缘检测。

 

 

Matlab里有两个函数

1)del2

计算公式:image image  

2)fspecial:图像处理中一般利用Matlab函数fspecial

h = fspecial('laplacian', alpha) returns a 3-by-3 filter approximating the shape of the two-dimensional Laplacian operator.
The parameter alpha controls the shape of the Laplacian and must be in the range 0.0 to 1.0. The default value for alpha is 0.2.

 

3.1.3 资源

http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html (非常清晰的Laplacian Operator介绍,本文的主要参考)

http://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm

 

 
 
 
 

sift算法

 

尺度不变特征转换(Scale-invariant feature transform 或 SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe 在1999年所发表,2004年完善总结。

Sift算法就是用不同尺度(标准差)的高斯函数对图像进行平滑,然后比较平滑后图像的差别,
差别大的像素就是特征明显的点。

sift可以同时处理亮度,平移,旋转,尺度的变化,利用特征点来提取特征描述符,最后在特征描述符之间寻找匹配

 

五个步骤

1构建尺度空间,检测极值点,获得尺度不变性

2特征点过滤并进行经确定位,剔除不稳定的特征点

3 在特征点处提取特征描述符,为特征点分配方向直

4声称特征描述子,利用特征描述符寻找匹配点

5计算变换参数

当2幅图像的sift特征向量生成以后,下一步就可以采用关键点特征向量的欧式距离来作为2幅图像中关键点的相似性判定量度

 

尺度空间:

尺度就是受delta这个参数控制的表示

而不同的L(x,y,delta)就构成了尺度空间,实际上具体计算的时候即使连续的高斯函数,都要被离散为矩阵来和数字图像进行卷积操作

L(x,y,delta)=G(x,y,e)*i(x,y)

尺度空间=原始图像(卷积)一个可变尺度的2维高斯函数G(x,y,e)

 

G(x,y,e) = [1/2*pi*e^2] * exp[ -(x^2 + y^2)/2e^2] 


为了更有效的在尺度空间检测到稳定的关键点,提出了高斯差分尺度空间,利用不同尺度的高斯差分核与原始图像i(x,y)卷积生成

D(x,y,e)=(G(x,y,ke)-G(x,y,e))*i(x,y)

=L(x,y,ke)-L(x,y,e)

(为避免遍历每个像素点)

 

高斯卷积:

在组建一组尺度空间后,再组建下一组尺度空间,对上一组尺度空间的最后一幅图像进行二分之一采样,得到下一组尺度空间的第一幅图像,然后进行像建立第一组尺度空间那样的操作,得到第二组尺度空间,公式定义为
         L(x,y,e) = G(x,y,e)*I(x,y)

    图像金字塔的构建:图像金字塔共O组,每组有S层,下一组的图像由上一组图像降采样得到、

高斯差分

    在尺度空间建立完毕后,为了能够找到稳定的关键点,采用高斯差分的方法来检测那些在局部位置的极值点,即采用俩个相邻的尺度中的图像相减,即公式定义为:
        D(x,y,e) = ((G(x,y,ke) - G(x,y,e)) * I(x,y) 
                 = L(x,y,ke) - L(x,y,e)
 咱们再来具体阐述下构造D(x,y,e)的详细步骤:
    1、首先采用不同尺度因子的高斯核对图像进行卷积以得到图像的不同尺度空间,将这一组图像作为金子塔图像的第一层。
    2、接着对第一层图像中的2倍尺度图像(相对于该层第一幅图像的2倍尺度)以2倍像素距离进行下采样来得到金子塔图像的第二层中的第一幅图像,对该图像采用不同尺度因子的高斯核进行卷积,以获得金字塔图像中第二层的一组图像。
    3、再以金字塔图像中第二层中的2倍尺度图像(相对于该层第一幅图像的2倍尺度)以2倍像素距离进行下采样来得到金字塔图像的第三层中的第一幅图像,对该图像采用不同尺度因子的高斯核进行卷积,以获得金字塔图像中第三层的一组图像。这样依次类推,从而获得了金字塔图像的每一层中的一组图像,
 4、对上图得到的每一层相邻的高斯图像相减,就得到了高斯差分图像,如下述第一幅图所示。下述第二幅图中的右列显示了将每组中相邻图像相减所生成的高斯差分图像的结果,限于篇幅,图中只给出了第一层和第二层高斯差分图像的计算
 

 

图像处理之卷积概念

 

我们来看一下一维卷积的概念.
连续空间的卷积定义是 f(x)与g(x)的卷积是 f(t-x)g(x) 在t从负无穷到正无穷的积分值.t-x要在f(x)定义域内,所以看上去很大的积分实际上还是在一定范围的.
实际的过程就是f(x) 先做一个Y轴的反转,然后再沿X轴平移t就是f(t-x),然后再把g(x)拿来,两者乘积的值再积分.想象一下如果g(x)或者f(x)是个单位的阶越函数. 那么就是f(t-x)与g(x)相交部分的面积.这就是卷积了.
把积分符号换成求和就是离散空间的卷积定义了.

 

么在图像中卷积卷积地是什么意思呢,就是图像f(x),模板g(x),然后将模版g(x)在模版中移动,每到一个位置,就把f(x)与g(x)的定义域相交的元素进行乘积并且求和,得出新的图像一点,就是被卷积后的图像. 模版又称为卷积核.卷积核做一个矩阵的形状.


卷积定义上是线性系统分析经常用到的.线性系统就是一个系统的输入和输出的关系是线性关系.就是说整个系统可以分解成N多的无关独立变化,整个系统就是这些变化的累加.
如 x1->y1, x2->y2; 那么A*x1 + B*x2 -> A*y1 + B*y2 这就是线性系统. 表示一个线性系统可以用积分的形式 如 Y = Sf(t,x)g(x)dt S表示积分符号,就是f(t,x)表示的是A B之类的线性系数.
看上去很像卷积呀,,对如果f(t,x) = F(t-x) 不就是了吗.从f(t,x)变成F(t-x)实际上是说明f(t,x)是个线性移不变,就是说 变量的差不变化的时候,那么函数的值不变化. 实际上说明一个事情就是说线性移不变系统的输出可以通过输入和表示系统线性特征的函数卷积得到.

 

http://dept.wyu.edu.cn/dip/DIPPPT2005/����������ϵͳ.ppt


 
 
 
 
 
谈起卷积分当然要先说说冲击函数—-这个倒立的小蝌蚪,卷积其实就是为它诞生的。”冲击函数”是狄拉克为了解决一些瞬间作用的物理现象而提出的符号。
古人曰:”说一堆大道理不如举一个好例子”,冲量这一物理现象很能说明”冲击函数”。在t时间内对一物体作用F的力,我们可以让作用时间t很小,作用力F很大,但让Ft的乘积不变,即冲量不变。于是在用t做横坐标、F做纵坐标的坐标系中,就如同一个面积不变的长方形,底边被挤的窄窄的,高度被挤的高高的,在数学中它可以被挤到无限高,但即使它无限瘦、无限高、但它仍然保持面积不变(它没有被挤没!),为了证实它的存在,可以对它进行积分,积分就是求面积嘛!于是”卷积” 这个数学怪物就这样诞生了。说它是数学怪物是因为追求完美的数学家始终在头脑中转不过来弯,一个能瘦到无限小的家伙,竟能在积分中占有一席之地,必须将这个细高挑清除数学界。但物理学家、工程师们确非常喜欢它,因为它解决了很多当时数学家解决不了的实际问题。最终追求完美的数学家终于想通了,数学是来源于实际的,并最终服务于实际才是真。于是,他们为它量身定做了一套运作规律。于是,妈呀!你我都感觉眩晕的卷积分产生了。
例子:
有一个七品县令,喜欢用打板子来惩戒那些市井无赖,而且有个惯例:如果没犯大罪,只打一板,释放回家,以示爱民如子。
有一个无赖,想出人头地却没啥指望,心想:既然扬不了善名,出恶名也成啊。怎么出恶名?炒作呗!怎么炒作?找名人呀!他自然想到了他的行政长官——县令。
无赖于是光天化日之下,站在县衙门前撒了一泡尿,后果是可想而知地,自然被请进大堂挨了一板子,然后昂首挺胸回家,躺了一天,嘿!身上啥事也没有!第二天如法炮制,全然不顾行政长管的仁慈和衙门的体面,第三天、第四天……每天去县衙门领一个板子回来,还喜气洋洋地,坚持一个月之久!这无赖的名气已经和衙门口的臭气一样,传遍八方了!
县令大人噤着鼻子,呆呆地盯着案子上的惊堂木,拧着眉头思考一个问题:这三十个大板子怎么不好使捏?……想当初,本老爷金榜题名时,数学可是得了满分,今天好歹要解决这个问题:
——人(系统!)挨板子(脉冲!)以后,会有什么表现(输出!)?
——费话,疼呗!
——我问的是:会有什么表现?
——看疼到啥程度。像这无赖的体格,每天挨一个板子啥事都不会有,连哼一下都不可能,你也看到他那得意洋洋的嘴脸了(输出0);如果一次连揍他十个板子,他可能会皱皱眉头,咬咬牙,硬挺着不哼
(输出1);揍到二十个板子,他会疼得脸部扭曲,象猪似地哼哼(输出3);揍到三十个板子,他可能会象驴似地嚎叫,一把鼻涕一把泪地求你饶他一命(输出5);揍到四十个板子,他会大小便失禁,勉
强哼出声来(输出1);揍到五十个板子,他连哼一下都不可能(输出0)——死啦!
县令铺开坐标纸,以打板子的个数作为X轴,以哼哼的程度(输出)为Y轴,绘制了一条曲线:
——呜呼呀!这曲线象一座高山,弄不懂弄不懂。为啥那个无赖连挨了三十天大板却不喊绕命呀?
—— 呵呵,你打一次的时间间隔(Δτ=24小时)太长了,所以那个无赖承受的痛苦程度一天一利索,没有叠加,始终是一个常数;如果缩短打板子的时间间隔(建议 Δτ=0.5秒),那他的痛苦程度可就迅速叠加了;等到这无赖挨三十个大板(t=30)时,痛苦程度达到了他能喊叫的极限,会收到最好的惩戒效果,再多打就显示不出您的仁慈了。
——还是不太明白,时间间隔小,为什么痛苦程度会叠加呢?
——这与人(线性时不变系统)对板子(脉冲、输入、激励)的响应有关。什么是响应?人挨一个板子后,疼痛的感觉会在一天(假设的,因人而异)内慢慢消失(衰减),而不可能突然消失。这样一来,只要打板子的时间间隔很小,每一个板子引起的疼痛都来不及完全衰减,都会对最终的痛苦程度有不同的贡献:
t个大板子造成的痛苦程度=Σ(第τ个大板子引起的痛苦*衰减系数)
[衰减系数是(t-τ)的函数,仔细品味]
数学表达为:y(t)=∫T(τ)H(t-τ)
——拿人的痛苦来说卷积的事,太残忍了。除了人以外,其他事物也符合这条规律吗?
——呵呵,县令大人毕竟仁慈。其实除人之外,很多事情也遵循此道。好好想一想,铁丝为什么弯曲一次不折,快速弯曲多次却会轻易折掉呢?
——恩,一时还弄不清,容本官慢慢想来——但有一点是明确地——来人啊,将撒尿的那个无赖抓来,狠打40大板!
卷积及拉普拉斯变换的通俗解释–对于我这类没学过信号系统的人来说太需要了
卷积(convolution, 另一个通用名称是德文的Faltung)的名称由来,是在于当初定义它时,定义成 integ(f1(v)*f2(t-v))dv,积分区间在0到t之间。举个简单的例子,大家可以看到,为什么叫”卷积”了。比方说在(0,100)间积分,用简单的辛普生积分公式,积分区间分成100等分,那么看到的是f1(0)和f2(100)相乘,f1(1)和f2(99)相乘,f1(2)和f2 (98)相乘,……… 等等等等,就象是在坐标轴上回卷一样。所以人们就叫它”回卷积分”,或者”卷积”了。
为了理解”卷积”的物理意义,不妨将那个问题”相当于它的时域的信号与系统的单位脉冲响应的卷积”略作变化。这个变化纯粹是为了方便表达和理解,不影响任何其它方面。将这个问题表述成这样一个问题:一个信号通过一个系统,系统的响应是频率响应或波谱响应,且看如何理解卷积的物理意义。
假设信号函数为f, 响应函数为g。f不仅是时间的函数(信号时有时无),还是频率的函数(就算在某一固定时刻,还有的地方大有的地方小);g也是时间的函数(有时候有反应,有时候没反应),同时也是频率的函数(不同的波长其响应程度不一样)。那我们要看某一时刻 t 的响应信号,该怎么办呢?
这就需要卷积了。
要看某一时刻 t 的响应信号,自然是看下面两点:
1。你信号来的时候正赶上人家”系统”的响应时间段吗?
2。就算赶上系统响应时间段,响应有多少?
响 应不响应主要是看 f 和 g 两个函数有没有交叠;响应强度的大小不仅取决于所给的信号的强弱,还取决于在某频率处对单位强度响应率。响应强度是信号强弱和对单位强度信号响应率的乘积。”交叠”体现在f(t1)和g(t-t1)上,g之所以是”(t-t1)”就是看两个函数错开多少。
由于 f 和 g 两个函数都有一定的带宽分布(假若不用开头提到的”表述变化”就是都有一定的时间带宽分布),这个信号响应是在一定”范围”内广泛响应的。算总的响应信号,当然要把所有可能的响应加起来,实际上就是对所有可能t1积分了。积分范围虽然一般在负无穷到正无穷之间;但在没有信号或者没有响应的地方,积也是白积,结果是0,所以往往积分范围可以缩减。
这就是卷积及其物理意义啊。并成一句话来说,就是看一个时有时无(当然作为特例也可以永恒存在)的信号,跟一个响应函数在某一时刻有多大交叠。
*********拉普拉斯*********
拉普拉斯(1729-1827) 是法国数学家,天文学家,物理学家。他提出拉普拉斯变换(Laplace Transform) 的目的是想要解决他当时研究的牛顿引力场和太阳系的问题中涉及的积分微分方程。
拉普拉斯变换其实是一个数学上的简便算法;想要了解其”物理”意义 — 如果有的话 — 请看我举这样一个例子:
问题:请计算十万乘以一千万。
对于没学过指数的人,就只会直接相乘;对于学过指数的人,知道不过是把乘数和被乘数表达成指数形式后,两个指数相加就行了;如果要问究竟是多少,把指数转回来就是。
“拉 普拉斯变换” 就相当于上述例子中把数转换成”指数” 的过程;进行了拉普拉斯变换之后,复杂的微分方程(对应于上例中”复杂”的乘法) 就变成了简单的代数方程,就象上例中”复杂”的乘法变成了简单的加减法。再把简单的代数方程的解反变换回去(就象把指数重新转换会一般的数一样),就解决了原来那个复杂的微分方程。
所以要说拉普拉斯变换真有” 物理意义”的话,其物理意义就相当于人们把一般的有理数用指数形式表达一样。
另外说两句题外话:
1 。拉普拉斯变换之所以现在在电路中广泛应有,根本原因是电路中也广泛涉及了微分方程。
2。拉普拉斯变换与Z变换当然有紧密联系;其本质区别在于拉氏变换处理的是时间上连续的问题,Z变换处理的是时间上分立的问题。
Signals, Linear Systems, and Convolution
Download from here
 
我们都知道卷积公式,但是它有什么物理意义呢?平时我们用卷积做过很多事情,信号处理时,输出函数是输入函数和系统函数的卷积;在图像处理时,两组幅分辨率不同的图卷积之后得到的互相平滑的图像可以方便处理。卷积甚至可以用在考试作弊中,为了让照片同时像两个人,只要把两人的图像卷积处理即可,这就是一种平滑的过程,可是我们怎么才能真正把公式和实际建立起一种联系呢?生活中就有实例:
     比如说你的老板命令你干活,你却到楼下打台球去了,后来被老板发现,他非常气愤,扇了你一巴掌(注意,这就是输入信号,脉冲),于是你的脸上会渐渐地(贱贱地)鼓起来一个包,你的脸就是一个系统,而鼓起来的包就是你的脸对巴掌的响应。
      好,这样就和信号系统建立起来意义对应的联系。下面还需要一些假设来保证论证的严谨:假定你的脸是线性时不变系统,也就是说,无论什么时候老板打你一巴掌,打在你脸的同一位置(这似乎要求你的脸足够光滑,如果你说你长了很多青春痘,甚至整个脸皮处处连续处处不可导,那难度太大了,我就无话可说了),你的脸上总是会在相同的时间间隔内鼓起来一个相同高度的包来,并且假定以鼓起来的包的大小作为系统输出。好了,那么,下面可以进入核心内容——卷积了!
      如果你每天都到楼下去打台球,那么老板每天都要扇你一巴掌,不过当老板打你一巴掌后,你5分钟就消肿了,所以时间长了,你甚至就适应这种生活了……如果有一天,老板忍无可忍,以0.5秒的间隔开始不间断的扇你的过程,这样问题就来了:第一次扇你鼓起来的包还没消肿,第二个巴掌就来了,你脸上的包就可能鼓起来两倍高,老板不断扇你,脉冲不断作用在你脸上,效果不断叠加了,这样这些效果就可以求和了,结果就是你脸上的包的高度岁时间变化的一个函数了(注意理解)!
      如果老板再狠一点,频率越来越高,以至于你都辨别不清时间间隔了,那么,求和就变成积分了。可以这样理解,在这个过程中的某一固定的时刻,你的脸上的包的鼓起程度和什么有关呢?和之前每次打你都有关!但是各次的贡献是不一样的,越早打的巴掌,贡献越小,这就是说,某一时刻的输出是之前很多次输入乘以各自的衰减系数之后的叠加而形成某一点的输出,然后再把不同时刻的输出点放在一起,形成一个函数,这就是卷积。卷积之后的函数就是你脸上的包的大小随时间变化的函数。本来你的包几分钟就可以消肿,可是如果连续打,几个小时也消不了肿了,这难道不是一种平滑过程么?反映到公式上,f(a)就是第a个巴掌,g(x-a)就是第a个巴掌在x时刻的作用程度,乘起来再叠加就ok了,这就是卷积!
     最后提醒各位,请勿亲身尝试……

卷积的物理意义?
在信号与系统中,两个函数所要表达的物理含义是什么?例如,一个系统,其单位冲激响应为h(t),当输入信号为f(t)时,该系统的输出为y(t)。为什么y(t)是f(t)和h(t)的卷积?(从数学推导我明白,但其物理意义不明白。)y(t)是f(t)和h(t)的卷积表达了一个什么意思?

卷积(convolution, 另一个通用名称是德文的Faltung)的名称由来,是在于当初定义它时,定义成 integ(f1(v)*f2(t-v))dv,积分区间在0到t之间。举个简单的例子,大家可以看到,为什么叫“卷积”了。比方说在(0,100)间积分,用简单的辛普生积分公式,积分区间分成100等分,那么看到的是f1(0)和f2(100)相乘,f1(1)和f2(99)相乘,f1(2)和f2(98)相乘,......... 等等等等,就象是在坐标轴上回卷一样。所以人们就叫它“回卷积分”,或者“卷积”了。
为了理解“卷积”的物理意义,不妨将那个问题“相当于它的时域的信号与系统的单位脉冲响应的卷积”略作变化。这个变化纯粹是为了方便表达和理解,不影响任何其它方面。将这个问题表述成这样一个问题:一个信号通过一个系统,系统的响应是频率响应或波谱响应,且看如何理解卷积的物理意义。
假设信号函数为f, 响应函数为g。f不仅是时间的函数(信号时有时无),还是频率的函数(就算在某一固定时刻,还有的地方大有的地方小);g也是时间的函数(有时候有反应,有时候没反应),同时也是频率的函数(不同的波长其响应程度不一样)。那我们要看某一时刻 t 的响应信号,该怎么办呢?
这就需要卷积了。
其实卷积积分应用广泛用在信号里面,一个是频域一个是时域
 

卷积是个啥?我忽然很想从本质上理解它。于是我从抽屉里翻出自己珍藏了许多年,每每下决心阅读却永远都读不完的《应用傅立叶变换》。
 
3.1 一维卷积的定义
 
函数f(x)与函数h(x)的卷积,由函参量的无穷积分

  定义。这里参量x和积分变量α皆为实数;函数f和h可实可复。
 
定义虽然找到了,但我还是一头雾水。卷积是个无穷积分吗?那它是干啥用的?再往后翻:几何说明、运算举例、基本性质,一堆的公式,就是没有说它是干啥用的。我于是坐在那呆想,忽然第二个困扰我的问题冒了出来:傅立叶变换是个啥?接着就是第三个、第四个、……、第N个问题。
 
傅立叶变换是个啥?听说能将时域上的东东变到频域上分析?哎?是变到频域上还是空间域上来着?到底啥是时域,频域,空间域?
 
上网查傅立叶变换的物理意义,没发现明确答案,倒发现了许多和我一样晕着问问题的人。结果又多出了许多名词,能量?功率谱?图像灰度域?……没办法又去翻那本教材。
 
1.1 一维傅立叶变换的定义与傅立叶积分定理
 
设f(x)是实变量x的函数,该函数可实可复,称积分

为函数f(x)的傅立叶变换。
 
吐血,啥是无穷积分来着?积分是啥来着?还能记起三角函数和差化积、积化和差公式吗?我忽然有种想把高中课本寻来重温的冲动。
 
卷积主要是为了将信号运算从时域转换为频域。
信号的时域的卷积等于频域的乘积。
利用这个性质以及特殊的δ函数可以通过抽样构造简单的调制电路
 
 
我比较赞同卷积的相关性的作用  在通信系统中的接收机部分MF匹配滤波器等就是本质上的相关
匹配滤波器最简单的形式就是原信号反转移位相乘积分得到的近似=相关
相关性越好得到的信号越强   这个我们有一次大作业做的  做地做到呕吐  呵呵
还有解调中一些东西本质就是相关
 

卷积公式  解释  卷积公式是用来求随机变量和的密度函数(pdf)的计算公式。  定义式:  z(t)=x(t)*y(t)= ∫x(m)y(t-m)dm.   已知x,y的pdf,x(t),y(t).现在要求z=x+y的pdf. 我们作变量替显,令  z=x+y,m=x. 雅可比行列式=1.那么,z,m联合密度就是f(z,m)=x(m)y(z-m)*1. 这样,就可以很容易求Z的在(z,m)中边缘分布  即fZ(z)=∫x(m)y(z-m)dm..... 由于这个公式和x(t),y(t)存在一一对应的关系。为了方便,所以记 ∫x(m)y(z-m)dm=x(t)*y(t)   长度为m的向量序列u和长度为n的向量序列v,卷积w的向量序列长度为(m+n-1),   u(n)与v(n)的卷积w(n)定义为: w(n)=u(n)@v(n)=sum(v(m)*u(n-m)),m from 负无穷到正无穷;   当m=n时w(1) = u(1)*v(1)   w(2) = u(1)*v(2)+u(2)*v(1)   w(3) = u(1)*v(3)+u(2)*v(2)+u(3)*v(1)   …   w(n) = u(1)*v(n)+u(2)*v(n-1)+ … +u(n)*v(1)   …   w(2*n-1) = u(n)*v(n)   当m≠n时,应以0补齐阶次低的向量的高位后进行计算  这是数学中常用的一个公式,在概率论中,是个重点也是一个难点。

  卷积公式是用来求随机变量和的密度函数(pdf)的计算公式。
  定义式:
  z(t)=x(t)*y(t)= ∫x(m)y(t-m)dm.
  已知x,y的pdf,x(t),y(t).现在要求z=x+y的pdf. 我们作变量替显,令
  z=x+y,m=x. 雅可比行列式=1.那么,t,m联合密度就是f(z,m)=x(m)y(z-m)*1. 这样,就可以很容易求Z的在(z,m)中边缘分布
  即fZ(z)=∫x(m)y(z-m)dm..... 由于这个公式和x(t),y(t)存在一一对应的关系。为了方便,所以记 ∫x(m)y(z-m)dm=x(t)*y(t)
 
卷积是一种线性运算,图像处理中常见的mask运算都是卷积,广泛应用于图像滤波。castlman的书对卷积讲得很详细。
高斯变换就是用高斯函数对图像进行卷积。高斯算子可以直接从离散高斯函数得到:
for(i=0; i<N; i++)
{
for(j=0; j<N; j++)
{
g[i*N+j]=exp(-((i-(N-1)/2)^2+(j-(N-1)/2)^2))/(2*delta^2));
sum += g[i*N+j];
}
}
再除以 sum 得到归一化算子
N是滤波器的大小,delta自选
首先,再提到卷积之前,必须提到卷积出现的背景。卷积是在信号与线性系统的基础上或背景中出现的,脱离这个背景单独谈卷积是没有任何意义的,除了那个所谓褶反公式上的数学意义和积分(或求和,离散情况下)。
信号与线性系统,讨论的就是信号经过一个线性系统以后发生的变化(就是输入输出和所经过的所谓系统,这三者之间的数学关系)。所谓线性系统的含义,就是,这个所谓的系统,带来的输出信号与输入信号的数学关系式之间是线性的运算关系。
因此,实际上,都是要根据我们需要待处理的信号形式,来设计所谓的系统传递函数,那么这个系统的传递函数和输入信号,在数学上的形式就是所谓的卷积关系。
卷积关系最重要的一种情况,就是在信号与线性系统或数字信号处理中的卷积定理。利用该定理,可以将时间域或空间域中的卷积运算等价为频率域的相乘运算,从而利用FFT等快速算法,实现有效的计算,节省运算代价
2019-08-09 09:32:50 jesseyule 阅读数 1080
  • (二)矩阵基本运算

    本课程由专业数学系老师讲解,从数学背景和现实应用中讲解线性代数的相关知识,摆脱传统的讲概念、记公式、解体的数学学习模式,让听众对线性代数有一个全新的、深刻的认识。使听众能够了解矩阵和空间的概念、性质。...

    825人学习 AI100讲师
    免费试看

在介绍卷积神经网络之前,我们需要先了解以下卷积运算和互相关运算。很多时候,我们都说卷积神经网络在图像处理方面具有很大的优势,主要原因就在于卷积运算,所以接下来就主要从图像处理和卷积的联系入手进行分析。

卷积运算在概率统计中关于随机变量的和的计算中也有出现,从数学上的定义来看,当我们有两个随机变量分别服从密度函数f和g,如果需要求它们的和t的概率密度,那么就有:

s(t)=fg=f(x)g(x+t)dxs(t) = f*g = \int _{-\infty} ^{\infty} f(x) g(-x+t)dx

这是一维的情况,对于二维,我们可以进一步推广:

s(u,v)=fg=f(x,y)g(x+u,y+v)dxdys(u,v) = f*g = \int _{-\infty} ^{\infty} \int _{-\infty} ^{\infty} f(x,y) g(-x+u, -y+v)dxdy

与卷积运算非常相似的还有互相关运算:

r(t)=fg=fu(x)fv(x+t)dxr (t) = f \bigodot g = \int _{-\infty} ^{\infty} f_u (x) f_v (x+t)dx

二维形式:

r(u,v)=fg=fu(x,y)fv(x+u,y+v)dxdyr (u,v) = f \bigodot g = \int _{-\infty} ^{\infty} \int _{-\infty} ^{\infty} f_u (x,y) f_v (x+u, y+v)dxdy

对比卷积运算和互相关运算公式,就可以发现它们形式上是多么相似,唯一的区别只是正负号,这个区别导致卷积运算涉及到函数翻转而互相关运算不涉及的原因,但这里不细说,而是把重点放在分析为什么这种形式的运算会和图像处理产生联系。在运用卷积神经网络进行图像处理时,原则上既可以使用卷积运算也可以使用互相关运算,两者的效果可以做到完全等同只是算法的细节处理上有所不同,而实际上出于简便性的考虑一般会采用互相关运算,所以接下来的分析主要从互相关运算入手,而后文提到的卷积实际上也是指互相关运算。

首先关于图像处理,我们来看这样一个例子,现在我们有一副黑白图像,用矩阵表示,黑色像素点为1,白色像素点为0:

00000100010001000000 \begin{matrix} 0 &amp; 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 0 &amp; 0 &amp; 0 \\ \end{matrix}
如果我们现在想要检测这幅图像有没有一条垂直的直线,应该怎么做,这里有一个方法,那就是构建一个反映特征的矩阵,或者说一个具有明显特征的图像矩阵:
010010010 \begin{matrix} 0 &amp; 1 &amp; 0 \\ 0 &amp; 1 &amp; 0 \\ 0 &amp; 1 &amp; 0 \\ \end{matrix}
然后,我们就可以把原图划分成一个个小块,对比是否具有类似我们分析的特征的区域,这个构成,也像我们人类观察的过程,比如我们找一幅图里面有没有眼镜,我们也需要一处处仔细观察,而不能一下子就看完整张图片。那么从数学的角度,这个过程就是原图的矩阵分成和特征矩阵同等大小的一个个小矩阵,比如从左上角开始划分,得到第一个小矩阵:

000010010 \begin{matrix} 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 \\ 0 &amp; 1 &amp; 0 \\ \end{matrix}
用它和特征矩阵按对应元素相乘得到:

000010010 \begin{matrix} 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 \\ 0 &amp; 1 &amp; 0 \\ \end{matrix}
r(1,1)=2r(1,1) = 2

按照这样遍历整副图像,最后把每次计算的结果写成矩阵形式,得到:

203020 \begin{matrix} 2 &amp; 0 \\ 3 &amp; 0 \\ 2 &amp; 0 \\ \end{matrix}
根据这个矩阵,我们可以确定阈值为3,达到阈值就认为原图像存在特征,然后就可以判断在原图像中间偏左的位置存在一条垂直直线。

以上的这个过程,其实就是卷积神经网络的核心思想,也是反映了卷积运算和图像处理的关系,其中,反映特征的矩阵称为卷积核,图像就是我们的输入,两者相乘得到的结果称为特征映射图。

还记得一开始说过,互相关运算反映了函数的平移,这种平移就体现在上面提到的卷积核对输入图像遍历过程中的移动,而互相关运算中的两个函数相乘就反映了输入图像和卷积核相乘,最后积分的步骤就反映了卷积核对输入图像的遍历,所以一次遍历得到一个输出结果,作为特征映射图的一个点的值。

想浏览更多关于数学、机器学习、深度学习的内容,可浏览本人博客

2019-05-04 08:59:07 zaishuiyifangxym 阅读数 1628
  • (二)矩阵基本运算

    本课程由专业数学系老师讲解,从数学背景和现实应用中讲解线性代数的相关知识,摆脱传统的讲概念、记公式、解体的数学学习模式,让听众对线性代数有一个全新的、深刻的认识。使听众能够了解矩阵和空间的概念、性质。...

    825人学习 AI100讲师
    免费试看

目录

1 图像顶帽运算(原始图像 — 开运算)

1.1 基本原理

1.2 代码示例

2 图像黑帽运算(闭运算 — 原始图像)

2.1 基本原理

2.2 代码示例

参考资料


前面介绍了 形态学处理——图像开运算与图像闭运算,其中:

图像开运算先腐蚀,后膨胀。一般会平滑物体的轮廓、断开较窄的狭颈并消除细的突出物。

图像闭运算先膨胀,后腐蚀。同样也会平滑轮廓的一部分。但与开操作相反,它通常会弥合较窄的间断和细长的沟壑,消除小的孔洞,填补轮廓线中的断裂。

 

1 图像顶帽运算(原始图像 — 开运算)

1.1 基本原理

图像顶帽(或图像礼帽)运算是原始图像减去图像开运算的结果,得到图像的噪声。如下图所示:

顶帽运算:原始图像 — 图像开运算

 

 

1.2 代码示例

图像顶帽运算使用函数 morphologyEx() ,其参数 cv2.MORPH_TOPHAT 对应顶帽运算。

morphologyEx() 函数形式如下:

dst = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)

其中,参数:

dst 表示处理的结果;

src 表示原图像;

cv2.MORPH_TOPHAT 表示顶帽运算;

kernel 表示卷积核。

例如,下图表示 5\times5 的卷积核,可以采用函数 np.ones((5,5), np.uint8) 构建。

 

(1)卷积核大小为 5\times

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np

#读取图片
src = cv2.imread('test5.bmp', cv2.IMREAD_UNCHANGED)

#设置卷积核
kernel = np.ones((5,5), np.uint8)

#图像闭运算
result = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)

#显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)

#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

 

运行结果如下图所示:

 

(2)卷积核大小为 15\times15 

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np

#读取图片
src = cv2.imread('test5.bmp', cv2.IMREAD_UNCHANGED)

#设置卷积核
kernel = np.ones((15,15), np.uint8)

#图像闭运算
result = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)

#显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)

#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

 

运行结果如下图所示:


 

2 图像黑帽运算(闭运算 — 原始图像)

2.1 基本原理

图像黑帽运算是图像闭运算操作减去原始图像的结果,得到图像内部的小孔,或者前景色中的小黑点。如下图所示:

黑帽运算:闭运算 — 原始图像

 

2.2 代码示例

图像黑帽运算使用函数 morphologyEx() ,其参数 cv2.MORPH_BLACKHAT 对应黑帽运算。

morphologyEx() 函数形式如下:

dst = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)

其中,参数:

dst 表示处理的结果;

src 表示原图像;

cv2.MORPH_BLACKHAT 表示黑帽运算;

kernel 表示卷积核。

例如,下图表示 5\times5 的卷积核,可以采用函数 np.ones((5,5), np.uint8) 构建。

 

(1)卷积核大小为 5\times5

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np

#读取图片
src = cv2.imread('test6.bmp', cv2.IMREAD_UNCHANGED)

#设置卷积核
kernel = np.ones((5,5), np.uint8)

#图像闭运算
result = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)

#显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)

#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

 

运行结果如下图所示:

 

 

(2)卷积核大小为 15\times15

代码如下所示:

#encoding:utf-8
import cv2
import numpy as np

#读取图片
src = cv2.imread('test6.bmp', cv2.IMREAD_UNCHANGED)

#设置卷积核
kernel = np.ones((15,15), np.uint8)

#图像闭运算
result = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)

#显示图像
cv2.imshow("src", src)
cv2.imshow("result", result)

#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

 

运行结果如下图所示:

 


 

参考资料

[1] https://blog.csdn.net/Eastmount/article/details/83692456

[2] Python+OpenCV图像处理

开运算—闭运算

阅读数 666