/*
 * cvsmooth.c
 *
 * Created: 11/29/2012 5:48:58 PM
 *  Author: andy.gao
 */ 

#include "_cv.h"

/****************************************************************************************\
                                         Box Filter
\****************************************************************************************/

static void icvSumRow_8u32s( const uchar* src0, int* dst, void* params );
static void icvSumRow_32f64f( const float* src0, double* dst, void* params );
static void icvSumCol_32s8u( const int** src, uchar* dst, int dst_step,
                             int count, void* params );
static void icvSumCol_32s16s( const int** src, short* dst, int dst_step,
                             int count, void* params );
static void icvSumCol_32s32s( const int** src, int* dst, int dst_step,
                             int count, void* params );
static void icvSumCol_64f32f( const double** src, float* dst, int dst_step,
                              int count, void* params );

CvBoxFilter::CvBoxFilter()
{
    min_depth = CV_32S;
    sum = 0;
    sum_count = 0;
    normalized = false;
}


CvBoxFilter::CvBoxFilter( int _max_width, int _src_type, int _dst_type,
                          bool _normalized, CvSize _ksize,
                          CvPoint _anchor, int _border_mode,
                          CvScalar _border_value )
{
    min_depth = CV_32S;
    sum = 0;
    sum_count = 0;
    normalized = false;
    init( _max_width, _src_type, _dst_type, _normalized,
          _ksize, _anchor, _border_mode, _border_value );
}


CvBoxFilter::~CvBoxFilter()
{
    clear();
}


void CvBoxFilter::init( int _max_width, int _src_type, int _dst_type,
                        bool _normalized, CvSize _ksize,
                        CvPoint _anchor, int _border_mode,
                        CvScalar _border_value )
{
    CV_FUNCNAME( "CvBoxFilter::init" );

    __BEGIN__;
    
    sum = 0;
    normalized = _normalized;

    if( normalized && CV_MAT_TYPE(_src_type) != CV_MAT_TYPE(_dst_type) ||
        !normalized && CV_MAT_CN(_src_type) != CV_MAT_CN(_dst_type))
        CV_ERROR( CV_StsUnmatchedFormats,
        "In case of normalized box filter input and output must have the same type.\n"
        "In case of unnormalized box filter the number of input and output channels must be the same" );

    min_depth = CV_MAT_DEPTH(_src_type) == CV_8U ? CV_32S : CV_64F;

    CvBaseImageFilter::init( _max_width, _src_type, _dst_type, 1, _ksize,
                             _anchor, _border_mode, _border_value );
    
    scale = normalized ? 1./(ksize.width*ksize.height) : 1;

    if( CV_MAT_DEPTH(src_type) == CV_8U )
        x_func = (CvRowFilterFunc)icvSumRow_8u32s;
    else if( CV_MAT_DEPTH(src_type) == CV_32F )
        x_func = (CvRowFilterFunc)icvSumRow_32f64f;
    else
        CV_ERROR( CV_StsUnsupportedFormat, "Unknown/unsupported input image format" );

    if( CV_MAT_DEPTH(dst_type) == CV_8U )
    {
        if( !normalized )
            CV_ERROR( CV_StsBadArg, "Only normalized box filter can be used for 8u->8u transformation" );
        y_func = (CvColumnFilterFunc)icvSumCol_32s8u;
    }
    else if( CV_MAT_DEPTH(dst_type) == CV_16S )
    {
        if( normalized || CV_MAT_DEPTH(src_type) != CV_8U )
            CV_ERROR( CV_StsBadArg, "Only 8u->16s unnormalized box filter is supported in case of 16s output" );
        y_func = (CvColumnFilterFunc)icvSumCol_32s16s;
    }
	else if( CV_MAT_DEPTH(dst_type) == CV_32S )
	{
		if( normalized || CV_MAT_DEPTH(src_type) != CV_8U )
			CV_ERROR( CV_StsBadArg, "Only 8u->32s unnormalized box filter is supported in case of 32s output");

		y_func = (CvColumnFilterFunc)icvSumCol_32s32s;
	}
    else if( CV_MAT_DEPTH(dst_type) == CV_32F )
    {
        if( CV_MAT_DEPTH(src_type) != CV_32F )
            CV_ERROR( CV_StsBadArg, "Only 32f->32f box filter (normalized or not) is supported in case of 32f output" );
        y_func = (CvColumnFilterFunc)icvSumCol_64f32f;
    }
	else{
		CV_ERROR( CV_StsBadArg, "Unknown/unsupported destination image format" );
	}

    __END__;
}


