1. 程式人生 > >二,影象資料與python儲存結構

二,影象資料與python儲存結構

# -*- coding: utf-8 -*-
"""
Created on Sat Nov 17 08:40:21 2018

@author: shenfangyuan
"""

# -*- coding:utf-8 -*-
import tensorflow as tf
import numpy as np
'''
1,tensorflow中為了充分利用GPU,減少GPU等待資料的空閒時間,
使用了"兩個"執行緒分別執行資料讀入和資料計算。
2,一個執行緒源源不斷的將硬碟中的圖片資料讀入到一個"記憶體佇列"中,
另一個執行緒負責計算任務,所需資料直接從記憶體佇列中獲取。
3,tf在記憶體佇列之前,還設立了一個檔名佇列,檔名佇列存放的是參與訓練的檔名,
  要訓練 N個epoch,則檔名佇列中就含有N個批次的所有檔名。 示例圖如下:
'''

'''
 np.ones([2, 5, 4, 3]) 看看這個張量對應的python儲存方式
 1,每個最內層括號內包含3個數據
 2,4個內層括號組成倒數第二層括號
 3,5個倒數第二層的括號,組成倒數第三層的資料結構
 4,2個內層中括號則合成最外層.

 [  [  [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]
    ]


    [  [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]
 
       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]

       [ [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.]   [1. 1. 1.] ]
    ]
 
'''
tf.reset_default_graph()
# 樣本個數
sample_num=2
# 設定迭代次數
epoch_num = 2
# 設定一個批次中包含樣本個數
batch_size = 2 #試驗的時候,可以把這個值改動一下,例如:
#3,對應的batch資料和標籤維度為 :(3, 224, 224, 3) [0 1 2]
#4,對應的batch資料和標籤維度為 :(4, 224, 224, 3) [4 5 6 7]
# 計算每一輪epoch中含有的batch個數
batch_total = int(sample_num/batch_size)+1

print('batch_total = ',batch_total)
 
# 生成4個數據和標籤
def generate_data(sample_num=sample_num):
    labels = np.asarray(range(0, sample_num)) #用隨機數產生標籤:0--sample_num
#    print('labels',labels)
#    images = np.random.random([sample_num, 5, 4, 3]) #用隨機數產生 測試圖片資料,每張圖片資料是:[224,224,3]
    images = np.ones([sample_num, 5, 4, 3]) #用隨機數產生 測試圖片資料,每張圖片資料是:[224,224,3]
    print('images',images)
    #列印 資料的shape
    print('image size {},label size :{}'.format(images.shape, labels.shape))
    return images,labels
 
def get_batch_data(batch_size=batch_size):
    images, label = generate_data() #用隨機數產生標籤和對應的圖片資料.
    # 資料型別轉換為tf.float32
    images = tf.cast(images, tf.float32) #資料轉換為float32
    label = tf.cast(label, tf.int32) #標籤資料轉換為整型數
    #從tensor列表中按順序或隨機抽取一個tensor
    #這裡的tensor列表是:[images, label],該列表的元素為images和label
    #這兩個元素images和label也都是列表,他們的第一個維度必須相等,
    #一般這兩個列表的第一維對應的shape值  labels.shape[0]==images.shape[0],就是樣本數量.
    '''
    tf.train.slice_input_producer是一個tensor生成器,作用是按照設定,
    每次從一個tensor列表中按順序或者隨機抽取出一個tensor放入檔名佇列。
    這個函式的輸出input_queue實質上也是一個"複合"結構,這個函式輸入的tensor_list列表
    中包含兩個元素:images,label,那麼input_queue就對應兩個"子佇列",分別是:
        input_quque[0]和input_queue[1]
    '''
    input_queue = tf.train.slice_input_producer([images, label], shuffle=False)
    
    
    '''
    tf.train.batch是一個tensor佇列生成器,作用是按照給定的tensor順序,
    把batch_size個tensor推送到檔案佇列,作為訓練一個batch的資料,
    等待tensor出隊執行計算。
    tf.train.batch是一個tensor佇列生成器,作用是按照給定的tensor順序,
    把batch_size個tensor推送到檔案佇列,作為訓練一個batch的資料,
    等待tensor出隊執行計算。
    第一個引數tensors:tensor序列或tensor字典,可以是含有單個樣本的序列;
    第二個引數batch_size: 生成的batch的大小;
    第三個引數num_threads:執行tensor入隊操作的執行緒數量,可以設定使用多個執行緒同時並行執行,提高執行效率,但也不是數量越多越好;
    第四個引數capacity: 定義生成的tensor序列的最大容量;
    第五個引數enqueue_many: 定義第一個傳入引數tensors是多個tensor組成的序列,還是單個tensor;
    第六個引數shapes: 可選引數,預設是推測出的傳入的tensor的形狀;
    第七個引數dynamic_pad: 定義是否允許輸入的tensors具有不同的形狀,設定為True,會把輸入的具有不同形狀的tensor歸一化到相同的形狀;
    第八個引數allow_smaller_final_batch: 設定為True,表示在tensor佇列中剩下的tensor數量不夠一個batch_size的情況下,允許最後一個batch的數量少於batch_size, 設定為False,則不管什麼情況下,生成的batch都擁有batch_size個樣本;
    第九個引數shared_name: 可選引數,設定生成的tensor序列在不同的Session中的共享名稱;
    第十個引數name: 操作的名稱;
    '''
    #從佇列中讀取指定batch_size個樣本資料
    image_batch, label_batch = tf.train.batch(input_queue, batch_size=batch_size, num_threads=1, capacity=64)
    return image_batch, label_batch
 
