輸入有兩張圖,引導圖I和輸入的圖p,輸出的圖為q。
在我們的matting的例子里面,引導圖是原圖或者是原圖的灰度圖,輸入p為粗粗的分割后的結果, 輸出q是具有精細邊緣的matting圖。
I和輸出q之間需要滿足一個關系,在任意一個半徑為r的窗口內,滿足q和I之間是線性的,ak和bk在窗口wk內是常數。
這個設想是合理的,在邊界的地方,是想要服從這個的。但是在背景和前景區域,實際上並不想要符合這個,可以看鴨子的脖子和身體之間藍黃色的交界處,實際上並不想要這個梯度的,所以需要引入下一個約束
要讓q和p之間的對應像素間差值盡可能的小,這樣在大塊的前景和背景區域,就都還是原來的值了。在這種窗口wk內如果輸入p都是背景(p=0)的話,那么直接ak=0,bk=0,就可以得到q=0,整個窗口q還是背景。如果p=255,那么ak=0,bk = 255,整個窗口q是前景255。
這都比較好理解,那么在窗口既有背景又有前景的時候(就是邊界),就需要保留I中的像素梯度關系了,這樣就需要去求這個窗口wk中的ak和bk,求解也比較簡單,就是在這個半徑為r的窗口尋找一個ak和一個bk滿足以下方程最小,ƹ是正則項。
線性回歸,得到
這樣就可以求到ak,然后每一個點在實際算的時候,被很多個窗口包圍,每個窗口都可以算出一個a和b來,如下圖qi這個點被3個窗口包含,實際上比這個還要多。
所以在算的時候會把所有點在這里的a和b去求一個平均,求了平均值后語義就跟之前有一些不一樣了,q和I就不是成線性的關系了,但是因為a和b都是平均后的值,梯度非常小,所以在I上有大梯度的時候,仍然可以看做I的梯度和q的梯度是線性的,如下圖,后面兩項可以忽略不計。
整個算法的流程如下
可以看到,主要是一個boxfilter,即半徑為r的窗口內求所有像素的平均
采用積分圖,讓這個運算的復雜度和r的大小無關,那么這個是算法就是O(N)的,N是圖像的像素數,只與分辨率有關。
為了加速這個引導濾波,作者提出來可以在小分辨率上計算a和b然后upscale到大分辨率再和I取計算q。這種方法可能會損失一部分非常小的細節,大部分的是可以保留的。
------------------------------------------------------------------------------------------------------------------
Guided filter現在有一個trainable的版本,意思是可以和前面的cnn網絡一起訓練,不僅僅是一個后處理的模塊,這樣end-to-end的訓練會讓結果更好。論文主要是論證了guided Filter的反向傳播是可微的。
論文在這里 https://arxiv.org/pdf/1803.05619v1.pdf
具體推導是這樣的
這個是guided filter的流程圖
有三個輸入,Il是小分辨率的RGB圖像,Ol是經過網絡后初步的mask,也是小分辨率的。根據Il和Ol去計算出小分辨率上面的ak和bk然后upscale,再對輸入的大分辨率的Ih去做計算,得到大分辨率的Oh,這里需要注意的是,Il對應的是I,Ol對應的是p,Ih對應的是I,Oh對應的是q。所以Ol和Oh長的像但是不是一個意思。
仿照guided filter的求解過程,計算Al和Bl。這里先暫時忽略F(I), 這個是作者自己提出來的做guided map的幾層卷積,可忽略。那么有以下的推導過程
簡單的,如果去計算
反向傳播的時候,需要計算
然后就可以一步一步推導下去,直到推導出
具體過程如下圖所示,其中的
可以看到確實是可反向求導的。
這篇文章有source code可以看看具體是怎么用pytorch code實現guided fitler的
def forward(self, lr_x, lr_y, hr_x): n_lrx, c_lrx, h_lrx, w_lrx = lr_x.size() n_lry, c_lry, h_lry, w_lry = lr_y.size() n_hrx, c_hrx, h_hrx, w_hrx = hr_x.size() assert n_lrx == n_lry and n_lry == n_hrx assert c_lrx == c_hrx and (c_lrx == 1 or c_lrx == c_lry) assert h_lrx == h_lry and w_lrx == w_lry assert h_lrx > 2*self.r+1 and w_lrx > 2*self.r+1 ## N N = self.boxfilter(Variable(lr_x.data.new().resize_((1, 1, h_lrx, w_lrx)).fill_(1.0))) ## mean_x mean_x = self.boxfilter(lr_x) / N ## mean_y mean_y = self.boxfilter(lr_y) / N ## cov_xy cov_xy = self.boxfilter(lr_x * lr_y) / N - mean_x * mean_y ## var_x var_x = self.boxfilter(lr_x * lr_x) / N - mean_x * mean_x ## A A = cov_xy / (var_x + self.eps) ## b b = mean_y - A * mean_x ## mean_A; mean_b mean_A = F.interpolate(A, (h_hrx, w_hrx), mode='bilinear', align_corners=True) mean_b = F.interpolate(b, (h_hrx, w_hrx), mode='bilinear', align_corners=True) return mean_A*hr_x+mean_b
看起來也還是可以的。
文章后面又提出了conv guided filter,用卷積去做的幾層, 其中dilated conv的rate就是radius,感覺可解釋性下降了。下面是conv guided filter的具體思想。