当从图像金字塔中的每一层图像上提取特征点之后,都要先用四叉树技术对这些特征点进行管理
//该类中定义了四叉树创建的函数以及树中结点的属性
//bool bNoMore: 根据该结点中被分配的特征点的数目来决定是否继续对其进行分割
//DivisionNode():实现如何对一个结点进行分割
//vKeys:用来存储被分配到该结点区域内的所有特征点
//UL, UR, BL, BR:四个点定义了一个结点的区域
//lit:list的迭代器,遍历所有生成的节点
class ExtractorNode
{
public:
///用初始化列表来初始化本类内的成员变量
ExtractorNode():bNoMore(false){}
void DivideNode(ExtractorNode &n1, ExtractorNode &n2, ExtractorNode &n3, ExtractorNode &n4);
std::vector<cv::KeyPoint> vKeys;
cv::Point2i UL, UR, BL, BR;
std::list<ExtractorNode>::iterator lit;
bool bNoMore;
};
下面的函数实现利用四叉树来管理从金字塔中的每一层图像上提取的特征点
//vToDistributeKeys变量中存储的是从金字塔中某一层图像上提取的特征点
//minX, maxX, minY, maxY:是该层图像去除了边界的区域
//N: mnFeaturesPerLevel[i]表示该层图像上应该提取的特征点的个数
//level: 该图像处在金字塔上的层数
vector<cv::KeyPoint> ORBextractor::DistributeOctTree(const vector<cv::KeyPoint>& vToDistributeKeys, const int &minX,
const int &maxX, const int &minY, const int &maxY, const int &N, const int &level)
{
//常用的相机kinect v1的分辨率是:640*480 kinect v2的分辨率是:1920*1080
//为了尽量使得每一个结点的区域形状接近正方形所以图像的长宽比决定了四叉树根节点的数目
//如果使用kinect v1那么只有一个根结点,如果使用kinect v2那么就会有两个根结点
const int nIni = round(static_cast<float>(maxX-minX)/(maxY-minY));
//hX变量可以理解为一个根节点所占的宽度
const float hX = static_cast<float>(maxX-minX)/nIni;
//lNodes中存储生成的树结点
list<ExtractorNode> lNodes;
//vpIniNodes变量中存储的是结点的地址
vector<ExtractorNode*> vpIniNodes;
//vpIniNodes的大小先设置成根结点的个数
vpIniNodes.resize(nIni);
for(int i=0; i<nIni; i++)
{
ExtractorNode ni;
//四叉树是每次根据特定条件将一个结点分成四等分,四个区域左上(UL),右上(UR),
//左下(BL),右下(BR)
//左上角位置坐标
ni.UL = cv::Point2i(hX*static_cast<float>(i),0);
//右上角位置坐标
ni.UR = cv::Point2i(hX*static_cast<float>(i+1),0);
///左下角的位置坐标
ni.BL = cv::Point2i(ni.UL.x,maxY-minY);
///右下角的位置坐标
ni.BR = cv::Point2i(ni.UR.x,maxY-minY);
//vKeys的大小为在上面的这个根节点范围内总共提取的特征点的个数
ni.vKeys.reserve(vToDistributeKeys.size());
//将创建的根节点插入到list lNodes中
lNodes.push_back(ni);
////将lNodes变量中的最后存储的那个结点的地址存储到vector变量vpIniNodes中
//暂时还不知道这个变量做何用
//看都了吧vpIniNodes总是把最后插入到lNodes中的结点的地址拿走,然后要为
//该结点的vKeys成员变量内部添加属于该结点的特征点。
vpIniNodes[i] = &lNodes.back();
}
//Associate points to childs
//要一直记得vToDistributeKeys变量中存储的是该层图像中提取的特征点
//遍历在该层图像上提取的所有特征点
for(size_t i=0;i<vToDistributeKeys.size();i++)
{
const cv::KeyPoint &kp = vToDistributeKeys[i];
//将所有提取的特征点根据坐标位置将其分配到对应的根节点中去
//如果使用kinect b=v1那么所有的kp.pt.x都小于hX,所以所有的特征点都被分配到
//vpIniNodes的第0个元素中存储的结点指针所指向的空间中去了