小优化:
for(int shiftY = 0; shiftY < rangeY; shiftY++) {
for(int shiftX = 0; shiftX < rangeX; shiftX++) {
// if you can assume SAD is already filled with 0-es,
// you don't need the next line
SAD[shiftX][shiftY]=0;
for(int tx = 0, imx=shiftX; x < lenTemplateX; tx++,imx++) {
for(int ty = 0, imy=shiftY; y < lenTemplateY; ty++,imy++) {
// two increments of imx/imy may be cheaper than
// two addition with offsets
SAD[shiftY][shiftX]+=abs(template_grid[tx][ty] - image[imx][imy]);
}
}
}
}
使用 C++ 模板展开循环
对于您的配置来说可能是一个疯狂的想法(C++ 编译器让我担心),但它可能工作。我不提供任何保证,但请尝试一下。
这个想法可能会奏效,因为您的 template_grid 大小和范围是恒定的 - 因此在编译时已知。
此外,要使其工作,您的 image 和 template_grid 必须使用相同的布局进行组织(列第一或行第一) - 问题中描述的“示例代码”的方式将SAD x/y 与template_grid y/x 混合在一起。
在下文中,我将假设一个“列优先”的组织,因此SAD[ix] 表示您的SAD** 矩阵的ixth 列。代码与“行优先”相同,只是变量的名称与值数组的含义不匹配。
那么,让我们开始吧:
template <
typename sad_type, typename val_type,
size_t template_len
> struct sad1D_simple {
void operator()(
const val_type* img, const val_type* templ,
sad_type& result
) {
// template specialization recursion, with one less element to add
sad1D_simple<sad_type, val_type, template_len-1> one_shorter;
// call it incrementing the img and template offsets
one_shorter(img+1, templ+1, result);
// the add the contribution of the first diff we skipped over above
result+=abs(*(img+template_len-1)-*(templ+template_len-1));
}
};
// at len of 0, the result is zero. We need it to stop the
template <
typename sad_type, typename val_type
>
struct sad1D_simple<sad_type, val_type, 0> {
void operator()(
const val_type* img, const val_type* templ,
sad_type& result
) {
result=0;
}
};
为什么是函子结构 - 带有运算符的结构? C++ 不允许函数模板的部分特化。
sad1D_simple 的作用:展开一个for 循环,该循环计算输入中两个数组的SAD,没有任何偏移,基于template_grid 数组的长度是编译时已知的常量这一事实。这与“使用 C++ 模板计算编译时间的阶乘”相同
这有什么帮助?
下面代码中的使用示例:
typedef ulong SAD_t;
typedef int16_t pixel_val_t;
const size_t lenTemplateX = 7; // number of cols in the template_grid
const size_t lenTemplateY = 61;
const size_t rangeX=10, rangeY=45;
pixel_val_t **image, **template_grid;
SAD_t** SAD;
// assume those are initialized somehow
for(size_t tgrid_col=0; tgrid_col<lenTemplateX; tgrid_col++) {
pixel_val_t* template_col=template_grid[tgrid_col];
// the X axis - horizontal - is the column axis, right?
for(size_t shiftX=0; shiftX < rangeX; shiftX++) {
pixel_val_t* img_col=image[shiftX];
for(size_t shiftY = 0; shiftY < rangeY; shiftY++) {
// the Y axis - vertical - is the "offset in a column"=row, isn't it?
pixel_val_t* img_col_offsetted=img_col+shiftY;
// this functor is made by recursive specialization
// there's no cycle inside it, it was unrolled into
// lenTemplateY individual subtractions, abs-es and additions
sad1D_simple<SAD_t, pixel_val_t, lenTemplateY> calc;
calc(img_col_offsetted, template_col, SAD[shiftX][shiftY]);
}
}
}
嗯……我们能做得更好吗?不,它不会是 X 轴展开,我们仍然希望停留在 1D 区域,但是......好吧,也许如果我们创建一个范围 sad1D 并在同一轴上展开另一个循环?
它会起作用如果frangeX 也是恒定的。
template <
typename sad_type, typename val_type,
size_t range, size_t template_len
> struct sad1D_ranged {
void operator()(
const val_type* img, const val_type* templ,
// result is assumed to have at least `range` slots
sad_type* result
) {
// we'll compute here the first slot of the result
sad1D_simple<sad_type, val_type, template_len>
calculator_for_first_sad;
calculator_for_first_sad(img, templ, *(result));
// now, ask for a recursive specialization for
// the next (range-1) sad-s
sad1D_ranged<sad_type, val_type, range-1, template_len>
one_less_in_range;
// when calling, pass the shifted img and result
one_less_in_range(img+1, templ, result+1);
}
};
// for a range of 0, there's nothing to do, but we need it
// to stop the template specialization recursion
template <
typename sad_type, typename val_type,
size_t template_len
> struct sad1D_ranged<sad_type, val_type, 0, template_len> {
void operator()(
const val_type* img, const val_type* templ,
// result is assumed to have at least `range` slots
sad_type* result
) {
}
};
这是你如何使用它的:
for(size_t tgrid_col=0; tgrid_col<lenTemplateX; tgrid_col++) {
pixel_val_t* template_col=template_grid[tgrid_col];
for(size_t shiftX=0; shiftX < rangeX; shiftX++) {
pixel_val_t* img_col=image[shiftX];
SAD_t* sad_col=SAD[shiftX];
sad1D_ranged<SAD_t, pixel_val_t, rangeY, lenTemplateY> calc;
calc(img_col, template_col, sad_col);
}
}
是的...但问题是:这会提高性能吗?
见鬼,如果我知道。对于循环内的少量循环和强数据局部性(值彼此接近,以便它们位于 CPU 缓存中),循环展开应该会提高性能。对于更多的循环,您可能会对 CPU 分支预测和其他 mumbo-jumbo-I-know-may-impact-performance-but-I-don't-know-how 产生负面影响。
胆量:即使相同的展开技术可能适用于其他两个循环,使用它很可能会导致性能下降:我们需要从一个连续向量(image 列)跳转到另一个 - 整个图像可能不适合 CPU 缓存。
注意:如果您的template_grid 数据也是常量(或者您有一组有限的常量模板网格),则可以更进一步,创建带有专用掩码的结构函子。但是我今天没有动力了。