圖像訓練時的數據處理

當訓練數據時,如果數據只是一列,而輸出也是一列,那就對應着(train,label)輸入模型即可。但是如果處理對象是圖片時,應該如何將圖片作爲數據輸入模型。我只記錄到目前爲止的理解作爲參考,後續還會修改(應該會的吧!)

一、對圖片的結構的瞭解

圖片分成了彩色圖和非彩色圖:

彩色圖,我們用python中任意一個可以讀入圖片的函數讀入查看,可以得到彩色圖的維度是(H,W,channel=3)

非彩色圖,我認爲可以分爲灰度圖和黑白圖,非彩色圖他們都只有二維,即(H,W),我原本以爲會有一個channel=1,後來發現並木有。灰度圖是(H,W)維的0~255的數值,而黑白圖除了0就是255,沒有別的其他值(哦,順便提一句,H是height,W是width,channel是顏色通道)

在這裏,我想要着重的對mask圖(也就是image對應的label)進行分析,我們輸入的image就是良民,沒什麼特殊的。但是我們輸入的mask圖卻有的不太對勁:

如果是二分類問題,那麼mask圖應當是二值的,如果二值指的是0和255,那麼畫面就十分和諧了,就像我們常規認識的那樣:

 

輸入的image(灰度圖)

 

對應的mask圖(二值圖)

 

如果是多分類問題,當我們輸下左邊圖一的照片時,在我的印象中應該對應的mask是一個美美的就像圖二有木有,

但是實際的圖是個什麼玩意,居然全是黑的圖三!!(於是我一度認爲這個數據集是壞的)

圖1 輸入的image(彩色圖)
圖2 理想中的mask圖

 

 

 

 

 

 

 

 

圖3 實際的mask圖

 

 

 

 

 

 

 

 

 

實際上這個黑乎乎的圖並沒有壞!它就是這個樣兒!因爲多分類的mask圖裏每一個像素點的值其實是類別值,比如說我們要分成12類,那麼這張圖裏面的像素點的值全都是在0-11的數字。而我們知道0是黑,255是白,在0-11之間的數字在我們人眼看來也和全黑差不多了。但是當你把這張圖讀入並查看它的每一個像素點時,你就會發現其實裏面是有蹊蹺的。

如果想要將這張黑圖顯示出來,我們可以使用一些塗色的手段,在這裏貼一下我之前找到的函數。

(1)首先是創建一個顏色字典,留着爲後面用

Sky = [128,128,128]
Building = [128,0,0]
Pole = [192,192,128]
Road = [128,64,128]
Pavement = [60,40,222]
Tree = [128,128,0]
SignSymbol = [192,128,128]
Fence = [64,64,128]
Car = [64,0,128]
Pedestrian = [64,64,0]
Bicyclist = [0,128,192]
Unlabelled = [0,0,0]

COLOR_DICT = np.array([Sky, Building, Pole, Road, Pavement,
                          Tree, SignSymbol, Fence, Car, Pedestrian, Bicyclist, Unlabelled])
#首先創建一個顏色字典,可以把它想象成一個染色盤(小學美術課用的那種),然後我們按照序號每一類取一個
#顏色出來塗上,至於名字爲什麼取什麼sky,buliding?哎想怎麼叫怎麼叫唄!

 (2)其次是調用染色函數(染好色以後上述的黑黑的mask圖三就會變成美美的圖二了)(問題1:除255如果有人看懂了可以留個言告告我哈哈。。我也沒整明白,感jio不除纔對,結果除了數值沒變,不除反而報錯,哎你說我這暴脾氣

def labelVisualize(num_class,color_dict,img):
    
#img就是我們的黑黑的mask圖,而img是(H,W)二維的,我們需要加上一維,也就是下面這行
    img_out = np.zeros(img.shape + (3,))
#img.shape返回的是(H,W)的元組,而(3,)也代表元組,只不過是一維的,二者相加後就是一個
#三維的元組(H,W,channel)
    for i in range(num_class):
        img_out[img == i,:] = color_dict[i]
#總共循環num_class次,比如numclass=11,那麼我們在循環11次過程中,每一次我們都找到img上像素點等於i
#的位置爲它塗上第i種顏色(因爲我們的黑黑的mask圖像素點都是0-11之間的值),塗完後就可以了
    return img_out / 255
#這兒爲啥除以255呢???
#不除的話會報錯ValueError: Images of type float must be between -1 and 1 具體原因希望有人可以tell me

此外,雖然我沒有見過,但是如果某一天二分類問題時候的mask圖也是黑色說不定裏邊就只有0和1,處理同上 。

 

二、訓練過程中圖片結構的變化了解

我們知道,我們在訓練時候加入輸入的image結構是s1,而訓練時輸入的mask是s2。那麼,我們在訓練好後測試時也應當輸入結構爲s1的圖片,並且我們會得到結構爲s2的結果。另外,有一點需要注意的是,s1和s2我們也不能隨意規定,因爲你的model的輸入輸出維度纔可以決定你要放入的圖片維度。那麼,我們怎麼去修改圖片的結構呢?

爲了具體,我在這裏選擇了一個模型(FCN)作爲講解的model。首先,我們將模型summary一下觀察一下它的整體結構:

省略了中間部分,這裏只截取了輸入和輸出模型時的維度:

輸入部分的維度
輸出部分的維度

首先分析輸入,輸入部分這裏只放了第一層卷積後的層爲(none,256,256,64),none指的是batchsize可以爲任意值,256,256是指本次卷積後的feature-map的長和寬,最後一維64指的是有64個filter因此生成深度爲64。從這些分析,我認爲輸入的image應該是(batchsize,H,W,channel),channel應該是可有可無的,因爲它並沒有什麼作用(因爲卷積過程會根據是否爲彩色圖對卷積核作相應的調整,因爲我在本次實驗中用到了flow_from_directory這樣一個生成器函數,生成的返回值中是四維,也就是包括了channel所以我在這裏也帶上channel一起說)。

其次分析輸出,輸出的(none,65536,2)是指(none,H*W,num_class)。這裏稍作解釋,模型的輸出是將一個(H,W)的mask圖(上面包括了num_class種種類),轉變成了一個(H*W,num_class)維度的one-hot編碼形式。並且這裏的模型由於加入了softmax層,輸出的是one-hot編碼下各種類的概率值。什麼??沒聽懂怎麼就是one-hot編碼形式了??那麼我手畫個圖給大家演示一波!

大致就是這樣轉換成了one-hot編碼方式,具體轉換的代碼如下:

    new_mask = np.zeros(mask.shape+(num_class,))
    # new_mask的shape是(batchsize,H,W,num_class)
    for i in range(num_class):
        new_mask[mask==i,i]=1
        #new_mask的每一層都標記上該層上像素值等於i的位置
   
    new_mask=np.reshape(new_mask,new_mask.shape[0],new_mask.shape[1]*new_mask.shape[2],mask.shape[3]))
