`
ihuashao
  • 浏览: 4562763 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

Learning OpenCV (第6.2节)—— 卷积

阅读更多

Convolution 卷积<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

卷积是本章所讨论的很多转换的基础。抽象的说,这个术语意味着我们对图像的每一个部分所做的操作。从这个意义上讲,我们在第五章所看到的许多操作可以被理解成普通卷积的特殊情况。一个特殊的卷积所实现的功能是由所用的卷积核的形式决定的。这个核本质上是一个大小固定,由数值参数构成的数组,数组的标定点通常位于数组的中心。数组的大小被称为核支撑。单就技术而言,核支撑实际上仅仅由核数组的非零部分组成。

6-1描述了以数组中心为定标点的3×3卷积核。若要计算一个特定点的卷积值,首先将核的标定点定位到图像的第一个像素点,核的其余元素覆盖图像中其相对应的局部像素点。对于每一个核点,我们可以得到这个点的核的值以及图像中相应图像点的值。将这些值相乘并求和,并将这个结果放置在与输入图像标定点所相对应的位置。 通过在整个图像上扫描卷积核,对图像的每个点重复此操作。

6-1. Sobel 微分的3×3核,可以注意到标定点在核的中心

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="图片_x0020_1" style="VISIBILITY: visible; WIDTH: 119.25pt; HEIGHT: 119.25pt" alt="getfile?item=NjNmNTZnOWRpLzkvdGFwZ203ZWNzODByLzE1MTBpczZnLm01cG4vZ2kx" type="#_x0000_t75" o:spid="_x0000_i1025"><imagedata o:title="getfile?item=NjNmNTZnOWRpLzkvdGFwZ203ZWNzODByLzE1MTBpczZnLm01cG4vZ2kx" src="file:///D:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msoclip1/02/clip_image001.png"></imagedata><textbox style="mso-rotate-with-shape: t"></textbox></shape>

当然我们可以用方程来表示这个过程,如果我们定义图像为I(x,y),核为G(i,j) (其中 0 < i < Mi –1 0 < j < Mj –1),标定点位于相应核的(ai,aj)坐标上,则卷积H(x,y)定义为:

<shape id="图片_x0020_2" style="VISIBILITY: visible; WIDTH: 315.75pt; HEIGHT: 39.75pt" alt="getfile?item=NjNmNTZnOWRpLzkvdGFwZ203ZWNzODByLzE1MTBpczZnLm03cG4vZ2kx" type="#_x0000_t75" o:spid="_x0000_i1026"><imagedata o:title="getfile?item=NjNmNTZnOWRpLzkvdGFwZ203ZWNzODByLzE1MTBpczZnLm03cG4vZ2kx" src="file:///D:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msoclip1/02/clip_image003.png"></imagedata><textbox style="mso-rotate-with-shape: t"></textbox></shape>

注意到运算次数,至少第一眼看似乎等于图像的像素数乘以核的像素数[63]。这需要很大的计算量并且也不是仅仅用其中的一些for循环以及许多指针再分配就能做的事情。类似这种情况,你最好让OpenCV来做这个工作以利用OpenCV已编程实现的最优方法。其函数为cvFilter2D ();

[63]这里我们说“第一眼看”的意思是在频域中也可能进行卷积操作。在这种情况下,对于一个N×N的图像和一个M×M的核(N>M),计算复杂度将会按照N2 log(N)成比例增加,而不是在空间域内预计的N2M2。这是因为频域的计算量同核的大小是相对独立的,对于大核更加有效。OpenCV会根据核的大小自动决定是否做频域内的卷积。

void cvFilter2D(

const CvArr* src,

CvArr* dst,

const CvMat* kernel,

CvPoint anchor = cvPoint(-1,-1)

);

这里我们创建一个适当大小的矩阵,将系数连同原图像和目标图像一起传递给cvFilter2D()。我们还可以有选择地输入一个CvPoint指出核的中心位置,但默认值(cvPoint(-1,-1))就会被认为是核的中心。如果定义了标定点,核的大小可以是任意偶数尺寸,否则大小就是奇数。

原图像src和目标图像dst大小应该是相同的,有些人可能认为考虑到卷积核的额外的长和宽,原图像src应该大于目标图像dst。但是在OpenCV里原图像src和目标图像dst的大小是可以一样的,因为在默认情况下,在卷积之前,OpenCV通过复制原图像src的边界创建了虚拟像素,这样以便于目标图像dst边界的像素可以被填充。复制是通过input(–dx, y) = input(0, y), input(w + dx, y) = input(w – 1, y)等实现的,还有一些可以替换此默认行为的方法,我们将在下一节讨论。

提示一下,这里我们所讨论的卷积核的系数应该是浮点类型的,这就意味着我们必须用CV_32F来初始化矩阵。

做卷积时自然出现的一个问题是如何处理卷积边界。例如,在使用刚才所讨论的卷积核时,当卷积点在图像边缘时会发生什么?许多使用cvFilter2D()OpenCV内置函数必须用各种方式来解决这个问题。同样在你做卷积时,有必要知道如何有效解决这个问题。这个解决方法就是使用cvCopyMakeBorder()函数,它可以将特定的图像轻微变大,然后以各种方式自动填充图像边界。

void cvCopyMakeBorder(

const CvArr* src,

CvArr* dst,

CvPoint offset,

int bordertype,

CvScalar value = cvScalarAll(0)

);

Offset变量告诉cvCopyMakeBorder()将原图像的副本放到目标图像中什么位置。典型情况是,如果核为N×N(N为奇数)时,那么边界在每一侧的宽度都应是 (N – 1)/2,即这幅图像比原图像宽或高N – 1。在这种情况下,可以把Offset设置为cvPoint((N-1)/2,(N-1)/2),使得边界在每一侧都是偶数。[64]

[64]当然,标定点在中心、N×N并且N是奇数时的情形是最简单的。在一般情况下,如果核是N×M并且标定点在(ax,ay),那么目标图像将比原图像宽N-1,高M-1个像素。Offset的值仅仅是(ax,ay)

Bordertype既可以是IPL_BORDER_CONSTANT,也可以是IPL_BORDER_REPLICATE(见图6-2)。在第一种情况下,value变量被认为是所有在边界的像素应该设置的值。在第二种情况下,原始图像边缘的行和列被复制到大图像的边缘。注意到测试的模板图像边缘是比较精细的(注意图6-2右上角的图像)。在测试的模板图像中,除了在圆图案边缘附近的像素变白外,有一个像素宽的黑色边界。这里定义了另外两种边界类型,IPL_BORDER_REFLECT IPL_BORDER_WRAP,目前还没有被OpenCV所实现,但以后可能会在OpenCV中实现。

6-2 扩大的图像边界,左边一列显示的是IPL_BORDER_CONSTANT,边界是用零值填充的,右面一列是IPL_BORDER_REPLICATE,在水平和垂直两个方向复制边界像素。

<shape id="图片_x0020_3" style="VISIBILITY: visible; WIDTH: 200.25pt; HEIGHT: 151.5pt" alt="getfile?item=NjNmNTZnOWRpLzkvdGFwZ203ZWNzODByLzE1MTBpczZnLm05cG4vZ2kx" type="#_x0000_t75" o:spid="_x0000_i1027"><imagedata o:title="getfile?item=NjNmNTZnOWRpLzkvdGFwZ203ZWNzODByLzE1MTBpczZnLm05cG4vZ2kx" src="file:///D:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msoclip1/02/clip_image005.png"></imagedata><textbox style="mso-rotate-with-shape: t"></textbox></shape>

我们在前面已经提到,当调用OpenCv库函数中的卷积功能时,cvCopyMakeBorder()函数就会被调用。在大多数情况下,边界类型为IPL_BORDER_REPLICATE,但有时并不希望用它。所以在另一种场合,可能用到cvCopyMakeBorder()。你可以创造一幅具有比想要得到的边界稍微大一些的图像,无论调用任何常规操作,接下来就可以剪切到对原图像所感兴趣的部分。这样一来, OpenCV的自动加边就不会影响所关心的像素。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics