1. 程式人生 > >[TensorFlow]入門學習筆記(3)-影象預處理

[TensorFlow]入門學習筆記(3)-影象預處理

影象預處理

前言

因為在做目標追蹤方面,一直在matlab中寫程式碼,不得不說改程式碼改的又複雜又難改,優化難做啊。就把影象預處理過程直接放到tensorflow中學習吧。

TFRecord資料格式

對於資料量較小而言,可能一般選擇直接將資料載入進記憶體,然後再分batch輸入網路進行訓練(tip:使用這種方法時,結合yield 使用更為簡潔,之前我一直用的這個方法)。
如果資料量較大,這樣的方法就不適用了,因為太耗記憶體,所以這時最好使用tensorflow提供的佇列queue,也就是第二種方法 從檔案讀取資料。對於一些特定的讀取,比如csv檔案格式,官網有相關的描述,在這兒我介紹一種比較通用,高效的讀取方法(官網介紹的少),即使用tensorflow內定標準格式——TFRecords。這種方式也對影象檔案比較友好,主要是影象的都比較大。

不多說,直接貼程式碼,有些寫在註釋裡了。
讀取TFRecord中的樣例,以佇列的形式。

#-*-encoding:UTF-8 -*-
import tensorflow as tf

#建立一個reader來讀取TFRecord中的樣例
reader = tf.TFRecordReader()

#建立一個臨時佇列用於維護輸入檔案列表
filename_queue = tf.train.string_input_producer(["path/to/output.tfrecords"])

#從檔案中讀取一個樣例。read_up_to函式用於一次性讀取多個樣例
_,serialized_example = reader.read(filename_queue)
#解析讀入的樣例。如果需要解析多個樣例,用parse_example
features = tf.parse_single_example(serialized_example,features={ #TensorFlow提供了兩種屬性解析方法 #1.tf.FixedLenFeature解析結果為Tensor. #2.tf.VarLenFeature,這種解析方法解析為一個SparseTensor,用於處理稀疏矩陣 #格式需要與上面的寫入資料的格式相一致 '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) 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) #每次讀取一個樣例,當所有樣例讀取完之後,從頭讀取。 for i in range(10): image,label,pixel = sess.run([images,labels,pixels])

那麼如何將已經存在的資料儲存為TFRecord呢?
以mnist的資料集為例,

  • 獲取你的資料
  • 將資料填入到Example協議記憶體塊(protocol buffer)
  • 將資料中的資訊feature寫入這個結構
  • 之後,通過tf.python_io.TFRecordWriter 寫入到TFRecords檔案
# -*- coding: UTF-8 -*
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("../MNIST_data/", dtype=tf.uint8,one_hot=True)
images = mnist.train.images
labels = mnist.train.labels
pixels = images.shape[1] #畫素值可以作為Example的一個屬性
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)
    }))
    #寫入TFRecord
    writer.write(example.SerializeToString())

writer.close()

影象預處理

影象預處理有很多過程。這裡只介紹函式。方便使用。

  • 影象讀取原始
    • tf.gfile.FastGFile().read()
  • 影象格式的編碼解碼 :影象不直接記錄影象上的不同位置,不同顏色的亮度。而是記錄壓縮編碼之後的結果。所以要還原成三維矩陣,需要解碼。
    • tf.image.decode_jpeg()
    • tf.image.encode_jpeg()
    • 轉換函式 tf.image.convert_image_dtype
  • 影象大小調整
    • tf.image.resize_images(image,[size],method)
    • method 0:雙線性插值 1:最近鄰居法 2: 雙三次插值法 3:面積差值法
    • tf.image.resize_image_with_crop_pad 自動裁剪或者填充
  • 影象翻轉
    • tf.image.flip_up_down()
    • tf.image.filp_left_right()
    • tf.image.transpose_image()
  • 影象色彩調整
    • 亮度調整 tf.image.adjust_brightness(image,brightness)
    • 隨機亮度調整 tf.image.random_brightness(image,max_delta)
    • 同理調整,tf.image.adjust_contrast,tf.image.adjust_hue,tf.image.
      saturation.
    • 影象標準化 tf.image.per_image_whitening(image)
  • 標註框

    • tf.image.draw_bounding_boxes(batch,boxes) 這個函式要求影象矩陣的數字為實數,而且輸入是一個batch的資料,即多張影象組成的四維矩陣,所以將編碼後的影象矩陣加一維。
    • tf.expand_dims() 這個加的維度大家自己要看api去理解
    • tf.image.sample_distorted_bounding_box(size,boxes) 隨機擷取影象資訊

      隨機翻轉影象,隨機調整顏色,隨機截圖影象中的有資訊含量的部分,這些事提高模型健壯性的一種方式。這樣可以使是訓練得到的模型不受被識別物體大小的影響。
      下面貼出完整程式碼:

# -*- coding: UTF-8 -*
import tensorflow as tf

import numpy as np
import matplotlib.pyplot as plt

def distort_color(image,color_ordering=0):
    if color_ordering == 0:
        image = tf.image.random_brightness(image,max_delta=32./255.)#亮度
        image = tf.image.random_saturation(image,lower=0.5,upper=1.5)#飽和度
        image = tf.image.random_hue(image,max_delta=0.2)#色相
        image = tf.image.random_contrast(image,lower=0.5,upper=1.5)#對比度
    elif color_ordering == 1:
        image = tf.image.random_brightness(image, max_delta=32. / 255.)  # 亮度
        image = tf.image.random_hue(image, max_delta=0.2)  # 色相
        image = tf.image.random_saturation(image, lower=0.5, upper=1.5)  # 飽和度
        image = tf.image.random_contrast(image, lower=0.5, upper=1.5)  # 對比度
    return tf.clip_by_value(image,0.0,1.0) #將張量值剪下到指定的最小值和最大值

def preprocess_for_train(image,height,width,bbox):
    #如果沒有提供標註框,則認為整個影象就是需要關注的部分
    if bbox is None:
        bbox = tf.constant([0.0,0.0,1.0,1.0],dtype=tf.float32,shape=[1,1,4])

    #轉換影象張量的型別
    if image.dtype != tf.float32:
        image = tf.image.convert_image_dtype(image,dtype=tf.float32)

    #隨機擷取影象,減少需要關注的物體大小對影象識別的影響
    bbox_begin,bbox_size,_ = tf.image.sample_distorted_bounding_box(tf.shape(image),
                                                                    bounding_boxes=bbox)
    distort_image = tf.slice(image,bbox_begin,bbox_size)

    #將隨機截圖的影象調整為神經網路輸入層的大小。大小調整的演算法是隨機的
    distort_image = tf.image.resize_images(
        distort_image,[height,width],method=np.random.randint(4)
    )
    #隨機左右翻轉影象
    distort_image = tf.image.random_flip_left_right(distort_image)
    #使用一種隨機的順序調整影象色彩
    distort_image = distort_color(distort_image,np.random.randint(1))
    return distort_image

image_raw_data = tf.gfile.FastGFile("../cat.jpg",'r').read()

with tf.Session() as Sess:
    ima_data = tf.image.decode_jpeg(image_raw_data)
    boxes = tf.constant([[[0.05,0.05,0.9,0.7],[0.35,0.47,0.5,0.56]]])

    #執行6次獲得6中不同的影象,在圖中顯示效果
    for i in range(6):
        #將影象的尺寸調整為299*299
        result = preprocess_for_train(ima_data,299,299,boxes)

        plt.imshow(result.eval())
        plt.show()