TensorFlow學習筆記(五)圖像數據處理

目錄:python

  1、TFRecord輸入數據格式git

  1.1 TFrecord格式介紹正則表達式

  1.2 TFRecord樣例程序算法

  2、圖像數據處理數組

  2.1TensorFlow圖像處理函數網絡

  2.2圖像預處理完整樣例數據結構

  3、多線程數據輸入處理框架多線程

  3.1 隊列與多線程框架

  3.2輸入文件隊列dom

  3.3組合訓練數據(batching)

  3.4輸入數據處理框架

 

 1、TFRecord輸入數據格式

  TensorFlow提供了一種統一的格式來存儲數據,這個格式就是TFRecord

  1.1 TFrecord格式介紹

 

  TFRecord文件中的數據都是經過tf.train.Example Protocol Buffer 的格式存儲的。格式以下

message Example{
    Features features=1;
    }
message Features{
  map<string Feature> feature = 1;  
}
message Feature{
  oneof kind {
      BytesList bytes_list = 1;
      FloatList  float_list =2 ;
      Int64List int64_list =3;  
    }  
}    

  1.2 TFRecord樣例程序


將數據轉化爲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 _byte_feature(value):
    return tf.train.Feature(bytes_list = tf.train.BytesList(value=[value]))

mnsit = input_data.read_data_sets("mnist_set",one_hot=True)
images = mnsit.train.images
#訓練數據所對應的正確答案,能夠做爲一個屬性保存在TFRecord中。
labels = mnsit.train.labels
#訓練數據的圖像分辨率,這能夠做爲Example中的一個屬性。
pixels = images.shape[1]
num_examples = mnsit.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':_byte_feature(image_raw)
    }))
    #將一個Example寫入TFrecord中
    writer.write(example.SerializeToString())
writer.close()

 

 讀取TFRecord文件

import tensorflow as tf

#建立一個reader來讀取TFRecord文件中的數據
reader = tf.TFRecordReader()
#建立一個隊列來維護輸入文件列表
#tf.train.string_input_producer函數
filename_queue = tf.train.string_input_producer(["path/to/output.tfrecords"])
#從文件中讀出一個樣例。也可使用read_up_to函數一次性讀取多個樣例
_,serialed_example = reader.read(filename_queue)
#解析讀入的一個樣例。若是須要解析多個樣例,能夠用parse_example函數
features = tf.parse_single_example(serialed_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)
labels = tf.cast(features['label'],tf.int32)
pixels = tf.cast(features['pixels'],tf.int32)

#啓動多線程處理數據
sess = tf.Session()
coord = tf.train.Coordinator()
thread = tf.train.start_queue_runners(sess=sess,coord= coord)
#每次運行能夠讀取Tfrecord文件中的一個樣例。當全部的樣例都讀完以後,在此樣例程序中會重頭讀取
for i in range(10):
    image,label,pixel = sess.run([images,labels,pixels])

 

2、圖像數據處理

  經過圖像的預處理,能夠儘可能避免模型受到無關因素的干擾。

  2.1TensorFlow圖像處理函數

    tensorflow提供了幾類圖像處理的函數。

  圖像編碼處理

    一個RGB模式的彩色模式的圖像能夠看做一個三維矩陣。然而圖像的存儲並非記錄這些矩陣中的數組,而是通過壓縮編碼以後的結果。TensorFlow提供了對圖像的編碼、解碼函數。例如tf.image.decode_jpeg等

  

# -*- coding:utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取圖像的原始數據
image_raw_data = tf.gfile.FastGFile('path/to/picture.jpeg','rb').read()
with tf.Session() as sess:
    #將圖像解碼,獲得三維矩陣。解碼爲一個張量,在使用它的值以前須要明確調用運行的過程
    img_data = tf.image.decode_jpeg(image_raw_data)
    print(img_data.eval())
#使用pyplot工具可視化獲得圖像
    plt.imshow(img_data.eval())
    plt.show()
    #將數據的類型轉化爲實數,方便後面的程序對圖像處理
    img_data = tf.image.convert_image_dtype(img_data,dtype=tf.uint8)
    #將表示一張圖像的三維矩陣按照jpeg的格式從新編碼並保存。可獲得與原圖同樣的圖像。
    encode_image = tf.image.encode_jpeg(img_data)
    with tf.gfile.GFile("path/to/output_image.png","wb") as f:
        f.write(encode_image.eval())

  圖像大小調整

  通常來講,網絡上獲取的圖像大小是不固定的,但神經網絡的節點的個數是固定的。TensorFlow提供了四種方法,而且將它們分裝在tf.image.resize_images函數中。

