matlab实现双边滤波_【他山之石】pytorch 实现双边滤波
生活随笔
收集整理的這篇文章主要介紹了
matlab实现双边滤波_【他山之石】pytorch 实现双边滤波
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更遠。在科研的道路上,更需借助東風才能更快前行。為此,我們特別搜集整理了一些實用的代碼鏈接,數據集,軟件,編程技巧等,開辟“他山之石”專欄,助你乘風破浪,一路奮勇向前,敬請關注。
作者:知乎—尹相楠原文地址:https://zhuanlan.zhihu.com/p/310710051
前幾天研究了傳統的美顏算法,了解到雙邊濾波(bilateral filtering)。在看懂原理后,為加深理解,抽時間用 pytorch 重新造了個輪子。雖然效率肯定比不上 opencv ,但當個小練習也不錯。為了方便復習以及幫助初學者,在此記錄。01
高斯濾波1. 高斯核函數圖像領域的高斯濾波器是個二維的矩陣。矩陣中每個元素的值與它與矩陣中心的距離有關,計算公式就是二維高斯函數的公式:為了讓卷積前后的圖像亮度保持不變,需要對 (1) 計算的矩陣歸一化(除以矩陣所有元素的和),因此 (1) 中 exp 之前的系數部分可以省略。生成高斯濾波器的代碼如下:@torch.no_grad()def getGaussianKernel(ksize, sigma=0): if sigma <= 0: # 根據 kernelsize 計算默認的 sigma,和 opencv 保持一致 sigma = 0.3 * ((ksize - 1) * 0.5 - 1) + 0.8 center = ksize // 2 xs = (np.arange(ksize, dtype=np.float32) - center) # 元素與矩陣中心的橫向距離 kernel1d = np.exp(-(xs ** 2) / (2 * sigma ** 2)) # 計算一維卷積核 # 根據指數函數性質,利用矩陣乘法快速計算二維卷積核 kernel = kernel1d[..., None] @ kernel1d[None, ...] kernel = torch.from_numpy(kernel) kernel = kernel / kernel.sum() # 歸一化 return kernel2. 高斯濾波器pytorch 自帶的 conv2d 可以很方便地對圖像施加高斯濾波,代碼如下:def GaussianBlur(batch_img, ksize, sigma=None): kernel = getGaussianKernel(ksize, sigma) # 生成權重 B, C, H, W = batch_img.shape # C:圖像通道數,group convolution 要用到 # 生成 group convolution 的卷積核 kernel = kernel.view(1, 1, ksize, ksize).repeat(C, 1, 1, 1) pad = (ksize - 1) // 2 # 保持卷積前后圖像尺寸不變 # mode=relfect 更適合計算邊緣像素的權重 batch_img_pad = F.pad(batch_img, pad=[pad, pad, pad, pad], mode='reflect') weighted_pix = F.conv2d(batch_img_pad, weight=kernel, bias=None, stride=1, padding=0, groups=C) return weighted_pix關于 group convolution,如果不熟悉可以看我這篇回答:什么是「Grouped Convolution」?https://www.zhihu.com/question/60484190/answer/150778317902
雙邊濾波高斯濾波器的權重完全由距離決定。在大塊顏色差不多、偶有噪點的區域,它可以把顏色平均化,從而過濾掉噪點。但是在顏色變化劇烈的邊緣區域,它還是一視同仁地把所有像素做加權平均,這讓本應該清晰銳利的邊緣也變得模糊不清了,這就造成了如下圖所示的效果,在做人像美顏時是不希望看到的。這里,就引入了雙邊濾波(bilateral filtering)。雙邊濾波的權重公式也基于高斯函數。但和高斯濾波的區別是,決定卷積核權重的,不單純是像素之間的空間距離,還包括像素之間的亮度差異。以卷積核中心為坐標原點,該處像素值為I(0,0)。那么,坐標為 (u, v) 處的像素,對應的權重為:(2) 中 exp 的第一個指數項和高斯核函數相同,與像素的空間距離有關;第二個指數項則是像素值距離的函數。以e為底對這兩項做指數運算,再相乘即得到了公式 (2)。根據公式 (2) 計算的卷積核有如下性質:- 距離中心像素越遠的像素,其權重就越小
- 亮度和中心像素亮度差異越大的像素,其權重就越小
03
代碼實現由于 (2) 中卷積核的權重不僅僅依賴于空間距離,還依賴于像素的亮度,因此卷積核的權重是不固定的,不能簡單地利用 pytorch 的 conv2d 來實現。pytorch 的 tensor 自帶了一個 unfold 方法,正好可以用在這里。unfold 的作用是把圖像拆分成 patch,每個patch 為卷積核覆蓋的像素。下面舉個小例子:import torchx = torch.arange(12).view(3, 4)xOut[4]: tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])# 沿著行,以步長 1 拆分 x,每個 patch 為 2 行,列保持不變,y = x.unfold(dimension=0, size=2, step=1) y.shapeOut[6]: torch.Size([2, 4, 2])y[0]Out[7]: tensor([[0, 4], [1, 5], [2, 6], [3, 7]])y[1]Out[8]: tensor([[ 4, 8], [ 5, 9], [ 6, 10], [ 7, 11]])# 直接對 y 的第二個維度拆分,例如拆分成 3 列,步長仍為 1z = y.unfold(dimension=1, size=3, step=1)z.shapeOut[10]: torch.Size([2, 2, 2, 3])# 觀察 z[0, 0],發現正是 x 左上角的六個元素z[0,0]Out[11]: tensor([[0, 1, 2], [4, 5, 6]])# z[0, 1] 也同樣符合預期z[0,1]Out[12]: tensor([[1, 2, 3], [5, 6, 7]])實現的思路是:把原始圖像 unfold 成一個個的 patch,對每個 patch 計算權重以及加權平均。代碼如下:def bilateralFilter(batch_img, ksize, sigmaColor=None, sigmaSpace=None): device = batch_img.device if sigmaSpace is None: sigmaSpace = 0.15 * ksize + 0.35 # 0.3 * ((ksize - 1) * 0.5 - 1) + 0.8 if sigmaColor is None: sigmaColor = sigmaSpace pad = (ksize - 1) // 2 batch_img_pad = F.pad(batch_img, pad=[pad, pad, pad, pad], mode='reflect') # batch_img 的維度為 BxcxHxW, 因此要沿著第 二、三維度 unfold # patches.shape: B x C x H x W x ksize x ksize patches = batch_img_pad.unfold(2, ksize, 1).unfold(3, ksize, 1) patch_dim = patches.dim() # 6 # 求出像素亮度差 diff_color = patches - batch_img.unsqueeze(-1).unsqueeze(-1) # 根據像素亮度差,計算權重矩陣 weights_color = torch.exp(-(diff_color ** 2) / (2 * sigmaColor ** 2)) # 歸一化權重矩陣 weights_color = weights_color / weights_color.sum(dim=(-1, -2), keepdim=True) # 獲取 gaussian kernel 并將其復制成和 weight_color 形狀相同的 tensor weights_space = getGaussianKernel(ksize, sigmaSpace).to(device) weights_space_dim = (patch_dim - 2) * (1,) + (ksize, ksize) weights_space = weights_space.view(*weights_space_dim).expand_as(weights_color) # 兩個權重矩陣相乘得到總的權重矩陣 weights = weights_space * weights_color # 總權重矩陣的歸一化參數 weights_sum = weights.sum(dim=(-1, -2)) # 加權平均 weighted_pix = (weights * patches).sum(dim=(-1, -2)) / weights_sum return weighted_pix最終結果為下圖,雀斑都沒了!同時人臉的輪廓和五官的細節依然被很好地保留下來:輸入圖片尺寸為 256 x 256,ksize=15,sigmaColor=0.15,sigmaSpace=5 。需要注意的是,由于 bilateral filter 的權重和像素值相關,因此設置 sigmaColor 時要注意輸入圖像的像素范圍,看清楚到底是 0-1 還是 0-255(上圖像素范圍為 0-1)。04
總結本文介紹了雙邊濾波的基本原理,并附帶了 pytorch 的實現。雖然不如 opencv 快,但優點是 backward trackable ,適合包裝為模塊加入網絡中。利用 unfold 實現的缺點是很占內存/顯存,kernelsize 越大,unfold 出來的冗余數據就越多,如果有大神知道更高效的實現方式,還望不吝賜教。05
后記我發現網上搜到的很多磨皮祛斑的算法,主要的目標是設計一個高通濾波器,從而得到一個基于像素亮度的 mask,亮的地方權重大(對應皮膚區域),暗的地方權重小(對應雀斑、噪點區域)。將原圖 I 和 模糊化的圖I_blur(各種模糊化方式都可以,目標是把較暗的斑點模糊掉)利用 mask 融合:I_mask+I_blur(1-mask)?。這種方法既保留了原圖的細節,又能模糊掉斑點,不過在不同圖片上應用時,仍然免不了調整一些超參數,而真有調參的功夫,直接調一下雙邊濾波的幾個參數,最后得到的效果未必比那些復雜的算法差。本文目的在于學術交流,并不代表本公眾號贊同其觀點或對其內容真實性負責,版權歸原作者所有,如有侵權請告知刪除。
“他山之石”歷史文章
編譯PyTorch靜態庫
工業界視頻理解解決方案大匯總
動手造輪子-rnn
憑什么相信你,我的CNN模型?關于CNN模型可解釋性的思考
“最全PyTorch分布式教程”來了!
c++接口libtorch介紹& vscode+cmake實踐
python從零開始構建知識圖譜
一文讀懂 PyTorch 模型保存與載入
適合PyTorch小白的官網教程:Learning PyTorch With Examples
pytorch量化備忘錄
LSTM模型結構的可視化
PointNet論文復現及代碼詳解
SCI寫作常用句型之研究結果&發現
白話生成對抗網絡GAN及代碼實現
pytorch的余弦退火學習率
更多他山之石專欄文章,
請點擊文章底部“閱讀原文”查看
分享、點贊、在看,給個三連擊唄!
總結
以上是生活随笔為你收集整理的matlab实现双边滤波_【他山之石】pytorch 实现双边滤波的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: less linux命令,less 命令
- 下一篇: python自动控制库_一个可以自动化控