Bootstrap

java图像处理图片卷积浮雕化,数字图像处理:卷积的概念

本文部分内容引用自:http://blog.csdn.net/zouxy09/article/details/49080029(该博主写的比我好)

一、卷积的概念

在图像处理中,卷积操作指的是使用一个卷积核(kernel)对图像中的每一个像素进行一些列操作。

卷积核(算子)是用来做图像处理时的矩阵,图像处理时也称为掩膜,是于原图像做运算的参数。卷积核通常是一个方形的网格结构,该区域上的每一个方格都有一个权重值。如下面的sobel算子(用于求取图像边缘线算子)

soble算子:

a4c26d1e5885305701be709a3d33442f.png

二、卷积算法

卷积的计算过程为将卷积核的中心放置在要计算的像素上,然后计算卷积核中每个元素和其覆盖下的图像像素值的乘积并就和,得到的结果就是该位置的新像素值,注意要开另一buffer来存储得到的值,不可直接替换原图像的像素值。(因为如果直接替换的话会影响到其他像素值的计算)

a4c26d1e5885305701be709a3d33442f.png

其中h为卷积核(kernel)

步骤:

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

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

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

【例】

a4c26d1e5885305701be709a3d33442f.png

(在求和的过程中得到的结果可能超过了255或者低于0,那么可以将超过255的值当做255,低于0的值当做0来处理)

三、边缘处理

在处理过程中,遇到图像边缘怎么办?例如图像顶部的像素,它的上面已经没有像素了,那么它的值如何计算?目前有四种主流的处理方法,我们用一维卷积来说明。

我们在1D图像中,用每个像素和它的二邻域的平均值来取代它的值。假设我们有个1D的图像I是这样的:

a4c26d1e5885305701be709a3d33442f.png

对非图像边界的像素的操作比较简单。假设我们对I的第四个像素3做局部平均。也就是我们用2,3和7做平均,来取代这个位置的像素值。也就是,平均会产生一副新的图像J,这个图像在相同位置J

(4) = (I(3)+I(4)+I(5))/3 = (2+3+7)/3 = 4。同样,我们可以得到J(3) =

(I(2)+I(3)+I(4))/3 =(4+2+3)/3 = 3。需要注意的是,新图像的每个像素都取决于旧的图像,在计算J

(4)的时候用J (3)是不对的,而是用I(3),I(4)和I(5)。所以每个像素都是它和它邻域两个像素的平均。

对卷积,也有必须要考虑的情况是,在图像边界的时候,怎么办?J(1)的值应该是什么?它取决于I(0),I(1)和I(2)。但是我们没有I(0)呀!图像左边没有值了。有四种方式来处理这个问题:

1)第一种就是想象I是无限长的图像的一部分,除了我们给定值的部分,其他部分的像素值都是0。在这种情况下,I(0)=0。所以J(1)

= (I(0) + I(1) + I(2))/3 = (0 + 5 + 4)/3= 3. 同样,J(10) =

(I(9)+I(10)+I(11))/3 = (3+ 6 + 0)/3 = 3.

a4c26d1e5885305701be709a3d33442f.png

2)第二种方法也是想象I是无限图像的一部分。但没有指定的部分是用图像边界的值进行拓展。在我们的例子中,因为图像I最左边的值I(1)=5,所以它左边的所有值,我们都认为是5

。而图像右边的所有的值,我们都认为和右边界的值I(10)一样,都是6。这时候J(1) = (I(0) + I(1) + I(2))/3

= (5 + 5 + 4)/3= 14/3. 而J(10) = (I(9)+I(10)+I(11))/3 = (3 + 6 +

6)/3 = 5。

a4c26d1e5885305701be709a3d33442f.png

3)第三种情况就是认为图像是周期性的。也就是I不断的重复。周期就是I的长度。在我们这里,I(0)和I(10)的值就是一样的,I(11)的值和I(1)的值也是一样的。所以J(1)

= (I(0) + I(1) + I(2))/3= (I(10) + I(1)+ I(2))/3 = (6 + 5 + 4)/3 =

5 。

a4c26d1e5885305701be709a3d33442f.png

4)最后一种情况就是不管其他地方了。我们觉得I之外的情况是没有定义的,所以没办法使用这些没有定义的值,所以要使用图像I没有定义的值的像素都没办法计算。在这里,J(1)和J(10)都没办法计算,所以输出J会比原图像I要小。

这四种方法有各自的优缺点。如果我们想象我们使用的图像只是世界的一个小窗口,然后我们需要使用窗口边界外的值,那么一般来说,外面的值和边界上的值是几乎相似的,所以第二种方法可能更说得过去。

六、常见kernel

6.1、啥也不做

哈哈,大家可以看到啥了吗?这个滤波器啥也没有做,得到的图像和原图是一样的。因为只有中心点的值是1。邻域点的权值都是0,对滤波后的取值没有任何影响。

a4c26d1e5885305701be709a3d33442f.png

下面我们动点真格的。

6.2、图像锐化滤波器Sharpness Filter

图像的锐化和边缘检测很像,首先找到边缘,然后把边缘加到原来的图像上面,这样就强化了图像的边缘,使图像看起来更加锐利了。这两者操作统一起来就是锐化滤波器了,也就是在边缘检测滤波器的基础上,再在中心的位置加1,这样滤波后的图像就会和原始的图像具有同样的亮度了,但是会更加锐利。

