Tensorflow图像处理

图像处理

图像数据处理

在前面的实验中,使用了卷积神经网络训练图像。但是在很多图像识别的问题中,相同物体在不同的亮度、对比度下的差别非常大,但这些因素都不应该影响最后的识别结果。此次实验将对图像数据进行预处理,使得训练得到的神经网络模型尽可能小地被无关因素所影响。

TFRecord输入数据格式

TensorFlow提供了一种统一的格式来存储数据,就是TFRecord。在上一个实验中,使用了一个从类别名称到所有数据列表的词典来维护图像和类别的关系。但是这种方式的可扩展性非常差,当数据来源更加复杂、每个样例中的信息更加丰富之后,这种方式很难有效地记录输入数据中的信息,于是采用TensorFlow提供的TFRecord的格式来统一输入数据的格式。

TFRecord格式

TFRecord 文件中的数据都是通过 tf.train.Example 以 Protocol Buffer(以下简称PB) 的格式存储。PB是Google的一种数据交换的格式,他独立于语言,独立于平台,以二进制的形式存在。它能更好的利用内存,方便复制和移动。
下面给出tf.train.Example的定义:

message Example {  
Features features = 1;
};

message Features{
map<string,Feature> featrue = 1;
};

message Feature{
oneof kind{
BytesList bytes_list = 1;
FloatList float_list = 2;
Int64List int64_list = 3;
}
};

从代码中我们可以看出, tf.train.Example 包含了一个字典,key是字符串,value为Feature,Feature可以取值为字符串(BytesList )、浮点数列表(FloatList )、整型数列表(Int64List )。

TFRecord样例

下面,使用样例,将MNIST输入数据转化为TFRecord的格式。

写入TFRecord文件主要分为下面几个步骤:

  • 获取需要转换的数据
  • 将数据填入Example PB,并将Example PB转换为一个字符串
  • 通过tf.python_io.TFRecordWriter将字符串写入TFRecord文件中。
#coding:utf-8
#将MNIST输入数据转化为TFRecord的格式

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np

#把传入的value转化为整数型的属性,int64_list对应着 tf.train.Example 的定义
def _int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

#把传入的value转化为字符串型的属性,bytes_list对应着 tf.train.Example 的定义
def _bytes_feature(value):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

#读取MNIST数据
mnist =input_data.read_data_sets("/home/sun/AI/CNN/handWrite1/data", dtype=tf.uint8, one_hot=True)
#训练数据的图像,可以作为一个属性来存储
images = mnist.train.images
#训练数据所对应的标签,可以作为一个属性来存储
labels = mnist.train.labels
#训练数据的图像分辨率,可以作为一个属性来存储
pixels = images.shape[0]
#训练数据的数量
num_examples = mnist.train.num_examples
#指定要写入TFRecord文件的地址
filename = "./TFRecord/output.tfrecords"

#创建一个write来写TFRecord文件
writer = tf.python_io.TFRecordWriter(filename)
for index in range(num_examples):
#把图像矩阵转化为字符串
image_raw = images[index].tostring()
#将一个样例转化为Example Protocol Buffer,并将所有的信息写入这个数据结构
example = tf.train.Example(features=tf.train.Features(feature={
'pixels': _int64_feature(pixels),
'label': _int64_feature(np.argmax(labels[index])),
'image_raw': _bytes_feature(image_raw)}))
#将 Example 写入TFRecord文件
writer.write(example.SerializeToString())

writer.close()

以上代码可以将MNIST数据集中的所有训练数据存储到一个TFRecord文件中。当数据量较大时,可以写入到多个TFRecord文件。
接下来读取TFRecord文件:

#读取TFRecord文件中的数据

import tensorflow as tf