# -*- coding:utf-8 -*-
import matplotlib.pyplot as plt
import tensorflow as tf
#讀取圖像的原始數據
image_raw_data = tf.gfile.FastGFile('path/to/picture.jpeg','rb').read()
with tf.Session() as sess:
    #將圖像解碼,獲得三維矩陣。解碼爲一個張量,在使用它的值以前須要明確調用運行的過程
    img_data = tf.image.decode_jpeg(image_raw_data)
    img_data = tf.image.convert_image_dtype(img_data,dtype=tf.float32)
    #經過tf.image.resize_images調整圖像大小。第一個參數爲原始圖像,第二和第三個參數爲調整圖像大小,method爲調整圖像大小的算法
    resized = tf.image.resize_images(img_data,[300,300],method=0)
    print(resized.get_shape())
    plt.imshow(resized.eval())
    plt.show()

  圖像的剪裁和填充

#resize_image_with_crop_or_pad  超過原始圖像的大小,自動填充,小於原始圖像的大小自動裁剪
  resized = tf.image.resize_image_with_crop_or_pad(img_data,1500,1800)
    print(resized.get_shape())
    plt.imshow(resized.eval())
    plt.show()

  等比例裁剪

    #經過tf.image.central_crop等比例調整圖像大小。第一個參數爲原始圖像,第二個參數爲調整圖像大小的比例,範圍(0,1】
    resized = tf.image.central_crop(img_data,0.3)
    print(resized.get_shape())
    plt.imshow(resized.eval())
    plt.show()

   上述函數都是截取或填充圖像中間的部分,TF還提供了裁剪或填充指定區域的函數,如tf.image.crop_to_bounding_box或 tf.image.pad_to_bounding_box

  圖像翻轉

    #上下翻轉
    fliped_up_down = tf.image.flip_up_down(img_data)
    #左右翻轉
    fliped_l_r = tf.image.flip_left_right(img_data)
    #沿着對角線翻轉
    flip_transpose = tf.image.transpose_image(img_data)
    #隨機翻轉
    ram_flip_up_dn = tf.image.random_flip_up_down(img_data)
    ram_flip_left_right = tf.image.random_flip_left_right(img_data)

  圖像色彩調整

  

    #圖像亮度調整
    adjusted_down = tf.image.adjust_brightness(img_data,-0.5)
    adjusted_up = tf.image.adjust_brightness(img_data,0.5)
    #隨機在【-0.5,0.5】之間調整
    adjusted_random = tf.image.random_brightness(img_data,0.5)

相似的函數還有:對比度adjust_contrast、random_contrast,色相adjust_hue、random_hue,飽和度adjust_saturation、random_saturation

 

 圖像的標準化

  所謂圖像的標準化就是將圖像上的亮度均值變爲0,方差爲1

adjusted_ = tf.image.per_image_whitening (img_data)

 處理標註框

  

    #縮小圖像
    image_data = tf.image.resize_images(img_data,[180,267],method=1)
    #圖像矩陣轉化爲實數類型.tf.image.draw_bounding_boxes輸入的是一個batch也就是多張圖像組成的四維矩陣,全部須要在怎講一維
    batched = tf.expand_dims(tf.image.convert_image_dtype(image_data,dtype=tf.float32),0)
    #標註框須要四個數字【Ymin,Xmin,Ymax,Xmax】
    boxes = tf.constant([[[0.05,0.05,0.97,0.7],[0.35,0.47,0.5,0.56]]])
    #添加標註
    result = tf.image.draw_bounding_boxes(batched,boxes)

 

  2.2圖像預處理完整樣例

  

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,32.0/255.0)
        image = tf.image.random_saturation(image,lower=0.5,upper=1.5)
        image = tf.image.random_hue(image,0.2)
        image = tf.image.random_contrast(image,lower=0.5,upper=1.5)
    elif color_ordering == 1:
        image = tf.image.random_saturation(image,lower=0.5,upper=1.5)
        image = tf.image.random_brightness(image,32.0/255.0)
        image = tf.image.random_hue(image,0.2)
        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)
    distorted_image = tf.slice(image,bbox_begin,bbox_size)
    #將截取的圖像調整爲神經網絡輸入層大小。大小調整的算法是隨機的
    distorted_image = tf.image.resize_images(distorted_image,[height,width],method=np.random.randint(4))
    #隨機左右翻轉圖像
    distorted_image = tf.image.flip_left_right(distorted_image)
    #使用一種隨機的順序調整圖像色彩
    distorted_image = distort_color(distorted_image,np.random.randint(2))
    return distorted_image