a4c26d1e5885305701be709a3d33442f.png

我们把核加大,就可以得到更加精细的锐化效果

a4c26d1e5885305701be709a3d33442f.png

另外,下面的滤波器会更强调边缘:

a4c26d1e5885305701be709a3d33442f.png

主要是强调图像的细节。最简单的3x3的锐化滤波器如下:

a4c26d1e5885305701be709a3d33442f.png

实际上是计算当前点和周围点的差别,然后将这个差别加到原来的位置上。另外,中间点的权值要比所有的权值和大于1,意味着这个像素要保持原来的值。

6.3、边缘检测Edge Detection

我们要找水平的边缘:需要注意的是,这里矩阵的元素和是0,所以滤波后的图像会很暗,只有边缘的地方是有亮度的。

a4c26d1e5885305701be709a3d33442f.png

为什么这个滤波器可以寻找到水平边缘呢?因为用这个滤波器卷积相当于求导的离散版本:你将当前的像素值减去前一个像素值,这样你就可以得到这个函数在这两个位置的差别或者斜率。下面的滤波器可以找到垂直方向的边缘,这里像素上和下的像素值都使用:

a4c26d1e5885305701be709a3d33442f.png

再下面这个滤波器可以找到45度的边缘:取-2不为了什么,只是为了让矩阵的元素和为0而已。

a4c26d1e5885305701be709a3d33442f.png

那下面这个滤波器就可以检测所有方向的边缘:

a4c26d1e5885305701be709a3d33442f.png

为了检测边缘,我们需要在图像对应的方向计算梯度。用下面的卷积核来卷积图像,就可以了。但在实际中,这种简单的方法会把噪声也放大了。另外,需要注意的是,矩阵所有的值加起来要是0.

a4c26d1e5885305701be709a3d33442f.png

6.4、浮雕Embossing Filter

浮雕滤波器可以给图像一种3D阴影的效果。只要将中心一边的像素减去另一边的像素就可以了。这时候,像素值有可能是负数,我们将负数当成阴影,将正数当成光,然后我们对结果图像加上128的偏移。这时候,图像大部分就变成灰色了。

下面是45度的浮雕滤波器

a4c26d1e5885305701be709a3d33442f.png

我们只要加大滤波器,就可以得到更加夸张的效果了

a4c26d1e5885305701be709a3d33442f.png

这种效果非常的漂亮,就像是将一副图像雕刻在一块石头上面一样,然后从一个方向照亮它。它和前面的滤波器不同,它是非对称的。另外,它会产生负数值,所以我们需要将结果偏移,以得到图像灰度的范围。

a4c26d1e5885305701be709a3d33442f.png

a4c26d1e5885305701be709a3d33442f.png

A:原图像。B:锐化。C:边缘检测。D:浮雕

6.5、均值模糊Box Filter

(Averaging)

我们可以将当前像素和它的四邻域的像素一起取平均,然后再除以5,或者直接在滤波器的5个地方取0.2的值即可,如下图:

a4c26d1e5885305701be709a3d33442f.png

可以看到,这个模糊还是比较温柔的,我们可以把滤波器变大,这样就会变得粗暴了:注意要将和再除以13.

a4c26d1e5885305701be709a3d33442f.png

所以,如果你想要更模糊的效果,加大滤波器的大小即可。或者对图像应用多次模糊也可以。

a4c26d1e5885305701be709a3d33442f.png

a4c26d1e5885305701be709a3d33442f.png

2.6、高斯模糊

均值模糊很简单,但不是很平滑。高斯模糊就有这个优点,所以被广泛用在图像降噪上。特别是在边缘检测之前,都会用来移除细节。高斯滤波器是一个低通滤波器。

a4c26d1e5885305701be709a3d33442f.png

a4c26d1e5885305701be709a3d33442f.png

2.7、运动模糊Motion Blur

运动模糊可以通过只在一个方向模糊达到,例如下面9x9的运动模糊滤波器。注意,求和结果要除以9。

a4c26d1e5885305701be709a3d33442f.png

这个效果就好像,摄像机是从左上角移动的右下角。

七、参考代码

可以利用OpenCV提供的filter2D函数完成对图像进行卷积操作,其函数接口为:

1

2

3

4

5

6

7

8

CV_EXPORTS_W void filter2D(

InputArray src, OutputArray dst, int ddepth,

InputArray kernel, Point anchor=Point(-1,-1),

double delta=0, int borderType=BORDER_DEFAULT );

第一个参数: 输入图像 第二个参数: 输出图像,和输入图像具有相同的尺寸和通道数量 第三个参数: 目标图像深度,输入值为-1时,目标图像和原图像深度保持一致。 第四个参数: 卷积核,是一个矩阵 第五个参数:内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。 第五个参数: 在储存目标图像前可选的添加到像素的值,默认值为0 第六个参数: 像素向外逼近的方法,默认值是BORDER_DEFAULT

NormalText

Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

int main()

{

Mat srcImage = imread("1.jpg");

namedWindow("srcImage", WINDOW_AUTOSIZE);

imshow("原图", srcImage);

Mat kernel = ( -1, 0 ,1,

-2, 0, 2,

-1, 0, 1); Mat dstImage; filter2D(srcImage,dstImage,srcImage.depth(),kernel); //进行卷积操作

namedWindow("dstImage",WINDOW_AUTOSIZE); imshow("卷积图",dstImage);

waitKey(0);

return 0;

}

;