#创建一个reader来读取TFRecord文件中的样例
reader = tf.TFRecordReader()
#通过 tf.train.string_input_producer 创建输入队列
filename_queue = tf.train.string_input_producer(["./TFRecord/output.tfrecords"])
#从文件中读取一个样例
_, serialized_example = reader.read(filename_queue)
#解析读入的一个样例
features = tf.parse_single_example(
serialized_example,
features={
#这里解析数据的格式需要和上面程序写入数据的格式一致
'image_raw': tf.FixedLenFeature([], tf.string),
'pixels': tf.FixedLenFeature([], tf.int64),
'label': tf.FixedLenFeature([], tf.int64),
})
#tf.decode_raw可以将字符串解析成图像对应的像素数组
images = tf.decode_raw(features['image_raw'], tf.uint8)
#tf.cast可以将传入的数据转化为想要改成的数据类型
labels = tf.cast(features['label'], tf.int32)
pixels = tf.cast(features['pixels'], tf.int32)

sess = tf.Session()
#启动多线程处理输入数据
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

#每次运行可以读取TFRecord文件中的一个样例。当所有样例都读完之后,在此样例中的程序会从头读取
for i in range(10):
image, label, pixel = sess.run([images, labels, pixels])
print(label)

图像数据处理

在之前的实验中,都是直接使用图像的原始像素矩阵输入模型,本次实验会通过对图像的预处理,来尽可能避免模型受到无关因素的影响。大多数情况下,图象的预处理过程可以提高模型的准确率。

TensorFlow图像处理函数

图像编码处理

RGB色彩模式的图像可以看成一个三维矩阵,矩阵中的每一个数表示了图像上的不同位置,不同颜色的亮度。然而图像在存储时并不是直接记录这些矩阵中的数字,而是记录经过压缩编码之后的结果。所以要将一张图像还原成一个三维矩阵,需要解码的过程。Tensorflow提供了对jpeg和png格式图像的编码/解码函数。以下代码示范了如何使用Tensorflow中对jpeg格式图像的编码/解码函数:
以下面这张图片为例:



import matplotlib.pyplot as plt
import tensorflow as tf


image_raw_data = tf.gfile.FastGFile("/path/to/picture", 'rb').read()
with tf.Session() as sess:
img_data = tf.image.decde_jpeg(image_raw_data)
print(img_data.eval())
# 输出解码后的三维矩阵,打印的内容为:
'''
[[[252 252 252]
...
[243 244 238]]

[[252 252 252]
...
[243 244 238]]

[[252 252 252]
...
[243 244 238]]

...

[[249 249 249]
...
[249 250 245]]

[[249 249 249]
...
[249 250 245]]

[[249 249 249]
...
[249 250 245]]]


'''
plt.imshow(img_data.eval())
plt.show()
img_data = tf.image.convert_image_dtype(img_data, dtype=tf.float32)

plt.show得到图像:



接下来以这张图像为例,对 图像进行简单变换。

图像大小调整

神经网络输入节点的个数是固定的,所以在将图像的像素作为输入提供给神经网络之前,需要先将图像的大小统一。这就是图像大小调整需要完成的任务。

图像大小调整有两种方式,第一种是通过算法使得新的图像尽量保存原始图像上的所有信息。

resized = tf.image.resize_images(img_data, [300, 300], method=0)

其中img_data是上一步中已经解码后的图像数据。

Tensorflow提供了四种不同的方法,并且将它们封装到了tf.image.resize_image函数:method参数给出了调整图像大小的算法。

  1. method = Bilinear interpolation双线性插值法
  2. method = Nearest neighbor interpolation最近邻法
  3. method = Bicubic interpolation双三次插值法
  4. method = Area interpolation面积插值法

经过裁剪填充之后,得到下图:




从图中可以看出不同的算法产生的结果会有细微的区别。除了将整张图像信息完整保存,tensorflow还提供了API对图像进行裁剪或者填充。

图像的裁剪和填充

croped = tf.image.resize_image_with_crop_or_pad(img_data, 1000, 1000)
padded = tf.image.resize_image_with_crop_or_pad(img_data, 3000, 3000)

生成的图像如下所示:



通过tf.image.central_crop函数可以按比例裁剪图像,这个函数的第一个参数为原始图像,第二个为调整比例,这个需要是一个(0,1]的实数。

