openCV 中值滤波算法解析
中值濾波算法是使用一個模板,在圖像中移動的過程中,取模板內的排列中間的值替代模板中心的值。
常用的中值濾波的快速算法見論文“A Fast Two-Dimensional Median Filtering Algorithm”。本人按照論文上的方法實現了一下,發現效率僅是openCV的十分之一,研究了一下openCV1.0的源碼,發現他也是用的論文上的原理,但是,他的編程手法就高明多了。下面,咱們就看一下openCV是怎么處理的!
/*
1.src原圖
2.src_step原圖步長
3.dst處理后
4.dst圖步長
5.size 圖像大小
6.m模板大小
7.cn圖像通道數
*/
static CvStatus CV_STDCALL icvMedianBlur_8u_CnR( uchar* src, int src_step, uchar* dst, int dst_step, CvSize size, int m, int cn )
{? ?//定義16級灰度直方圖
? ? #define N ?16
? ? int zone0[4][N];
? ? //定義256級灰度直方圖? ? int zone1[4][N*N];
? ? int x, y;
? ? int n2 = m*m/2;?
? ? int nx = (m + 1)/2 - 1;
? ? uchar* ?src_max = src + size.height*src_step;
? ? uchar* ?src_right = src + size.width*cn;
? ? //更新直方圖
? ? #define UPDATE_ACC01( pix, cn, op ) \
? ? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
? ? ? ? int p = (pix); ? ? ? ? ? ? ? ? ?\
? ? ? ? zone1[cn][p] op; ? ? ? ? ? ? ? ?\
? ? ? ? zone0[cn][p >> 4] op; ? ? ? ? ? \
? ? }
? ? if( size.height < nx || size.width < nx )
? ? ? ? return CV_BADSIZE_ERR;
? ? //模板大小為3時候,單獨處理(效率高)
? ? if( m == 3 )? ? {
? ? ? ? size.width *= cn;
? ? ? ? for( y = 0; y < size.height; y++, dst += dst_step )
? ? ? ? {
? ? ? ? ? ? const uchar* src0 = src + src_step*(y-1);
? ? ? ? ? ? const uchar* src1 = src0 + src_step;
? ? ? ? ? ? const uchar* src2 = src1 + src_step;
? ? //處理第一列的時候src0 = src1
? ? ? ? ? ? if( y == 0 )? ? ? ? ? ? ? ? src0 = src1;
? ?//處理最后一列的時候?src2 = src1
? ? ? ? ? ? else if( y == size.height - 1 )? ? ? ? ? ? ? ? src2 = src1;
? ? ? ? ? ?//考慮多通道循環
? ? ? ? ? ? for( x = 0; x < 2*cn; x++ )
? ? ? ? ? ? {
? ? int x0 = x < cn ? x : size.width - 3*cn + x;
int x2 = x < cn ? x + cn : size.width - 2*cn + x;
int x1 = x < cn ? x0 : x2, t;
? ? ? ? ? ? ? ? int p0 = src0[x0], p1 = src0[x1], p2 = src0[x2];
? ? ? ? ? ? ? ? int p3 = src1[x0], p4 = src1[x1], p5 = src1[x2];
? ? ? ? ? ? ? ? int p6 = src2[x0], p7 = src2[x1], p8 = src2[x2];
? ? ? ? ? ? ? ? CV_MINMAX_8U(p1, p2); CV_MINMAX_8U(p4, p5);
CV_MINMAX_8U(p7, p8); CV_MINMAX_8U(p0, p1);
CV_MINMAX_8U(p3, p4); CV_MINMAX_8U(p6, p7);
//代碼圖解:0~5代表執行順序,
??
CV_MINMAX_8U(p1, p2); CV_MINMAX_8U(p4, p5);
CV_MINMAX_8U(p7, p8); CV_MINMAX_8U(p0, p3);
CV_MINMAX_8U(p5, p8); CV_MINMAX_8U(p4, p7);
? ? ? ? ? ? ? ? //代碼圖解:0~5代表執行順序,
//經過第一步六次對比,再加上本次的0~2的三次對比,3*3的數據已經達到行向有序(從小到大)
? ? ? ? ? ? ? ? //P0 P3 P6 是行向最小值 ,P2 P5 P8 是行向最大值
? ? ? ? ? ? ? ? CV_MINMAX_8U(p3, p6); CV_MINMAX_8U(p1, p4);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p2, p5); CV_MINMAX_8U(p4, p7);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p4, p2); CV_MINMAX_8U(p6, p4);
//代碼圖解:0~5代表執行順序,
//經過第二步的六次對比,再加上本次的0~3的四次對比,中間列(P1、P4、P7)已到達列向有序,第一列和第三列都沒有嚴格有序
//但是,可以確定的是P6是行向小值和列向最大值, P2 是行向最大值和列向最小值
? ? ? ? ? ? ? ? CV_MINMAX_8U(p4, p2);
//第三部經過4~5兩次斜向對比,再加上本步一次對比,實現了(P2、P4、P6)的斜向有序。
//對比完成后P4就是中值
//總之,其算法的基本思路是先對數據進行排序(算法中的行向有序),然后再在三組有序的數據中找到數據合并后的中位數
//那就是,取出有序序列組的最小值中的最大值、最大值中的最小值、中間列向的中值,三個數進行比較,取其中值就是
//三個有序序列的中值
? ? ? ? ? ? ? ? dst[x1] = (uchar)p4;? ? ? ? ? ? }
? ? ? ? ? ? for( x = cn; x < size.width - cn; x++ )
? ? ? {
int p0 = src0[x-cn], p1 = src0[x], p2 = src0[x+cn];
int p3 = src1[x-cn], p4 = src1[x], p5 = src1[x+cn];
int p6 = src2[x-cn], p7 = src2[x], p8 = src2[x+cn];
int t;
? ? ? ? ? ? ? ? CV_MINMAX_8U(p1, p2); CV_MINMAX_8U(p4, p5);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p7, p8); CV_MINMAX_8U(p0, p1);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p3, p4); CV_MINMAX_8U(p6, p7);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p1, p2); CV_MINMAX_8U(p4, p5);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p7, p8); CV_MINMAX_8U(p0, p3);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p5, p8); CV_MINMAX_8U(p4, p7);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p3, p6); CV_MINMAX_8U(p1, p4);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p2, p5); CV_MINMAX_8U(p4, p7);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p4, p2); CV_MINMAX_8U(p6, p4);
? ? ? ? ? ? ? ? CV_MINMAX_8U(p4, p2);
? ? ? ? ? ? ? ? dst[x] = (uchar)p4;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return CV_OK;
? ? }
? ? for( x = 0; x < size.width; x++, dst += cn )
? ? {
? ? ? ? uchar* dst_cur = dst;
? ? ? ? uchar* src_top = src;
? ? ? ? uchar* src_bottom = src;
? ? ? ? int k, c;
? ? ? ? int x0 = -1;
if( x <= m/2 )
nx++;
? ? ? ? if( nx < m )
? ? ? ? ? ? x0 = x < m/2 ? 0 : (nx-1)*cn;
? ? ? ? ? ??
? ? ? ? // init accumulator
? ? ? ? memset( zone0, 0, sizeof(zone0[0])*cn );
? ? ? ? memset( zone1, 0, sizeof(zone1[0])*cn );
? ? ? ??
? ? ? ? for( y = -m/2; y < m/2; y++ )
? ? ? ? {
? ? ? ? ? ? for( c = 0; c < cn; c++ )
? ? ? ? ? ? {
if( x0 >= 0 )
UPDATE_ACC01( src_bottom[x0+c], c, += (m - nx) );
for( k = 0; k < nx*cn; k += cn )
UPDATE_ACC01( src_bottom[k+c], c, ++ );
? ? ? ? ? ? }
? ? ? ? ? ? if( (unsigned)y < (unsigned)(size.height-1) )
? ? ? ? ? ? ? ? src_bottom += src_step;
? ? ? ? }
? ? ? ? for( y = 0; y < size.height; y++, dst_cur += dst_step )
? ? ? ? {
? ? ? ? ? ? if( cn == 1 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for( k = 0; k < nx; k++ )
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k], 0, ++ );
? ? ? ? ? ? }
? ? ? ? ? ? else if( cn == 3 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for( k = 0; k < nx*3; k += 3 )
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k], 0, ++ );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k+1], 1, ++ );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k+2], 2, ++ );
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? assert( cn == 4 );
? ? ? ? ? ? ? ? for( k = 0; k < nx*4; k += 4 )
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k], 0, ++ );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k+1], 1, ++ );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k+2], 2, ++ );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[k+3], 3, ++ );
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if( x0 >= 0 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for( c = 0; c < cn; c++ )
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_bottom[x0+c], c, += (m - nx) );
? ? ? ? ? ? }
? ? ? ? ? ? if( src_bottom + src_step < src_max )
? ? ? ? ? ? ? ? src_bottom += src_step;
? ? ? ? ? ? // find median
? //重點介紹一下這部分代碼
//計算過16級和256級的灰度直方圖之后,查找中值就很簡單了,相見論文“A Fast Two-Dimensional Median Filtering Algorithm”。
//這里看一下openCV的實現過程,論文中介紹了利用直方圖進行中位數計算的方法,但是并未介紹使用16級的直方圖,openCV編程的巧妙之處就在于他把兩種直方圖相
?//結合,這樣就會大大加速程序的執行效率。因為遍歷一個16級的直方圖很快,但是256級的直方圖就會非常慢。openCV的這種思路還是值得大家學習的。
? //至于里面的編程技巧,大家看著學吧!
? ? ? ? ? ? for( c = 0; c < cn; c++ )? ? ? ? ? ? {
? ? ? ? ? ? ? ? int s = 0;
? ? ? ? ? ? ? ? for( k = 0; ; k++ )
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? int t = s + zone0[c][k];
? ? ? ? ? ? ? ? ? ? if( t > n2 ) break;
? ? ? ? ? ? ? ? ? ? s = t;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? for( k *= N; ;k++ )
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? s += zone1[c][k];
? ? ? ? ? ? ? ? ? ? if( s > n2 ) break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? dst_cur[c] = (uchar)k;
? ? ? ? ? ? }
? ? ? ? ? ? if( cn == 1 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for( k = 0; k < nx; k++ )
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k], 0, -- );
? ? ? ? ? ? }
? ? ? ? ? ? else if( cn == 3 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for( k = 0; k < nx*3; k += 3 )
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k], 0, -- );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k+1], 1, -- );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k+2], 2, -- );
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? assert( cn == 4 );
? ? ? ? ? ? ? ? for( k = 0; k < nx*4; k += 4 )
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k], 0, -- );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k+1], 1, -- );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k+2], 2, -- );
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[k+3], 3, -- );
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if( x0 >= 0 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for( c = 0; c < cn; c++ )
? ? ? ? ? ? ? ? ? ? UPDATE_ACC01( src_top[x0+c], c, -= (m - nx) );
? ? ? ? ? ? }
? ? ? ? ? ? if( y >= m/2 )
? ? ? ? ? ? ? ? src_top += src_step;
? ? ? ? }
? ? ? ? if( x >= m/2 )
? ? ? ? ? ? src += cn;
? ? ? ? if( src + nx*cn > src_right ) nx--;
? ? }
#undef N
#undef UPDATE_ACC
? ? return CV_OK;
}
總結
以上是生活随笔為你收集整理的openCV 中值滤波算法解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [搜片神器]使用C#实现DHT磁力搜索的
- 下一篇: BCH码(能纠正多个随机错误的循环码)