void CvBoxFilter::start_process( CvSlice x_range, int width )
{
    CvBaseImageFilter::start_process( x_range, width );
    int i, psz = CV_ELEM_SIZE(work_type);
    uchar* s;
    buf_end -= buf_step;
    buf_max_count--;
    assert( buf_max_count >= max_ky*2 + 1 );
    s = sum = buf_end + cvAlign((width + ksize.width - 1)*CV_ELEM_SIZE(src_type), ALIGN);
    sum_count = 0;

    width *= psz;
    for( i = 0; i < width; i++ )
        s[i] = (uchar)0;
}


static void
icvSumRow_8u32s( const uchar* src, int* dst, void* params )
{
    const CvBoxFilter* state = (const CvBoxFilter*)params;
    int ksize = state->get_kernel_size().width;
    int width = state->get_width();
    int cn = CV_MAT_CN(state->get_src_type());
    int i, k;

    width = (width - 1)*cn; ksize *= cn;

    for( k = 0; k < cn; k++, src++, dst++ )
    {
        int s = 0;
        for( i = 0; i < ksize; i += cn )
            s += src[i];
        dst[0] = s;
        for( i = 0; i < width; i += cn )
        {
            s += src[i+ksize] - src[i];
            dst[i+cn] = s;
        }
    }
}


static void
icvSumRow_32f64f( const float* src, double* dst, void* params )
{
    const CvBoxFilter* state = (const CvBoxFilter*)params;
    int ksize = state->get_kernel_size().width;
    int width = state->get_width();
    int cn = CV_MAT_CN(state->get_src_type());
    int i, k;

    width = (width - 1)*cn; ksize *= cn;

    for( k = 0; k < cn; k++, src++, dst++ )
    {
        double s = 0;
        for( i = 0; i < ksize; i += cn )
            s += src[i];
        dst[0] = s;
        for( i = 0; i < width; i += cn )
        {
            s += (double)src[i+ksize] - src[i];
            dst[i+cn] = s;
        }
    }
}


static void
icvSumCol_32s8u( const int** src, uchar* dst,
                 int dst_step, int count, void* params )
{
#define BLUR_SHIFT 24
    CvBoxFilter* state = (CvBoxFilter*)params;
    int ksize = state->get_kernel_size().height;
    int i, width = state->get_width();
    int cn = CV_MAT_CN(state->get_src_type());
    double scale = state->get_scale();
    int iscale = cvFloor(scale*(1 << BLUR_SHIFT));
    int* sum = (int*)state->get_sum_buf();
    int* _sum_count = state->get_sum_count_ptr();
    int sum_count = *_sum_count;

    width *= cn;
    src += sum_count;
    count += ksize - 1 - sum_count;

    for( ; count--; src++ )
    {
        const int* sp = src[0];
        if( sum_count+1 < ksize )
        {
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
                sum[i] += sp[i];

            sum_count++;
        }
        else
        {
            const int* sm = src[-ksize+1];
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                int t0 = CV_DESCALE(s0*iscale, BLUR_SHIFT), t1 = CV_DESCALE(s1*iscale, BLUR_SHIFT);
                s0 -= sm[i]; s1 -= sm[i+1];
                sum[i] = s0; sum[i+1] = s1;
                dst[i] = (uchar)t0; dst[i+1] = (uchar)t1;
            }

            for( ; i < width; i++ )
            {
                int s0 = sum[i] + sp[i], t0 = CV_DESCALE(s0*iscale, BLUR_SHIFT);
                sum[i] = s0 - sm[i]; dst[i] = (uchar)t0;
            }
            dst += dst_step;
        }
    }

    *_sum_count = sum_count;
#undef BLUR_SHIFT
}


