[轉]用深度學習給黑白照片上色


用深度學習給黑白照片上色

覺得有用的話,歡迎一起討論相互學習~

我的微博我的github我的B站

轉載自
https://www.jianshu.com/p/ab1a003f2275#4
真誠感謝簡書博主氧化反應的分享,讓我學習到了很多!

  • 深度學習里面有很多看起來很簡單但是實際卻有大用場的算法。Autoencoder作為其中的一種就是。作為一種無監督學習的手段,autoencoder在維度災難里為數據降維有着深遠的意義。

  • 什么是Autoencoder呢?我大概的理解是這樣的,比如說我們提取一張500x500的彩色圖片的時候,按照每個像素來算就有500x500x3(RGB顏色)=750000個像素,這么多的像素就得對應這么多的權重,如果再是大一點的數據那訓練的時候用的資源就海了去了。但是,圖片里每一個像素都是有用的么?不盡然,比如我想要一個模型,來感知一張人臉是開心還是不開心,除了面部的那些像素,其余的很多像素都是浪費的,或者說對判別人的表情不是那么重要的,而且這些有用的像素分布在一個三維的空間里,就像一大袋米里面的幾顆綠豆,這種稀疏性也會給訓練帶來不必要的麻煩。那我怎么能把圖中的信息濃縮起來?用極少甚至一個維度的向量就能表示最重要的信息呢?

  • Autoencoder就是很好的一個工具。給一個圖。

  • 假設我們用cnn的方法對一張圖做autoencoding, 大家知道cnn網絡是提取圖里的有效信息,pooling作為一種降維的手段濃縮信息。大概濃縮了幾次之后,形成中間那短短的一條,我們就叫它羞羞的棒棒吧(因為它沒別人長,哈哈哈哈)。 它不是真短,可以很長,但是比起原本輸入的數量那是要短的多。好,濃縮的這一邊我們叫它encoding。在另一邊,有個做完全相反的事情,就是從這條羞羞的棒棒逐步撐回到原來圖片大小的維度,得到一個輸出。這個輸出會和輸入的圖片做對比,如果有差異,做反向傳播得到新權重。最終得到的這個羞羞的棒棒里的權重值,就是原圖的濃縮精華。

  • “我對着鏡子做一個惟妙惟肖的小泥像。” 大概這個意思。 這條羞羞的棒棒是很厲害的,不僅能濃縮精華,還能記錄一些少量的差異。比如輸入的圖像有些噪點,對比的圖像是高清,這個羞棒可以記錄下噪點到高清的邏輯關系,理論上就能解馬賽克啦(哈哈哈)。同樣,如果輸入是黑白照片,訓啦的對比圖是彩色照片,那這個羞棒可以描述照片上黑白到彩色的關系,就能給他沒見過的黑白照片上色啦。

  • 扯太多了,直接上code,還是用keras。

  • 我的訓練數據是前陣子做kaggle上辨別貓狗的訓練數據,這里可以下到,人太懶,懶得找其他數據,沒辦法。

ROW = 80
COL = 80
CHANNELS = 3

TRAIN_DIR = 'cat_dog_train/'
TEST_DIR = 'cat_dog_test/'

train_images = [TRAIN_DIR+i for i in os.listdir(TRAIN_DIR)] #讀取數據

def readImg(imgFile):
    colorImg = cv2.imread(imgFile, cv2.IMREAD_COLOR)
    colorImg = cv2.cvtColor(colorImg, cv2.COLOR_BGR2RGB)
    colorImg = cv2.resize(colorImg, (ROW, COL))/255.0
    greyImg = cv2.imread(imgFile, cv2.IMREAD_GRAYSCALE)
    greyImg = cv2.cvtColor(greyImg,cv2.COLOR_GRAY2RGB)
    greyImg = cv2.resize(greyImg, (ROW, COL))/255.0
    return greyImg,colorImg

def generateDate(imgFiles):
    count = len(imgFiles)
    dataX = np.ndarray((count, ROW, COL,CHANNELS), dtype=float)
    dataY = np.ndarray((count, ROW, COL,CHANNELS), dtype=float)
    for i, image_file in enumerate(imgFiles):
      gImg,cImg = readImg(image_file)
      dataX[i] = gImg
      dataY[i] = cImg  
    return dataX,dataY

import math
def chunked(iterable, n):
    chunksize = int(math.ceil(len(iterable) / n))
    return (iterable[i * chunksize:i * chunksize + chunksize]
            for i in range(n))

BATCHNUMBER= 20
chuckedTrainList = list(chunked(train_images,BATCHNUMBER))

