介紹
大多數影象去噪器技術專注於去除AWGN(高斯白噪聲)。 通常,噪聲是綜合新增的並且涉及各種技術來去除這些影象。 但是隨著深度學習的進步,重點已轉向為現實世界中的嘈雜彩色影象設計降噪架構。 實際的嘈雜影象是透過具有不同設定或在弱光條件下的不同攝像機獲得的。 在較低的相機ISO設定下或在強光條件下,也可以獲得相應的清晰影象。 具有乾淨且嘈雜的影象對,我們可以訓練深度學習卷積體系結構以對影象進行降噪。 影象去噪效果可能是肉眼可見的。 我使用PSNR和SSIM指標來衡量影象去噪器效能。
要解決的問題
不能完全保證在攝影中提供高質量的影象。 有時由於光線不足或相機快門速度慢而導致影象損壞。 影象在傳輸過程中以及壓縮時都會被破壞。 對這些低質量影象進行降噪以使其與理想條件下的影象相匹配是一個非常苛刻的問題。
將歸納到DL的問題
我們有兩個影象對,一個是嘈雜的,另一個是乾淨或真實的影象。我們訓練卷積架構以消除噪聲。這不是分類問題。在分類中,將“ X”視為特徵,將“ Y”視為二進位制值或分類值。在影象降噪器中,我們將“ X”作為噪點影象,將“ Y”作為真實影象或乾淨影象。當我們在影象畫素級別上進行操作時,我們將平方損耗用作損耗函式。我們試圖使總畫素級別損失最小化。諸如adadelta,adam之類的任何現代最佳化器都可以用作最佳化器。
測量指標:
PSNR:PSNR塊計算兩個影象之間的峰值信噪比,以分貝為單位。該比率用作原始影象和壓縮影象之間的質量度量。 PSNR越高,壓縮或重構影象的質量越好。
均方誤差(MSE)和峰值信噪比(PSNR)用於比較影象壓縮質量。 MSE代表壓縮影象和原始影象之間的累積平方誤差,而PSNR代表峰值誤差的量度。 MSE的值越小,誤差越小。
PSNR = 10log10(R * R / MSE)
R =畫素的最大值
MSE =乾淨畫素和噪聲畫素的均方誤差
SSIM:這是一種預測數字電視和電影影象以及其他型別的數字影象和影片的感知質量的方法。 SSIM用於測量兩個影象之間的相似度。 SSIM索引是完整的參考指標;換句話說,影象質量的測量或預測基於初始未壓縮或無失真的影象作為參考。
資料來源
我從下面的連結中收集了“Renoir”和“ NIND”資料集。 歸功於準備這些資料集的人員。 從下面的連結中找到有關專案和資料集相關人員的資訊。
arxiv:1906。00270
我最初從這些來源收集了約600張影象。 影象平均大小為30 MB,並且大小超過2500 * 2500。 由於在訓練時很難將這些影象適配到記憶體中,因此我將它們的大小調整為256 * 256並訓練了模型。 但是後來我發現調整大小並不是一個好主意,因為它會在壓縮時增加自身的噪音或資訊丟失。 然後,我將原始影象切成小塊,這很好,沒有任何資訊丟失。 例如,如果影象尺寸為2560 * 2560,我將其切成100塊256 * 256。 僅用一張影象,我就生成了100多幅影象用於訓練。 這樣,我準備了3791張影象進行訓練而577張影象進行測試的資料集。
資料擴充應用於翻轉和旋轉資料集。
嘈雜和乾淨的影象的例子
不同的架構/模型
三星MRDNet
三星團隊在NTIRE 2020挑戰中使用了此體系結構。
相關論文
arxiv。org:2005。04117。 本文提出了10多種用於現實世界影象降噪的架構,作為2020年CVPRW競賽的一部分。我使用的是獲得第三名的架構。
基於多尺度殘差密集塊的實像去噪。三星SLSI MSL團隊在“NTIRE 2020真實影象降噪挑戰”競賽中提出了MRDN體系結構。
多尺度殘差密集網路(Multi-scale Residual Dense Network, MRDN)是基於一種新的基本模組——多尺度殘差密集塊(Multi-scale Residual Dense Block, MRDB),如圖2 (a)所示。MRDB結合了來自ASPP的多尺度特徵和傳統殘差密集塊(Residual Dense Block, RDB)的其他特徵。如圖2 (b)所示,ASPP包含四個並行網路塊,分別是Conv 1×1、Conv Rate 6、Conv Rate 12和pooling。Conv Rate 6和Conv Rate 12分別表示3×3膨脹卷積,膨脹率為6和12。Conv Rate 6、Conv Rate 12和影象池化可以很好地捕獲塊輸入的多尺度特徵。從ASPP輸出的特性被連線和壓縮以與RDB的其他特性相結合。為了有一個無縫的本地剩餘連線,這個連線特性被另一個Conv 1×1壓縮在一個元素級加器之前。MRDB的輸出保持了輸入的通道數不變,從而避免了複雜度的指數級增長。MRDB作為構建模組,MRDN採用與RDN類似的方式構建網路,MRDB之間透過密集連線進行級聯。採用Conv 1×1對mrdb的輸出進行級聯壓縮,並採用全域性殘差連接獲取乾淨特徵。
Keras 程式碼實現
def denseBlock(previous_output,ks,depth):
op_x1=Conv2D(depth,(ks,ks),padding=‘same’,kernel_initializer=‘he_normal’,kernel_regularizer=l2(0。03),\
bias_regularizer=l2(0。03))(previous_output)
op_x2=Activation(‘relu’)(op_x1)
conc1=concatenate([previous_output,op_x2],axis=-1)
op_x3=Conv2D(depth,(ks,ks),padding=‘same’,kernel_initializer=‘he_normal’,kernel_regularizer=l2(0。03), \
bias_regularizer=l2(0。03))(conc1)
op_x4=Activation(‘relu’)(op_x3)
conc2=concatenate([previous_output,conc1,op_x4],axis=-1)
op_x5=Conv2D(depth,(ks,ks),padding=‘same’,kernel_initializer=‘he_normal’,kernel_regularizer=l2(0。03), \
bias_regularizer=l2(0。03))(conc2)
op_x6=Activation(‘relu’)(op_x5)
conc3=concatenate([previous_output,conc1,conc2,op_x6],axis=-1)
op_x7=Conv2D(depth,(ks,ks),padding=‘same’,kernel_initializer=‘he_normal’,kernel_regularizer=l2(0。03), \
bias_regularizer=l2(0。03))(conc3)
op_x8=Activation(‘relu’)(op_x7)
out_aspp=ASPP(previous_output,depth)
conc3=concatenate([previous_output,conc1,conc2,conc3,op_x8,out_aspp],axis=-1)
mdr_out=Conv2D(128, (1,1), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(conc3)
final_mdr_out=Add()([mdr_out,previous_output])
return final_mdr_out
#ASPP block
def ASPP(previous_output,depth):
op_x1=Conv2D(depth,(3,3),padding=‘same’,kernel_initializer=‘he_normal’,kernel_regularizer=l2(0。03), \
bias_regularizer=l2(0。03))(previous_output)
op_x2=Activation(‘relu’)(op_x1)
op_x3 = Conv2D(depth, (1,1), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(op_x2)
op_x3 = Dropout(0。3)(op_x3)
op_x4 = Conv2D(depth, (3,3), padding=‘same’,dilation_rate=6,kernel_regularizer=l2(0。03), \
bias_regularizer=l2(0。03))(op_x2)
op_x4 = Dropout(0。3)(op_x4)
op_x5 = Conv2D(depth, (3,3), padding=‘same’,dilation_rate=12,kernel_regularizer=l2(0。03), \
bias_regularizer=l2(0。03))(op_x2)
op_x5 = Dropout(0。3)(op_x5)
op_x6 = MaxPooling2D((3,3), strides=(1,1), padding=‘same’)(op_x2)
conc4 = concatenate([op_x3,op_x4,op_x5,op_x6],axis=-1)
op_x7 = Conv2D(depth, (1,1), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(conc4)
return op_x7
#Sequential model starts from here。
depth=128
first_input=Input(shape=(256,256,3))
inp1 = Conv2D(depth, (3,3), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(first_input)
inp2 = Conv2D(depth, (3,3), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(inp1)
inp3 = denseBlock(inp2,3,128)
inp3 = Dropout(0。3)(inp3)
inp4 = denseBlock(inp3,3,128)
inp4 = Dropout(0。3)(inp4)
conc = concatenate([inp2,inp3,inp4],axis=-1)
conv3 = Conv2D(depth, (1,1), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(conc)
conv4 = Conv2D(depth, (3,3), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(conv3)
add = Add()([inp1,conv4])
conv5 = Conv2D(depth, (3,3), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(add)
outfinal = Conv2D(3, (3,3), padding=‘same’,kernel_regularizer=l2(0。03), bias_regularizer=l2(0。03))(conv5)
#create model
model=Model(inputs=first_input,outputs = outfinal)
在下面的圖中可以看到使用上述模型的預測影象的去噪效果。
MWRCAnet
上述去噪架構由百度Research Vision和HITVPC&HUAWEI團隊提出。
arxiv:2005。04117。作為NTIRE 2020年競賽的一部分,本文介紹了10多個用於真實世界影象去噪的架構。我使用的是一個贏得了第二排名的架構,如上所示。該體系結構包括一個稱為Residual Channel attention block的特殊塊。
class dwt(Layer):
def __init__(self, **kwargs):
super()。__init__(**kwargs)
def get_config(self):
config = super()。get_config()。copy()
return config
def call(self, x):
x1 = x[:, 0::2, 0::2, :] #x(2i−1, 2j−1)
x2 = x[:, 1::2, 0::2, :] #x(2i, 2j-1)
x3 = x[:, 0::2, 1::2, :] #x(2i−1, 2j)
x4 = x[:, 1::2, 1::2, :] #x(2i, 2j)
print(x1)
x_LL = x1 + x2 + x3 + x4
x_LH = -x1 - x3 + x2 + x4
x_HL = -x1 + x3 - x2 + x4
x_HH = x1 - x3 - x2 + x4
return Concatenate(axis=-1)([x_LL, x_LH, x_HL, x_HH])
class iwt(Layer):
def __init__(self, **kwargs):
super()。__init__(**kwargs)
def get_config(self):
config = super()。get_config()。copy()
return config
def call(self, x):
x_LL = x[:, :, :, 0:x。shape[3]//4]
x_LH = x[:, :, :, x。shape[3]//4:x。shape[3]//4*2]
x_HL = x[:, :, :, x。shape[3]//4*2:x。shape[3]//4*3]
x_HH = x[:, :, :, x。shape[3]//4*3:]
x1 = (x_LL - x_LH - x_HL + x_HH)/4
x2 = (x_LL - x_LH + x_HL - x_HH)/4
x3 = (x_LL + x_LH - x_HL - x_HH)/4
x4 = (x_LL + x_LH + x_HL + x_HH)/4
y1 = K。stack([x1,x3], axis=2)
y2 = K。stack([x2,x4], axis=2)
shape = K。shape(x)
return K。reshape(K。concatenate([y1,y2], axis=-1), K。stack([shape[0],\
shape[1]*2, shape[2]*2, shape[3]//4]))
def channel_attention(input_feature,channel,ratio):
x=GlobalAveragePooling2D()(input_feature)
x=Reshape((1,1,channel))(x)
assert x。shape[1:] == (1,1,channel)
x=Conv2D(channel // ratio,1,activation=‘relu’,kernel_initializer=‘he_normal’,\
use_bias=True,bias_initializer=‘zeros’)(x)
assert x。shape[1:] == (1,1,channel//ratio)
x = Conv2D(channel,1,activation=‘sigmoid’,kernel_initializer=‘he_normal’,\
use_bias=True,bias_initializer=‘zeros’)(x)
x = multiply([input_feature, x])
return x
#channel_attention(first_input,64,4)
def RCAB(prev_input,filters,kernal_size,blocks):
for i in range(blocks):
if (i==0):
x=Conv2D(filters,kernal_size,padding=‘same’)(prev_input)
else:
x=Conv2D(filters,kernal_size,padding=‘same’)(lip)
x= PReLU(alpha_initializer=‘he_normal’)(x)
x=Conv2D(filters,1,padding=‘same’)(x)
x=channel_attention(x,filters,4)
if (i==0):
lip=Add()([prev_input,x])
else:
lip=Add()([lip,x])
x=Conv2D(filters,kernal_size,padding=‘same’)(x)
x=Add()([prev_input,x])
return x
#return Model(inputs=prev_input,outputs=x)
def Model_Creation():
first_input=Input(shape=(256,256,3))
#encoder3
first=dwt()(first_input)
inp=Conv2D(64,3,padding=‘same’)(first)
inp=PReLU(alpha_initializer=‘he_normal’)(inp)
second=RCAB(inp,64,3,3)
#encoder2
out_dwt_second = dwt()(second)
inp=Conv2D(256,3,padding=‘same’)(out_dwt_second)
inp=PReLU(alpha_initializer=‘he_normal’)(inp)
third=RCAB(inp,256,3,3)
#encoder1
out_dwt_third=dwt()(third)
inp=Conv2D(512,3,padding=‘same’)(out_dwt_third)
inp=PReLU(alpha_initializer=‘he_normal’)(inp)
inp=RCAB(inp,512,3,3)
#decoder1
inp=RCAB(inp,512,3,3)
inp=Conv2D(1024,3,padding=‘same’)(inp)
inp=PReLU(alpha_initializer=‘he_normal’)(inp)
inp=iwt()(inp)
inp=Add()([third,inp])
#decoder2
inp=RCAB(inp,256,3,3)
inp=Conv2D(256,3,padding=‘same’)(inp)
inp=PReLU(alpha_initializer=‘he_normal’)(inp)
inp=iwt()(inp)
inp=Add()([second,inp])
#decoder3
inp=RCAB(inp,64,3,3)
inp=Conv2D(12,3,padding=‘same’)(inp)
inp=PReLU(alpha_initializer=‘he_normal’)(inp)
inp=iwt()(inp)
out=Add()([first_input,inp])
return Model(inputs=first_input,outputs=out)
model=Model_Creation()
在下圖中,使用上述模型可以在預測影象中看到去噪效果。
EDSR模型(Enhanced Deep Residual Network):
arxiv:1707。02921
概念:實際上,這個網路模型是為了提高調整後的影象的質量,當它們再次轉換到一個更高的維度。我對上述架構進行了修改,用於對攝影影象進行影象去噪
def EDSR(scale, num_filters=256, res_blocks=8, res_block_scaling=None):
x_input = Input(shape=(256, 256, 3))
# assign value of x to x_res block for further operations
x = x_res_block = Conv2D(num_filters, 3, padding=‘same’)(x_input)
# Goes in number of res block
for i in range(res_blocks):
x_res_block = ResBlock(x_res_block, num_filters, res_block_scaling)
# convolution
x_res_block = Conv2D(num_filters, 3, padding=‘same’,kernel_initializer=‘he_normal’)(x_res_block)
x_res_block=LeakyReLU(alpha=0。1)(x_res_block)
# add res_block output and original normalizwd input
x = Add()([x, x_res_block])
# upsampling
x = Upsampling(x, scale, num_filters)
x = Conv2D(3, 3, padding=‘same’)(x)
x=AveragePooling2D(pool_size=(2,2),strides=(2,2),padding=‘same’)(x)
x = Conv2D(3, 3, padding=‘same’)(x)
return Model(x_input, x, name=“EDSR”)
def ResBlock(x_input, num_filters):
‘’‘This function Implementes Proposed ResBlock Architecture as per EDSR paper’‘’
# proposed ResBlock ==> Conv ——> Relu ——> Conv ——> Scaling(mul) ——> Add
x = Conv2D(num_filters, 3, padding=‘same’, kernel_initializer=‘he_normal’)(x_input)
x=LeakyReLU(alpha=0。1)(x)
x = Conv2D(num_filters, 3, padding=‘same’,kernel_initializer=‘he_normal’)(x)
x=LeakyReLU(alpha=0。1)(x)
x=AveragePooling2D(pool_size=(2,2),strides=(1,1),padding=‘same’)(x)
return x
def Upsampling(x, scale, num_filters):
‘’‘This function upsampling as mentioned in EDSR paper’‘’
def upsample(x, factor, **kwargs):
x = Conv2D(num_filters * (factor ** 2), 3, padding=‘same’, **kwargs)(x)
return Lambda(shuffle_pixels(scale=factor))(x)
if scale == 2:
x = upsample(x, 2, name=‘conv2d_1_scale_2’)
elif scale == 3:
x = upsample(x, 3, name=‘conv2d_1_scale_3’)
elif scale == 4:
x = upsample(x, 2, name=‘conv2d_1_scale_2’)
x = upsample(x, 2, name=‘conv2d_2_scale_2’)
return x
model=EDSR(2, num_filters=128, res_blocks=8, res_block_scaling=None)
在下面的圖中可以看到使用上述模型的預測影象的去噪效果。
效果總結
PSNR
如上圖所示,mwrcanet體系結構顯示了PSNR值的最高。
SSIM
如上圖所示,samsung_mrdnet顯示了SSIM方面的最高改進。
我還做過的其他嘗試:
我用adam optimizer嘗試了各種初始學習率,0。0001效果最好
嘗試了3種不同的架構,涉及不同的研究
最初,我使用了影象後,調整他們,但調整使資訊損失。所以我把原始影象切成小塊,用它來訓練。這對提高結果有很好的效果。
例如,如果影象大小是3000
3000,我從一個完整的影象中獲得了300
300總共100張影象,以避免在調整大小後丟失資訊
由於mrdn模型是過擬合的,採用了正則化和dropout
使用新的概念,如PRelu啟用,iwt和dwt(小波變換)與mwrcanet模型
結論
三種模型均獲得了較好的結果。在PSNR值方面,mwrcanet優於其他所有架構。在SSIM方面,三星- mrdn優於其他任何架構。但是mwrcanet架構產生的結果非常接近於人眼的乾淨影象。從EDSR架構修改中獲得的結果也非常好,接近頂層架構,我認為這是一個基線模型
進一步的討論
在此,將所有三個顏色通道同時輸入到模型中,得到去噪影象。我們可以嘗試將單獨的通道分別輸入,得到每個部分對應的去噪影象,然後將它們組合。所以對於每個通道,我們可以獲得單獨的權值或者給每個通道,使用單一的架構得到去噪後的通道影象,使用於訓練的資料點數量增加3倍。
我已經把原始影象切成碎片,但我沒有重新組合它們。我們可以對影象的去噪部分進行估計,並將其組合生成一幅大影象。
最後本文的程式碼:
github/Anand310892/Real-world-photographic-image-denoiser
deephub翻譯組