static void
icvSumCol_32s16s( const int** src, short* dst,
                  int dst_step, int count, void* params )
{
    CvBoxFilter* state = (CvBoxFilter*)params;
    int ksize = state->get_kernel_size().height;
    int ktotal = ksize*state->get_kernel_size().width;
    int i, width = state->get_width();
    int cn = CV_MAT_CN(state->get_src_type());
    int* sum = (int*)state->get_sum_buf();
    int* _sum_count = state->get_sum_count_ptr();
    int sum_count = *_sum_count;

    dst_step /= sizeof(dst[0]);
    width *= cn;
    src += sum_count;
    count += ksize - 1 - sum_count;

    for( ; count--; src++ )
    {
        const int* sp = src[0];
        if( sum_count+1 < ksize )
        {
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
                sum[i] += sp[i];

            sum_count++;
        }
        else if( ktotal < 128 )
        {
            const int* sm = src[-ksize+1];
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                dst[i] = (short)s0; dst[i+1] = (short)s1;
                s0 -= sm[i]; s1 -= sm[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
            {
                int s0 = sum[i] + sp[i];
                dst[i] = (short)s0;
                sum[i] = s0 - sm[i];
            }
            dst += dst_step;
        }
        else
        {
            const int* sm = src[-ksize+1];
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                dst[i] = CV_CAST_16S(s0); dst[i+1] = CV_CAST_16S(s1);
                s0 -= sm[i]; s1 -= sm[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
            {
                int s0 = sum[i] + sp[i];
                dst[i] = CV_CAST_16S(s0);
                sum[i] = s0 - sm[i];
            }
            dst += dst_step;
        }
    }

    *_sum_count = sum_count;
}

static void
icvSumCol_32s32s( const int** src, int * dst,
                  int dst_step, int count, void* params )
{
    CvBoxFilter* state = (CvBoxFilter*)params;
    int ksize = state->get_kernel_size().height;
    int i, width = state->get_width();
    int cn = CV_MAT_CN(state->get_src_type());
    int* sum = (int*)state->get_sum_buf();
    int* _sum_count = state->get_sum_count_ptr();
    int sum_count = *_sum_count;

    dst_step /= sizeof(dst[0]);
    width *= cn;
    src += sum_count;
    count += ksize - 1 - sum_count;

    for( ; count--; src++ )
    {
        const int* sp = src[0];
        if( sum_count+1 < ksize )
        {
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
                sum[i] += sp[i];

            sum_count++;
        }
        else
        {
            const int* sm = src[-ksize+1];
            for( i = 0; i <= width - 2; i += 2 )
            {
                int s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                dst[i] = s0; dst[i+1] = s1;
                s0 -= sm[i]; s1 -= sm[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
            {
                int s0 = sum[i] + sp[i];
                dst[i] = s0;
                sum[i] = s0 - sm[i];
            }
            dst += dst_step;
        }
    }

    *_sum_count = sum_count;
}


static void
icvSumCol_64f32f( const double** src, float* dst,
                  int dst_step, int count, void* params )
{
    CvBoxFilter* state = (CvBoxFilter*)params;
    int ksize = state->get_kernel_size().height;
    int i, width = state->get_width();
    int cn = CV_MAT_CN(state->get_src_type());
    double scale = state->get_scale();
    bool normalized = state->is_normalized();
    double* sum = (double*)state->get_sum_buf();
    int* _sum_count = state->get_sum_count_ptr();
    int sum_count = *_sum_count;

    dst_step /= sizeof(dst[0]);
    width *= cn;
    src += sum_count;
    count += ksize - 1 - sum_count;

    for( ; count--; src++ )
    {
        const double* sp = src[0];
        if( sum_count+1 < ksize )
        {
            for( i = 0; i <= width - 2; i += 2 )
            {
                double s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                sum[i] = s0; sum[i+1] = s1;
            }

            for( ; i < width; i++ )
                sum[i] += sp[i];

            sum_count++;
        }
        else
        {
            const double* sm = src[-ksize+1];
            if( normalized )
                for( i = 0; i <= width - 2; i += 2 )
                {
                    double s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                    double t0 = s0*scale, t1 = s1*scale;
                    s0 -= sm[i]; s1 -= sm[i+1];
                    dst[i] = (float)t0; dst[i+1] = (float)t1;
                    sum[i] = s0; sum[i+1] = s1;
                }
            else
                for( i = 0; i <= width - 2; i += 2 )
                {
                    double s0 = sum[i] + sp[i], s1 = sum[i+1] + sp[i+1];
                    dst[i] = (float)s0; dst[i+1] = (float)s1;
                    s0 -= sm[i]; s1 -= sm[i+1];
                    sum[i] = s0; sum[i+1] = s1;
                }

            for( ; i < width; i++ )
            {
                double s0 = sum[i] + sp[i], t0 = s0*scale;
                sum[i] = s0 - sm[i]; dst[i] = (float)t0;
            }
            dst += dst_step;
        }
    }

    *_sum_count = sum_count;
}


/****************************************************************************************\
                                      Median Filter
\****************************************************************************************/

#define CV_MINMAX_8U(a,b) \
    (t = CV_FAST_CAST_8U((a) - (b)), (b) += t, a -= t)
static CvStatus CV_STDCALL
icvMedianBlur_8u_CnR( uchar* src, int src_step, uchar* dst, int dst_step,
CvSize size, int m, int cn )
{
	#define N  16
	int     zone0[4][N];
	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;

	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;
			if( y == 0 )
			src0 = 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);
				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[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
			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;
}

//////////////////////////////// IPP smoothing functions /////////////////////////////////

icvFilterMedian_8u_C1R_t icvFilterMedian_8u_C1R_p = 0;
icvFilterMedian_8u_C3R_t icvFilterMedian_8u_C3R_p = 0;
icvFilterMedian_8u_C4R_t icvFilterMedian_8u_C4R_p = 0;

icvFilterBox_8u_C1R_t icvFilterBox_8u_C1R_p = 0;
icvFilterBox_8u_C3R_t icvFilterBox_8u_C3R_p = 0;
icvFilterBox_8u_C4R_t icvFilterBox_8u_C4R_p = 0;
icvFilterBox_32f_C1R_t icvFilterBox_32f_C1R_p = 0;
icvFilterBox_32f_C3R_t icvFilterBox_32f_C3R_p = 0;
icvFilterBox_32f_C4R_t icvFilterBox_32f_C4R_p = 0;

typedef CvStatus (CV_STDCALL * CvSmoothFixedIPPFunc)
( const void* src, int srcstep, void* dst, int dststep,
  CvSize size, CvSize ksize, CvPoint anchor );
CV_IMPL void
cvSmooth( const void* srcarr, void* dstarr, int smooth_type,
int param1, int param2, double param3, double param4 )
{
    CvBoxFilter box_filter;
    CvSepFilter gaussian_filter;

	CvMat* temp = 0;

	CV_FUNCNAME( "cvSmooth" );

	__BEGIN__;

    int coi1 = 0, coi2 = 0;
    CvMat srcstub, *src = (CvMat*)srcarr;
    CvMat dststub, *dst = (CvMat*)dstarr;
    CvSize size;
    int src_type, dst_type, depth, cn;
    double sigma1 = 0, sigma2 = 0;
    bool have_ipp = icvFilterMedian_8u_C1R_p != 0;

    CV_CALL( src = cvGetMat( src, &srcstub, &coi1 ));
    CV_CALL( dst = cvGetMat( dst, &dststub, &coi2 ));

	if( coi1 != 0 || coi2 != 0 )
	CV_ERROR( CV_BadCOI, "" );

	src_type = CV_MAT_TYPE( src->type );
	dst_type = CV_MAT_TYPE( dst->type );
	depth = CV_MAT_DEPTH(src_type);
	cn = CV_MAT_CN(src_type);
	size = cvGetMatSize(src);

	if( !CV_ARE_SIZES_EQ( src, dst ))
	CV_ERROR( CV_StsUnmatchedSizes, "" );

	if( smooth_type != CV_BLUR_NO_SCALE && !CV_ARE_TYPES_EQ( src, dst ))
	CV_ERROR( CV_StsUnmatchedFormats,
	"The specified smoothing algorithm requires input and ouput arrays be of the same type" );

    if( smooth_type == CV_BLUR || smooth_type == CV_BLUR_NO_SCALE ||
        smooth_type == CV_GAUSSIAN || smooth_type == CV_MEDIAN )
    {
        // automatic detection of kernel size from sigma
        if( smooth_type == CV_GAUSSIAN )
        {
            sigma1 = param3;
            sigma2 = param4 ? param4 : param3;

            if( param1 == 0 && sigma1 > 0 )
                param1 = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
            if( param2 == 0 && sigma2 > 0 )
                param2 = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
        }

		if( param2 == 0 )
		param2 = size.height == 1 ? 1 : param1;
		if( param1 < 1 || (param1 & 1) == 0 || param2 < 1 || (param2 & 1) == 0 )
		CV_ERROR( CV_StsOutOfRange,
		"Both mask width and height must be >=1 and odd" );

		if( param1 == 1 && param2 == 1 )
		{
			cvConvert( src, dst );
			EXIT;
		}
	}

	if( have_ipp && (smooth_type == CV_BLUR || smooth_type == CV_MEDIAN) &&
	size.width >= param1 && size.height >= param2 && param1 > 1 && param2 > 1 )
	{
		 CvSmoothFixedIPPFunc ipp_median_box_func = 0;

        if( smooth_type == CV_BLUR )
        {
            ipp_median_box_func =
                src_type == CV_8UC1 ? icvFilterBox_8u_C1R_p :
                src_type == CV_8UC3 ? icvFilterBox_8u_C3R_p :
                src_type == CV_8UC4 ? icvFilterBox_8u_C4R_p :
                src_type == CV_32FC1 ? icvFilterBox_32f_C1R_p :
                src_type == CV_32FC3 ? icvFilterBox_32f_C3R_p :
                src_type == CV_32FC4 ? icvFilterBox_32f_C4R_p : 0;
        }
        else if( smooth_type == CV_MEDIAN )
        {
            ipp_median_box_func =
                src_type == CV_8UC1 ? icvFilterMedian_8u_C1R_p :
                src_type == CV_8UC3 ? icvFilterMedian_8u_C3R_p :
                src_type == CV_8UC4 ? icvFilterMedian_8u_C4R_p : 0;
        }

		if( ipp_median_box_func )
		{
		CvSize el_size = { param1, param2 };
	CvPoint el_anchor = { param1/2, param2/2 };
	int stripe_size = 1 << 14; // the optimal value may depend on CPU cache,
	// overhead of the current IPP code etc.
	const uchar* shifted_ptr;
	int y, dy = 0;
	int temp_step, dst_step = dst->step;

	CV_CALL( temp = icvIPPFilterInit( src, stripe_size, el_size ));

	shifted_ptr = temp->data.ptr +
	el_anchor.y*temp->step + el_anchor.x*CV_ELEM_SIZE(src_type);
	temp_step = temp->step ? temp->step : CV_STUB_STEP;

	for( y = 0; y < src->rows; y += dy )
	{
		dy = icvIPPFilterNextStripe( src, temp, y, el_size, el_anchor );
		 IPPI_CALL( ipp_median_box_func( shifted_ptr, temp_step,
		dst->data.ptr + y*dst_step, dst_step, cvSize(src->cols, dy),
		el_size, el_anchor ));
	}
	EXIT;
}
		}

    if( smooth_type == CV_BLUR || smooth_type == CV_BLUR_NO_SCALE )
    {
        CV_CALL( box_filter.init( src->cols, src_type, dst_type,
            smooth_type == CV_BLUR, cvSize(param1, param2) ));
        CV_CALL( box_filter.process( src, dst ));
    }
    else if( smooth_type == CV_MEDIAN )
    {
        if( depth != CV_8U || cn != 1 && cn != 3 && cn != 4 )
            CV_ERROR( CV_StsUnsupportedFormat,
            "Median filter only supports 8uC1, 8uC3 and 8uC4 images" );

       IPPI_CALL( icvMedianBlur_8u_CnR( src->data.ptr, src->step,
            dst->data.ptr, dst->step, size, param1, cn ));
    }
    else if( smooth_type == CV_GAUSSIAN )
    {
        CvSize ksize = { param1, param2 };
        float* kx = (float*)cvStackAlloc( ksize.width*sizeof(kx[0]) );
        float* ky = (float*)cvStackAlloc( ksize.height*sizeof(ky[0]) );
        CvMat KX = cvMat( 1, ksize.width, CV_32F, kx );
        CvMat KY = cvMat( 1, ksize.height, CV_32F, ky );
        
        CvSepFilter::init_gaussian_kernel( &KX, sigma1 );
        if( ksize.width != ksize.height || fabs(sigma1 - sigma2) > FLT_EPSILON )
            CvSepFilter::init_gaussian_kernel( &KY, sigma2 );
        else
            KY.data.fl = kx;
        
        if( have_ipp && size.width >= param1*3 &&
            size.height >= param2 && param1 > 1 && param2 > 1 )
        {
            int done;
            CV_CALL( done = icvIPPSepFilter( src, dst, &KX, &KY,
                        cvPoint(ksize.width/2,ksize.height/2)));
            if( done )
                EXIT;
        }

        CV_CALL( gaussian_filter.init( src->cols, src_type, dst_type, &KX, &KY ));
        CV_CALL( gaussian_filter.process( src, dst ));
        cvFree(&kx); //andy
   	 cvFree(&ky); //andy
    }
    /*else if( smooth_type == CV_BILATERAL )
    {
        if( param1 < 0 || param2 < 0 )
            CV_ERROR( CV_StsOutOfRange,
            "Thresholds in bilaral filtering should not bee negative" );
        param1 += param1 == 0;
        param2 += param2 == 0;

        if( depth != CV_8U || cn != 1 && cn != 3 )
            CV_ERROR( CV_StsUnsupportedFormat,
            "Bilateral filter only supports 8uC1 and 8uC3 images" );

        IPPI_CALL( icvBilateralFiltering_8u_CnR( src->data.ptr, src->step,
            dst->data.ptr, dst->step, size, param1, param2, cn ));
    }*/

    __END__;

    cvReleaseMat( &temp );
}

/* End of file. */
