【OpenCV】鄰域濾波:方框、高斯、中值、雙邊濾波
鄰域濾波(卷積)
通用線性鄰域濾波是一種常用的鄰域運算元,輸入畫素加權得到輸出畫素:
其中權重核 為“濾波係數”。上面的式子可以簡記為:
【方框濾波】
最簡單的線性濾波是移動平均或方框濾波,用 視窗中的畫素值平均後輸出,核函式為:
其實等價於影像與全部元素值為1的核函式進行卷積再進行尺度縮放。
程式碼
OpenCV中的 blur函式是進行標準方框濾波:
void cv::blur( InputArray src, OutputArray dst,
Size ksize, Point anchor, int borderType )
{
boxFilter( src, dst, -1, ksize, anchor, true, borderType );
}
而boxFilter函式原始碼如下:cv::Ptr<cv::FilterEngine> cv::createBoxFilter( int srcType, int dstType, Size ksize,
Point anchor, bool normalize, int borderType )
{
int sdepth = CV_MAT_DEPTH(srcType);
int cn = CV_MAT_CN(srcType), sumType = CV_64F;
if( sdepth <= CV_32S && (!normalize ||
ksize.width*ksize.height <= (sdepth == CV_8U ? (1<<23) :
sdepth == CV_16U ? (1 << 15) : (1 << 16))) )
sumType = CV_32S;
sumType = CV_MAKETYPE( sumType, cn );
Ptr<BaseRowFilter> rowFilter = getRowSumFilter(srcType, sumType, ksize.width, anchor.x );
Ptr<BaseColumnFilter> columnFilter = getColumnSumFilter(sumType,
dstType, ksize.height, anchor.y, normalize ? 1./(ksize.width*ksize.height) : 1);
return Ptr<FilterEngine>(new FilterEngine(Ptr<BaseFilter>(0), rowFilter, columnFilter,
srcType, dstType, sumType, borderType ));
}
這裡 blur 和 boxFilter 的區別是,blur是標準化後的 boxFilter,即boxFilter的核函式:其中,
blur( src, dst, Size( 1, 1 ), Point(-1,-1));
blur( src, dst, Size( 4, 4 ), Point(-1,-1));
blur( src, dst, Size( 8, 8 ), Point(-1,-1));
blur( src, dst, Size( 16, 16 ), Point(-1,-1));
實驗結果
下圖是對一幅影像分別用1*1,4*4,8*8,16*16標準方框濾波後的影像:
【高斯濾波】
高斯濾波器是一類根據高斯函式的形狀來選擇權值的線性平滑濾波器。它對去除服從正態分佈的噪聲很有效。
常用的零均值離散高斯濾波器函式:
常用的零均值離散高斯濾波器函式:
2D影像中表示為:
程式碼
/****************************************************************************************\
Gaussian Blur
\****************************************************************************************/
cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
{
const int SMALL_GAUSSIAN_SIZE = 7;
static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
{
{1.f},
{0.25f, 0.5f, 0.25f},
{0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
{0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
};
const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
small_gaussian_tab[n>>1] : 0;
CV_Assert( ktype == CV_32F || ktype == CV_64F );
Mat kernel(n, 1, ktype);
float* cf = (float*)kernel.data;
double* cd = (double*)kernel.data;
double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;
double scale2X = -0.5/(sigmaX*sigmaX);
double sum = 0;
int i;
for( i = 0; i < n; i++ )
{
double x = i - (n-1)*0.5;
double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x);
if( ktype == CV_32F )
{
cf[i] = (float)t;
sum += cf[i];
}
else
{
cd[i] = t;
sum += cd[i];
}
}
sum = 1./sum;
for( i = 0; i < n; i++ )
{
if( ktype == CV_32F )
cf[i] = (float)(cf[i]*sum);
else
cd[i] *= sum;
}
return kernel;
}
cv::Ptr<cv::FilterEngine> cv::createGaussianFilter( int type, Size ksize,
double sigma1, double sigma2,
int borderType )
{
int depth = CV_MAT_DEPTH(type);
if( sigma2 <= 0 )
sigma2 = sigma1;
// automatic detection of kernel size from sigma
if( ksize.width <= 0 && sigma1 > 0 )
ksize.width = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
if( ksize.height <= 0 && sigma2 > 0 )
ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
ksize.height > 0 && ksize.height % 2 == 1 );
sigma1 = std::max( sigma1, 0. );
sigma2 = std::max( sigma2, 0. );
Mat kx = getGaussianKernel( ksize.width, sigma1, std::max(depth, CV_32F) );
Mat ky;
if( ksize.height == ksize.width && std::abs(sigma1 - sigma2) < DBL_EPSILON )
ky = kx;
else
ky = getGaussianKernel( ksize.height, sigma2, std::max(depth, CV_32F) );
return createSeparableLinearFilter( type, type, kx, ky, Point(-1,-1), 0, borderType );
}
void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
double sigma1, double sigma2,
int borderType )
{
Mat src = _src.getMat();
_dst.create( src.size(), src.type() );
Mat dst = _dst.getMat();
if( borderType != BORDER_CONSTANT )
{
if( src.rows == 1 )
ksize.height = 1;
if( src.cols == 1 )
ksize.width = 1;
}
if( ksize.width == 1 && ksize.height == 1 )
{
src.copyTo(dst);
return;
}
#ifdef HAVE_TEGRA_OPTIMIZATION
if(sigma1 == 0 && sigma2 == 0 && tegra::gaussian(src, dst, ksize, borderType))
return;
#endif
Ptr<FilterEngine> f = createGaussianFilter( src.type(), ksize, sigma1, sigma2, borderType );
f->apply( src, dst );
}
實驗結果
下圖是對一幅影像分別用1*1,3*3,5*5,9*9標準方框濾波後的影像:
非線性濾波
線性濾波易於構造,且易於從頻率響應的角度分析,但如果噪聲是散粒噪聲而非高斯噪聲時線性濾波不能去除噪聲。如影像突然出現很大的值,線性濾波只是轉換為柔和但仍可見的散粒。這時需要非線性濾波。
簡單的非線性濾波有 中值濾波, -截尾均值濾波,定義域濾波 和值域濾波 。
中值濾波選擇每個鄰域畫素的中值輸出; -截尾均值濾波是指去掉百分率為 的最小值和最大值;定義域濾波中沿著邊界的數字是畫素的距離;值域就是去掉值域外的畫素值。
中值濾波程式碼
medianBlur ( src, dst, i );
中值濾波實驗
下圖是對一幅影像分別用3*3,5*5,7*7,9*9(這裡必須是奇數)標準方框濾波後的影像:【雙邊濾波】
雙邊濾波的思想是抑制與中心畫素值差別太大的畫素,輸出畫素值依賴於鄰域畫素值的加權合:權重係數 取決於定義域核
雙邊濾波原始碼
/****************************************************************************************\
Bilateral Filtering
\****************************************************************************************/
namespace cv
{
static void
bilateralFilter_8u( const Mat& src, Mat& dst, int d,
double sigma_color, double sigma_space,
int borderType )
{
int cn = src.channels();
int i, j, k, maxk, radius;
Size size = src.size();
CV_Assert( (src.type() == CV_8UC1 || src.type() == CV_8UC3) &&
src.type() == dst.type() && src.size() == dst.size() &&
src.data != dst.data );
if( sigma_color <= 0 )
sigma_color = 1;
if( sigma_space <= 0 )
sigma_space = 1;
double gauss_color_coeff = -0.5/(sigma_color*sigma_color);
double gauss_space_coeff = -0.5/(sigma_space*sigma_space);
if( d <= 0 )
radius = cvRound(sigma_space*1.5);
else
radius = d/2;
radius = MAX(radius, 1);
d = radius*2 + 1;
Mat temp;
copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
vector<float> _color_weight(cn*256);
vector<float> _space_weight(d*d);
vector<int> _space_ofs(d*d);
float* color_weight = &_color_weight[0];
float* space_weight = &_space_weight[0];
int* space_ofs = &_space_ofs[0];
// initialize color-related bilateral filter coefficients
for( i = 0; i < 256*cn; i++ )
color_weight[i] = (float)std::exp(i*i*gauss_color_coeff);
// initialize space-related bilateral filter coefficients
for( i = -radius, maxk = 0; i <= radius; i++ )
for( j = -radius; j <= radius; j++ )
{
double r = std::sqrt((double)i*i + (double)j*j);
if( r > radius )
continue;
space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff);
space_ofs[maxk++] = (int)(i*temp.step + j*cn);
}
for( i = 0; i < size.height; i++ )
{
const uchar* sptr = temp.data + (i+radius)*temp.step + radius*cn;
uchar* dptr = dst.data + i*dst.step;
if( cn == 1 )
{
for( j = 0; j < size.width; j++ )
{
float sum = 0, wsum = 0;
int val0 = sptr[j];
for( k = 0; k < maxk; k++ )
{
int val = sptr[j + space_ofs[k]];
float w = space_weight[k]*color_weight[std::abs(val - val0)];
sum += val*w;
wsum += w;
}
// overflow is not possible here => there is no need to use CV_CAST_8U
dptr[j] = (uchar)cvRound(sum/wsum);
}
}
else
{
assert( cn == 3 );
for( j = 0; j < size.width*3; j += 3 )
{
float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
int b0 = sptr[j], g0 = sptr[j+1], r0 = sptr[j+2];
for( k = 0; k < maxk; k++ )
{
const uchar* sptr_k = sptr + j + space_ofs[k];
int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2];
float w = space_weight[k]*color_weight[std::abs(b - b0) +
std::abs(g - g0) + std::abs(r - r0)];
sum_b += b*w; sum_g += g*w; sum_r += r*w;
wsum += w;
}
wsum = 1.f/wsum;
b0 = cvRound(sum_b*wsum);
g0 = cvRound(sum_g*wsum);
r0 = cvRound(sum_r*wsum);
dptr[j] = (uchar)b0; dptr[j+1] = (uchar)g0; dptr[j+2] = (uchar)r0;
}
}
}
}
雙邊濾波呼叫
bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace,
int borderType=BORDER_DEFAULT );
d 表示濾波時畫素鄰域直徑,d為負時由 sigaColor計算得到;d>5時不能實時處理。sigmaColor、sigmaSpace非別表示顏色空間和座標空間的濾波係數sigma。可以簡單的賦值為相同的值。<10時幾乎沒有效果;>150時為油畫的效果。
borderType可以不指定。
雙邊濾波實驗
用sigma為10,150,240,480時效果如下:
參考文獻:
Richard Szeliski 《Computer Vision: Algorithms and Applications》
http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
《The OpenCV Tutorials》 Release 2.4.2
《The OpenCV Reference Manual 》 Release 2.4.2
轉載請註明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7785365
相關文章
- OpenCV計算機視覺學習(4)——影像平滑處理(均值濾波,高斯濾波,中值濾波,雙邊濾波)OpenCV計算機視覺
- [Python影象處理] 四.影象平滑之均值濾波、方框濾波、高斯濾波及中值濾波Python
- OpenCV 線性濾波OpenCV
- 頻率域濾波基本操作
- 均值濾波
- 演算法 | 數字影像處理之「中值濾波」演算法
- 數字濾波器和模擬濾波器(一)
- 圖片濾波
- 資料平滑處理-均值|中值|Savitzky-Golay濾波器Go
- 影像濾波演算法整理--均值、中值、高斯、拉普拉斯運算元、梯度運算元:演算法梯度
- vivado fir濾波器
- 卡爾曼濾波
- 引導濾波GuidedFilterGUIIDEFilter
- 點雲濾波器與過濾器過濾器
- 濾波演算法——十大濾波演算法程式大全演算法
- Python 影像處理 OpenCV (7):影像平滑(濾波)處理PythonOpenCV
- OpenCV計算機視覺學習(10)——影像變換(傅立葉變換,高通濾波,低通濾波)OpenCV計算機視覺
- 頻率域濾波去除週期性噪聲
- 空域濾波演算法演算法
- LMS自適應濾波
- 詳解數字影像的濾波和邊緣檢測
- 音訊降噪-fir濾波器音訊
- 實驗六 有源濾波器
- 利用Matlab filterDesigner 工具生成FIR濾波器函式,並呼叫實現低通濾波MatlabFilter函式
- 卡爾曼濾波器(Kalman Filters)Filter
- 1.影像濾波與影像融合
- [快速閱讀九] 自適應中值濾波及保守濾波在去除椒鹽噪音或脈衝噪音上的應用。
- 卡爾曼濾波學習資料
- Kalman濾波器的原理與實現
- 卡爾曼濾波 跑通調參
- matlab 濾波器中用到的函式Matlab函式
- 卡爾曼濾波器預測原理
- 10. 邊緣保留濾波EPF-cv2.bilateralFilter()、cv2.pyrMeanShiftFiltering()Filter
- 詳解卡爾曼濾波(Kalman Filter)原理Filter
- 卡爾曼濾波器階次問題
- Unity元件:AudioChorusFilterPROonly音訊合聲濾波器Unity元件Filter音訊
- 比例閥驅動電路後級PWM濾波尖刺如何消除?PWM通過RC低通濾波器模擬DAC
- 圖解卡爾曼濾波(Kalman Filter)--理解1圖解Filter