
本文收錄于CVPR2020,是華為諾亞方舟研究院的成果,主要解決的是,去除對數字螢幕拍照産生摩爾紋,有一定的應用價值。
論文位址:https://arxiv.org/pdf/2004.00406.pdf
代碼位址(Tensorflow+Keras實作):https://github.com/zhenngbolun/Learnbale_Bandpass_Filter
Image demoireing是涉及紋理和顔色恢複的多方面圖像恢複任務。在本文中,提出了一種新穎的多尺度bandpass 卷積神經網絡(MBCNN)來解決這個問題。作為端到端解決方案,MBCNN分别解決了兩個子問題。對于紋理恢複子問題,提出了一個可學習的帶通濾波器(LBF),以了解去除摩爾紋之前的頻率。對于顔色恢複子問題,提出了兩步色調映射政策,該政策首先應用全局色調映射來校正全局色彩shift,然後對每個像素執行顔色的局部微調。通過消融研究,我們證明了MBCNN不同元件的有效性。在兩個公共資料集上的實驗結果表明,本文的方法大大優于最新方法(在PSNR方面超過2dB)。
簡介
數字螢幕在現代日常生活中無處不在:我們在家裡有電視螢幕,在辦公室有筆記本電腦/桌上型電腦螢幕,在公共場所有大尺寸LED螢幕。拍攝這些螢幕的圖檔以快速儲存資訊已成為一種慣例。然而,在對這些螢幕拍照的時候通常會出現波紋圖像,進而降低了照片的圖像品質。當兩個重複的圖案互相幹擾時,出現摩爾紋圖案。在拍攝螢幕圖檔的情況下,相機濾色鏡陣列(CFA)會幹擾螢幕的亞像素布局。
與去噪、去馬賽克、顔色恒定、銳化等其他圖像修複問題不同,人們對圖像去僞存真(demireing)的關注較少,它是指從被摩爾紋污染的圖像中恢複基本的幹淨圖像。這個問題在很大程度上仍然是一個未解決的問題,由于摩爾紋圖案在頻率、形狀、顔色等方面的巨大變化。
最近的很多工作試圖通過多尺度設計來消除不同頻段的摩爾紋。DMCNN 提出使用具有多分辨率分支的多尺度CNN處理摩爾紋圖案,并對不同尺度的輸出求和以獲得最終輸出。MDDM 通過引入基于動态特征編碼器的自适應執行個體規範化改進了DMCNN。DCNN提出了一種從粗到細的結構來去除兩尺度的摩爾條紋。對粗尺度結果進行上采樣,并将其與細尺度輸入連接配接起來,以進行進一步的殘差學習。MopNet 使用多尺度特征聚合子子產品來處理複雜頻率,并使用另外兩個子子產品來處理邊沿和預定義的波紋類型。本文的模型還采用了針對三個不同比例的分支的多比例設計。在不同尺度之間,本文的模型采用漸進式上采樣政策以平滑地提高分辨率。
本文的方法:Multiscale bandpass CNN
數位相機捕獲的含摩爾紋的圖像可以模組化為:
其中ψ-1是ψ的反函數,在圖像處理領域被稱為色調映射函數。以此模型模組化,圖像去摩爾紋任務可以分為兩步,即摩爾條紋去除和色調映射。
1、Multiscale bandpass CNN
為了對遙感圖像中的物體分割前景進行顯式模組化,本文提出了一種前景感覺關系網絡(FarSeg),如圖2所示。FarSeg由特征金字塔網絡(FPN)、前景場景(F-S)關系子產品、輕量級解碼器和前景感覺(F-A)優化組成。FPN負責多尺度對象分割。在F-S關系子產品中,首先将誤報問題表述為前景中缺乏區分性資訊的問題,然後介紹潛在場景語義和F-S關系以改善對前景特征的區分。輕量級解碼器僅設計用于恢複語義特征的空間分辨率。為了使網絡在訓練過程中集中在前景上,提出了F-A優化來減輕前景背景不平衡的問題。
1.1、 Multi-Branch Encoder
整體的模型在三個scales上工作,并具有三種不同類型的blocks,分别是波紋紋理去除塊(MTRB),全局色調映射塊(GTMB)和局部色調映射塊(LTMB)。
首先将具有h×w×c形狀的輸入圖像I可逆地向下采樣為四個h/2×w/2×4c形狀的子圖像。下面的網絡由三個分支組成,每個分支用于恢複特定比例的波紋圖像,同時每個分支順序地執行摩爾紋去除和色調映射,最終輸出放大後的圖像,并将其融合到更小比例的分支中。在分支I和II中,将目前分支的特征和較粗的縮放分支的輸出特征融合後,将其他GTMB和MTRB堆疊在一起,以消除縮放比例引起的紋理和顔色錯誤。
def MBCNN(nFilters, multi=True):
conv_func = conv_relu
def pre_block(x, d_list, enbale = True):
t = x
for i in range(len(d_list)):
_t = conv_func(t, nFilters, 3, dilation_rate=d_list[i])
t = layers.Concatenate(axis=-1)([_t,t])
t = conv(t, 64, 3)
t = adaptive_implicit_trans()(t)
t = conv(t,nFilters*2,1)
t = ScaleLayer(s=0.1)(t)
if not enbale:
t = layers.Lambda(lambda x: x*0)(t)
t = layers.Add()([x,t])
return t
def pos_block(x, d_list):
t = x
for i in range(len(d_list)):
_t = conv_func(t, nFilters, 3, dilation_rate=d_list[i])
t = layers.Concatenate(axis=-1)([_t,t])
t = conv_func(t, nFilters*2, 1)
return t
def global_block(x):
t = layers.ZeroPadding2D(padding=(1,1))(x)
t = conv_func(t, nFilters*4, 3, strides=(2,2))
t = layers.GlobalAveragePooling2D()(t)
t = layers.Dense(nFilters*16,activation='relu')(t)
t = layers.Dense(nFilters*8, activation='relu')(t)
t = layers.Dense(nFilters*4)(t)
_t = conv_func(x, nFilters*4, 1)
_t = layers.Multiply()([_t,t])
_t = conv_func(_t, nFilters*2, 1)
return _t
output_list = []
d_list_a = (1,2,3,2,1)
d_list_b = (1,2,3,2,1)
d_list_c = (1,2,2,2,1)
x = layers.Input(shape=(None, None, 3)) #16m*16m
_x = Space2Depth(scale=2)(x)
t1 = conv_func(_x,nFilters*2,3, padding='same') #8m*8m
t1 = pre_block(t1, d_list_a, True)
t2 = layers.ZeroPadding2D(padding=(1,1))(t1)
t2 = conv_func(t2,nFilters*2,3, padding='valid',strides=(2,2)) #4m*4m
t2 = pre_block(t2, d_list_b,True)
t3 = layers.ZeroPadding2D(padding=(1,1))(t2)
t3 = conv_func(t3,nFilters*2,3, padding='valid',strides=(2,2)) #2m*2m
t3 = pre_block(t3,d_list_c, True)
t3 = global_block(t3)
t3 = pos_block(t3, d_list_c)
t3_out = conv(t3, 12, 3)
t3_out = Depth2Space(scale=2)(t3_out) #4m*4m
output_list.append(t3_out)
_t2 = layers.Concatenate()([t3_out,t2])
_t2 = conv_func(_t2, nFilters*2, 1)
_t2 = global_block(_t2)
_t2 = pre_block(_t2, d_list_b,True)
_t2 = global_block(_t2)
_t2 = pos_block(_t2, d_list_b)
t2_out = conv(_t2, 12, 3)
t2_out = Depth2Space(scale=2)(t2_out) #8m*8m
output_list.append(t2_out)
_t1 = layers.Concatenate()([t1, t2_out])
_t1 = conv_func(_t1, nFilters*2, 1)
_t1 = global_block(_t1)
_t1 = pre_block(_t1, d_list_a, True)
_t1 = global_block(_t1)
_t1 = pos_block(_t1, d_list_a)
_t1 = conv(_t1,12,3)
y = Depth2Space(scale=2)(_t1) #16m*16m
output_list.append(y)
if multi != True:
return models.Model(x,y)
else:
return models.Model(x,output_list)
複制
1.2、Moire texture removal
摩爾紋可以表示為:
按照這種公式,我們可以先估計不同尺度和頻率的波紋紋理的分量,然後基于所有估計的分量重建波紋紋理。Block-DCT是處理頻率相關問題的有效方法。
其中D表示Block-DCT函數。
Learnable Bandpass Filter
受隐式DCT的啟發,可以用深度CNN直接估計 implicit frequency spectrum(IFS) 。由于變換都是線性的,是以可以用一個簡單的卷積層來模組化。由于Moire紋理的頻譜總是有規律的,我們可以使用帶通濾波器來放大某些頻率,減弱其他頻率。然而,在模組化之前我們很難得到頻譜,因為在不同的尺度上,會有幾個頻率,而且它們也會互相影響。為了解決這個問題,提出了一種可學習的帶通濾波器(LBF)來學習摩爾紋圖像的先驗。LBF為每一個頻率引入了一個可學習的權重。
class adaptive_implicit_trans(layers.Layer):
def __init__(self, **kwargs):
super(adaptive_implicit_trans, self).__init__(**kwargs)
def build(self, input_shape):
conv_shape = (1,1,64,64)
self.it_weights = self.add_weight(
shape = (1,1,64,1),
initializer = initializers.get('ones'),
constraint = constraints.NonNeg(),
name = 'ait_conv')
kernel = np.zeros(conv_shape)
r1 = sqrt(1.0/8)
r2 = sqrt(2.0/8)
for i in range(8):
_u = 2*i+1
for j in range(8):
_v = 2*j+1
index = i*8+j
for u in range(8):
for v in range(8):
index2 = u*8+v
t = cos(_u*u*pi/16)*cos(_v*v*pi/16)
t = t*r1 if u==0 else t*r2
t = t*r1 if v==0 else t*r2
kernel[0,0,index2,index] = t
self.kernel = k.variable(value = kernel, dtype = 'float32')
def call(self, inputs):
#it_weights = k.softmax(self.it_weights)
#self.kernel = self.kernel*it_weights
self.kernel = self.kernel*self.it_weights
y = k.conv2d(inputs,
self.kernel,
padding = 'same',
data_format='channels_last')
return y
def compute_output_shape(self, input_shape):
return input_shape
複制
1.3 Tone mapping 色調映射
RGB顔色空間是一個非常大的空間,包含256的3次方種顔色,是以很難進行逐點色調映射。觀察到摩爾紋圖像和幹淨圖像之間存在顔色偏移,本文提出了一種兩步色調映射政策,其中包含兩種類型的色調映射塊:全局色調映射塊(GTMB)和局部色調映射塊(LTMB)。
全局色調映射塊Global tone mapping block
注意力機制已經被證明在許多任務中是有效的,并且已經提出了幾種通道注意子產品。GTMB可以看作是一個通道注意子產品。然而,GTMB與現有的通道注意子產品在幾個方面有所不同。 首先,現有的通道注意力塊總是由一個Sigmoid單元激活,而GTMB中的γ沒有這樣的限制。其次,通道注意力是直接應用在現有通道注意力塊的輸入上,而GTMB中的γ是應用在局部特征Flocal上。最後,現有的通道注意力子產品的目的是進行自适應的channel-wise特征重新校準;GTMB的目标是進行全局的顔色偏移,避免不規則和不均勻的局部顔色僞影。
局部色調映射塊Local tone mapping block
2、 損失函數
在本文中,将L1損失用作基本損失函數,因為已經證明 L1損失比L2損失對圖像恢複任務更有效。但是,L1損失本身是不夠的,因為它是無法提供結構資訊的逐點損失,而摩爾紋是 structural artifact。本文提出了Advanced Sobel Loss(ASL)來解決此問題。
與經典Sobel Loss相比,ASL提供了兩個額外的45°方向Loss,它們可以提供更豐富的結構資訊。
總的損失函數為:
實驗與結果
資料集: LCDMoire(Aim 2019 challenge on image demoreing: datasetand study. InICCVW, 2019)、TIP2018( Moire photorestoration using multiresolution convolutional neural net-works.)
實驗結果