opencv滤波器FilterEngine

 

 

  • 结构图

暂时不画 

  • filterengine类

class FilterEngine
{
public:
    //默认构造函数
    FilterEngine();
   //完全构造函数,参数_filter2D或者_rowFilter和_columnFilter两者之间有一个非空
    FilterEngine(const Ptr<BaseFilter>& _filter2D,
                 const Ptr<BaseRowFilter>& _rowFilter,
                 const Ptr<BaseColumnFilter>& _columnFilter,
                 int srcType, int dstType, int bufType,
                 int _rowBorderType = BORDER_REPLICATE,
                 int _columnBorderType = -1,
                 const Scalar& _borderValue = Scalar());
   //析构函数
    virtual ~FilterEngine();
  //重新初始化引擎,之前的滤波器将被释放
    void init(const Ptr<BaseFilter>& _filter2D,	
              const Ptr<BaseRowFilter>& _rowFilter,	
              const Ptr<BaseColumnFilter>& _columnFilter,	
              int srcType, int dstType, int bufType,
              int _rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,
              //const cv::Scalar & _borderValue = Scalar()这个参数是存在的,csdn有bug,我只能注释掉
              );
    //
    virtual int start(const cv::Size &wholeSize, const cv::Size &sz, const cv::Point &ofs);
    virtual int start(const Mat& src, const cv::Size &wsz, const cv::Point &ofs);
    //真正的处理滤波函数,其中srcCount表示处理的行数
    virtual int proceed(const uchar* src, int srcStep, int srcCount,
                        uchar* dst, int dstStep);
     //引擎启动函数,一般上层应用使用这个apply进行启动引擎,他实际就是调用上面processed函数,然后processed执行滤波核心
    //应用滤波器对指定区域滤波,如果srcRoi=(0,0,-1,-1),则整张图像将会被滤波
	//其中wsz为全局大小,ofs为原图像起点在全局中的位置
    virtual void apply(const Mat& src, Mat& dst, const cv::Size &wsz, const cv::Point &ofs);
    //返回滤波器是否是可分离的
    bool isSeparable() const { return !filter2D; }
    //剩余输入行数
    int remainingInputRows() const;
    //剩余输出行数
    int remainingOutputRows() const;
    int srcType; //源数据类型
    int dstType; //目标数据类型
    int bufType;  //中间数据类型,或者说缓冲类型
    Size ksize;  //核大小
    Point anchor; //核锚点位置
    int maxWidth;  //最大宽度,(指的应该是滤波区域图像宽度不能小于这个)
    Size wholeSize; // 全局大小
    Rect roi; //滤波区域
    int dx1; //行,左边需要补齐个数
    int dx2; //行,右边需要补齐个数
    int rowBorderType; //行边界类型
    int columnBorderType; //列边界类型
    std::vector<int> borderTab; //边界表,这个是在边界类型为非常量时候使用,记录的是再roi区域的位置
    int borderElemSize;  //边界元素大小,这里指的是每个像素需要的元素个数,假如3通道,那么个数就是3
    std::vector<uchar> ringBuf;//环形缓冲区
    std::vector<uchar> srcRow; //原图像行,在可分离中使用
    std::vector<uchar> constBorderValue; //常量边界值
    std::vector<uchar> constBorderRow; //常量边界行 在不可分离滤波器中使用常量边界行 这个只在不可分离滤波器中直接使用,否则要通过srcRow进行行滤波间接使用
    int bufStep;//中间数据步长
    int startY; //这个变量初始值和startY一样,描述的也是行的位置,
    int startY0;//滤波器计算开始行,roi.y-ksize.y
    int endY; //源图像的结束行 ,滤波计算的结束行,roi.y+roi.height+(ksize.height-ksize.y-1)
    int rowCount;//辅助行计数器,在process中列滤波和不可分离滤波器中使用,和for循环中的索引作用一样,范围从0到bufRows(rows.size()),一旦为bufRow则数值不变
    int dstY; //目标矩阵的行数
    std::vector<uchar*> rows;//缓冲区,这个和ringBuf是相关的
//滤波器的指针,下面的滤波器才是真正的滤波器,process中就是将数据交给下面的滤波器处理
    Ptr<BaseFilter> filter2D;
    Ptr<BaseRowFilter> rowFilter;
    Ptr<BaseColumnFilter> columnFilter;
};

 