image_raw_data = tf.gfile.FastGFile("path/to/picture.jpeg",'rb').read()
with tf.Session() as sess:
    img_data = tf.image.decode_jpeg(image_raw_data)
    boxes = tf.constant([[[0.05,0.05,0.97,0.7],[0.35,0.47,0.5,0.56]]])
    for i in range(6):
        result = preprocess_for_train(img_data,299,299,boxes)
        plt.imshow(result.eval())
        plt.show()

 

  3、多線程數據輸入處理框架

  爲了不圖像與處理成爲神經網絡模型訓練效率的瓶頸,TensorFlow提供了一套多線程處理數據的框架。下面提供了一個經典的數據處理的流程圖。

 

 

  3.1 隊列與多線程

    在TF中,隊列和變量相似,都是計算圖上有狀態的節點。對於隊列,修改隊列狀態的操做主要有Enqueue、EnqueueMany和Dequeue。 

import tensorflow as tf
#建立一個先進先出的隊列,指定隊列最多能夠保存連個元素,斌指定類型爲整數。
q = tf.FIFOQueue(2,"int32")
#使用enqueue_many函數來初始化隊列中的元素。和變量初始化相似,在使用隊列以前須要明確的調用這個初始化的過程
init = q.enqueue_many(([0,10],))
#使用Dequeue函數將隊列中的第一個元素出隊列。這個元素的值將被存在變量x中。
x = q.dequeue()
#將獲得的值加1
y = x + 1
#將加1 後的值從新加入隊列
q_inc = q.enqueue([y])

with tf.Session() as sess:
    #運行初始化隊列操做
    init.run()
    for _ in range(5):
        #運行q_inc將執行數據出隊列、出隊列的元素 +一、從新加入隊列的整個過程
        v , _  = sess.run([x,q_inc])
        print(v)

  TF提供了FIFOQueue和RandomShufflerQueue兩種隊列。

  TF提供了tf.coordinator和tf.QueueRunner兩個類來完成多線程的協同功能。tf.coordinator 主要應用於多個線程協同中止,有request_stop 、should_stop、join三個函數。

import tensorflow as tf
import numpy as np
import threading
import time

#線程中運行的程序,這個程序每隔1S判斷是否須要中止而且打印本身的ID
def MyLoop(coord,worker_id):
    while not coord.should_stop():
        #隨機中止全部線程
        if np.random.rand() < 0.1:
            print("stoping from id %d\n"%worker_id)
            #調用request_stop()函數來通知其餘線程中止
            coord.request_stop()
        else:
            print("working from id :%s\n"%worker_id)
        time.sleep(1)
#聲明一個Coordinator來協同多個線程
coord = tf.train.Coordinator()
#建立5個線程
threads=[threading.Thread(target=MyLoop,args=(coord,i,)) for i in range(5)]
#啓動全部的線程
for t in threads:
    t.start()
coord.join(threads)

 tf.QueueRunner主要用於啓動多個線程來操做同一個隊列。

import tensorflow as tf
import  numpy as np
#聲明一個先進先出的隊列,隊列中最多100個元素,類型爲實數
queue = tf.FIFOQueue(100,"float")
#定義隊列的入隊操做
enqueue_op = queue.enqueue([tf.random_normal([1])])
#tf.train.QueueRunner建立多個線程運行隊列的入隊操做
#參數1:須要操做的隊列,參數2:須要啓動的線程
qr = tf.train.QueueRunner(queue,[enqueue_op]*5)