上面介绍的图像裁剪函数都是截取或者填充图像中间的部分。Tensorflow也提供了tf.image.crop_to_bounding_box函数和tf.image.pad_to_bounding_box函数来裁剪或者填充给定区域的图像。

图像翻转

Tensorflow提供了一些函数来支持对图像的翻转。以下代码实现了将图像上下翻转、左右翻转已经沿对角线翻转的功能:

import tensorflow as tf

# 将图像上下翻转
flipped_up_down = tf.image.flip_up_down(img_data)
# 将图像左右翻转
flipped_left_right = tf.image.flip_left_right(img_data)
# 将图像沿对角线翻转
transposed = tf.image.transpose_image(img_data)

得到的图像如下所示:



在很多图像识别问题中,图像的翻转不会影响识别的结果。于是在训练图像识别的神经网络模型时,可以随机地翻转训练图像,这样训练得到的模型就可以识别不同角度的实体。比如假设在训练数据中所有的猫头都是向右的,那么训练出来的模型就无法很好的识别猫头向左的猫。虽然这个问题可以通过收集更多的训练数据来解决,但是通过随机翻转训练图像的方式可以在零成本的情况下很大程度地缓解该问题。所以随机翻转训练图像是一种很常用的图像预处理方式。Tensorflow提供了方便的API完成随机图像翻转的过程。

# 以一定概率上下翻转图像
flipped = tf.image.random_flip_up_down(img_data)
# 以一定概率左右翻转图像
flipped = tf.image.random_flip_left_right(img_data)

图像色彩调整

和图像翻转类似,调整图像的亮度、对比度、饱和度和色相在很多图像识别应用中都不会影响识别结果。所以在训练神经网络模型时,可以随机调整训练图像的这些属性,从而使得训练得到的模型尽可能小地受到无关因素的影响。

  • 调整图像亮度
# 将图像的亮度-0.5
adjusted = tf.image.adjust_brightness(img_data, -0.5)
# 将图像的亮度+0.5
adjusted = tf.image.adjust_brightness(img_data, 0.5)
#在[-max_delta, max_delta]的范围随机调整图像的亮度
adjusted = tf.image.random_brightness(img_data, max_delta)

将max_delta设置为1后,调整后所得图像为:



  • 调整图像对比度
# 将图像的对比度-5
adjusted = tf.image.adjust_contrast(img_data, -5)
# 将图像的对比度+5
adjusted = tf.image.adjust_contrast(img_data, 5)
# 在[lower, upper]的范围随机调整图的对比度
adjusted = tf.image.random_contrast(img_data, lower, upper)

将lower, upper设置为0, 5后(lower不能为负数),调整后所得图像为:



  • 调整图像色相
# 将图像的色相加0.1
adjusted = tf.image.adjust_hue(img_data, 0.1)
# 将图像的色相加0.6
adjusted = tf.image.adjust_hue(img_data, 0.6)
# 在[0, max_delta]的范围随机调整图像的色相
adjusted = tf.image.random_hue(img_data, max_delta)

将max_delta设置为0.5后,得到的结果为:



  • 调整图像饱和度
# 将图像的饱和度-5
adjusted = tf.image.adjust_saturation(img_data, -5)
# 将图像的饱和度+5
adjusted = tf.image.adjust_saturation(img_data, 5)
# 在[lower, upper]的范围随机调整图的饱和度
adjusted = tf.image.random_saturation(image, lower, upper)

将lower, upper设置为0, 5后(lower不能为负数),调整后所得图像为:



图像标准化

# 将代表一张图像的三维矩阵中的数字均值变为0,方差变为1
adjusted = tf.image.per_image_standardization(image)

标准化后和原图对比:



处理标注框

在很多图像识别的数据集中,图像中需要关注的物体通常会被标注框圈出来。下面这段代码展示了如何通过tf.image.draw_bounding_boxes函数在图像中加入标注框。

Author: NYY
Link: http://yoursite.com/2018/12/06/tensorflow/5.1Image_processing/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.