導讀
在圖像濾波算法中,導向濾波、雙邊濾波、最小二乘濾波並稱三大保邊濾波器,他們是各向異性濾波器。相對於常見的均值濾波、高斯濾波等各向同性濾波器,他們最大的特點是在去除噪聲的同時,能最大限度保持邊緣不被平滑。本文講解導向濾波及其應用。
總的來講,導向濾波就是儘可能讓輸出圖像的梯度和導向圖相似,同時讓輸出圖像的灰度(亮度)與輸入圖像相似,以此來保留邊緣並且濾除噪聲。
原理推導
我們先看下圖:
輸入圖像,經過引導圖像, 濾波得到輸出圖像, 導向濾波算法中有一個重要假設:即在局部窗口上,導向圖和輸出圖存在局部線性關係:
同時在窗口上, 濾波後的圖像和輸入圖像有如下關係:
這樣公式的線性關係保證瞭如果在每個局部窗口中,如果導向圖中存在一個邊緣,輸出圖像將保持邊緣不變。同時,濾波結果圖要儘可能與輸入圖像相同以此減小濾波帶來的信息損失,該算法的最小二乘表示即:
這是求解最優值的問題,引入一個正則化參數防止過大,得到損失函數:
運用最小二乘法求解極小值,利用極小值處導數爲0,求解過程如下:
由上面推到得出:
其中:
將代入計算可得:
上下兩邊同除以,爲窗口像素數量。得到:
最後:
得到上述公式後,可以對每個窗口計算一個,但是,每個像素都被包含在多個窗口中,對每個像素,都能計算出多個,我麼將使用多個計算得到的值求平均得到輸出值,上述過程描述如下:
其中:
導向濾波的應用
- 保邊濾波
當時,該算法成爲一個保邊濾波器。上述計算公式變化爲:
考慮以下兩種情況:- Case 1:平坦區域。如果在某個濾波窗口內,該區域是相對平滑的,方差將遠遠小於。從而。相當於對該區域作均值濾波。
- Case 2:高方差區域。相反,如果該區域是邊緣區域,方差很大,將遠遠大於。從而。相當於在區域保持原有梯度。
以上可以出:爲界定平滑區域和邊緣區域的閾值。
- 圖像去霧
在圖像去霧中,導向濾波一般用來細化透射率圖像,以原圖的灰度圖爲導向圖,以粗投射率圖爲輸入圖,能得到非常精細的透射率圖像。
當然,導向濾波的應用不止以上兩種,網上還有圖像融合等應用,本人沒有去了解。
導向濾波的實現
導向濾波的代碼實現較爲簡單,我們直接貼出計算流程
快速導向濾波的實現
由於導向濾波效率問題,何凱明博士在2015年,對其做了優化,基本原理是將導向圖,輸入圖都進行下采樣計算,然後對進行上採樣恢復原始大小,整個算法流程如下:
算法效果
我們演示一下保邊濾波效果:
代碼
接下來,廢話不多說,我們上代碼:https://github.com/EthanAndEvan/ImageAlgorithmDraft,爲防止github無法訪問,我們直接貼上代碼:
- 導向濾波
#include <opencv2/opencv.hpp>
//導向濾波
void GuidedFilter(cv::Mat& srcImage, cv::Mat& guidedImage, cv::Mat& outputImage, int filterSize, double eps)
{
try
{
if (srcImage.empty() || guidedImage.empty() || filterSize <= 0 || eps < 0 ||
srcImage.channels() != 1 || guidedImage.channels() != 1)
{
throw "params input error";
}
cv::Mat srcImageP, srcImageI, meanP, meanI, meanIP, meanII, varII, alfa, beta;
srcImage.convertTo(srcImageP, CV_32FC1);
guidedImage.convertTo(srcImageI, CV_32FC1);
cv::boxFilter(srcImageP, meanP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageI, meanI, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageI.mul(srcImageP), meanIP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageI.mul(srcImageI), meanII, CV_32FC1, cv::Size(filterSize, filterSize));
varII = meanII - meanI.mul(meanI);
alfa = (meanIP - meanI.mul(meanP)) / (varII + eps);
beta = meanP - alfa.mul(meanI);
cv::boxFilter(alfa, alfa, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(beta, beta, CV_32FC1, cv::Size(filterSize, filterSize));
outputImage = (alfa.mul(srcImageI) + beta);
}
catch (cv::Exception& e)
{
throw e;
}
catch (std::exception& e)
{
throw e;
}
}
- 快速導向濾波
#include <opencv2/opencv.hpp>
//快速導向濾波
void FastGuidedFilter(cv::Mat& srcImage, cv::Mat& guidedImage, cv::Mat& outputImage, int filterSize, double eps, int samplingRate)
{
try
{
if (srcImage.empty() || guidedImage.empty() || filterSize <= 0 || eps < 0 ||
srcImage.channels() != 1 || guidedImage.channels() != 1 || samplingRate < 1)
{
throw "params input error";
}
cv::Mat srcImageP, srcImageSubI, srcImageI, meanP, meanI, meanIP, meanII, var, alfa, beta;
cv::resize(srcImage, srcImageP, cv::Size(srcImage.cols / samplingRate, srcImage.rows / samplingRate));
cv::resize(guidedImage, srcImageSubI, cv::Size(srcImage.cols / samplingRate, srcImage.rows / samplingRate));
filterSize = filterSize / samplingRate;
srcImageP.convertTo(srcImageP, CV_32FC1);
guidedImage.convertTo(srcImageI, CV_32FC1);
srcImageSubI.convertTo(srcImageSubI, CV_32FC1);
cv::boxFilter(srcImageP, meanP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageSubI, meanI, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageSubI.mul(srcImageP), meanIP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageSubI.mul(srcImageSubI), meanII, CV_32FC1, cv::Size(filterSize, filterSize));
var = meanII - meanI.mul(meanI);
alfa = (meanIP - meanI.mul(meanP)) / (var + eps);
beta = meanP - alfa.mul(meanI);
cv::boxFilter(alfa, alfa, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(beta, beta, CV_32FC1, cv::Size(filterSize, filterSize));
cv::resize(alfa, alfa, cv::Size(srcImage.cols, srcImage.rows));
cv::resize(beta, beta, cv::Size(srcImage.cols, srcImage.rows));
outputImage = alfa.mul(srcImageI) + beta;
}
catch (cv::Exception& e)
{
throw e;
}
catch (std::exception& e)
{
throw e;
}
}
參考
[1] 視覺一隻白 .《導向濾波的原理及實現》[DB/OL].
[2] lsflll.《導向濾波(Guided Filter)公式詳解》[DB/OL]
[3] SongpingWang.《OpenCV—Python 導向濾波》[DB/OL]