#將定義過的QueueRunner加入計算圖指定的集合
tf.train.add_queue_runner(qr)
#定義出隊操做
out_queue = queue.dequeue()
with tf.Session() as sess:
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess,coord)
    #獲取隊列中的取值
    for _ in range(3):
        print(sess.run(out_queue)[0])
    coord.request_stop()
    coord.join(threads)

 

  3.2輸入文件隊列

  數據量較大的時候,能夠將數據分紅多個TFRecord文件來提升處理效率。TensorFlow提供了tf.train.match_filename_once函數來獲取符合正則表達式的全部文件列表,獲得的文件列表可經過tf.train.string_input_producer函數進行有效管理。

  經過設置shuffle參數,tf.train.string_input_producer函數支持隨機打亂文件列表中文件出隊的順序。

  當一個輸入隊列中的文件都被處理完後,它會將初始化時提供的文件列表中的文件從新加入隊列。tf.train.string_input_producer函數能夠設置num_epochs參數來限制加載初始文件的最大輪數。

  生成樣例數據

  

import tensorflow as tf
#建立TFRecord文件的幫助函數
def _int64_feature(value):
    return tf.train.Feature(int64_list = tf.train.Int64List(value=[value]))
#模擬海量數據寫入不一樣文件。num_shards定義有多少文件,instance_per_shard定義每一個文件多少數據
num_shards = 2
instance_per_shard = 2

for i in range(num_shards):
    filename = "path/to/data.tfrecords-%.5d-of-%.5d"%(i,num_shards)
    writer = tf.python_io.TFRecordWriter(filename)
    #將數據封裝成Example結構並寫入TFRecord文件
    for j in range(instance_per_shard):
        example = tf.train.Example(features=tf.train.Features(feature = {
            "i":_int64_feature(i),
            "j":_int64_feature(value=j),
        }))
        writer.write(example.SerializeToString())
    writer.close()

用tf.train.match_filename_once和tf.train.string_input_producer來處理生成的樣例

import tensorflow as tf
#使用tf.train.match_filename_once的正則表達式方式獲取文件列表
files = tf.train.match_filenames_once("path/to/data.tfrecords-*")
#經過tf.train.string_input_producer建立輸入隊列
filename_queue = tf.train.string_input_producer(files,shuffle=False)

recorder = tf.TFRecordReader()
_,serialize_example = recorder.read(filename_queue)


features = tf.parse_single_example(serialize_example,features={
    "i":tf.FixedLenFeature([],tf.int64),
    "j":tf.FixedLenFeature([],tf.int64),
})
with tf.Session() as sess:
    #使用tf.train.string_input_producer函數也須要初始化變量,注意要初始化本地變量
    tf.global_variables_initializer().run()
    tf.local_variables_initializer().run()

    #聲明tf.train.Coordinator類來協同不一樣的線程,並啓動線程
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess,coord=coord)
    for i in range(6):
        print(sess.run([features['i'],features['j']]))
    coord.request_stop()
    coord.join(threads)

 

  3.3組合訓練數據(batching)

  TF提供了tf.train.batch和tf.train.shuffle_batch函數來說單個的樣例組織成batch的形式輸出。

  

import tensorflow as tf
#使用tf.train.match_filename_once的正則表達式方式獲取文件列表
files = tf.train.match_filenames_once("path/to/data.tfrecords-*")
#經過tf.train.string_input_producer建立輸入隊列
filename_queue = tf.train.string_input_producer(files,shuffle=False)

recorder = tf.TFRecordReader()
_,serialize_example = recorder.read(filename_queue)


features = tf.parse_single_example(serialize_example,features={
    "i":tf.FixedLenFeature([],tf.int64),
    "j":tf.FixedLenFeature([],tf.int64),
})

example,label = features['i'],features['j']
#一個batch中樣例的個數
batch_size = 3
#組合樣例的隊列中最多能夠存儲的樣例的個數。這個隊列若是太大,消耗內存。若是過小,出隊受阻。
capacity = 1000 +batch_size*3
#使用tf.train.batch來組合樣例。【exmple,label】參數:須要組合樣例的元素。
example_batch,label_batch = tf.train.batch([example,label],batch_size,capacity=capacity)

