-
结构图
暂时不画
-
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中,核中心走过的行数
}