#在reshape後的new_mask的維度成了(batchsize,H*W,num_class)也就是onehot編碼了

那麼,既然我們訓練時候的mask是one-hot形式,那麼我想,當我們輸入訓練集的時候,輸出應該也是一個onehot編碼方式,並且因爲加入了softmax層後輸出的是概率值,應該不只是在對應的類別那一列標1,應該是每一類的概率。那麼如果我們想要把每一個像素究竟屬於哪一類找出來,應該找出每一列中概率值最大的作爲該像素點的類別,之後再reshape成正常的(H,W)圖就可以了。

具體的實現可以這樣做:

        mask=np.argmax(item,axis=1)#橫着一行一行看,記錄下來每一行的最大值概率所在的位置也就是該像素的類別
        mask = np.reshape(mask,(H,W))

這樣我們得到的就是和訓練時候一樣的(H,W)的mask圖了,如果是多分類可以用上面的塗色的函數顯示,如果是二分類可以直接乘以255。然後保存即可!

三、一個bug,but我還沒有想好,但是有可能是你出錯的地方

我們知道,在訓練的時候,我們會使用數據增強來增加數據,比如說我使用的方法如下:

data_gen_args = dict(rotation_range=0.2,
                    width_shift_range=0.05,
                    height_shift_range=0.05,
                    shear_range=0.05,
                    zoom_range=0.05,
                    horizontal_flip=True,
                    fill_mode='nearest')
#用於數據增強的選項
mask_datagen = ImageDataGenerator(**aug_dict)
mask_generator = mask_datagen.flow_from_directory(   
        train_path,
        classes = [mask_folder],
        class_mode = None,
        color_mode = mask_color_mode,
        target_size = target_size,
        batch_size = batch_size,
        save_to_dir = save_to_dir,
        save_prefix  = mask_save_prefix,
        seed = seed
)
#通過flow_from_directory可以無限的生成增強後的圖片,是一個棒棒的生成器函數

但是如果使用生成器的話,就會產生如下的問題:

我輸入的mask是二值圖(只有0和255)

當我歸一化後應當只有0和1,然後採用上述轉化方法轉換爲one-hot編碼是沒有問題的,但是如果數據增強的話,其中就會不只是0和255,而是產生了一些浮動的float類型的數字,如下:

當前的mask圖狀態呢如下

可見,這裏的image和mask都變成float類型了,而且我發現mask中除了0和255還多出來不少別的數,怪不得我用newmask[mask==i,i]發現白色(也就是類別1)全是0,因爲這個圖上根本就找不到1啊(歸一化後)!!

並且如果是多分類問題的話,那麼原本輸入的mask圖中可能是0-numclass的一些整數,使用newmask[mask==i,i]就可以轉爲one-hot編碼,但是若是數據增強後便會有浮動而非是0-numclass的整數,這樣的話,想要轉爲onehot編碼採用我上述的手段就不行了,bug源頭出在這裏,如果是二分類我們可以直接除以255後根據結果是否小於0.5來賦值0還是1,但是如果是多分類問題該如何轉換呢?暫時還沒想出來,這是我的第二個問題。

 

先記錄至此,未完待續。。。。。希望有人可以告我如何解決(#^.^#)!

 

 

 

 

 

 

 

發佈了15 篇原創文章 · 獲贊 1 · 訪問量 6766
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章