with tf.Session() as sess:
    tf.global_variables_initializer().run()
    tf.local_variables_initializer().run()
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess,coord=coord)
    for i in range(2):
        cur_example_batch,cur_label_batch = sess.run([example_batch,label_batch])
        print((cur_example_batch,cur_label_batch))
    coord.request_stop()
    coord.join(threads)

打亂batch順序,只須要將example_batch,label_batch = tf.train.batch([example,label],batch_size,capacity=capacity)改爲example_batch,label_batch = tf.train.shuffle_batch([example,label],batch_size,capacity=capacity,min_after_dequeue =30)min_after_dequeue限制了出隊時隊列中元素的最少個數,這個值應該不大於capacity

tf.train.batch和tf.train.shuffle_batch函數的參數num_threads,可指定多個線程同時執行入隊操做

tf.train.batch_join和tf.train.shuffle_batch_join函數,從輸入文件隊列中獲取不一樣文件中的樣例分配給不一樣的線程


  3.4輸入數據處理框架

   按照本節開頭給出的流程圖,用一個完整程序串聯起來以前的步驟實現一個TF處理輸入數據。在看一下流程圖

import tensorflow as tf
import numpy as np

#建立文件列表,建立文件輸入隊列

files = tf.train.match_filenames_once("path/to/pattern-*")
filename_queue = tf.train.string_input_producer(files,shuffle=False)
#解析TFRecord格式文件的數據
reader = tf.TFRecordReader()
_,serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(serialized_example,features = tf.train.Features({
    "image":tf.FixedLenFeature([],tf.string),
    "label":tf.FixedLenFeature([],tf.int64),
    "height":tf.FixedLenFeature([],tf.int64),
    "width":tf.FixedLenFeature([],tf.int64),
    "channels":tf.FixedLenFeature([],tf.int64),
}))

image,label = features['image'],features['label']
height,width = features['height'],features['weight']
channels = features['channels']
#從原始圖像解析出像素矩陣,並根據圖像尺寸還原圖像
decoded_image = tf.decode_raw(image,tf.uint8)
decoded_image.set_shape([height,width,channels])
#定義神經網絡輸入層圖片的大小
image_size = 299


def distort_color(image,color_ordering=0):
    if color_ordering == 0:
        image = tf.image.random_brightness(image,32.0/255.0)
        image = tf.image.random_saturation(image,lower=0.5,upper=1.5)
        image = tf.image.random_hue(image,0.2)
        image = tf.image.random_contrast(image,lower=0.5,upper=1.5)
    elif color_ordering == 1:
        image = tf.image.random_saturation(image,lower=0.5,upper=1.5)
        image = tf.image.random_brightness(image,32.0/255.0)
        image = tf.image.random_hue(image,0.2)
        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)
    distorted_image = tf.slice(image,bbox_begin,bbox_size)
    #將截取的圖像調整爲神經網絡輸入層大小。大小調整的算法是隨機的
    distorted_image = tf.image.resize_images(distorted_image,[height,width],method=np.random.randint(4))
    #隨機左右翻轉圖像
    distorted_image = tf.image.flip_left_right(distorted_image)
    #使用一種隨機的順序調整圖像色彩
    distorted_image = distort_color(distorted_image,np.random.randint(2))
    return distorted_image


distorted_image = preprocess_for_train(decoded_image,image_size,image_size,None)

#將樣例組合batch
min_after_dequeue = 10000
batch_size =100
capacity = min_after_dequeue+ batch_size *3
image_batch,label_batch = tf.train.shuffle_batch([decoded_image,label],batch_size,capacity=capacity,min_after_dequeue=min_after_dequeue)


#定義神經網絡的結構及優化過程
def inference(image_data):
    """
    計算前向傳播,參考前面的內容
    :param image_data:
    :return:
    """
    pass
def calc_loss(logit,label):
    """
    bp ,calc the loss,參考前面的內容
    :param logit:
    :param label:
    :return:
    """
    pass
logit = inference(image_batch)
loss = calc_loss(logit,label_batch)

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

with tf.Session() as sess:
    tf.global_variables_initializer().run()
    tf.local_variables_initializer().run()
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess,coord=coord)
    for i in range(10000):
        sess.run(train_step)
    coord.request_stop()
    coord.join(threads)

整個數據處理的流程以下

相關文章
相關標籤/搜索