//构造函数,一般创建滤波器引擎使用的是这个完全构造函数
FilterEngine::FilterEngine( const Ptr<BaseFilter>& _filter2D,
                            const Ptr<BaseRowFilter>& _rowFilter,
                            const Ptr<BaseColumnFilter>& _columnFilter,
                            int _srcType, int _dstType, int _bufType,
                            int _rowBorderType, int _columnBorderType,
                            const Scalar& _borderValue )
    : srcType(-1), dstType(-1), bufType(-1), maxWidth(0), wholeSize(-1, -1), dx1(0), dx2(0),
      rowBorderType(BORDER_REPLICATE), columnBorderType(BORDER_REPLICATE),
      borderElemSize(0), bufStep(0), startY(0), startY0(0), endY(0), rowCount(0), dstY(0)
{
    init(_filter2D, _rowFilter, _columnFilter, _srcType, _dstType, _bufType,
         _rowBorderType, _columnBorderType, _borderValue);
}

 


void FilterEngine::init( const Ptr<BaseFilter>& _filter2D,
                         const Ptr<BaseRowFilter>& _rowFilter,
                         const Ptr<BaseColumnFilter>& _columnFilter,
                         int _srcType, int _dstType, int _bufType,
                         int _rowBorderType, int _columnBorderType,
                         const Scalar& _borderValue )
{
    _srcType = CV_MAT_TYPE(_srcType);
    _bufType = CV_MAT_TYPE(_bufType);
    _dstType = CV_MAT_TYPE(_dstType);

    srcType = _srcType;
    int srcElemSize = (int)getElemSize(srcType);//获取元素大小
    dstType = _dstType;
    bufType = _bufType;

    filter2D = _filter2D;
    rowFilter = _rowFilter;
    columnFilter = _columnFilter;

    if( _columnBorderType < 0 )  //列边界为空,则和行一样
        _columnBorderType = _rowBorderType;

    rowBorderType = _rowBorderType;
    columnBorderType = _columnBorderType;

    CV_Assert( columnBorderType != BORDER_WRAP );//不等于反向填充

    if( isSeparable() )//是可分离滤波器器,如行,列滤波器
    {
        CV_Assert( rowFilter && columnFilter );//行列滤波器不能为空
        ksize = Size(rowFilter->ksize, columnFilter->ksize); //大小,等于行列滤波器的大小
        anchor = Point(rowFilter->anchor, columnFilter->anchor);//锚点等于行列滤波器的锚点
    }
    else  //不可分离滤波器
    {
        CV_Assert( bufType == srcType );//原数据类型和buf数据类型一致
        ksize = filter2D->ksize;
        anchor = filter2D->anchor;
    }

    CV_Assert( 0 <= anchor.x && anchor.x < ksize.width &&
               0 <= anchor.y && anchor.y < ksize.height );
    //获取每个像素中需要填充的元素个数
    borderElemSize = srcElemSize/(CV_MAT_DEPTH(srcType) >= CV_32S ? sizeof(int) : 1);
    int borderLength = std::max(ksize.width - 1, 1);//边界长度,等于核宽度-1
    borderTab.resize(borderLength*borderElemSize);//边界表大小等边界长度*每个像素中元素的个数

    maxWidth = bufStep = 0;//最大宽度和缓冲区步长设置为0
    constBorderRow.clear();

    if( rowBorderType == BORDER_CONSTANT || columnBorderType == BORDER_CONSTANT )
    {   //行边界或者列边界为常量时
        constBorderValue.resize(srcElemSize*borderLength);//更改大小为边界长度*原图像数据大小
        int srcType1 = CV_MAKETYPE(CV_MAT_DEPTH(srcType), MIN(CV_MAT_CN(srcType), 4));//
        //初始化constBorderValue矩阵
        scalarToRawData(_borderValue, &constBorderValue[0], srcType1,
                        borderLength*CV_MAT_CN(srcType));
    }
    //设置全局大小
    wholeSize = Size(-1,-1);
}
//我会注释掉一些不相关的东西 
//
void FilterEngine::apply(const Mat& src, Mat& dst, const Size & wsz, const Point & ofs)
{
    CV_INSTRUMENT_REGION()
   //断言,一个就是元目标的数据类型和目标类型要与filterengine保存的一致
    CV_Assert( src.type() == srcType && dst.type() == dstType );
    //调用成员函数start,返回的原图像中起始行位置。
    int y = start(src, wsz, ofs);
    // 执行滤波,
    proceed(src.ptr() + y*src.step,//原图像中起始行的首地址
            (int)src.step,         //原图像的步长
            endY - startY,         //参与计算的行数
            dst.ptr(),             //目标矩阵的起始行的首地址
            (int)dst.step );       //目标矩阵的步长
}

}
int FilterEngine::start(const Mat& src, const Size &wsz, const Point &ofs)
{
    //输入参数,wsz应该是全局大小,图像的大小,全局偏移;
    start( wsz, src.size(), ofs);//可以计算得到起始行
    return startY - ofs.y;//得到起始行在全局中的位置
}
int FilterEngine::start(const Size &_wholeSize, const Size &sz, const Point &ofs)
{
    int i, j;

    wholeSize = _wholeSize;//设置全局大小
    roi = Rect(ofs, sz);//获取roi区域   这里sz等于原图像大小
    //断言,判断roi区域是否在全局大小范围内
    CV_Assert( roi.x >= 0 && roi.y >= 0 && roi.width >= 0 && roi.height >= 0 &&
        roi.x + roi.width <= wholeSize.width &&
        roi.y + roi.height <= wholeSize.height );
    
    int esz = (int)getElemSize(srcType);//获取原图像元素大小
    int bufElemSize = (int)getElemSize(bufType);//获取中间数据类型大小
    //获取constBorderValue一维矩阵首地址,矩阵为空,则为nullptr
    const uchar* constVal = !constBorderValue.empty() ? &constBorderValue[0] : 0;
    //缓冲矩阵最大行数
    int _maxBufRows = std::max(ksize.height + 3,
                               std::max(anchor.y,
                                        ksize.height-anchor.y-1)*2+1);
    //最大宽度小于roi区域的宽度,或者矩阵最大缓冲行数不等于rows的大小
    if( maxWidth < roi.width || _maxBufRows != (int)rows.size() )
    {  
        //最大宽度小于roi区域的宽度,或者缓冲区的行数不等于rows的大小
        //一般发生在,给更大图像滤波时,或者给引擎初始化的时候
        rows.resize(_maxBufRows);//将rows大小更新为_maxBufRows
        maxWidth = std::max(maxWidth, roi.width);//更新或修改最大宽度为max(maxWidth,roi.width)
        int cn = CV_MAT_CN(srcType);//得到原图像的通道数
        //更新或者修改srcRow大小,为元素大小esz *(滤波区域的宽度+核的宽度-1)
        srcRow.resize(esz*(maxWidth + ksize.width - 1));
  
        if( columnBorderType == BORDER_CONSTANT )
        {
            //列边界为常量

            CV_Assert(constVal != NULL);//断言 constBorderValue矩阵不为空
            //修改矩阵大小
            constBorderRow.resize(getElemSize(bufType)*(maxWidth + ksize.width - 1 + VEC_ALIGN));
            //定义变量,原因还未清楚,64字节对齐,指向constBorderRow矩阵
            uchar *dst = alignPtr(&constBorderRow[0], VEC_ALIGN), *tdst;
            //常量边界值数组大小
            int n = (int)constBorderValue.size(), N;
            N = (maxWidth + ksize.width - 1)*esz;//原始行数据大小
            tdst = isSeparable() ? &srcRow[0] : dst;//如果是可分离滤波器使用srcRow,不可分离使用constBorderRow

            for( i = 0; i < N; i += n )
            {
                n = std::min( n, N - i );
                for(j = 0; j < n; j++)
                    tdst[i+j] = constVal[j];
            }

            if( isSeparable() )
                (*rowFilter)(&srcRow[0], dst, maxWidth, cn);//行滤波
        }

        int maxBufStep = bufElemSize*(int)alignSize(maxWidth +
            (!isSeparable() ? ksize.width - 1 : 0),VEC_ALIGN);//得到最大缓冲区步长
        ringBuf.resize(maxBufStep*rows.size()+VEC_ALIGN);//更新环形缓冲区大小
    }

    // adjust bufstep so that the used part of the ring buffer stays compact in memory
    //调整bufstep,使得环形缓冲区的已使用部分在内存中保存紧凑
    bufStep = bufElemSize*(int)alignSize(roi.width + (!isSeparable() ? ksize.width - 1 : 0),16);
    //分别表示原图像外推后,从图像行来看,dx1主要描述左边,dx2描述右边,
	//作用,大概就是,模板的中心在roi区域内行走,所有的模板上的单元,
    //都应该在wholeSize内,大致就是看看外推像素是否足够,不足,重新计算边界表
    dx1 = std::max(anchor.x - roi.x, 0);
    dx2 = std::max(ksize.width - anchor.x - 1 + roi.x + roi.width - wholeSize.width, 0);

    // recompute border tables
   // 重新计算边界表
    if( dx1 > 0 || dx2 > 0 )
    {
        if( rowBorderType == BORDER_CONSTANT )
        {
            //边界类型为常量
            CV_Assert(constVal != NULL);//保证边界值矩阵不为空
            int nr = isSeparable() ? 1 : (int)rows.size();//可分离,则函数为1,不可分离则函数为实际行数
            for( i = 0; i < nr; i++ )
            {
                uchar* dst = isSeparable() ? &srcRow[0] : alignPtr(&ringBuf[0],VEC_ALIGN) + bufStep*i;//定位到开头
                memcpy( dst, constVal, dx1*esz );//在开始处补齐
                memcpy( dst + (roi.width + ksize.width - 1 - dx2)*esz, constVal, dx2*esz );//在末尾处补齐
            }
        }
        else
        {
            //边界类型不为常数
            int xofs1 = std::min(roi.x, anchor.x) - roi.x;
            //
            int btab_esz = borderElemSize, wholeWidth = wholeSize.width;
            int* btab = (int*)&borderTab[0];//边界表地址
            //边界表保存的索引位置
            for( i = 0; i < dx1; i++ )
            {
                int p0 = (borderInterpolate(i-dx1, wholeWidth, rowBorderType) + xofs1)*btab_esz;
                for( j = 0; j < btab_esz; j++ )
                    btab[i*btab_esz + j] = p0 + j;
            }

            for( i = 0; i < dx2; i++ )
            {
                int p0 = (borderInterpolate(wholeWidth + i, wholeWidth, rowBorderType) + xofs1)*btab_esz;
                for( j = 0; j < btab_esz; j++ )
                    btab[(i + dx1)*btab_esz + j] = p0 + j;
            }
        }
    }

    rowCount = dstY = 0;
    startY = startY0 = std::max(roi.y - anchor.y, 0);//计算起始行
    endY = std::min(roi.y + roi.height + ksize.height - anchor.y - 1, wholeSize.height);//计算结束行
    if( columnFilter )
        columnFilter->reset();
    if( filter2D )
        filter2D->reset();

    return startY;//返回起始行
}

