相关文章:
https://blog.csdn.net/zhaohui1995_yang/article/details/51346695
https://blog.csdn.net/qq_25394511/article/details/79437850
块匹配:
https://blog.csdn.net/u012507022/article/details/51446891
https://blog.csdn.net/liulina603/article/details/53302168
源码流程分析:
1、首先读取64*64大小的图像。这个模块所用的摄像头是MT9V034,原始像素752*480,行列都采用4xbinning模式后大小为188*120.然后采用窗口从中截取图像。如下,左图是没有使用窗口截取的图像,也就是read only模式下输出的图像。右图为正常计算光流的模式,可以看到图像的范围缩小了而且取得中间的一部分图像。
2、计算特征点的像素梯度。
一共选择了25个特征点,也就是上图右边的5*5的白点所示。特征点是固定的,起始行列是5,然后以11为步长一次处理。每个特征点的大小是8*8的像素块。
uint16_t pixLo = SEARCH_SIZE + 1;//5
uint16_t pixHi = FRAME_SIZE - (SEARCH_SIZE + 1) - TILE_SIZE;//50
uint16_t pixStep = (pixHi - pixLo) / NUM_BLOCKS + 1;//11
compute_diff函数计算特征点的像素梯度。这个函数实际计算的是4*4像素块,这个4*4的像素块是由每个特征点8*8的像素块取其偶数行列得来。这里计算的像素梯度就是相邻行对应像素差的绝对值和加上相邻列对应像素差的绝对值和。为加快计算效率,这里使用了simd指令。
static inline uint32_t compute_diff(uint8_t *image, uint16_t offX, uint16_t offY, uint16_t row_size)
{
/* calculate position in image buffer */
uint16_t off = (offY + 2) * row_size + (offX + 2); // we calc only the 4x4 pattern
uint32_t acc;
/* calc row diff */
acc = __USAD8 (*((uint32_t*) &image[off + 0 + 0 * row_size]), *((uint32_t*) &image[off + 0 + 1 * row_size]));
acc = __USADA8(*((uint32_t*) &image[off + 0 + 1 * row_size]), *((uint32_t*) &image[off + 0 + 2 * row_size]), acc);
acc = __USADA8(*((uint32_t*) &image[off + 0 + 2 * row_size]), *((uint32_t*) &image[off + 0 + 3 * row_size]), acc);
/* we need to get columns */
uint32_t col1 = (image[off + 0 + 0 * row_size] << 24) | image[off + 0 + 1 * row_size] << 16 | image[off + 0 + 2 * row_size] << 8 | image[off + 0 + 3 * row_size];
uint32_t col2 = (image[off + 1 + 0 * row_size] << 24) | image[off + 1 + 1 * row_size] << 16 | image[off + 1 + 2 * row_size] << 8 | image[off + 1 + 3 * row_size];
uint32_t col3 = (image[off + 2 + 0 * row_size] << 24) | image[off + 2 + 1 * row_size] << 16 | image[off + 2 + 2 * row_size] << 8 | image[off + 2 + 3 * row_size];
uint32_t col4 = (image[off + 3 + 0 * row_size] << 24) | image[off + 3 + 1 * row_size] << 16 | image[off + 3 + 2 * row_size] << 8 | image[off + 3 + 3 * row_size];
/* calc column diff */
acc = __USADA8(col1, col2, acc);
acc = __USADA8(col2, col3, acc);
acc = __USADA8(col3, col4, acc);
return acc;
}
3、判断特征点是否可用
PARAM_BOTTOM_FLOW_FEATURE_THRESHOLD默认值是40.特征点的像素梯度大于这个值则继续运算,否则舍弃这个点。
if (diff < global_data.param[PARAM_BOTTOM_FLOW_FEATURE_THRESHOLD])
{
continue;
}
4、计算有用特征点的SAD。
在image1找到有用特征点后,在image2中的相同的位置行列都向外扩展4个像素,如下图,找到SAD值最小的一个。并且记录这个8*8像素块的x,y轴偏移像素点的个数sumx,sumy.这里计算SAD值使用的函数是ABSDIFF,其实就是一个汇编语言块,代码中还有另一个函数也有同样的功能compute_sad_8x8,这个函数使用的是simd指令,被注释掉了。我尝试扩大特征点的个数,将IMAGE_WIDTH增大,但是没用。ABSDIFF处理图像的时候,行的大小是固定的(64),如果只改了图像宽度,那么计算的光流是错误的,所以只能手动将代码中的64改成自己期望的值。compute_sad_8x8则不需要这么做。
5、判断image1中的特征点在image2中是否有很好的匹配度。
其实这里就是判断上一步计算的最小的SAD值是否小于设定的阈值PARAM_BOTTOM_FLOW_VALUE_THRESHOLD,如果小于的话就可以大致认为两幅图像的点是地面上的同一个点,然后再进行下一步,亚像素级细分。
6、亚像素级细分。
之前所得的偏移sumx,sumy的精度是+-一个像素,通过这步将精度再提升半个像素。大致计算思路:
(1)由每个像素点(蓝色点)8领域中的像素(黄色点)取平均值得到如图中小点
(2)再由这些小点取平均值得到整个亚像素级的点,图中红点。
(3)image1中每个像素点与image2中对应点亚像素级中的每个点求差,并将差的绝对值相加得到acc[0~7].也就是acc[0],是由image1中所有像素点与image2中所有像素点亚像素级点0灰度值差的绝对值之和,可以看作将匹配的位置整体向0号位置移动半个像素,然后计算与image1的SAD值。
然后就找出这8个值中的最小值,作为亚像素级的偏移。
7、得到最终的像素偏移个数
如果不进行直方图滤波的话,就是把上面的像素偏移和亚像素偏移合并起来并累加然后取平均值。下面说采用直方图滤波。
根据上面的分析,相似块在x,y轴上最大各有正负4各像素的偏移量,转换到亚像素级就是正负8个像素的偏移量,在处理的时候没有采用以中心来计算偏移量,而是将图像左上角的点作为参考点。
然后记录每个偏移量出现的个数,再找个数最多的偏移量。如上图,如果在蓝色位置出现的次数最多,那么向其周围延伸2个亚级像素(在边缘位置时特殊处理)。在这个区域内,根据每个偏移量出现的次数,计算平均偏移量。再平均偏移量转换到以中心为参考,再除以2得到偏移像素个数。