基于深度學(xué)習(xí)cGAN模型進(jìn)行圖像去霧摘要相關(guān)工作提出模型實(shí)驗(yàn)成果代碼運(yùn)行環(huán)境要求使用步驟(按照實(shí)際遇到的問(wèn)題進(jìn)行了修改宴猾,與官方給出的有點(diǎn)不同)官方Replicating步驟
摘要
本方法選自WiSPNET2020的一篇關(guān)于使用深度學(xué)習(xí)進(jìn)行圖像去霧的文章贞瞒。傳統(tǒng)的從圖像中去除霧霾的方法依賴于對(duì)大氣透射等參數(shù)的估計(jì)笼痹,而當(dāng)處理單一圖像時(shí),由于缺乏深度信息识补,這成為一個(gè)不適定的問(wèn)題。在本文中辫红,作者提出了一種基于端到端學(xué)習(xí)的方法凭涂,利用改進(jìn)的條件生成對(duì)抗網(wǎng)絡(luò)(Conditional GAN, cGAN)直接去除圖像中的霧霾祝辣。文中采用Tiramisu模型代替經(jīng)典的U-Net模型作為生成器,因?yàn)樗哂懈叩膮?shù)效率和性能切油;同時(shí)還采用了基于區(qū)域的判別器來(lái)減少輸出中的偽像蝙斜。此外,為了進(jìn)一步提高輸出的感知質(zhì)量澎胡,作者設(shè)計(jì)了一種混合加權(quán)損失函數(shù)來(lái)訓(xùn)練模型孕荠。通過(guò)采集真實(shí)圖像進(jìn)行訓(xùn)練、驗(yàn)證攻谁、測(cè)試以及與傳統(tǒng)方法對(duì)比稚伍,該模型通過(guò)深度學(xué)習(xí)方法顯現(xiàn)出了一定的優(yōu)勢(shì)。
本文主要有以下四點(diǎn)成果:
介紹使用cGAN直接去除給定單個(gè)場(chǎng)景圖像的霧霾的方法戚宦。
用56層Tiramisu模型代替U-Net架構(gòu)个曙。后者是參數(shù)有效的,并且在語(yǔ)義分割方面具有最先進(jìn)的性能受楼。
在cGAN中使用塊判別器以減少偽像垦搬。
加權(quán)損失函數(shù)除了標(biāo)準(zhǔn)的cGAN生成器損失外,還考慮了L1損失和感知損失的影響艳汽,以激勵(lì)模型產(chǎn)生視覺(jué)上貼合人感知的輸出圖像猴贰。
相關(guān)工作
深度學(xué)習(xí)和計(jì)算機(jī)視覺(jué)的大多數(shù)研究都希望我們的模型告訴我們圖像中存在什么物體或場(chǎng)景,語(yǔ)義分割是這其中信息最豐富的河狐,我們希望對(duì)圖像中的每個(gè)像素進(jìn)行分類米绕。近年來(lái),這大多是通過(guò)深度學(xué)習(xí)完成的甚牲,以上所示的U-Net模型很好地說(shuō)明了語(yǔ)義分割的基本結(jié)構(gòu)义郑。其中,模型的左側(cè)表示經(jīng)過(guò)訓(xùn)練用于圖像分類的任何特征提取網(wǎng)絡(luò)丈钙。經(jīng)典U-Net的方法是基于CNN進(jìn)行構(gòu)建的非驮,主要包含3個(gè)組件:
一個(gè)降采樣模塊,主要用來(lái)提取特征;
一個(gè)升采樣模塊雏赦,被訓(xùn)練用來(lái)在輸出結(jié)果中逐步恢復(fù)輸入精度劫笙;
可選地,一個(gè)后處理模塊(如Conditional Radom Fields)來(lái)優(yōu)化模型輸出星岗。
提出模型
上圖為cGAN模型生成器的主要結(jié)構(gòu)填大,各模塊的聯(lián)合作用與U-Net相似。如圖所示俏橘,它由密度塊(DB)允华,向下過(guò)渡層(TD),向上過(guò)渡層(TU)和瓶頸層組成。另外靴寂,它包含一個(gè)輸入卷積和一個(gè)輸出卷積磷蜀。虛線表示的是串聯(lián)操作。
其中百炬,該模型在編碼側(cè)有5個(gè)密度塊褐隆,在解碼側(cè)也有5個(gè)密度塊,并且有一個(gè)密度塊作為瓶頸層剖踊。如圖是4層密度塊的示意圖庶弃。第一層用來(lái)從輸入圖像中提取出k個(gè)特征圖,并將這些特征圖連接到輸入圖像中德澈。然后第二層用來(lái)提取出另外的k個(gè)特征圖歇攻,這些特征圖又被連接到之前的特征圖中。重復(fù)以上操作4次之后圃验,密度塊的輸出是4層輸出的串聯(lián)掉伏,因此包含4 * k個(gè)特征圖。
在編碼側(cè)澳窑,每個(gè)密度塊(DB)后面都有一個(gè)向下過(guò)渡(TD)層斧散。TD層包括批處理標(biāo)準(zhǔn)化,激活函數(shù)摊聋,卷積鸡捐,丟棄和平均池化操作。類似地麻裁,在解碼側(cè)箍镜,每個(gè)密度塊(DB)后面都有一個(gè)向上過(guò)渡(TU)層。 TU層僅具有轉(zhuǎn)置卷積操作煎源。編碼側(cè)和解碼側(cè)中的每個(gè)DB層都有4個(gè)復(fù)合層色迂。瓶頸層中的DB層包含15個(gè)復(fù)合層。每個(gè)復(fù)合層都包含批處理標(biāo)準(zhǔn)化手销,激活函數(shù)歇僧,卷積和丟棄操作。從空間上說(shuō)锋拖,穿過(guò)TD層后诈悍,圖像的空間尺寸減半,而穿過(guò)TU層后兽埃,圖像的空間尺寸加倍侥钳。
以下是各層在生成器中發(fā)揮的作用:
BN層:加快網(wǎng)絡(luò)的訓(xùn)練和收斂的速度,控制梯度爆炸防止梯度消失柄错,防止過(guò)擬合舷夺。
ReLU層:增加了神經(jīng)網(wǎng)絡(luò)各層之間的非線性關(guān)系苦酱,否則,如果沒(méi)有激活函數(shù)冕房,層與層之間是簡(jiǎn)單的線性關(guān)系躏啰,每層都相當(dāng)于矩陣相乘。
Dropout層:防止過(guò)擬合耙册。
Average Pooling層:下采樣毫捣,降維详拙、去除冗余信息、對(duì)特征進(jìn)行壓縮蔓同、簡(jiǎn)化網(wǎng)絡(luò)復(fù)雜度饶辙、減少計(jì)算量、減少內(nèi)存消耗等等斑粱,實(shí)現(xiàn)非線性弃揽,可以擴(kuò)大感知野,可以實(shí)現(xiàn)不變性则北。
卷積層:下采樣矿微。
轉(zhuǎn)置卷積層:上采樣,從低維度進(jìn)入高維度尚揣。
上圖為cGAN模型判別器的主要結(jié)構(gòu)涌矢。它的輸入包含一張帶霧的圖像和該圖像對(duì)應(yīng)的一張生成網(wǎng)絡(luò)生成的有效圖像。它輸出一個(gè)30x30的矩陣快骗,用于測(cè)試圖像的真實(shí)性娜庇。同樣的,各層的作用和生成器中所對(duì)應(yīng)的層級(jí)是一樣的方篮。
該網(wǎng)絡(luò)對(duì)目標(biāo)圖像和生成的圖像執(zhí)行逐塊比較名秀,而不是逐像素進(jìn)行比較。 將圖像放入CNN來(lái)實(shí)現(xiàn)這一過(guò)程藕溅,該CNN輸出的接收區(qū)域大于一個(gè)像素(即對(duì)應(yīng)于原始圖像中的像素斑塊)匕得。我們使用的是70x70區(qū)域判別器,并且在最后一組特征圖上使用逐像素比較蜈垮。 特征圖上的有效接收區(qū)域大于一個(gè)像素耗跛,因此它覆蓋了圖像的一部分,這消除了圖像中的大量偽像攒发。
1x1 PixelGAN鼓勵(lì)更大的顏色多樣性调塌,但對(duì)空間統(tǒng)計(jì)沒(méi)有影響。16x16 PatchGAN可以產(chǎn)生局部清晰的結(jié)果惠猿,但也會(huì)導(dǎo)致超出其可觀察范圍的平鋪偽像羔砾。70x70 PatchGAN會(huì)在空間和色彩維度上使輸出即使是不正確的也要保持清晰。完整的286x286 ImageGAN產(chǎn)生的結(jié)果在視覺(jué)上與70x70 PatchGAN相似,但根據(jù)FCN網(wǎng)絡(luò)評(píng)分標(biāo)準(zhǔn)來(lái)判斷其質(zhì)量略低姜凄。
相比于直接使用標(biāo)準(zhǔn)生成器損失函數(shù)政溃,我們更傾向用L1損失和感知損失函數(shù)對(duì)其進(jìn)行增強(qiáng),最終組成一個(gè)加權(quán)生成器損失函數(shù)LT态秧。這樣做是為了提高生成圖像的質(zhì)量董虱。我們直接將LT最小化作為目標(biāo)來(lái)訓(xùn)練我們的生成器,公式如下申鱼。
其中愤诱,給定一個(gè)模糊圖像集X及其對(duì)應(yīng)的無(wú)霧圖像集Y,標(biāo)準(zhǔn)生成器目標(biāo)是使LG最小化捐友,公式如下淫半。
另外,使用加權(quán)的L1損失可減少輸出圖像中的偽像匣砖。 目標(biāo)圖像y與生成的圖像??(??)之間的L1損耗計(jì)算如下科吭。
同時(shí),我們將特征重建感知損失添加到了總損失中猴鲫。 但是对人,我們不是使用L2,而是使用按常數(shù)C縮放的均方誤差(MSE)变隔。生成的圖像和目標(biāo)圖像將通過(guò)不可訓(xùn)練的VGG-19網(wǎng)絡(luò)傳遞规伐,然后計(jì)算兩個(gè)圖像輸出的MSE損失。
而判別器的目標(biāo)是最大化以下目標(biāo)損失函數(shù)LD匣缘。
實(shí)驗(yàn)成果
以下是相關(guān)數(shù)據(jù)及參數(shù)的設(shè)定情況:
Dataset: NYU Depth V2 dataset (with the size [256, 256, 3]).
-
Training Details:
-
Performance Metrics:
除霧算法的性能可以從幾個(gè)因素進(jìn)行評(píng)估猖闪,其中兩個(gè)最常用的因素是PSNR和SSIM。峰值信噪比(PSNR)衡量算法從噪聲圖像中去除噪聲的能力肌厨。兩個(gè)相同的圖像將具有無(wú)窮大的PSNR值培慌。
為了測(cè)量除霧能力,較高的PSNR值表示較好的性能柑爸。結(jié)構(gòu)相似性指數(shù)度量(SSIM)吵护,用于度量?jī)蓚€(gè)圖像的相似度。兩個(gè)相同的圖像的SSIM為1表鳍。
相對(duì)而言馅而,PSNR高的圖像在視覺(jué)上無(wú)法保證令人感知質(zhì)量良好,而SSIM高的圖像能夠代表更好的去霧質(zhì)量譬圣。因此瓮恭,我們需要一個(gè)同時(shí)具有這兩個(gè)屬性的指標(biāo)。我們定義了一個(gè)稱為Score的度量值厘熟,它是圖像的PSNR和SSIM的加權(quán)和屯蹦。
以上是訓(xùn)練模型的過(guò)程中根據(jù)當(dāng)前階段網(wǎng)絡(luò)對(duì)圖像進(jìn)行處理维哈,并與原圖對(duì)比之后所得到的性能數(shù)據(jù)比較〉抢剑可以看到阔挠,網(wǎng)絡(luò)在訓(xùn)練過(guò)程中不斷對(duì)圖像進(jìn)行特征提取,包括圖中物體輪廓脑蠕、顏色购撼、亮度、對(duì)比度等因素谴仙,并與無(wú)霧圖像(也就是目標(biāo)圖像)進(jìn)行比較得出相應(yīng)的分?jǐn)?shù)份招。其中,G_D_Step代表的是當(dāng)前經(jīng)歷的生成器+判別器所組成循環(huán)的次數(shù)狞甚。本文中抽取十次優(yōu)化情況用作比較,可以從圖像中直觀地看出廓旬,該網(wǎng)絡(luò)正在逐步的對(duì)圖像的重要特征進(jìn)行逐步提取哼审,所生成的圖像也越來(lái)越相似于目標(biāo)圖像。而從量化數(shù)據(jù)也可以看出孕豹,隨著訓(xùn)練次數(shù)的迭代涩盾,生成圖像與目標(biāo)圖像對(duì)比所得出的Score、PSNR励背、SSIM都在逐漸增加春霍。
以上是帶霧未處理圖像衅疙、經(jīng)過(guò)epoch=5的神經(jīng)網(wǎng)絡(luò)圖像莲趣、經(jīng)過(guò)epoch=20的神經(jīng)網(wǎng)絡(luò)圖像以及原圖像對(duì)比的情況饱溢,以及以上三類圖像與原圖對(duì)比后的Score喧伞、PSNR、SSIM值潘鲫。可以看到彼念,相比于原圖來(lái)說(shuō)靠益,經(jīng)過(guò)訓(xùn)練的GAN模型處理后的圖像無(wú)論是感官上還是量化數(shù)據(jù)上都有了質(zhì)的飛躍,特別是Score、PSNR、SSIM三個(gè)值都接近翻倍羽嫡,可見(jiàn)通過(guò)該模型對(duì)帶霧圖像進(jìn)行去霧的效果較為明顯缸浦。值得注意的是卜高,以上也對(duì)經(jīng)歷了5個(gè)及20個(gè)epoch的模型進(jìn)行了比較,可以看到后者處理得到的圖像在細(xì)節(jié)上還是更為精確南片、輪廓及色彩較為明顯的掺涛,而且其Score、PSNR疼进、SSIM都有小幅的上漲薪缆。
比較可惜的是,由于帶霧圖像的確比較模糊伞广,在深度較大的地方該模型還是不能準(zhǔn)確的還原出圖像中少部分物體的準(zhǔn)確顏色及輪廓矮燎,這可能是由于深度較大的地方其霧污染已使環(huán)境中部分視野完全受限(具體表現(xiàn)為該像素區(qū)域顯示接近全白),從而導(dǎo)致信息量缺失赔癌,后續(xù)可以考慮通過(guò)強(qiáng)化輪廓等基本特征的作用效果進(jìn)行性能優(yōu)化。
代碼運(yùn)行
參考代碼:https://github.com/thatbrguy/Dehaze-GAN
環(huán)境要求
TensorFlow (version 1.4+)澜沟,代碼是基于TensorFlow 1.x編寫的灾票,現(xiàn)在tensorflow.org發(fā)布的最新版本已經(jīng)是2.x的,所以通過(guò)pip等方式安裝一般都是2.x版本茫虽,且1.x的版本通道已經(jīng)被關(guān)閉了刊苍,接下來(lái)我會(huì)講解如何將1.x版本代碼過(guò)渡到2.x版本。
Matplotlib
Numpy
Scikit-Image
NVIDIA GPU驅(qū)動(dòng) 418.x以上 + CUDA Toolkit 10.1 + cuDNN SDK 7.6 (版本不能錯(cuò))
由于本人用的是實(shí)驗(yàn)室臺(tái)式電腦加4塊1080Ti的小型服務(wù)器濒析,不太滿意跑的速度正什,最后是用pycharm連接租的云GPU跑的,硬件環(huán)境不太好的朋友可以參考号杏。
使用步驟(按照實(shí)際遇到的問(wèn)題進(jìn)行了修改婴氮,與官方給出的有點(diǎn)不同)
-
下載github的源碼,可以手動(dòng)下載zip盾致,也可以git:
git clone https://github.com/thatbrguy/Dehaze-GAN.git
需要下載一個(gè)VGG-19模型用于跑出感知損失主经,作者用的是Machrisaa的版本,點(diǎn)擊此處下載庭惜。
-
下載數(shù)據(jù)集罩驻,我用的是和作者一樣的NYU Depth Dataset V2數(shù)據(jù)集』ど蓿可以點(diǎn)擊此處手動(dòng)下載數(shù)據(jù)集胳赌,然后把數(shù)據(jù)集放到項(xiàng)目文件夾中運(yùn)行extract.py提取圖像(注意修改代碼中mat文件路徑)殖蚕,也可以在項(xiàng)目文件夾按照作者指引執(zhí)行命令:
wget -O data.mat http://horatio.cs.nyu.edu/mit/silberman/nyu_depth_v2/nyu_depth_v2_labeled.mat python extract.py
最終得到的是項(xiàng)目文件夾中A和B兩個(gè)文件夾猿棉,分別存放帶霧圖像和對(duì)應(yīng)的無(wú)霧圖像。當(dāng)然這一步也可以用你自己的數(shù)據(jù)集抽高,只需要按要求把圖分好放在兩個(gè)文件夾并確保圖的尺寸是(256, 256, 3)课锌。
-
(重點(diǎn))如上文所說(shuō)厨内,我們需要將TensorFlow 1的代碼遷移到TensorFlow 2適用的格式,這點(diǎn)使用TensorFlow 1的朋友可以跳過(guò)雏胃。在參考官方文檔《Migrate your TensorFlow 1 code to TensorFlow 2》之后得到以下較為簡(jiǎn)單的方法(直接修改代碼的方法可以參照文檔)固棚。
運(yùn)行自動(dòng)upgrade到2.x的命令行:tf_upgrade_v2 --intree my_project/ --outtree my_project_v2/ --reportfile report.txt
其中my_project處填需要轉(zhuǎn)換的項(xiàng)目文件夾路徑此洲,my_project_v2處填存放2.x版本項(xiàng)目的文件夾路徑厂汗,report.txt會(huì)記錄項(xiàng)目中代碼被修改和需要注意的地方娶桦。
另外,這個(gè)命令也不是萬(wàn)能的汁汗,一些沒(méi)有被遷移到2.x的函數(shù)和庫(kù)就需要我們進(jìn)行手動(dòng)替換衷畦,這個(gè)項(xiàng)目中只有一個(gè)需要替換的地方,那就是operations.py中的BN層函數(shù)用了contrib庫(kù)的函數(shù)知牌。通過(guò)參考官方文檔祈争,我們可以用2.x現(xiàn)有庫(kù)中的函數(shù)對(duì)不能使用的函數(shù)進(jìn)行替換,過(guò)程如下:
原代碼:def BatchNorm(input_, isTrain, name='BN', decay = 0.99): with tf.compat.v1.variable_scope(name) as scope: return tf.contrib.layers.batch_norm(input_, is_training = isTrain, decay = decay)
修改后代碼:
def BatchNorm(input_, isTrain, name='BN', decay = 0.99): with tf.compat.v1.variable_scope(name) as scope: # return tf.contrib.layers.batch_norm(input_, is_training = isTrain, decay = decay) return tf.compat.v1.layers.batch_normalization(inputs=input_, momentum=0.99, training=True)
如果還有其他問(wèn)題的話可以根據(jù)報(bào)錯(cuò)提示進(jìn)行修正角寸。
-
運(yùn)行以下命令行訓(xùn)練模型(參考main.py可以查看各參數(shù)說(shuō)明菩混,務(wù)必確保訓(xùn)練模型時(shí)
--mode=train --A_dir=A --B_dir=B --custom_data=true
,如果--custom_data=false
則會(huì)因?yàn)闆](méi)有預(yù)存好的npy文件而報(bào)錯(cuò)扁藕,其他更改可選):python main.py --A_dir=A --B_dir=B --batch_size=2 --epochs=20 --custom_data=true --mode=train --save_samples=false
一個(gè)比較有用的功能是在模型訓(xùn)練的過(guò)程中墨吓,我們可以在每次保存checkpoint的時(shí)候輸出當(dāng)前模型跑出來(lái)的圖像情況,實(shí)現(xiàn)的方法是在項(xiàng)目文件夾中新建文件夾命名為samples(名字可變)纹磺,里面存放符合格式的帶霧圖像文件帖烘,然后命令行修改一下以下參數(shù):
--save_samples=true --sample_image_dir=samples
,然后執(zhí)行橄杨。
在訓(xùn)練完模型后我們可以在文件夾model(或者你修改的model_name對(duì)應(yīng)的文件夾)中的checkpoint找到訓(xùn)練結(jié)果秘症,默認(rèn)保存的是后三次的checkpoint照卦。注意因?yàn)榇a編寫的問(wèn)題,打開(kāi)checkpoint文件會(huì)發(fā)現(xiàn)里面的路徑可能會(huì)被保存為絕對(duì)路徑乡摹,為了避免后續(xù)讀取checkpoint失敗可以將路徑改為相對(duì)路徑役耕。 -
運(yùn)行以下命令行測(cè)試模型(同樣要參考main.py可以查看各參數(shù)說(shuō)明):
python main.py --A_dir=input_dir --B_dir=GT_dir --mode=test
其中input_dir為存放待處理帶霧圖像的文件夾,GT_dir為存放對(duì)應(yīng)無(wú)霧圖像的文件夾聪廉,該命令會(huì)將帶霧圖像放到模型中處理瞬痘,然后把處理后圖像與ground truth圖像進(jìn)行對(duì)比得到Score、PSNR板熊、SSIM顯示在控制臺(tái)框全。
-
運(yùn)行以下命令行應(yīng)用模型(同樣要參考main.py可以查看各參數(shù)說(shuō)明):
python main.py --A_dir=input_dir --B_dir=result_dir --mode=inference
其中input_dir為存放待處理帶霧圖像的文件夾,result_dir為存放對(duì)應(yīng)的處理后圖像的文件夾干签,該命令會(huì)將帶霧圖像放到模型中處理津辩,然后把處理后圖像存放到result_dir中。
-
為了計(jì)算本地一些用其他方法跑出來(lái)的圖片對(duì)應(yīng)的Score容劳,我根據(jù)test函數(shù)修改了一版test_local函數(shù)喘沿,可以直接計(jì)算本地圖片之間的Score、PSNR竭贩、SSIM值蚜印,將代碼放到inference函數(shù)后面,然后新增main.py選項(xiàng)留量、運(yùn)行命令行時(shí)修改參數(shù)
--mode=test_local
就行晒哄,代碼如下:
test_local函數(shù)代碼:def test_local(self, input_dir, Real_dir): total_ssim = 0 total_psnr = 0 psnr_weight = 1/20 ssim_weight = 1 Real_list = os.listdir(Real_dir) input_list = os.listdir(input_dir) for i, (img_file, Real_file) in enumerate(zip(input_list, Real_list), 1): img = cv2.imread(os.path.join(input_dir, img_file), 1).astype(np.uint8) Real = cv2.imread(os.path.join(Real_dir, Real_file), 1).astype(np.uint8) print('Test image', i, end = '\r') psnr = compare_psnr(Real, img) ssim = compare_ssim(Real, img, multichannel = True) total_psnr = total_psnr + psnr total_ssim = total_ssim + ssim average_psnr = total_psnr / len(Real_list) average_ssim = total_ssim / len(Real_list) score = average_psnr * psnr_weight + average_ssim * ssim_weight line = 'Score: %.6f, PSNR: %.6f, SSIM: %.6f' %(score, average_psnr, average_ssim) print(line)
main.py執(zhí)行函數(shù)修改:
if __name__ == '__main__': args = parser.parse_args() net = GAN(args) if args.mode == 'train': net.train() if args.mode == 'test': net.test(args.A_dir, args.B_dir) if args.mode == 'inference': net.inference(args.A_dir, args.B_dir) # New part below for test_local function if args.mode == 'test_local': net.test_local(args.A_dir, args.B_dir)
官方Replicating步驟
為了讓每位用戶都能親身體驗(yàn)到最后模型的處理結(jié)果,作者提供了已經(jīng)訓(xùn)練好的checkpoint文件以及相應(yīng)的數(shù)據(jù)集肪获,只需要根據(jù)github中指引下載到legacy文件夾并解壓使用就行,此處不詳述柒傻。
最后想啰嗦一句的是在實(shí)際跑代碼的過(guò)程中我本人也有遇到挺多小問(wèn)題孝赫,但基本上只要不斷通過(guò)看錯(cuò)誤提示和debug都能夠找到解決辦法,大部分都是tensorflow版本不兼容红符、cuda運(yùn)行不正常青柄、路徑或文件格式不正確等等。另外神經(jīng)網(wǎng)絡(luò)的可擴(kuò)展性還是挺高的预侯,可以多多嘗試致开。