对图像进行增强处理的函数input,相关操作包括:随机裁剪、随机翻转、随机调整对比度、随机调整亮度以及标准化和归一化操作,目的在于能够产生更多训练样本。在大部分图像识别问题中,通过图像数据预处理可以尽量避免模型受到无关因素的影响或者达到数据增强的目的,从而提高模型的准确率,TensorFlow提供了几类简单的图像处理函数。
本节会用到一张分辨率为895x560的.png格式的图像。所有的代码为了展示图像处理的效果都用到了matplotlib.pyplot工具,这是Python的画图工具,接下来会对几个常用到的图像处理API(预定义的函数)详细介绍。
1、图像编解码处理
一副彩色的RGB图像可以看做是一个三维矩阵,矩阵中不同位置的数据表示图像不同位置颜色的亮度。然而图像在储存的时候不会直接存储为矩阵数据,而是记录经过编码后的结果,所以要将一副图像还原为一个三维矩阵,需要解码的过程,接下来的裁剪、翻转、调整色度等操作都是在图像解码操作为基础进行的。
图像一般会编码存储为.jpeg、.jpg、.png和.gif等格式,TensorFlow提供了一些函数实现对这些格式图像的编码\解码操作的支持
下面的样例展示了使用函数对.png格式图片进行解码/编码的过程:
API1:tf.gfile.FastGFile(path,decodestyle) 图片读取
1、函数功能:实现从存储的文件当中对图片的读取。
2、函数参数:
decodestyle:图片的解码方式。(‘r’:UTF-8编码; ‘rb’:非UTF-8编码)
UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。是一种编码方式,UFT-8是Unicode编码方式的一种。
API2:tf.image.decode_png(contents, channels=None, name=None) .png图片的解码函数
1、函数功能:TensorFlow提供了decode_png()函数将.png格式的图像解码从而得到图像对应的三维矩阵,数据类型为uint8。
2、函数参数:
3、其他解码函数
API3:tf.image.encode_png(image)
1、函数功能:表示一张图片的三维矩阵重新按照png格式编码并存入文件中
2、函数参数:
3、其他编码函数
import matplotlib.pyplot as plt
import tensorflow as tf
# 读取原始的图像,实现对图片的读取,读取到内存当中
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
# 输出解码之后的三维矩阵,并调用pyplot工具可视化得到的图像
print(img_after_decode.eval())
plt.imshow(img_after_decode.eval())#负责处理
plt.show()#负责执行
# 这一句是为了方便后续的样例程序对图像进行处理
# img_after_decode = tf.image.convert_image_dtype(img_after_decode,dtype = tf.float32)
# TensorFlow提供了encode_png()函数将解码后的图像进行再编码
# 表示一张图片的三维矩阵重新按照png格式编码并存入文件中。在资源管理器中打开这张图像,可以得到和原始图像一样的图像
encode_image = tf.image.encode_png(img_after_decode)
with tf.gfile.GFile("cat.png", "wb") as f:
f.write(encode_image.eval())
2、翻转图像
在数据增强的过程中,使用了image.random_flip_left_right()函数对图像进行随机左右翻转,这是image.py中的函数。
使用翻转函数的目的在于能够使得训练出来的网络具有识别经翻转后图像的能力。
API4 : tf.image.random_flip_left_right( image,seed=None) 随机左右翻转函数
1、函数功能:按水平 (从左向右)方向 随机翻转图像,将图像左右翻转的概率为0.5,即输出的图像有50%的可能性是上左右翻转的,否则就输出原图.
2、函数参数:
其他翻转函数:
图像固定左右翻转: lipped2 = tf.image.flip_left_right(image)
随机上下翻转图片: image.random_flip_up_down(image,seed)
图像固定上下翻转: image.flip_up_down(image)
图像固定对角线翻转: image.transpose_image(image)
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
#decode
img_after_decode = tf.image.decode_png(image)
# 以一定概率左右翻转图片。
# 函数原型为random_flip_left_right(image,seed)
flipped = tf.image.random_flip_left_right(img_after_decode)
# 用pyplot工具显示
plt.imshow(flipped.eval())
plt.show()
3、图像色彩调整
图像色彩的调整包括对图像的亮度、对比度、饱和度和色相方面的调整,训练神经网络模型时随机调整这些属性,目的是使经过训练的模型尽可能小地受到这些与图像识别无关因素的影响,使CNN模型更加稳定。
* 亮度调整
API 5:tf.image.random_brightness(image, max_delta,seed=None) 随机调整亮度函数
1、函数功能:在某范围[-max_delta, max_delta)中随机选取的delta,来随机调整图片亮度,公式为:????????=????????+?????newimage=oldimage+delta
2、函数参数:
其他亮度调整函数(固定调整亮度):
tf.image.adjust_brightness(image,delta)
说明:指定参数delta,参数为正值则图像的亮度会增加,为负值则图像的亮度会降低
#随机调整亮度 tf.image.random_brightness
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
#函数原型random_brightness(image,max_delta,seed)
#max_delta的值不能为负,函数会在[-max_delta,max_delta]值之间随机调整图像的亮度
adjusted_brightness = tf.image.random_brightness(img_after_decode,max_delta=0.8)
# 用pyplot工具显示
plt.imshow(adjusted_brightness.eval())
plt.show()
* 对比度调整
API 6:tf.image.random_contrast(image,lower,upper,seed=None) 随机对比度调整函数
1、函数功能:通过在某范围[lower,upper)中随机选取调整对比度因子contrast_factor,以随机调整图像的对比度. 改变原图片的对比度,公式为(?−????)∗??????????????+????(?−????)∗??????????????+????(x−mean)∗contrastfactor+mean(x−mean)∗contrastfactor+mean, 其中的x为原输入,mean为每个通道的均值。
2、函数参数:
其他亮度调整函数(固定调整对比度):
tf.image.adjust_contrast(images,contrast_factor)
说明:参数contrast_factor选取时可为正或为负,正值会增加对比度,负值会降低对比度。
#随机调整对比度tf.image.random_contrast
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
#函数原型random_contrast(image,lower,upper,seed)
#函数会在[lower upper]之间随机调整图像的对比度
#但要注意参数lower和upper都不能为负
adjusted_contrast = tf.image.random_contrast(img_after_decode, 0.2,18, )
plt.imshow(adjusted_contrast.eval())
plt.show()
* 色相调整
API 7:tf.image.adjust_hue(image,delta,name=None) 固定色相调整函数
1、函数功能:改变原图像的色彩(hue),本函数将原RGB图像转换到HSV空间后,在hue通道上加上delta后,再转换回RGB空间。
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间,这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)
RGB和HSV转换
2、函数参数:
3、注意:
对于色相和饱和度的调整,因为有RGB图像转换到HSV的过程,因此只能对RGB三通道的图像进行处理,因此这一步应该使用.jpeg格式的图像来进行操作
其他色相调整函数:
tf.image.random_hue(image, max_delta)
说明:功能是在[-max_delta, max_delta]的范围随机调整图片的色相。max_delta的取值在[0, 0.5]之间。
#固定调整色相adjusted_hue
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat2.jpeg", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_jpeg(image)
# 函数原型adjust_hue(image,delta,name)
adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.1)
adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.3)
adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.6)
adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.9)
plt.imshow(adjusted_hue.eval())
plt.show()
* 饱和度调整
API 8:tf.image.adjust_saturation(image,saturation_factor, name=None) 固定饱和度调整函数
1、函数功能:将RGB图像转换为浮点表示形式,将其转换为HSV,向饱和通道添加偏移量,再转换回RGB
2、函数参数:
其他亮度调整函数(随机调整对比度):
tf.image.random_saturation(image, lower, upper,seed)在[lower, upper]的范围随机调整图的饱和度。
#固定调整饱和度tf.image.adjust_saturation
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat2.jpeg", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_jpeg(image)
#函数原型adjust_contrast(images,contrast_factor)
#将图片的饱和度调整-6。
adjusted_saturation = tf.image.adjust_saturation(img_after_decode, -6)
#将图片的饱和度调整+6。
adjusted_saturation = tf.image.adjust_saturation(img_after_decode, 6)
plt.imshow(adjusted_saturation.eval())
plt.show()
5、调整图像大小
在实际情况中,从各渠道获取的图片不会是固定大小的。为例能够将图像的像素值输入到输入单元个数固定的网格中,需要将图像的大小统一,这就是图像大小调整需要完成的任务。调整图像的方式有多种,包括保留全部信息的缩放、以中心位置为中心的裁剪填充、指定区域的裁剪填充
*保留全部图像信息的缩放
API 9:tf.image.resize_images(image,size, method)
1、函数功能:使用指定的调整方法method调整images尺寸为为size的大小.
2、函数参数:
0:双线性插值法。
1:最近邻居法。
2:双三次插值法。
3:面积插值法。
四种方法有细微的差别,但是如果没有特别的要求,可以认为结果是相同的,一般采用双线插值法。
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
#函数原型resize_images(images,size,method,align_corners),原图大小895x560
resized0 = tf.image.resize_images(img_after_decode, [300, 300], method=0)
resized1 = tf.image.resize_images(img_after_decode, [300, 300], method=1)
resized2 = tf.image.resize_images(img_after_decode, [300, 300], method=2)
resized3 = tf.image.resize_images(img_after_decode, [300, 300], method=3)
print(resized.dtype)
#打印的信息
# 从print的结果看出经由resize_images()函数处理图片后返回的数据是float32格式的,
# 所以需要转换成uint8才能正确打印图片,这里使用np.asarray()存储了转换的结果
resized0 = np.asarray(resized0.eval(), dtype="uint8")
resized1 = np.asarray(resized1.eval(), dtype="uint8")
resized2 = np.asarray(resized2.eval(), dtype="uint8")
resized3 = np.asarray(resized3.eval(), dtype="uint8")
plt.imshow(resized0)
plt.show()
plt.imshow(resized1)
plt.show()
plt.imshow(resized2)
plt.show()
plt.imshow(resized3)
plt.show()
*图像裁剪或填充(中心位置)
API 10:tf.image.resize_image_with_crop_or_pad(image,target_height,target_width)
1、函数功能:实现了裁剪或者填充的功能,是第四节中用到的裁剪函数,在使用时会会传入的图像数据制定一个目标大小
如果原始图像的大小小于目标大小,则函数会在原始图像的四周进行全0填充,以达到目标大小;
如果原始图像的大小大于目标大小,则函数会以原始图像的中心为中心对图像进行裁剪,裁剪之后就得到了目标大小
2、函数参数:
以下代码是使用该函数的示范,实现了将图像裁剪到300x300和填充到1000x1000(原图像895x560)
#随机裁剪填充函数tf.image.resize_image_with_crop_or_pad()
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
#函数原型resize_image_with_crop_or_pad(image,target_height,target_width)
#裁剪图像
croped = tf.image.resize_image_with_crop_or_pad(img_after_decode, 300, 300)
#填充图像
padded = tf.image.resize_image_with_crop_or_pad(img_after_decode, 1000, 1000)
#用pyplot显示结果
plt.imshow(croped.eval())
plt.show()
plt.imshow(padded.eval())
plt.show()
*按比例裁剪图像
API 11:tf.image.central_crop(image,central_fraction)
1、函数功能:以原始图像的中心为中心,按照传递进来的比例参数将图像进行裁剪,保留中心区域。
2、函数参数:
以下代码是使用该函数的示范,实现了将图像裁剪到原始大小的40%。
#按比例调整图像大小函数 tf.image.central_crop()
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
# 函数原型central_crop(image,central_fraction)
#参数central_fraction是裁剪比例
central_cropped = tf.image.central_crop(img_after_decode, 0.4)
plt.imshow(central_cropped.eval())
plt.show()
*指定位置裁剪、填充图像
API 12.1:tf.image.crop_to_bounding_box(image,offset_height,offset_width,target_height,target_width)
API 12.2:tf.image.pad_to_bounding_box(image,offset_height,offset_width,target_height,target_width)
以上函数都是以图像中心为中心对图像进行裁剪或者填充的,除了这些函数,TensorFlow还提供了上面两个函数才裁剪或者填充图像的指定区域
这两个函数都是通过设置高度和宽度方向的偏移量来控制索要裁剪或填充的区域,将图像裁剪到指定的边界框.
tf.image.crop_to_bounding_box()
offset_height:表示目标图像的左上角距离原始顶部边的行数
offset_width:表示目标图像的左上角距离原始图像左侧边的列数
target_height,target_width:表示裁剪后的目标图像大小
tf.image.pad_to_bounding_box()
offset_height:表示在图像的顶部添加全0填充的行数
offset_width:表示在图像的左侧添加全0填充的列数
target_height,target_width:表示填充后的目标图像大小
以下代码是使用该函数的示范
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
# 函数原型
# crop_to_bounding_box(image,offset_height,offset_width,target_height,target_width)
# pad_to_bounding_box(image,offset_height,offset_width,target_height,target_width)
croped = tf.image.crop_to_bounding_box(img_after_decode, 100, 100, 300, 300)
padded = tf.image.pad_to_bounding_box(img_after_decode, 100, 100, 1000, 1000)
plt.imshow(croped.eval())
plt.show()
plt.imshow(padded.eval())
plt.show()
6、图像的标注框
在图像中添加标注框有着很大的作用,在一些用于图像识别的数据集中,图像中某些需要关注的特征通常会被标注框圈出来,或者在一些图像识别问题中需要模型将识别出的图像中的物体用标注框圈出来。
*矩形标注框
API 13:tf.image.draw_bounding_boxes(images,boxes,name=None)
1、函数功能:在一批图像上绘制边界框。
在图像像素之上绘制由位置指定的边界框boxes,可以指定一个或多个boxes。
每个边界框的坐标boxes编码为[y_min, x_min, y_max, x_max]。
对于图像大小为895x560,假设指定boxes的值为[0.05, 0.05, 0.9, 0.7],那么矩形标注框左上角在图像中的坐标为(0.05x895,0.05x560),矩形边框右下角的坐标为(0.9x895,0.7x560)
2、函数参数:
API 14:tf.expand_dims(input, dim, name=None)
1、函数功能:实现给定一个input,给input增加一个维度。tf.image.draw_bounding_boxes()
函数要求输入的图像数据是是实数型,并且输入是一个batch的数据,因此是一个四维矩阵,所以要增加一个维度
2、函数参数:
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
# tf.expand_dims()将矩阵转化为为四维矩阵,函数原型expand_dims(input,axis,name,dim)
# tf.image.convert_image_dtype()将数据类型转化为实数型
batched = tf.expand_dims(tf.image.convert_image_dtype(img_after_decode, tf.float32), 0)
# 定义边框的坐标系数
boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.20, 0.3, 0.5, 0.5]]])
# 绘制边框,函数原型draw_bounding_boxes(images,boxes,name)
image_boxed = tf.image.draw_bounding_boxes(batched, boxes)
# draw_bounding_boxes()函数处理的是一个batch的图片,如果此处给imshow()函数
# 传入image_boxed参数会造成报错(Invalid dimensions for image data)
plt.imshow(image_boxed[0].eval())
plt.show()
*随机变形的边界框
API 15:tf.image.sample_distorted_bounding_box(
image_size,
bounding_boxes,
seed=None,
seed2=None,
min_object_covered=None,
aspect_ratio_range=None,
area_range=None,
max_attempts=None,
use_image_if_no_bounding_boxes=None,
name=None
)
1、函数功能:此函数为图像生成单个随机变形的边界框。函数输出的是可用于配合tf.slice()函数裁剪原始图像;还可以配合tf.image.draw_bounding_boxes()产生随机标注框
一般在设置该函数参数时只需要设置前两个,我们了解一下前两个参数的用法
2、函数参数:
image_size:一个Tensor,必须是下列类型之一:uint8,int8,int16,int32,int64,是1维的,并且包含[height, width, channels]
bounding_boxes:一个float32类型的三维Tensor,形状为[batch, N, 4],描述与图像相关的Ñ个边界框。
例如bounding_boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.20, 0.3, 0.5, 0.5]]]),它的形状就是[1,2,4]
3、返回值:
返回值为3个张量:begin,size和 bboxes。
begin,size: 用于 tf.slice() 剪裁图像,作为输入的参数,随机裁剪
bboxes: 可以用于 tf.image.draw_bounding_boxes 函数来画出边界框,bboxes形状是固定的[1,1,4],因此最终只有一个标注边框的输出
下面的程序展示了通过tf.image.sample_distorted_bounding_box()函数配合函数tf.image.draw_bounding_boxes()以及函数tf.slice()来完成图像随机裁剪以及随机添加标注框
tf.image.sample_distorted_bounding_box()函数的返回值具有随机性,因此标注的框和裁剪出来的图片也都是随机的,所以每次得到的结果也不相同。
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()
with tf.Session() as sess:
img_after_decode = tf.image.decode_png(image)
boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.20, 0.3, 0.5, 0.5]]])
#函数原型
#sample_distorted_bounding_box(image_size,bounding_boxes,seed,seed2,min_object_covered,
# aspect_ratio_range,area_range,max_attempts,use_image_if_no_bounding_boxes,name)
begin, size, bounding_box = tf.image.sample_distorted_bounding_box(tf.shape(img_after_decode), bounding_boxes=boxes)
batched = tf.expand_dims(tf.image.convert_image_dtype(img_after_decode, tf.float32), 0)
image_boxed = tf.image.draw_bounding_boxes(batched, bounding_box)
#slice()函数原型slice(input_,begin,size,name)
sliced_image = tf.slice(img_after_decode,begin,size)
plt.imshow(image_boxed[0].eval())
plt.show()
plt.imshow(sliced_image.eval())
plt.show()