機器學習筆記(十八):TensorFlow實戰十(影象資料處理)
1 - 引言
之前我們介紹了通過卷積神經網路可以給影象識別技術帶來突破性的進展,現在我們從影象的預處理這個角度來繼續提升我們影象識別的準確率。
輸入的預處理需要使用TFRecord格式來同一不同的原始資料格式,並且更加有效的管理不同的屬性。
並且TensorFlow支援影象處理函式,可以通過預處理來弱化與影象識別無關的因素
2 TFRecord輸入資料格式
TFRecord檔案中的資料都是通過tf.train.Example Protocol Buffer的格式儲存的。
下面給出一個樣例程式將MNIST輸入資料轉換為TFRecord的格式:
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import numpy as np #生成整數型的屬性 def _int64_feature(value): return tf.train.Feature(int64_list = tf.train.Int64List(value=[value])) #生成字串型的屬性 def _bytes_feature(value): return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) mnist = input_data.read_data_sets("/path/to/MNIST_data", one_hot=True) images = mnist.train.images #訓練資料對應的正確答案,可以作為一個屬性儲存在TFRecord中 labels = mnist.train.labels #訓練資料的影象分別率,這可以作為Example中的一個屬性 pixels = images.shape[1] num_examples = mnist.train.num_examples #輸出TFRecord檔案的地址 filename = "/path/to/output.tfrecords" #建立一個writer來寫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()
下面程式實現怎樣讀取TFRecord資料:
#讀取TFRecord檔案中的資料 def read_tfrecords(): #建立一個reader來讀取TFRecord檔案中的樣例 reader = tf.TFRecordReader() #通過 tf.train.string_input_producer 建立輸入佇列 filename_queue = tf.train.string_input_producer(["./output.tfrecords"]) #從檔案中讀取一個樣例 _, serialized_example = reader.read(filename_queue) #解析讀入的一個樣例 features = tf.parse_single_example( serialized_example, features={ #這裡解析資料的格式需要和上面程式寫入資料的格式一致 'image_raw': tf.FixedLenFeature([], tf.string), 'label': tf.FixedLenFeature([], tf.int64), 'pixels':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) with tf.Session() as sess: init = tf.global_variables_initializer() sess.run(init) coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) # 每次執行可以讀取TFRecord檔案中的一個樣例。當所有樣例都讀完之後,再次樣例中的程式會重頭讀取 for i in range(5): images,labels,pixels = sess.run([images,labels,pixels])
3 - 影象資料處理
之前我們都是直接使用影象原始的畫素矩陣,下面我們將通過影象的預處理,可以儘量避免模型受到無關因素的影響。在大部分影象識別問題中,通過影象預處理過程可以提高模型的準確率。
3.1 - 影象編碼處理
RGB的影象可以看成一個三維矩陣,但是影象在儲存時並不是直接記錄這些矩陣中的數字,而是記錄經過壓縮編碼之後的結果,所以要將一張圖片還原成一個三維矩陣,需要解碼的過程。TensorFlow提供了對jpeg、png格式影象的編碼\解碼函式
以下是實現程式碼:
# -*- coding: utf-8 -*- import matplotlib.pyplot as plt import tensorflow as tf #讀取影象的原始資料 image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read() #讀入影象的原始資料 #image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read() with tf.Session() as sess: #將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣 #tf.image.decode_png函式對png格式的影象進行解碼。 img_data = tf.image.decode_jpeg(image_raw_data) plt.imshow(img_data.eval()) plt.show() # 將表示一張圖片的三維矩陣按照JPEG格式編碼並存入檔案中。開啟影象可以得到和原始影象一樣的影象 img_data = tf.image.convert_image_dtype(img_data,dtype=tf.uint8) enconed_image = tf.image.encode_jpeg(img_data) with tf.gfile.GFile("/path/to/output.jpeg",'wb') as f: f.write(enconed_image.eval())
3.2 - 影象大小調整
因為神經網路輸入的節點個數是固定的,事先設定的,但是圖片的大小往往不符合我們所設計的神經網路的輸入大小,因此,我們需要先將影象的大小統一
影象大小調整有兩種方式,第一種是通過演算法使得新的影象儘量儲存原始影象上的所有資訊。TensorFlow提供了四種不同的方法,並且將它們封裝到了tf.image.resize_images函式。
(當然這些演算法已經涉及到了數字影象處理的內容,要想學好影象識別,那麼數字影象處理的知識也要專門的學習!!!)
TensorFlow還提供了API對影象進行裁剪或者填充。一下程式碼展示了通過tf.image.resize_image_with_crop_or_pad函式來調整影象大小的功能
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
plt.imshow(img_data.eval())
plt.show()
#通過tf.image.resize_image_with_crop_or_pad函式調整影象的大小
#第一個引數為原始影象,後面兩個引數是調整後的影象大小,如果原始影象尺寸大於目標影象
#那麼函式就會自動擷取居中的部分,如果大於原始影象就會在原始影象的四周填充全0背景
croped = tf.image.resize_image_with_crop_or_pad(img_data, 100, 100)
padded = tf.image.resize_image_with_crop_or_pad(img_data, 500, 500)
plt.imshow(croped.eval())
plt.show()
plt.imshow(padded.eval())
plt.show()
原始影象
影象大小:100x100
影象大小:500x500
3.3 - 影象翻轉
TensorFlow提供了一些函式來支援影象的翻轉
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
#將影象上下翻轉
flipped = tf.image.flip_up_down(img_data)
plt.imshow(flipped.eval())
plt.show()
#將圖片左右翻轉
flipped = tf.image.flip_left_right(img_data)
plt.imshow(flipped.eval())
plt.show()
#將圖片沿對角線翻轉
transposed = tf.image.transpose_image(img_data)
plt.imshow(transposed.eval())
plt.show()
上下翻轉
左右翻轉
沿對角線翻轉
在影象識別問題中,影象的翻轉經常用來增加訓練集的數量,通過隨機的翻轉訓練影象的方式可以在零成本的情況下很大程度地緩解該問題。
TensorFlow也提供了API完成隨機影象翻轉的過程
#以一定概率上下翻轉影象
flipped = tf.image.random_flip_up_down(img_data)
#以一定概率左右翻轉影象
flipped = tf.image.random_flip_left_right(img_data)
3.4 - 影象色彩調整
調整影象的亮度、對比度、飽和度和相色在很多影象識別應用中都不會影響識別結果。所以在訓練神經網路模型時,可以隨機調整訓練影象的這些屬性,從而使得訓練得到的模型儘可能小地受到無關因素的影響。TensorFlow提供了調整這色色彩相關屬性的API,以下程式碼顯示瞭如何修改影象的亮度
亮度調整
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
# 將影象的亮度-0.5
adjusted = tf.image.adjust_brightness(img_data, -0.5)
plt.imshow(adjusted.eval())
plt.show()
#將影象的亮度+0.5
adjusted = tf.image.adjust_brightness(img_data,+0.5)
plt.imshow(adjusted.eval())
plt.show()
亮度減少0.5
亮度增加0.5
對比度調整
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
# 將影象的對比度-0.5
adjusted = tf.image.adjust_contrast(img_data, -0.5)
plt.imshow(adjusted.eval())
plt.show()
#將影象的對比度+0.5
adjusted = tf.image.adjust_contrast(img_data,+0.5)
plt.imshow(adjusted.eval())
plt.show()
對比度-0.5
對比度+0.5
色相調整
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
#下面四條命令分別將色相增加0.1、0.3、0.6、0.9
adjusted = tf.image.adjust_hue(img_data,0.1)
plt.imshow(adjusted.eval())
plt.show()
adjusted = tf.image.adjust_hue(img_data, 0.3)
plt.imshow(adjusted.eval())
plt.show()
adjusted = tf.image.adjust_hue(img_data, 0.6)
plt.imshow(adjusted.eval())
plt.show()
adjusted = tf.image.adjust_hue(img_data, 0.9)
plt.imshow(adjusted.eval())
plt.show()
色相+0.1
色相+0.3
色相+0.6
色相+0.9
調整飽和度
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
#下面四條命令分別將色相增加0.1、0.3、0.6、0.9
adjusted = tf.image.adjust_saturation(img_data,-5)
plt.imshow(adjusted.eval())
plt.show()
adjusted = tf.image.adjust_saturation(img_data, 5)
plt.imshow(adjusted.eval())
plt.show()
飽和度-5
飽和度+5
影象標準化
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
#下面四條命令分別將色相增加0.1、0.3、0.6、0.9
adjusted = tf.image.per_image_standardization(img_data)
plt.imshow(adjusted.eval())
plt.show()
處理標註框
在很多影象識別的例項中,我們都需要把需要關注的物體標註出來,例如人臉識別、自動駕駛等,TensorFlow提供了一些工具來處理標註框,下面展示瞭如何通過tf.image.draw_bounding_boxes函式在影象中加入標註框
下面這段程式碼就很好的把貓和貓眼都標註了
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取影象的原始資料
image_raw_data = tf.gfile.FastGFile("/path/to/picture.jpeg",'rb').read()
#讀入影象的原始資料
#image_raw_data = tf.gfile.FastGFile("F:/input.jpeg", 'rb').read()
with tf.Session() as sess:
#將影象使用jpeg的格式解碼從而得到影象對應的三維矩陣
#tf.image.decode_png函式對png格式的影象進行解碼。
img_data = tf.image.decode_jpeg(image_raw_data)
#將圖片縮小一些,這樣視覺化功能讓標註框更加清楚
img_data = tf.image.resize_images(img_data,[300,300],method=1)
#tf.image.draw_bounding_boxes函式要求影象矩陣中的數字為實數,所以需要先將影象矩陣轉化為實數型別
#tf.image.draw_bounding_boxes函式影象的輸入是一個batch的資料,也就是多張圖片組成的四維矩陣,所以要將解碼之後的影象
#矩陣加一維
batched = tf.expand_dims(tf.image.convert_image_dtype(img_data,tf.float32),0)
#給出每一張影象的所有標註框,一個標註框有4個數字,分別代表,[y_min.x_min.y_max.x_max]
#注意這裡給出的數字都是影象的相對位置,比如在180x267的影象中
#[0.35,0.47,0.5,0.56]代表了從(63,125)到(90,150)的影象
boxes = tf.constant([[[0.05,0.05,0.9,0.7],[0.35,0.55,0.45,0.62]]])
#圖7-11顯示了加入標註框的影象
result = tf.image.draw_bounding_boxes(images=batched,boxes=boxes)
plt.imshow(result[0].eval())
plt.show()
4 - 總結
TensorFlow提供了許多影象處理的API幫助我們進一步的提高影象識別的準確率和影象目標檢測的功能,可見影象的預處理對於我們的影象識別也十分重要,但是數字影象處理的內容還有很多遠遠不止這些,需要我們進一步的學習,而且TensorFlow在影象處理方面的表現也遠不如OpenCV之後我們也要學習OpenCV的相關內容。