光流Zoom In and Out开发记录

简介

    2018年7月,合作伙伴提出了一个想法——使用光流算法估计相机前后移动。经典的光流算法(Lukas-Kanade算法)适用于相机左右和上下移动,但是在相机前后移动时,虽然各个像素都有移动,但是移动速度是矢量,综合起来,整个相机的移动却接近0。这个是经典的LK光流算法的问题,其根源是速度位矢量,而相机前后移动就像是图片的放大(Zoom in)和缩小(Zoom out),使所有像素的移动速度矢量呈放射状均匀分布在相机视野中。

    我想到了一个可能的解决方法——改变坐标系,使像素在新坐标系下的移动速度朝向一致,而极坐标系就是适合使用的一种。因此,本文集中考虑极坐标系下的LK光流算法应用,专门用来计算相机的Zoom in and out情况。

光流算法

    光流算法是用来检测相机移动的,与SLAM的3D空间定位不同,光流算法是在2D平面下定位。其理论是相机发生平移时,画面会发生移动,并且是整体移动。

极坐标变换

1 介绍

    针对Zoom in and out问题下的光流算法,首先要做的是极坐标变换。具体而言,照片的像素坐标是使用直角坐标系表示的,但在Zoom in and out问题下,向光流算法输入的像素需要在极坐标系下,而不能是直角坐标系。

2 编程

    极坐标系的变换原理很简单,而相对麻烦的是编程。由于我们的应用背景是单片机,其对编程语言的要求是C语言,因此在开发最开始的阶段,我虽然是在Mac OS X上使用g++编译器,语言也仍然使用C/C++,但避免使用类、Vector等C++的功能。

问题1 - 像素保存

    这样,最先遇到的问题就是以什么形式保存照片,无论是直角坐标系下的,还是极坐标系下的。这里我选择使用数组,而且是一维数组,这是最简单的形式,因而通用型和稳定性最强。同时,由于我在2017年学习矩阵运算时,也遇到了单片机下的矩阵构建和保存问题,当时使用的就是这套思路,具备开发经验,问题不大。

    照片是二维的,而且在极坐标系下也是这样,使用一维数组表示二维的像素的思路是以照片每行的像素数量为进制,例如一个像素位于极坐标系下(2,8)处(像素编号从0开始),且每一行有10个像素,那么这个像素在一维数组中的位置是(8*10+2)。通用的表示方法是像素坐标(i,j),每一行的像素数量为binx,则其在一维数组中的位置是(j*binx+i)。

问题2 - 区间划分

    极坐标系包含两个分量——角度(theta)和半径(r)。一张极坐标系下的照片,实际上就是一张二维直方图(2D Hisgram),这个直方图的X轴是角度,Y轴是半径;而直方图的Z轴是计数,在这里表示像素的灰度值。实际上,Z轴也可以表示RGB值等等,具体使用哪个需要参考实际使用背景,而这里选择使用灰度值,是因为光流算法以灰度值为输入。

    直方图的特点是每条轴上都被均匀划分为若干区间,区间的数量称为”bin”,区间的最小值称为”min”,区间的最大值称为”max”。在这里,X轴代表的角度范围是[-180, 180](degree),其中最小值和最大值都是可以达到的,这里就出现了编程时会出现的一个Bug——在180度或者-180度时,像素坐标会超过直方图X轴上限。

    解决方法是增加区间数量并且扩大X轴的最小和最大值,目标是使180度被分配到最后一个区间(或者是-180被分配到第一个区间),在这里区间数使用40,区间范围使用了(-200, 200),那么180度就被分配到了X轴的38号区间,实际是第39个区间(因为区间编号是从0开始的)。

问题3 - 区间确定

    极坐标系下,像素的坐标,也就是角度和半径分别通过atan2()和sqrt()计算得到,但是这两个数并不是二维直方图的X轴和Y轴的区间编号。

    区间编号仍然需要通过计算得到,理论上的计算方法是(theta-min_theta)/binSize_theta。其中,theta是当前处理的像素的角度;min_theta是二维直方图上X轴(默认X轴用来表示角度)的最小值;binSize_theta是X轴上一个区间的宽度,计算方法是(max_theta-min_theta)/bin_theta。

    实际上,由于数组的编号是整数,数组的编号输入强制为int类型,因此,所计算出的区间编号需要转换为整数。这里就涉及到小数位的取舍问题,在当前的工作中,我统一使用“进一法”,即只要小数位不为零,整数位就加一,对应到函数是ceil()。

问题4 - 照片中心点确定

    在相机的Zoom in and out情况中,所有像素都是相对照片中心进行移动的,也就是所有像素的移动速度都是朝向(Zoom in)或者背向(Zoom out)照片中心的。因此,在进行极坐标系变换的时候,所有像素的角度(theta)和半径(r)都需要相对于照片中心进行计算。

    在编程实现上,所有像素都是离散的,根据每行像素的数量和每列像素的数量,也就是照片的分辨率,照片的中心可能出现在一个像素的边角上,也可能出现在像素的中心。这与行列上的像素数量的奇偶有关——以行为例,像素数量为奇数时,中心出现在像素中心;数量为偶数时,中心出现在像素的角上。

    由于最终需要得到像素在极坐标系的直方图下对应的区间编号(整数),方便起见,这里将照片的中心点强行归结到像素的角上。具体操作是通过照片的行、列像素数量计算各自的中点,再使用“进一法”(ceil())得到整数值。

问题5 - 极坐标系下像素互相远离

猜你喜欢

转载自blog.csdn.net/qq_32454557/article/details/81427682