image_batch, label_batch = get_batch_data(batch_size=batch_size)
 
with tf.Session() as sess:
    coord = tf.train.Coordinator()
    '''
    tf.train.start_queue_runners 函式來啟動執行檔名佇列填充的執行緒,
    之後計算單元才可以把資料讀出來,否則檔名佇列為空的,
    計算單元就會處於一直等待狀態,導致系統阻塞。
    '''
    threads = tf.train.start_queue_runners(sess, coord) #啟動佇列,讀取資料到佇列
    try:
        for i in range(epoch_num):  # 每一輪迭代
#            print( '************')
            #在每輪中,把所有樣本按照batch_size大小為一組,遍歷每個組,逐個組進行訓練
            for j in range(batch_total):
#                print ('--------')
                # 獲取每一個batch中batch_size個樣本和標籤,這一步等價於從記憶體佇列裡面讀取資料進行計算
                image_batch_v, label_batch_v = sess.run([image_batch, label_batch])
                # for k in
                print(image_batch_v.shape, label_batch_v)
                #這裡列印的是:image_batch_v的維度shape,而label_batch_v列印的是原始資料
    except tf.errors.OutOfRangeError:
        print("done")
    finally:
        coord.request_stop()
    coord.join(threads)

===============================================================================================

# -*- coding: utf-8 -*-
"""
Created on Sat Nov 17 08:09:49 2018

@author: shenfangyuan
"""

import tensorflow as tf
tf.reset_default_graph()
images = ['img1', 'img2', 'img3', 'img4', 'img5']
labels= [1,2,3,4,5]
 
epoch_num=8
 
#f = tf.train.slice_input_producer([images, labels],num_epochs=None,shuffle=False)
f1 = tf.train.slice_input_producer([images, labels],num_epochs=None,shuffle=True)
 
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    for epoch in range(epoch_num):
        k = sess.run(f1)
        print ('************************')
        print ('epoch = ',epoch )
        print('f = ',k)
 
    coord.request_stop()
    coord.join(threads)

===========================================================================================

# -*- coding: utf-8 -*-
"""
Created on Sat Nov 17 10:39:30 2018

@author: shenfangyuan
"""
'''
準備tf訓練集的流程:
    1,使用作業系統os中的方法,得到訓練圖片和標註(圖片)的"檔名字列表",
      相當於在給訓練檔案集合(庫)建立起來一套"索引(名字)"
      image_filename_lists
      image_filename
      這兩個列表結構的第一個維度必須一樣,也就是:有多少條樣本資料(行)就有對應的標籤資料(行)
    2,呼叫tf.train.slice_input_producer()函式,依據名字(索引列表),建立一個佇列
      filename_queue=tf.train.slice_input_producer([image_filename_lists,label_filename_lists])
      該函式返回image_filename_lists和label_filename_lists對應的佇列:queue列表
      filename_queue[0]與image_filename對應;
      filename_queue[1]與image_filename對應
      使用佇列的理由是:可以對這些名字列表中的元素進行隨機化處理;
                      第二個理由是,可以使用多執行緒機制.
    3,我們可以以佇列的filename_queue[i]為基礎,定義tf資料讀取的操作符:例如:
      image_contents = tf.read_file(input_queue[0]) #定義讀取內容的操作符
      image = tf.image.decode_jpeg(image_contents, channels=1) #影象預處理操作符
      這一步就相當於:利用索引從資料庫中獲取對應的內容.執行資料庫的底層讀寫操作.
    4,上面定義了對從庫中讀取內容後的預處理操作符,接下來,把利用索引從庫中讀取的內容,
      按照batch_size的要求"封裝,打包",也就是把batch_size個預處理後的圖片內容,
      放在一個張量中,也相當於:從總表中得到包含batch_size這麼多"行"的"子表".
      這個運算子是由image_batch = tf.train.batch()函式完成的
      image_batch = tf.train.batch([image], batch_size=batch_size)
      這個操作的輸入引數是上一步對檔案內容進行預處理得到的結果的操作符:image
      函式返回的是:獲得一組(batch_size個)資料的操作符.
    5,使用控制檯的run函式,就可以得到一組batch_size資料:
      picture_batch = sess.run(image_batch)
      
需要強調的是:以上的操作都是定義的在tf中將要執行的一些列操作符號,在定義操作符的時候,
            這些操作並沒有被執行,他們相當於用語言,描繪一張tf使用的執行流程圖
            直到最後的sess.run(op操作符),才執行該流程圖中定義的一些列關聯的運算.
'''
import tensorflow as tf
import numpy as np  
import os
import matplotlib.pyplot as plt
tf.reset_default_graph()
 