# slice datasets for memory efficiency on Kaggle Kernels, delete if using full dataset
random.shuffle(train_images)
  • 兩個准備數據的方法,第一個是把每張訓練用的圖片,轉一份拷貝為黑白的,另一張彩色的用作對比Y。注意下,openCV這個鬼東西讀入圖片默認是BRG。。需要轉一下成RGB,不然顯示圖片的時候會出錯,對了 別忘了除上255做歸一化。第二個方法不用解釋了,第三個方法是用來吧數據分割成幾塊做訓練。

  • 下面到了最激動人心的搭建模型時刻

baseLevel = ROW//2//2
input_img = Input(shape=(ROW,COL,CHANNELS))
x = Convolution2D(256, 5, 5, activation='relu', border_mode='same')(input_img)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(128, 5, 5, activation='relu', border_mode='same')(x)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Flatten()(x)

encoded = Dense(2000)(x)
one_d = Dense(baseLevel*baseLevel*128)(encoded)
fold = Reshape((baseLevel,baseLevel,128))(one_d)

x = UpSampling2D((2, 2))(fold)
x = Convolution2D(128, 5, 5, activation='relu', border_mode='same')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(3, 5, 5, activation='sigmoid', border_mode='same')(x)

autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
  • input 是一張圖片80x80的像素,做兩層卷積的操作做加上兩次池化,每次長寬減半,到最后圖就剩下20x20了。再把這20x20xfilter#(128)個元素拉直,傳入傳說中羞羞的棒子(也不短啦其實,有2000呢)。這個羞羞的棒子就成為了一張圖片的精華! 下面的代碼就是上面部分的反操作。最后用sigmoid函數激活。簡單明了。
checkpoint = ModelCheckpoint(filepath="path/weights.hdf5", verbose=1, save_best_only=True)
earlyStopping=keras.callbacks.EarlyStopping(monitor='val_loss', patience=0, verbose=0, mode='auto')

cbks = [checkpoint,earlyStopping]

digitalImg = dict()
epoch = 500

for ii in range(epoch):
    i = ii%len(chuckedTrainList)
    print("epoch =====>"+str(ii))
    imgfiles = chuckedTrainList[i]
    if str(i) in digitalImg:
        dataX = digitalImg[str(i)][0]
        dataY = digitalImg[str(i)][1]
    else:
        dataX,dataY = generateDate(imgfiles)
        digitalImg[str(i)] = (dataX,dataY)

    autoencoder.fit(dataX, dataY,
                nb_epoch=1,
                batch_size=50,
                shuffle=True,
                verbose=2,
                validation_split=0.3,
                callbacks=cbks)
  • 接下來就開始訓練了,定500個epoch。兩個callback蠻有用的,一個是當訓練的過程有大的進步,就把最好的訓練成果備份下來。另一個是當訓練不再提升了,自動結束訓練。
  • 最后,我們來看看結果:

這是10個epoch產生的效果,卷積核3:

  • 最左邊的圖是模型從來沒見過的黑白照片,中間這張是模型根據訓練的羞羞棒棒給出的預測結果,右邊的是做對比的彩色原圖。

  • 10個epoch的訓練太少了,但是大概能看出那么點意思來,很多圖片里的細節被忽略掉了,顏色也和沙盤一樣灑滿一地。

400個epoch,卷積核3:

  • 現在照片已經有輪廓了,至少狗的形狀都能看見了。顏色嘛沒有那么明顯,但是模型大概知道應該往哪兒填和填什么了。

400個epoch 卷積核改成5:

  • 改成5后每個卷積核收集到的信息更多,能抓取更多的像素間的關系。效果是這樣的。

  • 看這個狗已經可以很看的清楚輪廓了,模型知道把周圍環境的顏色和狗給區分開來。

400個epoch 卷積核5 卷積層改為256 輸出:

  • 原來的卷積層只有128的輸出,這一步加了一倍,就是能提供給模型分析更多features的空間多了一倍。但已經是我可憐的GPU的極限了,話說贊一下我的GPU,文能打守望,武能算模型,全能小天才啊有沒有。

  • 到目前已經有更多的細節了,顏色也越來越能分得清楚了。最后一張狗簡直在賣萌啊有木有。

  • 總結:花了很多時間在調參數上,最后一批的結果算是在我硬件能力范圍比較好的結果了。Autoencoding真的是一項很牛逼的技術,簡單直接。但是也有一定的局限性。 它只考慮了重建誤差最小,而一張圖片的顏色和圖片的實質內容是有關聯的,這種信息不能被抓捕到, 最近很火的對抗網絡能從某種程度上很大的解決這個問題,所以用對抗網絡對照片填色這個工作有很好的效果。 另一個原因,也可能是我的訓練數據集實在是太小了,只有25000張訓練圖,新照片上很多信息模型都無法知道是什么,所以無法填色。 最后,再贊一下我的GPU,你要是內存再大些就好了。。。

原碼看這里


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM