zoukankan      html  css  js  c++  java
  • Opencv中图像滤波

    看了园子里的博友们,感觉自己也应该把自己以前学习到的东西写下来,通过写下来也正好是一个回顾的过程。于是决定把今年学到了什么写下来,至于还有很多不是很清楚的地方也写下来,以便以后更好的理解。

    这个写的是图像滤波。为什么要图像滤波呢,对于我们获取原始图像的时候,难免不被污染,就会有噪声的干扰,于是就有了图像滤波的操作,也就是对目标图像的噪声的抑制。这个操作也就是其他更深一层次的图像分析,图像理解来说的基础。

    对于滤波的方法,分频率滤波和空域滤波,这篇主要说的是空域滤波,均值滤波,中值滤波,高斯滤波,跟双边滤波。

    具体测试代码参考:

    http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/gausian_median_blur_bilateral_filter/gausian_median_blur_bilateral_filter.html#smoothing

    我们主要简单分析一下源码。

    1、均值滤波。

    K = \dfrac{1}{K_{width} \cdot K_{height}} \begin{bmatrix}
    1 & 1 & 1 & ... & 1 \\
    1 & 1 & 1 & ... & 1 \\
    . & . & . & ... & 1 \\
    . & . & . & ... & 1 \\
    1 & 1 & 1 & ... & 1
   \end{bmatrix}

    均值滤波的opencv中的源码在smooth.cpp中为

    void cv::blur( InputArray src, OutputArray dst,Size ksize, Point anchor, int borderType )
    {
        boxFilter( src, dst, -1, ksize, anchor, true, borderType );
    } 调用了盒子滤波,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT默认值

    对于盒子滤波在SURF算法中提到,对于盒子滤波本人的理解就是,对于均值滤波每个像素点以1加权,而盒子滤波不一定是以1加权,不知道有没有其它差别。
    void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth,
                    Size ksize, Point anchor,
                    bool normalize, int borderType )
    {
        Mat src = _src.getMat();//获得处理图像
        //创建输出OutputArray _dst
        int sdepth = src.depth(), cn = src.channels();//获取图像的深度和通道数
        if( ddepth < 0 )//这边均值滤波传值过来的时候是-1小于0
            ddepth = sdepth;
        _dst.create( src.size(), CV_MAKETYPE(ddepth, cn) );//CV_MAKETYPE创建图像类型根据深度和通道数,这句是创建_dst
        Mat dst = _dst.getMat();//通过OutputArray得到Mat类型
        //BORDER_CONSTANT边界类型,opencv根据边界类型进行不同的插值运算来扩展边界
    /*
    不同的边界类型, '|'表示图像边界
    * BORDER_REPLICATE:     aaaaaa|abcdefgh|hhhhhhh
    * BORDER_REFLECT:       fedcba|abcdefgh|hgfedcb
    * BORDER_REFLECT_101:   gfedcb|abcdefgh|gfedcba
    * BORDER_WRAP:          cdefgh|abcdefgh|abcdefg       
    * BORDER_CONSTANT:      iiiiii|abcdefgh|iiiiiii  使用特定的'i'
    */
        if( borderType != BORDER_CONSTANT && normalize )//normalize均值滤波调用的时候是true,borderType=BORDER_DEFAULT默认值为
                                                                                               //BORDE R_REFLECT_101
        {
            if( src.rows == 1 )//如果源图像的行为1则滤波窗口的高度为1
                ksize.height = 1;
            if( src.cols == 1 )//如果源图像的列为1则滤波窗口的宽度为1
                ksize.width = 1;
        }
        //创建盒子滤波 返回滤波引擎,不管创建什么滤波都返回滤波引擎,工厂模式是由滤波引擎可以创建各种滤波,这个是什么模式呢?
        Ptr<FilterEngine> f = createBoxFilter( src.type(), dst.type(),
                            ksize, anchor, normalize, borderType );
        f->apply( src, dst );//virtual void apply( const Mat& src, Mat& dst,const Rect& srcRoi=Rect(0,0,-1,-1),
                              //Point dstOfs=Point(0,0),bool isolated=false);可以控制滤波图像的大小,这边是全部滤波
    }
    下面是创建盒子滤波
    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;//定义类型为64位浮点数
        /*在types_c.h中有
    #define CV_8U 0
    #define CV_8S 1
    #define CV_16U 2
    #define CV_16S 3
    #define CV_32S 4
    #define CV_32F 5
    #define CV_64F 6
    */
        if( sdepth < CV_32S && (!normalize ||
            ksize.width*ksize.height <= (sdepth == CV_8U ? (1<<23) :
                sdepth == CV_16U ? (1 << 15) : (1 << 16))) )
            sumType = CV_32S;//如果图像的深度不是32位或者64位的浮点型并且不归一化或者滤波窗口的面积
        sumType = CV_MAKETYPE( sumType, cn );//这边也不是很清楚这么写的目的,感觉完全就是控制像素类型不是64位浮点就是32位int类型???,也就是CV_8U,CV_16U,CV_16S都对应CV_32S,其余对应CV_64F?
    
        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 ));
    }
    主要的就是两步getRowSumFilter跟getColumnSumFilter
    cv::Ptr<cv::BaseRowFilter> cv::getRowSumFilter(int srcType, int sumType, int ksize, int anchor)
    {
        int sdepth = CV_MAT_DEPTH(srcType), ddepth = CV_MAT_DEPTH(sumType);
        CV_Assert( CV_MAT_CN(sumType) == CV_MAT_CN(srcType) );
        //要进行滤波的像素点如果这边设置小于0的话就是滤波窗口的一半,也就是滤波窗体的中心,这边滤波窗体一般设置为奇数
        if( anchor < 0 )
            anchor = ksize/2;
        //对于不同的图像像素值类型计算一行的像素和
        if( sdepth == CV_8U && ddepth == CV_32S )
            return Ptr<BaseRowFilter>(new RowSum<uchar, int>(ksize, anchor));
        if( sdepth == CV_8U && ddepth == CV_64F )
            return Ptr<BaseRowFilter>(new RowSum<uchar, double>(ksize, anchor));
        if( sdepth == CV_16U && ddepth == CV_32S )
            return Ptr<BaseRowFilter>(new RowSum<ushort, int>(ksize, anchor));
        if( sdepth == CV_16U && ddepth == CV_64F )
            return Ptr<BaseRowFilter>(new RowSum<ushort, double>(ksize, anchor));
        if( sdepth == CV_16S && ddepth == CV_32S )
            return Ptr<BaseRowFilter>(new RowSum<short, int>(ksize, anchor));
        if( sdepth == CV_32S && ddepth == CV_32S )
            return Ptr<BaseRowFilter>(new RowSum<int, int>(ksize, anchor));
        if( sdepth == CV_16S && ddepth == CV_64F )
            return Ptr<BaseRowFilter>(new RowSum<short, double>(ksize, anchor));
        if( sdepth == CV_32F && ddepth == CV_64F )
            return Ptr<BaseRowFilter>(new RowSum<float, double>(ksize, anchor));
        if( sdepth == CV_64F && ddepth == CV_64F )
            return Ptr<BaseRowFilter>(new RowSum<double, double>(ksize, anchor));
    
        CV_Error_( CV_StsNotImplemented,
            ("Unsupported combination of source format (=%d), and buffer format (=%d)",
            srcType, sumType));
    
        return Ptr<BaseRowFilter>(0);
    }
    创建一行的像素和操作如下:
    这个继承对1维数据处理的基类,实现滤波操作,这边的参数const uchar* src, uchar* dst, int width, int cn怎么传的呢???望有人解答一下
    void operator()(const uchar* src, uchar* dst, int width, int cn)
        {
            const T* S = (const T*)src;//将src转换到对应的T模板类型
            ST* D = (ST*)dst;//dst转换到对应的ST模板类型
            int i = 0, k, ksz_cn = ksize*cn;
            
            width = (width - 1)*cn;
            for( k = 0; k < cn; k++, S++, D++ )
            {
                ST s = 0;
                for( i = 0; i < ksz_cn; i += cn )
                    s += S[i];
                D[0] = s;
                for( i = 0; i < width; i += cn )
                {
                    s += S[i + ksz_cn] - S[i];
                    D[i+cn] = s;
                }
            }
        }

    好了,就先写到这,其实最核心的东西没有写,自己也不甚理解,望有人给予一定的解惑,谢谢。

  • 相关阅读:
    /etc/alternatives
    linux Link
    JVM程序计数器
    面向对象的内存分析
    SpringBoot项目简单实现给邮箱发送验证码
    注解式SQL Select语句判空
    SQL Update时参数判空:传入参数为空时不更新对应字段,不为空时才更新
    MySQL数据库误Update找回数据的方法
    用户-角色-权限系统概述
    将一个数组转换为List
  • 原文地址:https://www.cnblogs.com/fengbing/p/2852086.html
Copyright © 2011-2022 走看看