研究这个前前后后也有快两三个月了,因为之前也一直在弄模板匹配方面的东西,所以偶尔还是有不少朋友咨询或者问你有没有研究过linemod这个算法啊,那个效率啥的还不错啊,有段时间一直不以为然,觉得我现在用的那个匹配因该很不错的,没必要深究了。后来呢,还是忍不住手痒,把论文打出来看了看,又找了点资料研究了下,结果没想到一弄又是两个月过去了,中间也折腾了很久,浪费了不少时间。总算还是有点收获,稍微整理下做个交流。
至于linemod的具体数学原理,我也不需要详谈,毕竟论文和opencv的代码就摆在那里, github上也有一些别人改进的版本。
我就觉得啊,linemod这个基于计算边缘的模板匹配啊,他使用的是选中的特征点的梯度的角度方向作为特征,而不是梯度的值,而后计算模板和测试不同位置角度的余弦的绝对值,这个都是常规的操作。 作者把这个角度线性量化为一些特定的值,这个本质上呢降低了算法的精度,但是由于特征点较多,基本不会影响识别结果。 关键是这个量化啊,能够带来很多很多的好处,有些真的是意想不到。
论文里呢把360的角度量化为8个值,即以45度为间隔,分别用整数0、1、2、3、4、5、6、7表示,这样呢不同的两个角度之间的差异绝对值呢,只有0、1、2、3、4这5种可能,分别对应5个得分,比如模板的某个特征点的角度为210度,则量化值为4,目标中某个位置的角度值为52度,则量化值为1,这样角度之间差异值为3,则对应的得分为1。
接着论文里说为了减少微小的变形引起的识别误差,建议将量化后的值进行扩散,这个扩散也是设计的非常有技巧,很有意思,充分利用了或运算的优异特性。
后续说为了减少计算量呢,可以提前计算出8个响应图,这样匹配计算时就可以直接查表,而无需实时计算。
再后续还有一个线性化内存,算了,我已经没看那个了,到前面这一步就已经打止了,因为我已经开始编程了。
第一步呢,我就是在考虑算法的优化问题,我看了下opencv的代码,写的很好,又很不好,让你读的很难受,但是写的确实稳健,考虑到了很多不同的硬件配置,这也许就是大工程的特性吧。
角度量化的问题和代码方面我不想提,也有很多可优化的地方,大家可自行考虑。
在谈到提速之前,我说一个重点,那就是所谓的梯度扩散、计算响应图都是在查找模板时进行的, 对不同的图都要有重新计算,而不是离线玩。所以这里的每个耗时,都和检测速度有关。
第一:那个梯度扩散,CV的代码有下面这一大堆:
/****************************************************************************************\
* Response maps *
\****************************************************************************************
static void orUnaligned8u(const uchar * src, const int src_stride,
uchar * dst, const int dst_stride,
const int width, const int height)
{
#if CV_SSE2
volatile bool haveSSE2 = checkHardwareSupport(CPU_SSE2);
#if CV_SSE3
volatile bool haveSSE3 = checkHardwareSupport(CPU_SSE3);
#endif
bool src_aligned = reinterpret_cast<unsigned long long>(src) % 16 == 0;
#endif
for (int r = 0; r < height; ++r)
{
int c = 0;
#if CV_SSE2
// Use aligned loads if possible
if (haveSSE2 && src_aligned)
{
for ( ; c < width - 15; c += 16)
{
const __m128i* src_ptr = reinterpret_cast<const __m128i*>(src + c);
__m128i* dst_ptr = reinterpret_cast<__m128i*>(dst + c);
*dst_ptr = _mm_or_si128(*dst_ptr, *src_ptr);
}
}
#if CV_SSE3
// Use LDDQU for fast unaligned load
else if (haveSSE3)
{
for ( ; c < width - 15; c += 16)
{
__m128i val = _mm_lddqu_si128(reinterpret_cast<const __m128i*>(src + c));
__m128i* dst_ptr = reinterpret_cast<__m128i*>(dst + c);
*dst_ptr = _mm_or_si128(*dst_ptr, val);
}
}
#endif
// Fall back to MOVDQU
else if (haveSSE2)
{
for ( ; c < width - 15; c += 16)
{
__m128i val = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + c));
__m128i* dst_ptr = reinterpret_cast<__m128i*>(dst + c);
*dst_ptr = _mm_or_si128(*dst_ptr, val);
}
}
#endif
for ( ; c < width; ++c)
dst[c] |= src[c];
// Advance to next row
src += src_stride;
dst += dst_stride;
}
}
/**
* \brief Spread binary labels in a quantized image.
*
* Implements section 2.3 "Spreading the Orientations."
*
* \param[in] src The source 8-bit quantized image.
* \param[out] dst Destination 8-bit spread image.
* \param T Sampling step. Spread labels T/2 pixels in each direction.
*/
static void spread(const Mat& src, Mat& dst, int T)
{
// Allocate and zero-initialize spread (OR'ed) image
dst = Mat::zeros(src.size(), CV_8U);

本文详细探讨了Linemod算法的模板匹配原理,指出基于边缘的匹配方法和角度量化策略。作者分析了梯度扩散和响应图计算的优化,通过调整扩散顺序和SIMD指令优化,显著提高了计算速度。尽管扩散过程被质疑对准确性的影响,但算法的基础优化仍有很大潜力。

1万+

被折叠的 条评论
为什么被折叠?



