tensorflow实现对彩色图像的均值滤波

Table of Contents

方法1:

方法2(不可行):

方法3:

总结:

 


想把输入神经网络的图像进行均值滤波处理。在opencv中当然有相关的操作函数,但是考虑到tensorflow的卷积操作可以用GPU加速,所以我想用tensorflow的卷积来实现图像的均值滤波。

首先,我找了tf.image模块,看有没有相关函数,发现没有。

然后,我又考虑到可以通过设置特定的卷积核来通过tf的卷积操作实现均值滤波。tensorflow设定特定卷积核的方法见:博客,在测试过程中发现以下问题:

方法1:

1. 通过设定特定的卷积核, tensorflow可以方便的实现单通道灰度图的均值滤波。这理所应当。

2. 根据上一条,我们可以把一个3通道的图像拆分成3个单通道的灰度图像,然后逐个灰度图像利用上一条的方式进行均值滤波,最后把均值滤波后的三个灰度图像再沿channel维度拼接起来,这就能完成一个彩色图像的均值滤波了。

方法2(不可行):

第一个方法有些麻烦,能不能使用tf的卷积层直接在彩色图像上操作(使卷积核的输入通道数=输出通道数=3),实现彩色图像的均值滤波呢?很遗憾,不能。下面介绍为什么:

令批量图像数据的shape=[N,H,W,C], 卷积层的卷积核的shape=[h,w,c,k],其中h,w代表卷积核的高度与宽度,c代表卷积核的通道数,通常要求c=C,使图像数据的每个channel(或切片)都有一个卷积核的channel(即切片)与之对应。除非特殊设定,一般卷积核的不同channel是不同的,即图像数据的不同channel对应的卷积核切片是不同的。k代表卷积核的个数。

在卷积操作中,k个卷积核是相互独立的,每个卷积核的不同通道也是相互独立的,仅对自己负责的图像切片进行处理。我们先描述每个卷积核的工作。在执行conv2d卷积时,卷积核的每个channel在对应的图像channel上进行卷积,在卷积完成后,conv2d会自动进行一个求和操作,即会将c个channel的卷积结果计算加和,所以每个卷积核(不论channel是多少)仅仅可以得到一个单一channel的图像数据。

进一步,k个卷积核就会得到k个单一channel的图像数据,最后conv2d把k个单一channel的图像数据沿channel维度拼接(cascade),得到卷积的输出结果,是一个k个channel的张量数据。

以上是conv2d的工作原理。借此我们可以得到结论:我们期望的彩色图像的均值滤波是每个通道独立的进行均值滤波,操作完后不同通道的处理结果仍旧按照原来的方式拼接。 但是conv2d操作会在对每个通道独立进行均值滤波之后,自动把多个通道的数据求和,合并成一个通道输出,这不是我们需要的。所以直接使用conv2d进行彩色图像的均值滤波操作得不到我们预期的效果。即使输出通道数目k=3也不是我们想要的结果。

方法3:

这可能是tf.nn.conv2d的一个bug,也可能是一个没有被说明的特殊设定。

如在方法2中描述的,在conv2d中,一般要求卷积核的通道数与输入数据的通道数相等,即c=C,在实际应用中也是这样,如果不符合这一要求会报错,但是我发现存在一个例外情况:

当输入数据的C等于卷积核的个数k,与此同时,每个卷积核的通道数c=1时,(即根据以上的变量定义,C=k,c=1时)conv2d不会报错。执行的应该是:给每个输入图像的channel分配一个独立的、相同的卷积核,然后在把每个通道的卷积结果按照原来的方式组装返回。测试实验如下:

import numpy as np
import tensorflow as tf
import cv2

blur_size = 3

# 读取图像
img = np.ones((1,5, 5, 8), dtype=np.float32) # 通道数为8

mean_filter = tf.ones((blur_size, blur_size, 1, 8), dtype=tf.float32) / (blur_size * blur_size)
img_mean_tf = tf.nn.conv2d(img, filter=mean_filter, strides=[1, 1, 1, 1], padding='SAME')