int FilterEngine::proceed( const uchar* src, int srcstep, int count,
                           uchar* dst, int dststep )
{
    CV_Assert( wholeSize.width > 0 && wholeSize.height > 0 );//断言

    const int *btab = &borderTab[0];//取borderTab表首地址
    int esz = (int)getElemSize(srcType), btab_esz = borderElemSize;
    uchar** brows = &rows[0];
    int bufRows = (int)rows.size();
    int cn = CV_MAT_CN(bufType);
    int width = roi.width, kwidth = ksize.width;
    int kheight = ksize.height, ay = anchor.y;
    int _dx1 = dx1, _dx2 = dx2;
    int width1 = roi.width + kwidth - 1;
    int xofs1 = std::min(roi.x, anchor.x);
    bool isSep = isSeparable();
    bool makeBorder = (_dx1 > 0 || _dx2 > 0) && rowBorderType != BORDER_CONSTANT;
    int dy = 0, i = 0;

    src -= xofs1*esz;
    count = std::min(count, remainingInputRows());

    CV_Assert( src && dst && count > 0 );

    for(;; dst += dststep*i, dy += i)
    {
        int dcount = bufRows - ay - startY - rowCount + roi.y;
        dcount = dcount > 0 ? dcount : bufRows - kheight + 1;
        dcount = std::min(dcount, count);
        count -= dcount;
        for( ; dcount-- > 0; src += srcstep )
        {
            int bi = (startY - startY0 + rowCount) % bufRows;
            uchar* brow = alignPtr(&ringBuf[0], VEC_ALIGN) + bi*bufStep;
            uchar* row = isSep ? &srcRow[0] : brow;

            if( ++rowCount > bufRows )
            {
                --rowCount;
                ++startY;
            }

            memcpy( row + _dx1*esz, src, (width1 - _dx2 - _dx1)*esz );

            if( makeBorder )
            {
                if( btab_esz*(int)sizeof(int) == esz )
                {
                    const int* isrc = (const int*)src;
                    int* irow = (int*)row;

                    for( i = 0; i < _dx1*btab_esz; i++ )
                        irow[i] = isrc[btab[i]];
                    for( i = 0; i < _dx2*btab_esz; i++ )
                        irow[i + (width1 - _dx2)*btab_esz] = isrc[btab[i+_dx1*btab_esz]];
                }
                else
                {
                    for( i = 0; i < _dx1*esz; i++ )
                        row[i] = src[btab[i]];
                    for( i = 0; i < _dx2*esz; i++ )
                        row[i + (width1 - _dx2)*esz] = src[btab[i+_dx1*esz]];
                }
            }

            if( isSep )
                (*rowFilter)(row, brow, width, CV_MAT_CN(srcType));
        }

        int max_i = std::min(bufRows, roi.height - (dstY + dy) + (kheight - 1));
        for( i = 0; i < max_i; i++ )
        {
            int srcY = borderInterpolate(dstY + dy + i + roi.y - ay,
                            wholeSize.height, columnBorderType);
            if( srcY < 0 ) // can happen only with constant border type
                brows[i] = alignPtr(&constBorderRow[0], VEC_ALIGN);
            else
            {
                CV_Assert( srcY >= startY );
                if( srcY >= startY + rowCount )
                    break;
                int bi = (srcY - startY0) % bufRows;
                brows[i] = alignPtr(&ringBuf[0], VEC_ALIGN) + bi*bufStep;
            }
        }
        if( i < kheight )
            break;
        i -= kheight - 1;
        if( isSeparable() )
            (*columnFilter)((const uchar**)brows, dst, dststep, i, roi.width*cn);
        else
            (*filter2D)((const uchar**)brows, dst, dststep, i, roi.width, cn);
    }

    dstY += dy;//得到目标图像的索引位置
    CV_Assert( dstY <= roi.height );
    return dy;//返回在peocess中,核中心走过的行数
}

 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值