def show_image(image):
    #顯示單張圖片
    image = np.transpose(image,(2,0,1)) #需要改變一下維度
    plt.imshow(image[0],cmap='Greys_r')                                  
    plt.show()
 
def show_batch_image(batch_image):
    #顯示一個batch的圖片
    for i in range(batch_size):
        image = batch_image[i]
        show_image(image)
           
image_W = 16
image_H = 16

 
train_dir = "..\\test_data\\" #檔案所在的路徑
image_list = []
for file in os.listdir(train_dir):  
    image_list.append(os.path.join(train_dir, file))  
for i in range(len(image_list)):
    print(image_list[i])
    
sample_num = len(image_list)  #樣本的總數量
epoch_num = 3    #用全部樣本迭代的次數
batch_size = 5   #
batch_total = int(sample_num/batch_size)+1 #一個epoch有的batch數目
#給予列表,產生"檔名字"佇列,這裡只有一個元素image_list,那也得用[]括起來
input_queue = tf.train.slice_input_producer([image_list],shuffle = False)  #生成檔名佇列,由於shuffle = True,隨機產生一個檔名路徑
#從文將名字佇列的輸出埠,讀取檔名字並呼叫tf.read_file(佇列埠),讀取檔案內容資料
image_contents = tf.read_file(input_queue[0])   #根據上面的檔名路徑,讀取樣本(處於編碼下)
#把讀取的內容進行jpg解碼
image = tf.image.decode_jpeg(image_contents, channels=1)    #進行解碼,由於讀取的為灰度圖,channels=1
#crop_image = tf.image.resize_image_with_crop_or_pad(image, image_W, image_H) #採用切割的方式改變大小
#對圖片進行裁切
crop_image = tf.image.resize_images(image, [image_W,image_H],method=0)  #採用放縮的方式改變大小,method=0為雙線性插值
#對圖片規範化
standard_image = tf.image.per_image_standardization(crop_image)   # 標準化資料
#把從檔名佇列,到檔案內容,再到用檔案內容構造成訓練batch資料,相當於
#上面的操作定義了tf中檔案讀取的操作,下面定義了構造batch的操作.
image_batch = tf.train.batch([crop_image], batch_size=batch_size, num_threads=2, capacity=64, allow_smaller_final_batch=False) #生成batch
 
with tf.Session() as sess:
    coord = tf.train.Coordinator() #建立執行緒管理器
    threads = tf.train.start_queue_runners(sess, coord) #啟動執行緒
    try:
        for i in range(epoch_num):  # 每一輪迭代
            print ('epoch is %d'%(i+1))
            for j in range(batch_total): #batch遍歷,在所有樣本中選擇batch_size個
                #print ('batch num is %d'%(j+1))
                #print ('input_queue:%s'%(sess.run(input_queue)))     
                #print ('image_contents:%s'%(sess.run(image_contents)))    
                #picture, crop_picture,standard_picture = sess.run([image,crop_image,standard_image])
                #show_image(picture)
                #show_image(crop_picture)
                #show_image(standard_picture)
                picture_batch = sess.run(image_batch) #讓tf執行以上定義的操作
                print(picture_batch.shape) #列印從tf返回到py環境的結果,這裡返回shape
                #(3, 64, 64, 1) 3是batch_size,也就是說,返回的結果包含batch_size張圖片
                #每張圖片是:64x64x1的圖片
#                show_batch_image(picture_batch )
    except tf.errors.OutOfRangeError:  #如果讀取到檔案佇列末尾會丟擲此異常
        print("done! now lets kill all the threads……")
    finally:
        # 協調器coord發出所有執行緒終止訊號
        coord.request_stop()
        print('all threads are asked to stop!')
    coord.join(threads) #把開啟的執行緒加入主執行緒,等待