with tf.Session() as sess:
	img_mean_tf = sess.run(img_mean_tf)[0, :, :, :]

	print("img_mean_tf:\n",img_mean_tf[:,:,0])
    print("img_mean_tf:\n",img_mean_tf[:,:,3])

实验结果:
img_mean_tf[0]:
 [[0.44444445 0.6666667  0.6666667  0.6666667  0.44444445]
 [0.6666667  1.         1.         1.         0.6666667 ]
 [0.6666667  1.         1.         1.         0.6666667 ]
 [0.6666667  1.         1.         1.         0.6666667 ]
 [0.44444445 0.6666667  0.6666667  0.6666667  0.44444445]]
img_mean_tf[3]:
 [[0.44444445 0.6666667  0.6666667  0.6666667  0.44444445]
 [0.6666667  1.         1.         1.         0.6666667 ]
 [0.6666667  1.         1.         1.         0.6666667 ]
 [0.6666667  1.         1.         1.         0.6666667 ]
 [0.44444445 0.6666667  0.6666667  0.6666667  0.44444445]]

根据实验结果可见,tensorflow没有报错,且成功执行了均值滤波操作,返回的结果也是正确的。

下面是在lena图像上进行的均值滤波对比实验(tf方法与opencv方法对比): 

import numpy as np
import matplotlib.pyplot as plt
import cv2


blur_size = 9

# 读取图像
filename = "./lena.jpeg"
img = cv2.imread(filename)[:,:,::-1]/255.0

# 定义均值滤波操作
input = np.expand_dims(img,axis=0)
mean_filter = tf.ones((blur_size, blur_size, 1, 3), dtype=tf.float64)/(blur_size*blur_size)
img_mean_tf = tf.nn.conv2d(input,filter=mean_filter,strides=[1,1,1,1],padding='VALID')

with tf.Session() as sess:
	img_mean_tf = sess.run(img_mean_tf)[0,:,:,:]
	img_mean_cv2 = cv2.blur(img, (blur_size, blur_size))[4:-4,4:-4,:] # 采用了切片操作,使得img_mean_cv2的尺寸与img_mean_tf相同,因为img_mean_tf使用了padding="VALID
	
	print(img_mean_tf.shape)
	print(img_mean_cv2.shape)
	print(np.sum(abs(img_mean_cv2 - img_mean_tf)))

	plt.subplot(131)
	plt.title("input img")
	plt.imshow(img)

	plt.subplot(132)
	plt.title("img_mean_tf")
	plt.imshow(img_mean_tf)

	plt.subplot(133)
	plt.title("img_mean_cv2")
	plt.imshow(img_mean_cv2)

	plt.show()

实验结果:

img_mean_tf.shape= (192, 192, 3)
img_mean_cv2.shape= (192, 192, 3)
differ_sum= 3.789497882156212e-11

结果分析:

可见tensorflow取得了与opencv非常相似的结果,二者的差距小到可以忽略。所以,我们可以利用tensorflow的这一特点进行均值滤波。

需要注意的是:opencv在计算均值滤波时会自动对图像进行padding操作,使输出图像与输入图像shape相同。这个padding过程是进行了优化的,不是tensorflow的简单的用0填充,所以,如果在tensorflow中使用padding=“SAME”,可能最后得到的differ_sum数据会比较大,大概在1165左右,差异主要在padding的位置。在实际应用中,这并不会对我们带来什么影响。

总结:

本文提供了3种用tensorflow进行彩色图像均值滤波的方法,其中两种可行,第一种方法略显繁琐,第三种方法有些另辟蹊径。供大家参考。或许真遇到这种情况,可能用opencv的方法更好? 我试了试,有点忍受不了用opencv处理时的训练速度。另外,这里用的是tf.nn.conv2d而没有用tf.keras等高级封装,因为tf.nn.conv2d可以方便的定制卷积核,而高级封装里这一点很不方便,或者不知道从哪里下手。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值