//二维不可分滤波器核心算法
template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFilter
{
typedef typename CastOp::type1 KT; //源类型
typedef typename CastOp::rtype DT; //目标类型
//完全构造函数
Filter2D( const Mat& _kernel, Point _anchor,
double _delta, const CastOp& _castOp=CastOp(),
const VecOp& _vecOp=VecOp() )
{
anchor = _anchor;//锚点
ksize = _kernel.size();//核大小
delta = saturate_cast<KT>(_delta);//偏移常量
castOp0 = _castOp; //类型转换类
vecOp = _vecOp; //不同实现类
CV_Assert( _kernel.type() == DataType<KT>::type );
preprocess2DKernel( _kernel, coords, coeffs );//调用核预处理函数系数
ptrs.resize( coords.size() );//更新指针向量大小
}
//进行滤波操作
void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn) CV_OVERRIDE
{
KT _delta = delta;//偏移
const Point* pt = &coords[0];//取非零核元素坐标
const KT* kf = (const KT*)&coeffs[0];//取核系数
const ST** kp = (const ST**)&ptrs[0];//取指针向量的地址,这个指针向量用来保存,每一计算行,核非零元素对应的源图像元素的位置
int i, k, nz = (int)coords.size();
CastOp castOp = castOp0;//类型转换对象
width *= cn;//宽度 roi.width*cn
for( ; count > 0; count--, dst += dststep, src++ ) //count表示要计算目标多少行
{
DT* D = (DT*)dst; //取目标一行的首地址
for( k = 0; k < nz; k++ ) //更新指针向量,存储要计算目标对应行对应的原图像上非零核元素对应的原图像元素的位置,并以这些元素的位置作为起始数据的位置
kp[k] = (const ST*)src[pt[k].y] + pt[k].x*cn;
i = vecOp((const uchar**)kp, dst, width);//其他 具体类型的按行滤波计算实现,如果存在具体类型的行滤波方法,则i的值会不小于width,后面的for循环通用行滤波器将不会进行操作
#if CV_ENABLE_UNROLLED
for( ; i <= width - 4; i += 4 )
{
KT s0 = _delta, s1 = _delta, s2 = _delta, s3 = _delta;
for( k = 0; k < nz; k++ )
{
const ST* sptr = kp[k] + i;
KT f = kf[k];
s0 += f*sptr[0];
s1 += f*sptr[1];
s2 += f*sptr[2];
s3 += f*sptr[3];
}
D[i] = castOp(s0); D[i+1] = castOp(s1);
D[i+2] = castOp(s2); D[i+3] = castOp(s3);
}
#endif
for( ; i < width; i++ )
{
KT s0 = _delta;
for( k = 0; k < nz; k++ )
s0 += kf[k]*kp[k][i];
D[i] = castOp(s0);
}
}
}
std::vector<Point> coords; //非零元素下标
std::vector<uchar> coeffs; //核系数
std::vector<uchar*> ptrs; //指针向量,临时变量,进行行滤波计算时,存储每行核非零对应的原图像的元素的位置
KT delta; //偏移
CastOp castOp0;//类型转换类对象
VecOp vecOp;//其他按行滤波 这个一般是用作优化的 例如 FilterVec_32f等
};