视觉SLAM十四讲CH7课后习题3_1

3. * 我们发现OpenCV 提供的ORB 特征点,在图像当中分布不够均匀。你是否能够找到或提出让特征点分布更加均匀的方法?

对四叉树算法感兴趣的可以找资料去了解一下。我在作者的基础上加了一些注释,不过代码量太大了,有的也没看懂写的是什么,想着后续如果学ORB-SLAM2的时候再回来看看。

orb_cv3.cpp:

#include <iostream>
#include <list>
#include <opencv2/core/core.hpp>//opencv核心模块
#include <opencv2/highgui/highgui.hpp>//opencv gui
#include <opencv2/features2d/features2d.hpp>//opencv特征点头文件
#include <opencv2/imgproc/imgproc.hpp>
 
using namespace std;
using namespace cv;
 
int features_num = 1000; //最大特征点数量
float scale_factor = 1.2f; //金字塔之间尺度参数
int levels_num = 8; //金字塔层数
int default_fast_threshold = 20; //fast角点检测时候的阈值
int min_fast_threshold = 7; //最小fast检测阈值
int EDGE_THRESHOLD = 19; //过滤掉边缘效应的阈值
int PATCH_SIZE = 31;
int HALF_PATH_SIZE = 15;
 
//定义一个四叉树节点类型的类,类里面定义了一些成员及函数
class ExtractorNode{
public:
  ExtractorNode() : bNoMore(false) {}
  void DivideNode(ExtractorNode &n1, ExtractorNode &n2, ExtractorNode &n3, ExtractorNode &n4); //分配节点函数
  std::vector<cv::KeyPoint> vKeys; //节点keypoints容器
  cv::Point2i UL,UR,BL,BR; //二维整数点类型数据u的上下左右像素
  std::list<ExtractorNode>::iterator lit; //节点类型列表迭代器
  bool bNoMore; //确认是否之含有一个特征点
};
 
 
//定义DivideNode函数
//DivisionNode():实现如何对一个结点进行分割
void ExtractorNode::DivideNode(ExtractorNode& n1, ExtractorNode& n2, ExtractorNode& n3, ExtractorNode& n4)
{
  /*
   * 
   * -----------------------------------------------------------------------
   *	/                               /                               /
   *	/                               /				/
   *	/                               /				/
   *	/                               /				/
   *	/             n1                /		n2		/
   *	/                               /				/
   *	/                               /				/
   *	/                               /				/
   *	/                               / 				/
   *	/-------------------------------/-------------------------------	 
   *	/                               / 				/
   *	/                               / 				/
   *	/                               / 				/
   *	/               n3              / 		n4		/
   *	/                               / 				/
   *	/                               / 				/
   *	/                               / 				/
   *	/                               / 				/
   *	/                               /				/
   *---------------------------------------------------------------------------
   * 
   */
  const int halfx = ceil(static_cast<float>(UR.x-UL.x)/2);
  const int halfy = ceil(static_cast<float>(BR.y-UL.y)/2);
  
  // 矩形切四块
  n1.UL = UL;
  n1.UR = cv::Point2i(UL.x+halfy, UL.y);
  n1.BL = cv::Point2i(UL.x, UL.y+halfy);
  n1.BR = cv::Point2i(UL.x+halfx, UL.y+halfy);
  n1.vKeys.reserve(vKeys.size());
  
  n2.UL = n1.UR;
  n2.UR = UR;
  n2.BL = n1.BR;
  n2.BR = cv::Point2i(UR.x, UL.y+halfy);
  n2.vKeys.reserve(vKeys.size());
  
  n3.UL = n1.BL;
  n3.UR = n1.BR;
  n3.BL = BL;
  n3.BR = cv::Point2i(n1.BR.x, BL.y);
  n3.vKeys.reserve(vKeys.size());
  
  n4.UL = n3.UR;
  n4.UR = n2.BR;
  n4.BL = n3.BR;
  n4.BR = BR;
  n4.vKeys.reserve(vKeys.size());
  
  for (size_t i=0; i<vKeys.size(); i++)
  {
    const cv::KeyPoint &kp = vKeys[i];
    if (kp.pt.x < n1.UR.x)
    {
      if (kp.pt.y < n1.BR.y)
	n1.vKeys.push_back(kp);
      else
	n3.vKeys.push_back(kp);
    }
    else if (kp.pt.y < n1.BR.y)
      n2.vKeys.push_back(kp);
    else
      n4.vKeys.push_back(kp);
  }
  
  if (n1.vKeys.size() == 1)
    n1.bNoMore = true;
  if (n2.vKeys.size() == 1)
    n2.bNoMore = true;
  if (n3.vKeys.size() == 1)
    n3.bNoMore = true;
  if (n4.vKeys.size() == 1)
    n4.bNoMore = true;
  
}
 
 
//计算描述子的pattern,高斯分布,也可以使用其他定义的pattern
 
static int bit_pattern_31[256*4] =
{
    8,-3, 9,5/*mean (0), correlation (0)*/,
    4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/,
    -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/,
    7,-12, 12,-13/*mean (5.62303e-05), correlation (0.0636977)*/,
    2,-13, 2,12/*mean (0.000134953), correlation (0.085099)*/,
    1,-7, 1,6/*mean (0.000528565), correlation (0.0857175)*/,
    -2,-10, -2,-4/*mean (0.0188821), correlation (0.0985774)*/,
    -13,-13, -11,-8/*mean (0.0363135), correlation (0.0899616)*/,
    -13,-3, -12,-9/*mean (0.121806), correlation (0.099849)*/,
    10,4, 11,9/*mean (0.122065), correlation (0.093285)*/,
    -13,-8, -8,-9/*mean (0.162787), correlation (0.0942748)*/,
    -11,7, -9,12/*mean (0.21561), correlation (0.0974438)*/,
    7,7, 12,6/*mean (0.160583), correlation (0.130064)*/,
    -4,-5, -3,0/*mean (0.228171), correlation (0.132998)*/,
    -13,2, -12,-3/*mean (0.00997526), correlation (0.145926)*/,
    -9,0, -7,5/*mean (0.198234), correlation (0.143636)*/,
    12,-6, 12,-1/*mean (0.0676226), correlation (0.16689)*/,
    -3,6, -2,12/*mean (0.166847), correlation (0.171682)*/,
    -6,-13, -4,-8/*mean (0.101215), correlation (0.179716)*/,
    11,-13, 12,-8/*mean (0.200641), correlation (0.192279)*/,
    4,7, 5,1/*mean (0.205106), correlation (0.186848)*/,
    5,-3, 10,-3/*mean (0.234908), correlation (0.192319)*/,
    3,-7, 6,12/*mean (0.0709964), correlation (0.210872)*/,
    -8,-7, -6,-2/*mean (0.0939834), correlation (0.212589)*/,
    -2,11, -1,-10/*mean (0.127778), correlation (0.20866)*/,
    -13,12, -8,10/*mean (0.14783), correlation (0.206356)*/,
    -7,3, -5,-3/*mean (0.182141), correlation (0.198942)*/,
    -4,2, -3,7/*mean (0.188237), correlation (0.21384)*/,
    -10,-12, -6,11/*mean (0.14865), correlation (0.23571)*/,
    5,-12, 6,-7/*mean (0.222312), correlation (0.23324)*/,
    5,-6, 7,-1/*mean (0.229082), correlation (0.23389)*/,
    1,0, 4,-5/*mean (0.241577), correlation (0.215286)*/,
    9,11, 11,-13/*mean (0.00338507), correlation (0.251373)*/,
    4,7, 4,12/*mean (0.131005), correlation (0.257622)*/,
    2,-1, 4,4/*mean (0.152755), correlation (0.255205)*/,
    -4,-12, -2,7/*mean (0.182771), correlation (0.244867)*/,
    -8,-5, -7,-10/*mean (0.186898), correlation (0.23901)*/,
    4,11, 9,12/*mean (0.226226), correlation (0.258255)*/,
    0,-8, 1,-13/*mean (0.0897886), correlation (0.274827)*/,
    -13,-2, -8,2/*mean (0.148774), correlation (0.28065)*/,
    -3,-2, -2,3/*mean (0.153048), correlation (0.283063)*/,
    -6,9, -4,-9/*mean (0.169523), correlation (0.278248)*/,
    8,12, 10,7/*mean (0.225337), correlation (0.282851)*/,
    0,9, 1,3/*mean (0.226687), correlation (0.278734)*/,
    7,-5, 11,-10/*mean (0.00693882), correlation (0.305161)*/,
    -13,-6, -11,0/*mean (0.0227283), correlation (0.300181)*/,
    10,7, 12,1/*mean (0.125517), correlation (0.31089)*/,
    -6,-3, -6,12/*mean (0.131748), correlation (0.312779)*/,
    10,-9, 12,-4/*mean (0.144827), correlation (0.292797)*/,
    -13,8, -8,-12/*mean (0.149202), correlation (0.308918)*/,
    -13,0, -8,-4/*mean (0.160909), correlation (0.310013)*/,
    3,3, 7,8/*mean (0.177755), correlation (0.309394)*/,
    5,7, 10,-7/*mean (0.212337), correlation (0.310315)*/,
    -1,7, 1,-12/*mean (0.214429), correlation (0.311933)*/,
    3,-10, 5,6/*mean (0.235807), correlation (0.313104)*/,
    2,-4, 3,-10/*mean (0.00494827), correlation (0.344948)*/,
    -13,0, -13,5/*mean (0.0549145), correlation (0.344675)*/,
    -13,-7, -12,12/*mean (0.103385), correlation (0.342715)*/,
    -13,3, -11,8/*mean (0.134222), correlation (0.322922)*/,
    -7,12, -4,7/*mean (0.153284), correlation (0.337061)*/,
    6,-10, 12,8/*mean (0.154881), correlation (0.329257)*/,
    -9,-1, -7,-6/*mean (0.200967), correlation (0.33312)*/,
    -2,-5, 0,12/*mean (0.201518), correlation (0.340635)*/,
    -12,5, -7,5/*mean (0.207805), correlation (0.335631)*/,
    3,-10, 8,-13/*mean (0.224438), correlation (0.34504)*/,
    -7,-7, -4,5/*mean (0.239361), correlation (0.338053)*/,
    -3,-2, -1,-7/*mean (0.240744), correlation (0.344322)*/,
    2,9, 5,-11/*mean (0.242949), correlation (0.34145)*/,
    -11,-13, -5,-13/*mean (0.244028), correlation (0.336861)*/,
    -1,6, 0,-1/*mean (0.247571), correlation (0.343684)*/,
    5,-3, 5,2/*mean (0.000697256), correlation (0.357265)*/,
    -4,-13, -4,12/*mean (0.00213675), correlation (0.373827)*/,
    -9,-6, -9,6/*mean (0.0126856), correlation (0.373938)*/,
    -12,-10, -8,-4/*mean (0.0152497), correlation (0.364237)*/,
    10,2, 12,-3/*mean (0.0299933), correlation (0.345292)*/,
    7,12, 12,12/*mean (0.0307242), correlation (0.366299)*/,
    -7,-13, -6,5/*mean (0.0534975), correlation (0.368357)*/,
    -4,9, -3,4/*mean (0.099865), correlation (0.372276)*/,
    7,-1, 12,2/*mean (0.117083), correlation (0.364529)*/,
    -7,6, -5,1/*mean (0.126125), correlation (0.369606)*/,
    -13,11, -12,5/*mean (0.130364), correlation (0.358502)*/,
    -3,7, -2,-6/*mean (0.131691), correlation (0.375531)*/,
    7,-8, 12,-7/*mean (0.160166), correlation (0.379508)*/,
    -13,-7, -11,-12/*mean (0.167848), correlation (0.353343)*/,
    1,-3, 12,12/*mean (0.183378), correlation (0.371916)*/,
    2,-6, 3,0/*mean (0.228711), correlation (0.371761)*/,
    -4,3, -2,-13/*mean (0.247211), correlation (0.364063)*/,
    -1,-13, 1,9/*mean (0.249325), correlation (0.378139)*/,
    7,1, 8,-6/*mean (0.000652272), correlation (0.411682)*/,
    1,-1, 3,12/*mean (0.00248538), correlation (0.392988)*/,
    9,1, 12,6/*mean (0.0206815), correlation (0.386106)*/,
    -1,-9, -1,3/*mean (0.0364485), correlation (0.410752)*/,
    -13,-13, -10,5/*mean (0.0376068), correlation (0.398374)*/,
    7,7, 10,12/*mean (0.0424202), correlation (0.405663)*/,
    12,-5, 12,9/*mean (0.0942645), correlation (0.410422)*/,
    6,3, 7,11/*mean (0.1074), correlation (0.413224)*/,
    5,-13, 6,10/*mean (0.109256), correlation (0.408646)*/,
    2,-12, 2,3/*mean (0.131691), correlation (0.416076)*/,
    3,8, 4,-6/*mean (0.165081), correlation (0.417569)*/,
    2,6, 12,-13/*mean (0.171874), correlation (0.408471)*/,
    9,-12, 10,3/*mean (0.175146), correlation (0.41296)*/,
    -8,4, -7,9/*mean (0.183682), correlation (0.402956)*/,
    -11,12, -4,-6/*mean (0.184672), correlation (0.416125)*/,
    1,12, 2,-8/*mean (0.191487), correlation (0.386696)*/,
    6,-9, 7,-4/*mean (0.192668), correlation (0.394771)*/,
    2,3, 3,-2/*mean (0.200157), correlation (0.408303)*/,
    6,3, 11,0/*mean (0.204588), correlation (0.411762)*/,
    3,-3, 8,-8/*mean (0.205904), correlation (0.416294)*/,
    7,8, 9,3/*mean (0.213237), correlation (0.409306)*/,
    -11,-5, -6,-4/*mean (0.243444), correlation (0.395069)*/,
    -10,11, -5,10/*mean (0.247672), correlation (0.413392)*/,
    -5,-8, -3,12/*mean (0.24774), correlation (0.411416)*/,
    -10,5, -9,0/*mean (0.00213675), correlation (0.454003)*/,
    8,-1, 12,-6/*mean (0.0293635), correlation (0.455368)*/,
    4,-6, 6,-11/*mean (0.0404971), correlation (0.457393)*/,
    -10,12, -8,7/*mean (0.0481107), correlation (0.448364)*/,
    4,-2, 6,7/*mean (0.050641), correlation (0.455019)*/,
    -2,0, -2,12/*mean (0.0525978), correlation (0.44338)*/,
    -5,-8, -5,2/*mean (0.0629667), correlation (0.457096)*/,
    7,-6, 10,12/*mean (0.0653846), correlation (0.445623)*/,
    -9,-13, -8,-8/*mean (0.0858749), correlation (0.449789)*/,
    -5,-13, -5,-2/*mean (0.122402), correlation (0.450201)*/,
    8,-8, 9,-13/*mean (0.125416), correlation (0.453224)*/,
    -9,-11, -9,0/*mean (0.130128), correlation (0.458724)*/,
    1,-8, 1,-2/*mean (0.132467), correlation (0.440133)*/,
    7,-4, 9,1/*mean (0.132692), correlation (0.454)*/,
    -2,1, -1,-4/*mean (0.135695), correlation (0.455739)*/,
    11,-6, 12,-11/*mean (0.142904), correlation (0.446114)*/,
    -12,-9, -6,4/*mean (0.146165), correlation (0.451473)*/,
    3,7, 7,12/*mean (0.147627), correlation (0.456643)*/,
    5,5, 10,8/*mean (0.152901), correlation (0.455036)*/,
    0,-4, 2,8/*mean (0.167083), correlation (0.459315)*/,
    -9,12, -5,-13/*mean (0.173234), correlation (0.454706)*/,
    0,7, 2,12/*mean (0.18312), correlation (0.433855)*/,
    -1,2, 1,7/*mean (0.185504), correlation (0.443838)*/,
    5,11, 7,-9/*mean (0.185706), correlation (0.451123)*/,
    3,5, 6,-8/*mean (0.188968), correlation (0.455808)*/,
    -13,-4, -8,9/*mean (0.191667), correlation (0.459128)*/,
    -5,9, -3,-3/*mean (0.193196), correlation (0.458364)*/,
    -4,-7, -3,-12/*mean (0.196536), correlation (0.455782)*/,
    6,5, 8,0/*mean (0.1972), correlation (0.450481)*/,
    -7,6, -6,12/*mean (0.199438), correlation (0.458156)*/,
    -13,6, -5,-2/*mean (0.211224), correlation (0.449548)*/,
    1,-10, 3,10/*mean (0.211718), correlation (0.440606)*/,
    4,1, 8,-4/*mean (0.213034), correlation (0.443177)*/,
    -2,-2, 2,-13/*mean (0.234334), correlation (0.455304)*/,
    2,-12, 12,12/*mean (0.235684), correlation (0.443436)*/,
    -2,-13, 0,-6/*mean (0.237674), correlation (0.452525)*/,
    4,1, 9,3/*mean (0.23962), correlation (0.444824)*/,
    -6,-10, -3,-5/*mean (0.248459), correlation (0.439621)*/,
    -3,-13, -1,1/*mean (0.249505), correlation (0.456666)*/,
    7,5, 12,-11/*mean (0.00119208), correlation (0.495466)*/,
    4,-2, 5,-7/*mean (0.00372245), correlation (0.484214)*/,
    -13,9, -9,-5/*mean (0.00741116), correlation (0.499854)*/,
    7,1, 8,6/*mean (0.0208952), correlation (0.499773)*/,
    7,-8, 7,6/*mean (0.0220085), correlation (0.501609)*/,
    -7,-4, -7,1/*mean (0.0233806), correlation (0.496568)*/,
    -8,11, -7,-8/*mean (0.0236505), correlation (0.489719)*/,
    -13,6, -12,-8/*mean (0.0268781), correlation (0.503487)*/,
    2,4, 3,9/*mean (0.0323324), correlation (0.501938)*/,
    10,-5, 12,3/*mean (0.0399235), correlation (0.494029)*/,
    -6,-5, -6,7/*mean (0.0420153), correlation (0.486579)*/,
    8,-3, 9,-8/*mean (0.0548021), correlation (0.484237)*/,
    2,-12, 2,8/*mean (0.0616622), correlation (0.496642)*/,
    -11,-2, -10,3/*mean (0.0627755), correlation (0.498563)*/,
    -12,-13, -7,-9/*mean (0.0829622), correlation (0.495491)*/,
    -11,0, -10,-5/*mean (0.0843342), correlation (0.487146)*/,
    5,-3, 11,8/*mean (0.0929937), correlation (0.502315)*/,
    -2,-13, -1,12/*mean (0.113327), correlation (0.48941)*/,
    -1,-8, 0,9/*mean (0.132119), correlation (0.467268)*/,
    -13,-11, -12,-5/*mean (0.136269), correlation (0.498771)*/,
    -10,-2, -10,11/*mean (0.142173), correlation (0.498714)*/,
    -3,9, -2,-13/*mean (0.144141), correlation (0.491973)*/,
    2,-3, 3,2/*mean (0.14892), correlation (0.500782)*/,
    -9,-13, -4,0/*mean (0.150371), correlation (0.498211)*/,
    -4,6, -3,-10/*mean (0.152159), correlation (0.495547)*/,
    -4,12, -2,-7/*mean (0.156152), correlation (0.496925)*/,
    -6,-11, -4,9/*mean (0.15749), correlation (0.499222)*/,
    6,-3, 6,11/*mean (0.159211), correlation (0.503821)*/,
    -13,11, -5,5/*mean (0.162427), correlation (0.501907)*/,
    11,11, 12,6/*mean (0.16652), correlation (0.497632)*/,
    7,-5, 12,-2/*mean (0.169141), correlation (0.484474)*/,
    -1,12, 0,7/*mean (0.169456), correlation (0.495339)*/,
    -4,-8, -3,-2/*mean (0.171457), correlation (0.487251)*/,
    -7,1, -6,7/*mean (0.175), correlation (0.500024)*/,
    -13,-12, -8,-13/*mean (0.175866), correlation (0.497523)*/,
    -7,-2, -6,-8/*mean (0.178273), correlation (0.501854)*/,
    -8,5, -6,-9/*mean (0.181107), correlation (0.494888)*/,
    -5,-1, -4,5/*mean (0.190227), correlation (0.482557)*/,
    -13,7, -8,10/*mean (0.196739), correlation (0.496503)*/,
    1,5, 5,-13/*mean (0.19973), correlation (0.499759)*/,
    1,0, 10,-13/*mean (0.204465), correlation (0.49873)*/,
    9,12, 10,-1/*mean (0.209334), correlation (0.49063)*/,
    5,-8, 10,-9/*mean (0.211134), correlation (0.503011)*/,
    -1,11, 1,-13/*mean (0.212), correlation (0.499414)*/,
    -9,-3, -6,2/*mean (0.212168), correlation (0.480739)*/,
    -1,-10, 1,12/*mean (0.212731), correlation (0.502523)*/,
    -13,1, -8,-10/*mean (0.21327), correlation (0.489786)*/,
    8,-11, 10,-6/*mean (0.214159), correlation (0.488246)*/,
    2,-13, 3,-6/*mean (0.216993), correlation (0.50287)*/,
    7,-13, 12,-9/*mean (0.223639), correlation (0.470502)*/,
    -10,-10, -5,-7/*mean (0.224089), correlation (0.500852)*/,
    -10,-8, -8,-13/*mean (0.228666), correlation (0.502629)*/,
    4,-6, 8,5/*mean (0.22906), correlation (0.498305)*/,
    3,12, 8,-13/*mean (0.233378), correlation (0.503825)*/,
    -4,2, -3,-3/*mean (0.234323), correlation (0.476692)*/,
    5,-13, 10,-12/*mean (0.236392), correlation (0.475462)*/,
    4,-13, 5,-1/*mean (0.236842), correlation (0.504132)*/,
    -9,9, -4,3/*mean (0.236977), correlation (0.497739)*/,
    0,3, 3,-9/*mean (0.24314), correlation (0.499398)*/,
    -12,1, -6,1/*mean (0.243297), correlation (0.489447)*/,
    3,2, 4,-8/*mean (0.00155196), correlation (0.553496)*/,
    -10,-10, -10,9/*mean (0.00239541), correlation (0.54297)*/,
    8,-13, 12,12/*mean (0.0034413), correlation (0.544361)*/,
    -8,-12, -6,-5/*mean (0.003565), correlation (0.551225)*/,
    2,2, 3,7/*mean (0.00835583), correlation (0.55285)*/,
    10,6, 11,-8/*mean (0.00885065), correlation (0.540913)*/,
    6,8, 8,-12/*mean (0.0101552), correlation (0.551085)*/,
    -7,10, -6,5/*mean (0.0102227), correlation (0.533635)*/,
    -3,-9, -3,9/*mean (0.0110211), correlation (0.543121)*/,
    -1,-13, -1,5/*mean (0.0113473), correlation (0.550173)*/,
    -3,-7, -3,4/*mean (0.0140913), correlation (0.554774)*/,
    -8,-2, -8,3/*mean (0.017049), correlation (0.55461)*/,
    4,2, 12,12/*mean (0.01778), correlation (0.546921)*/,
    2,-5, 3,11/*mean (0.0224022), correlation (0.549667)*/,
    6,-9, 11,-13/*mean (0.029161), correlation (0.546295)*/,
    3,-1, 7,12/*mean (0.0303081), correlation (0.548599)*/,
    11,-1, 12,4/*mean (0.0355151), correlation (0.523943)*/,
    -3,0, -3,6/*mean (0.0417904), correlation (0.543395)*/,
    4,-11, 4,12/*mean (0.0487292), correlation (0.542818)*/,
    2,-4, 2,1/*mean (0.0575124), correlation (0.554888)*/,
    -10,-6, -8,1/*mean (0.0594242), correlation (0.544026)*/,
    -13,7, -11,1/*mean (0.0597391), correlation (0.550524)*/,
    -13,12, -11,-13/*mean (0.0608974), correlation (0.55383)*/,
    6,0, 11,-13/*mean (0.065126), correlation (0.552006)*/,
    0,-1, 1,4/*mean (0.074224), correlation (0.546372)*/,
    -13,3, -9,-2/*mean (0.0808592), correlation (0.554875)*/,
    -9,8, -6,-3/*mean (0.0883378), correlation (0.551178)*/,
    -13,-6, -8,-2/*mean (0.0901035), correlation (0.548446)*/,
    5,-9, 8,10/*mean (0.0949843), correlation (0.554694)*/,
    2,7, 3,-9/*mean (0.0994152), correlation (0.550979)*/,
    -1,-6, -1,-1/*mean (0.10045), correlation (0.552714)*/,
    9,5, 11,-2/*mean (0.100686), correlation (0.552594)*/,
    11,-3, 12,-8/*mean (0.101091), correlation (0.532394)*/,
    3,0, 3,5/*mean (0.101147), correlation (0.525576)*/,
    -1,4, 0,10/*mean (0.105263), correlation (0.531498)*/,
    3,-6, 4,5/*mean (0.110785), correlation (0.540491)*/,
    -13,0, -10,5/*mean (0.112798), correlation (0.536582)*/,
    5,8, 12,11/*mean (0.114181), correlation (0.555793)*/,
    8,9, 9,-6/*mean (0.117431), correlation (0.553763)*/,
    7,-4, 8,-12/*mean (0.118522), correlation (0.553452)*/,
    -10,4, -10,9/*mean (0.12094), correlation (0.554785)*/,
    7,3, 12,4/*mean (0.122582), correlation (0.555825)*/,
    9,-7, 10,-2/*mean (0.124978), correlation (0.549846)*/,
    7,0, 12,-2/*mean (0.127002), correlation (0.537452)*/,
    -1,-6, 0,-11/*mean (0.127148), correlation (0.547401)*/
};
 
 
 
//这个是类ORBextractor的带参构造函数,并且使用初始化列表对该类中的这5个变量赋值
// ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels,
//          int _iniThFAST, int _minThFAST):
//     nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
//     iniThFAST(_iniThFAST), minThFAST(_minThFAST)

int main(int argc, char **argv) {
  
    //读取图像
    Mat img = imread("/home/liqiang/slambook2/ch7_1/1.png", CV_LOAD_IMAGE_COLOR);//读取图像  CV_LOAD_IMAGE_COLOR表示返回一张彩色图像
    if(img.empty())
      cout << "No picture was found ..." << endl;//输出No picture was found ...
    else
      cout << "Img loaded successed!" << endl;//输出Img loaded successed!
 
    vector<int> features_num_per_level; //每层特征点数
    vector<int> umax; //存储特征方向,每个v对应的最大u
    vector<float> vec_scale_factor; // 存储每层尺度因子
    
    //构建图像金字塔
            
    //初始化每层金字塔对应的尺度因子
    vec_scale_factor.resize(levels_num);    //vec_scale_factor是用来存储金字塔中每层图像对应的尺度因子的vector变量
    //所以他的大小就是金字塔的层数
    vec_scale_factor[0] = 1.0f;//将第一层金字塔初始化为1 因为在c++中是从0开始的
    
    //计算金字塔中每一层图像对应的尺度因子和尺度因子的平方。
    //可以发现金字塔中第0层的尺度因子是1,然后每向上高一层,图像的尺度因子是在上一层图像的尺度因子 
    //上乘以scaleFactor,在本工程下该值为1.2
    //1   1*1.2   1*1.2*1.2    1*1.2*1.2*1.2   ...

    for (int i=1; i<levels_num; i++)//levels_num 金字塔层数
    {
      //float scale_factor = 1.2f; //金字塔之间尺度参数
      vec_scale_factor[i] = vec_scale_factor[i-1] * scale_factor;//1   1*1.2   1*1.2*1.2    1*1.2*1.2*1.2   ...
 
    }
    
    std::vector<cv::Mat> vec_image_pyramid(levels_num); //图像金字塔容器
    
    //如果说上面是正向的尺度,那么下面的就是逆向尺度了,是正向尺度的倒数
    for (int level=0; level<levels_num; ++level)
    {
      float scale = 1.0f / vec_scale_factor[level]; //vec_scale_factor是用来存储金字塔中每层图像对应的尺度因子的vector变量
      //这里是倒数
      cv::Size sz(cvRound((float)img.cols*scale), cvRound((float)img.rows*scale)); //图像大小设置
    
      if(level == 0)
      {
	vec_image_pyramid[level] = img;	
      }
      else
      {
	resize(vec_image_pyramid[level-1], vec_image_pyramid[level], sz, 0, 0, CV_INTER_LINEAR); //双线性插值新图像
      }
      
      // 金字塔构建过程可视化
      cout << "正在构建第" << level+1 << "层图像金字塔" << endl;//输出正在构架第几层金字塔
      imshow("img", vec_image_pyramid[level]);//输出图像
      waitKey(100);//按键输入,执行下一步
    }
    
    
    cout << "*****************************************" << endl;
    
//四叉树划分特征点
//函数的三要素是:函数名称,函数参数, 函数返回值
//ComputeKeyPointsOctTree是类ORBextractor的成员函数
//参数是vector类型的引用变量allKeypoints.
//返回值是void类型
//在参数文件TUM1.yaml下预定义了一些变量的值
//ORBextractor. nFeatures: 1000
//ORBextractor. scaleFactor: 1.2
//ORBextractor. nlevels: 8
//ORBextractor. iniThFAST: 20
//ORBextractor. minThFAST: 7
//在ORBextractor.h中用带参构造函数来初始化类ORBextractor中的相应的变量
//在类ORBextractor中还有一串变量
//std::vector<float> mvScaleFactor;
//std::vector<float> mvInvScaleFactor;
//std::vector<float> mvlevelSifma2;
//std::vector<float> mvInvLevelSigma2;
//如果说第一组变量是金字塔中某一层图像的属性,那么第二组是成员变量是一幅图像的属性。

    //搜索每一层图像上的特征点
    std::vector<std::vector<cv::KeyPoint>> all_keypoints; // 所有图层图像上特征点容器,容器的容器
    
    //通过vector中的resize函数来重新将vector变量allKeypoints的大小设置为nlevels.
    //nlevels也就是图像金字塔中图像的层数
    all_keypoints.resize(levels_num);
    
    const float border_width = 30; // 设置栅栏格子大小
    
    //对金字塔中的每一层图像进行一系列的操作
    for (int level=0; level<levels_num; ++level)
    {
      // 得到每一层图像进行特征检测区域上下两坐标
      //EDGE_THRESHOLD=19 表示过滤掉边缘效应的阈值
      //min_border_x, min_border_y, max_border_x, max_border_y四个变量一起设定了用于提取特征的区域
      const int min_border_x = EDGE_THRESHOLD - 3; // 边缘阈值滤掉
      const int min_border_y = min_border_x;
      const int max_border_x = vec_image_pyramid[level].cols - EDGE_THRESHOLD + 3;
      const int max_border_y = vec_image_pyramid[level].rows - EDGE_THRESHOLD + 3;
      
      //每层待分配关键点数量
      std::vector<cv::KeyPoint> vec_to_distribute_keys;//定义变量vToDistributeKeys存储从每一层图像中提取出来的特征。
      //vector中存储的数据类型是在opencv中定义的KeyPoint类
      vec_to_distribute_keys.reserve(features_num * 10);//reserve:分配空间,更改capacity但是不改变size 预留空间
      //resize:分配空间,同时改变capacity和size
      //计算总面积长宽   丈量了可以用来进行操作的“场地”大小
      const float width = max_border_x - min_border_x;
      const float height = max_border_y - min_border_y;
      //记录划分完后格子行和列
      //预将图像划分为30*30的网状
      //计算每个小格子的长和宽各占多少个像素
      const int cols = width/border_width;
      const int rows = height/border_width;
      //重新计算格子大小
      //计算最终长和宽被分成了多少个小格子ceisl
      const int width_cell = ceil(width/cols);
      const int height_cell = ceil(height/rows);
      cout << "第" << level+1 << "层图像切割成" << rows << "行" << cols << "列, ";
      
      //开始对每个格子进行检测
      //通过控制i来纵向遍历每一个ceil
      for (int i=0; i<rows; i++)
      {            
        //这里maxY相当于给当前遍历的这个cell加上一个外框
        const float ini_y = min_border_y + i * height_cell; //格子高度下坐标
        float max_y = ini_y + height_cell + 6; //格子高度上坐标
        if(ini_y >= max_border_y - 3)
        continue;
        if(max_y >=max_border_y)
        max_y = max_border_y;
                    
        //通过控制j来横向遍历每一个ceil.
        for (int j=0; j<cols; j++)
        {
          const float ini_x = min_border_x + j * width_cell;
          float max_x = ini_x + width_cell + 6;
          if(ini_x >= max_border_x - 6) //一般认为相片宽度比高度大
          continue;
          if(max_x > max_border_x)
          max_x = max_border_x;
        //通过上面的两个for循环就可以完成遍历该层图像中所有的ceil.
	  
    //vKeysCell用来盛放该cell中提取的特征点
    std::vector<cv::KeyPoint> vec_keys_cell; // 用FAST特征检测并存储每个格子的特征点
	  cv::FAST(vec_image_pyramid[level].rowRange(ini_y, max_y).colRange(ini_x, max_x), vec_keys_cell, default_fast_threshold, true);
	  
    //变量i和j的组合控制,当遍历到(i, j)个ceil时,就提取这个ceil下的FAST角点
    //如下是opencv中FAST函数的原型
    //输入图像,输出提取的特征点, 选取特征点的阈值
    //FAST( InputArray image, CV_OUT vector<KeyPoint>& keypoints,
    // int threshold, bool nonmaxSuppression=true );
	    
      
	  //如果fast测空,降低阈值继续检测
    //如果按照上面的方法在这个cell中提取不到特征点,那么就放低要求,
    //用minThFAST阈值来提取FAST角点
	      if (vec_keys_cell.empty())
        {
          cv::FAST(vec_image_pyramid[level].rowRange(ini_y, max_y).colRange(ini_x, max_x), vec_keys_cell, min_fast_threshold, true);
        }
	  //计算特征点位置
    //如果已经提取到关键点,那么就遍历这些所有提取的关键点
	  if (!vec_keys_cell.empty())
	  {
      //迭代器vit就相当于一个指向vector中存储的KeyPoint的指针,通过*vit就可以获取指针所指向的特定的点
      for (std::vector<cv::KeyPoint>::iterator vit = vec_keys_cell.begin(); vit != vec_keys_cell.end(); vit++) //迭代法遍历每个格子中特征点容器的特征点
	    {
	      //记录特征点在图像中的绝对坐标
        //pt表示KeyPoint的位置属性
        //KeyPoint是opencv中的一个类,pt是该类中的一个属性,获取获取关键点的坐标
        //因为单纯的(*vit).pt.x和(*vit).pt.y表示在当前ceil下的坐标,还要转化为在可提取特征范围内的坐标

	      (*vit).pt.x += j * width_cell;
	      (*vit).pt.y += i * height_cell;
	      vec_to_distribute_keys.push_back(*vit);//把从每个cell中提取的特征点都存储到vector变量vToDistributeKeys中去	      
	    }
	  }
	}
      }
      
      cout << "这层图像共有" <<  vec_to_distribute_keys.size() << "个特征点" << endl;//输出这层图像共有多少个特征点
      
      //截止到这里已经将该层图像中的所有ceil遍历结束并且,将提取的所有的特征点都已经存储到vector变量vToDistributeKeys中去了
      //vector<vector<KeyPoint> >& allKeypoints
      //allKeypoints是一个用来存储vector的vector
      //allKeypoints的大小是金字塔的层数nlevels
      //allKeypoints[level]是一个对应于每层图像上提取的特征点的vector
      //allKeypoints[level].size也就是在该层上要提取的特征点的个数

      std::vector<cv::KeyPoint> & keypoints = all_keypoints[level];
      //keypoints的预留内存是每幅图像上所有要提取的特征数
      keypoints.reserve(features_num);

//********************************************************************************************************
//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)
    
      // 开始四叉树划分
      // 初始化几个节点,不难发现,由于长和宽比较接近,所以一般初始节点为1
      //常用的相机kinect v1的分辨率是:640*480 kinect v2的分辨率是:1920*1080
      //为了尽量使得每一个结点的区域形状接近正方形所以图像的长宽比决定了四叉树根节点的数目
      //如果使用kinect v1那么只有一个根结点,如果使用kinect v2那么就会有两个根结点

      const int init_node_num = round(static_cast<float>(max_border_x - min_border_x)/(max_border_y - min_border_y));
      cout << "初始时有" << init_node_num << "个节点";
      //节点间间隔
      //interval_x变量可以理解为一个根节点所占的宽度 
      const float interval_x = static_cast<float>(max_border_x - min_border_x)/init_node_num;
      cout << "节点间隔为" << interval_x << ", ";
      
      
      //四叉树设计
      // 定义节点类型的初始节点容器
      std::vector<ExtractorNode*> init_nodes;      //init_nodes变量中存储的是结点的地址
      init_nodes.resize(init_node_num);    //init_nodes的大小先设置成根结点的个数
      // 划分之后的节点列表
      std::list<ExtractorNode> list_nodes;    //list_nodes中存储生成的树结点
      // 处理初始节点
      for (int i=0; i<init_node_num; i++)
      {
	// 定义四叉树节点变量ni
	ExtractorNode ni;
  //四叉树是每次根据特定条件将一个结点分成四等分,四个区域左上(UL),右上(UR), 
  //左下(BL),右下(BR)
	ni.UL = cv::Point2i(interval_x * static_cast<float>(i), 0);//左上角位置坐标
	ni.UR = cv::Point2i(interval_x * static_cast<float>(i+1), 0);//右上角位置坐标
	ni.BL = cv::Point2i(ni.UL.x, max_border_y-min_border_y);  //左下角的位置坐标
	ni.BR = cv::Point2i(ni.UR.x, max_border_y-min_border_y);   //右下角的位置坐标
	ni.vKeys.reserve(vec_to_distribute_keys.size());        //vKeys的大小为在上面的这个根节点范围内总共提取的特征点的个数
	
	list_nodes.push_back(ni);        //将创建的根节点插入到list_nodes中
	init_nodes[i] = &list_nodes.back(); //返回list_nodes最后元素值	
  //将list_nodes变量中的最后存储的那个结点的地址存储到vector变量init_nodes中
      }
         
      //将点分配给初级节点
      for (size_t i=0; i<vec_to_distribute_keys.size(); i++)
      {
        //将所有提取的特征点根据坐标位置将其分配到对应的根节点中去
       //如果使用kinect b=v1那么所有的kp.pt.x都小于hX,所以所有的特征点都被分配到
        //vpIniNodes的第0个元素中存储的结点指针所指向的空间中去了。
        //到这里明白了这个四叉树的玩法了
        //定义一个list变量,用来存储生成的树节点本身
        //定义一个vector变量,用来存储结点的指针,这个指针可以指向该结点区域被分配的特征点的内存
        //list是一个双向链表容器,可高效地进行插入删除元素
        //vector是将元素置于一个动态数组中,vector可以随机存取元素,在头尾插入数据快,但是从中            
        //间插入数据很慢
        //正是利用了list和vector的特点,使得我们即可以快速高效的插入删除结点,又可以随机的存取
        //被分配到某一个结点区域的的特征点

	      const cv::KeyPoint &kp = vec_to_distribute_keys[i];
	      init_nodes[kp.pt.x/interval_x]->vKeys.push_back(kp);
      }
      
      //设计节点迭代器
      std::list<ExtractorNode>::iterator lit = list_nodes.begin();   
      //遍历节点列表
      while (lit != list_nodes.end())
      {
       	//只含有一个特征点,就不在划分;        //如果判断在一个结点里面只有一个特征点
	      if (lit->vKeys.size() == 1)
        {
          //将该结点的bNoMore属性设置为true,表示不再对这个结点进行分割
          lit->bNoMore = true;
	        lit++;
	      }
        //如果判断这个结点中没有被分配到任何的特征点那么就将这个结点删除
       	else if (lit->vKeys.empty())
          lit = list_nodes.erase(lit); // 如果这个节点没有特征点就删了
          else
          lit++;
      }
    //list_nodes中的结点和 init_nodes中的结点指针是同步的,只有在init_nodes中存储的结点中存储了 
    //特征点,才能根据特征点的数目来决定如何处理这个结点
    //那如果在list_nodes中删除一个没有特征点的结点,那么在 init_nodes中对应的这个地址也会被销毁吗?

      //完结标志定义
      bool is_finish = false;
      //迭代计数
      int iteration = 0;
      
      // 定义新数据类型节点及其所包含的特征数
      std::vector<std::pair<int, ExtractorNode*>> keys_size_and_node;
      keys_size_and_node.reserve(list_nodes.size() * 4);
      
      while (!is_finish)
      {
	      iteration++;
        //list_nodes中已经存储的结点的数目
	      //初始化节点个数,用于判断节点是否再次进行了划分
	     int pre_size = list_nodes.size();
	
	     lit = list_nodes.begin();
	    //定义节点分解次数
    	int to_expand_num = 0;
	    keys_size_and_node.clear();
	
	while (lit != list_nodes.end())
	{
    //如果结点内被分配的特征点的数目只有1个则不继续分割这个结点
	  if (lit->bNoMore)
	  {
	    lit++;
	    continue;
	  }
	  else
	  {
	    //超过一个特征点就继续划分                 // 如果结点中被分配到的特征点数大于1则要继续分割
	    ExtractorNode n1, n2, n3, n4;
	    lit -> DivideNode(n1, n2, n3, n4);
	    
	    //对划分后的节点进行判断,判断是否含有特征点,含有特征点则添加特征点
      //在下面在介绍这个函数
      //概括来说就是将上面这个结点分成了四个结点,并且已经完成了特征点的分配,以及特征
      //个数的检测设定好每个节点的bNoMore的值
	    if (n1.vKeys.size() > 0)
	    {
	      list_nodes.push_front(n1);
        //如果这个新结点中被分配的特征点的个数大于1,那么接下来要被分割的结点的数目就得加1了                    
	      if (n1.vKeys.size() > 1)
	      {
          //如果新分割出来的第一个结点中被分配的特征点的个数大于0那么就将这个结点
          //插入到list的头部
		      to_expand_num++;
          //变量vSizeAndPointerToNode中存储的是每一个结点的地址以及该结点中被分配到的特征点的个数。
		      keys_size_and_node.push_back(std::make_pair(n1.vKeys.size(), &list_nodes.front()));
		      list_nodes.front().lit = list_nodes.begin();
	      }
	    }
      //对新分配出的第二个结点进行同上面相同的测试和操作	    
	    if (n2.vKeys.size() > 0)
	    {
        //在list的头部插入元素
	      list_nodes.push_front(n2);
	      if (n2.vKeys.size() > 1)
	      {
	      	to_expand_num++;
		      keys_size_and_node.push_back(std::make_pair(n2.vKeys.size(), &list_nodes.front()));
          //每插入一个结点就要更新list的开始结点的位置
		      list_nodes.front().lit = list_nodes.begin();
	      }
	    }
	    
	    if (n3.vKeys.size() > 0)
	    {
	      list_nodes.push_front(n3);
	      if (n3.vKeys.size() > 1)
	      {
		      to_expand_num++;
		      keys_size_and_node.push_back(std::make_pair(n3.vKeys.size(), &list_nodes.front()));
		      list_nodes.front().lit = list_nodes.begin();
	      }
	    }
	    
	    if (n4.vKeys.size() > 0)
	    {
	      list_nodes.push_front(n4);
	      if (n4.vKeys.size() > 1)
	      {
		      to_expand_num++;
	       	keys_size_and_node.push_back(std::make_pair(n4.vKeys.size(), &list_nodes.front()));
		      list_nodes.front().lit = list_nodes.begin();
	      }
	    }
	    
	    lit = list_nodes.erase(lit);
	    continue;
	  }
	}
 
	
	//给每层分配特征点数先估计
	features_num_per_level.resize(levels_num);
	float factor = 1.0f / scale_factor;
	float desired_features_per_scale = features_num * (1-factor) / (1-(float)pow((double)factor, (double)levels_num)); // 构造等比数列
	int sum_features = 0;
	for (int level=0; level<levels_num; level++)
	{
	  features_num_per_level[level] = cvRound(desired_features_per_scale);
	  sum_features += features_num_per_level[level];
	  desired_features_per_scale *= factor;
	}
	features_num_per_level[levels_num-1] = std::max(features_num-sum_features, 0);
	
   // Finish if there are more nodes than required features
   // or all nodes contain just one point
   //当创建的结点的数目比要求的特征点还要多或者,每个结点中都只有一个特征点的时候就停止分割

	// 当节点个数大于需分配的特征数或者所有的节点只有一个特征点(节点不能划分)的时候,则结束。
	if ((int)list_nodes.size() >= features_num_per_level[level] || (int)list_nodes.size() == pre_size)
	{
	  is_finish = true;
	}
   //如果现在生成的结点全部进行分割后生成的结点满足大于需求的特征点的数目,但是不继续分割又
   //不能满足大于N的要求时
   //这里为什么是乘以三,这里也正好印证了上面所说的当一个结点被分割成四个新的结点时,
   //这个结点时要被删除的,其实总的结点时增加了三个

	else if (((int)list_nodes.size() + to_expand_num * 3) > features_num_per_level[level])// 节点展开次数乘以3用于表明下一次的节点分解可能超过特征数,即为最后一次分解
	{
	  
	  while (!is_finish)
	  {
	    pre_size = list_nodes.size();//这里将已经创建好的结点放到一个新的容器中
 
	    std::vector<std::pair<int, ExtractorNode*> > prev_size_and_node = keys_size_and_node;
	    keys_size_and_node.clear();

 //根据结点中被分配都的特征点的数目对结点进行排序
 //这里为何要排序,我们想要的结果是想让尽可能多的特征点均匀的分布在图像上
 //如果前面的特征分布相对均匀的结点中的特征点数目已经达到了指标那么就可以将
 //后面那些分布密集的特征点去掉了。

	    sort(prev_size_and_node.begin(), prev_size_and_node.end());
	    for (int j = prev_size_and_node.size()-1; j >= 0; j--)
	    {
	      
	      ExtractorNode n1, n2, n3, n4;
	      prev_size_and_node[j].second->DivideNode(n1, n2, n3, n4);
 
	      // 划分之后进一步的判断
	      if (n1.vKeys.size() > 0)
	      {
		      list_nodes.push_front(n1);
          if (n1.vKeys.size() > 1)
		      {
		        keys_size_and_node.push_back(std::make_pair(n1.vKeys.size(), &list_nodes.front()));
		        list_nodes.front().lit = list_nodes.begin();
	        }
	      }
          if (n2.vKeys.size() > 0)
	        {
		        list_nodes.push_front(n2);
            if (n2.vKeys.size() > 1)
		        {
		          keys_size_and_node.push_back(std::make_pair(n2.vKeys.size(), &list_nodes.front()));
		          list_nodes.front().lit = list_nodes.begin();
		        }
	        }
          if (n3.vKeys.size() > 0)
	         {
             list_nodes.push_front(n3);
             if (n3.vKeys.size() > 1)
		        {
		          keys_size_and_node.push_back(std::make_pair(n3.vKeys.size(), &list_nodes.front()));
		          list_nodes.front().lit = list_nodes.begin();
		        }
	         }
           if (n4.vKeys.size() > 0)
	         {
             list_nodes.push_front(n4);
             if (n4.vKeys.size() > 1)
		         {
		           keys_size_and_node.push_back(std::make_pair(n4.vKeys.size(), &list_nodes.front()));
		           list_nodes.front().lit = list_nodes.begin();
		         }
	         }
	      
	      list_nodes.erase(prev_size_and_node[j].second->lit);
        //如果多有的结点还没有被分割完就已经达到了大于N的要求那么就直接跳出循环
	      if ((int)list_nodes.size() >= features_num_per_level[level])
        break;
	     }
	      if ((int)list_nodes.size() >= features_num_per_level[level] || (int)list_nodes.size() == pre_size)
        is_finish = true;
	    }
	  }
 
 }
 
	
      // 用KeyPoints数据类型.response进行挑选保留每个节点下最好的特征点
      std::vector<cv::KeyPoint> result_keys;
      result_keys.reserve(features_num_per_level[level]);
      //遍历创建的所有结点
      for (std::list<ExtractorNode>::iterator lit = list_nodes.begin(); lit != list_nodes.end(); lit++)
      {
        //获取每个结点下的特征点
	      std::vector<cv::KeyPoint> &node_keys = lit->vKeys;
	      cv::KeyPoint* keypoint = &node_keys[0];
	      float max_response = keypoint->response;        
        //在每个结点中找到那个最强壮的特征点进行保存
      
        for (size_t k = 1; k < node_keys.size(); k++)
	      {
          if (node_keys[k].response > max_response)
	        {
	          keypoint = &node_keys[k];
	          max_response = node_keys[k].response;   //只将每个结点下最强壮的的特征点保存
	        }
	    }
      result_keys.push_back(*keypoint);
	}
    
    keypoints = result_keys;
    
    const int scaled_path_size = PATCH_SIZE * vec_scale_factor[level];
    // 换算特征点的真实值
    const int nkps = keypoints.size();
    for (int i=0; i<nkps; i++)
    {
      keypoints[i].pt.x += min_border_x;
      keypoints[i].pt.y += min_border_y;
      keypoints[i].octave = level;
      keypoints[i].size = scaled_path_size;
    }
    
   cout << "经过四叉树筛选,第" << level+1 << "层剩余" << result_keys.size() << "个特征点" << endl;
   
   } 
   
   
   cout << "*****************************************" << endl;
   
   //计算特征点的方向,尽管本题不涉及,但是为了大家方便理解ORB_SLAM2还是写一下
       
   //准备数学工具,在特征计算时,每个v坐标对应的最大u坐标
   vector<int> u_max; //在半径等于HALF_SIZE的范围内,v=x下,u所能取到的最大坐标的绝对值
   u_max.resize(HALF_PATH_SIZE + 1);
   //将v坐标分两部分计算,xy方向比较对称
   int v, v0, vmax = cvFloor(HALF_PATH_SIZE * sqrt(2.f) / 2 + 1);
   int vmin = cvCeil(HALF_PATH_SIZE * sqrt(2.f)/2);
   // 勾股定理
   const double hp2 = HALF_PATH_SIZE * HALF_PATH_SIZE;
   for (v = 0; v<=vmax; ++v)
      u_max[v] = cvRound(sqrt(hp2 - v*v));
    //确保对称是个圆
    for (v = HALF_PATH_SIZE, v0 = 0; v>=vmin; --v)
    {
      while (u_max[v0] == u_max[v0+1])
	++v0;
      u_max[v] = v0;
      ++v0;
     }
      
   for (int level=0; level<levels_num; level++)
   {
     //指针提取该层特征点
      vector<KeyPoint>& keypoints = all_keypoints[level];
 
      //逐个特征点计算
      for (size_t i=0; i<keypoints.size(); i++)
      {
	//计算特征点这个区域内所有像素与其x坐标的乘积以及所有像素与其对应y坐标的乘积
	int m_01 = 0, m_10 = 0;
	const uchar* center = &vec_image_pyramid[level].at<uchar> (cvRound(keypoints[i].pt.y), cvRound(keypoints[i].pt.y)); // 得到每个特征点的中心位置
	
	// 当v=0时单独计算
	for (int u = -HALF_PATH_SIZE; u<HALF_PATH_SIZE; ++u)
	  m_10 += u * center[u];
	
	int step = (int)vec_image_pyramid[level].step1();
	
	for (int v=1; v<=HALF_PATH_SIZE; ++v)
	{
	  int v_sum = 0;
	  int d = u_max[v];
	  for (int u=-d; u<=d; ++u)
	  {
	    int val_plus = center[u+v*step], val_minus = center[u-v*step];
	    v_sum += (val_plus-val_minus);
	    m_10 += u * (val_plus+val_minus);
	  }
	  m_01 += v * v_sum;
	 }
	
	keypoints[i].angle = cv::fastAtan2((float)m_01, (float)m_10);
        //cout << "检测到第" << level << "层,第" << i << "个特征点角度为" << keypoints[i].angle << "度" << endl;
       }
 
     }
 
   
   //设计描述子
   //拷贝模板
   //bit_pattern_31储存的是通过学习后选取的256个点对相较于特征点的位置,其中四各元素分别表示pair_l.x,pair_l.y,pail_r.x,pair_r.y,
   //共有256个点对,产生256维描述子,每个描述子长32个字节,可以imshow查看描述子的样子
   vector<Point> pattern;   
   const int num_points = 512;
   const Point* pattern0 = (const Point*)bit_pattern_31;
   std::copy(pattern0, pattern0+num_points, std::back_inserter(pattern)); //将从pattern0到pattern0+512序列(也就是前面定义一串数字)拷贝到pattern中
 
   
   Mat descriptors;
   int num_keypoints = 0;
   for (int level=0; level<levels_num; ++level)
     num_keypoints += (int)all_keypoints[level].size(); // 统计所有特征点数总和
   cout << "共有" << num_keypoints << "个特征点,现在开始添加描述子..." << endl;
   
   //设计总输出特征点容器
   vector<KeyPoint> out_put_keypoints (num_keypoints);
   
   if (num_keypoints !=0)
     descriptors.create(num_keypoints, 32, CV_8U);
   
   
   int offset = 0;
   for (int level=0; level<levels_num; ++level)
   {
      vector<KeyPoint>& keypoints = all_keypoints[level];
      
      int num_keypoints_level = (int)keypoints.size();
      
      if (num_keypoints_level == 0)
	continue;
      
      //高斯模糊是为了计算BRIEF时去噪
      cout << "开始将原图层高斯模糊,正在模糊第" << level+1 << "张图..." << endl;
      Mat working_mat = vec_image_pyramid[level].clone();
      GaussianBlur(working_mat, working_mat, Size(7,7), 2, 2, BORDER_REFLECT_101);
      imshow("img", working_mat);
      waitKey(100);
      
      //计算每一层的描述子
      Mat descriptors_per_level = descriptors.rowRange(offset, offset+num_keypoints_level);
      descriptors_per_level = Mat::zeros((int)keypoints.size(), 32, CV_8UC1); // 每一层的描述子
      
      const float factorPI = (float)(CV_PI/180.f);      
      for (size_t i=0; i<keypoints.size(); i++)
      {
	uchar* desc = descriptors_per_level.ptr((int)i);
	Point* ppattern = &pattern[0];
 
	float angle = (float)keypoints[i].angle * factorPI; // 转化为弧度
	float a = (float)cos(angle), b = (float)sin(angle);
	
	const uchar* center = &working_mat.at<uchar>(cvRound(keypoints[i].pt.y), cvRound(keypoints[i].pt.x));
	const int step = (int)working_mat.step;
        
	//取旋转后一个像素点的值
	#define GET_VALUE(idx) \
	  center[ cvRound(ppattern[idx].x*b + ppattern[idx].y*a)*step + \
	          cvRound(ppattern[idx].x*a - ppattern[idx].y*b)]
 
	 //循环32次,pattern取值16*32=512,也就是说每次取16个点,形成8个点对,8个点对比较可以形成8bit长度的特征描述数据
	 for (int i=0; i<32; ++i, ppattern += 16)
	 {
	   int t0, t1, val;
	   
	   t0 = GET_VALUE(0); 
	   t1 = GET_VALUE(1);
	   val = t0 < t1;
	   
	   t0 = GET_VALUE(2);
	   t1 = GET_VALUE(3);
	   val |= (t0 < t1) << 1;
	   
	   t0 = GET_VALUE(4); 
	   t1 = GET_VALUE(5);
           val |= (t0 < t1) << 2;
        
	   t0 = GET_VALUE(6); 
	   t1 = GET_VALUE(7);
           val |= (t0 < t1) << 3;
        
	   t0 = GET_VALUE(8); 
	   t1 = GET_VALUE(9);
           val |= (t0 < t1) << 4;
        
	   t0 = GET_VALUE(10); 
	   t1 = GET_VALUE(11);
           val |= (t0 < t1) << 5;
        
	   t0 = GET_VALUE(12); 
	   t1 = GET_VALUE(13);
           val |= (t0 < t1) << 6;
        
	   t0 = GET_VALUE(14); 
	   t1 = GET_VALUE(15);
           val |= (t0 < t1) << 7;
 
           desc[i] = (uchar)val; // 一共32*8维描述子
	 }
 
       #undef GET_VALUE
      }
   
      offset += num_keypoints_level;
   
      //对关键点进行尺度恢复,恢复到原图位置
      if (level != 0)
      {
        float scale = vec_scale_factor[level];
        for (vector<KeyPoint>::iterator keypoint = keypoints.begin(), keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint)
	  keypoint->pt *= scale;
      }  
    
    out_put_keypoints.insert(out_put_keypoints.end(), keypoints.begin(), keypoints.end());
    
    }
    
    destroyAllWindows();
    
    Mat out_img1;
    drawKeypoints(img, out_put_keypoints, out_img1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
    imshow("四叉树法ORB", out_img1);
    imwrite("NewORB.png", out_img1);
    waitKey(0);
  
    vector<KeyPoint> orb_keypoints;
    Ptr<ORB> orb = ORB::create(1000);    
    orb->detect(img,orb_keypoints);
    cout << "共找到了" << orb_keypoints.size() << "个特征点;" << endl;   
    
    cv::Mat img_orb;
    cv::drawKeypoints(img, orb_keypoints, img_orb, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT);
    cv::imshow("普通ORB算法", img_orb);
    cv::imwrite("NormalORB.png", img_orb);
    cv::waitKey(0);
    
    return 0;
      
}
 
 

CMakeLists.txt

如果你的系统是Ubuntu18的,把CMakeLists.txt中的c++改为11

cmake_minimum_required(VERSION 2.8)
project(ch7_1)

set(CMAKE_BUILD_TYPE "Release")
add_definitions("-DENABLE_SSE")
#set(CMAKE_CXX_FLAGS "-std=c++11 -O2 ${SSE_FLAGS} -msse4")
set(CMAKE_CXX_FLAGS "-std=c++14 -O2 ${SSE_FLAGS} -msse4") 

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

find_package(OpenCV 3 REQUIRED)
find_package(G2O REQUIRED)
find_package(Sophus REQUIRED)

include_directories(
        ${OpenCV_INCLUDE_DIRS}
        ${G2O_INCLUDE_DIRS}
        ${Sophus_INCLUDE_DIRS}
        "/usr/local/include/eigen3/"
       
)

add_executable(orb_cv3 orb_cv3.cpp)
target_link_libraries(orb_cv3 ${OpenCV_LIBS} )

执行结果:

./orb_cv3
Img loaded successed!
正在构建第1层图像金字塔
正在构建第2层图像金字塔
正在构建第3层图像金字塔
正在构建第4层图像金字塔
正在构建第5层图像金字塔
正在构建第6层图像金字塔
正在构建第7层图像金字塔
正在构建第8层图像金字塔
*****************************************
第1层图像切割成14行20列, 这层图像共有2704个特征点
初始时有1个节点节点间隔为608, 经过四叉树筛选,第1层剩余219个特征点
第2层图像切割成12行16列, 这层图像共有1645个特征点
初始时有1个节点节点间隔为501, 经过四叉树筛选,第2层剩余183个特征点
第3层图像切割成10行13列, 这层图像共有1005个特征点
初始时有1个节点节点间隔为412, 经过四叉树筛选,第3层剩余153个特征点
第4层图像切割成8行11列, 这层图像共有541个特征点
初始时有1个节点节点间隔为338, 经过四叉树筛选,第4层剩余127个特征点
第5层图像切割成6行9列, 这层图像共有412个特征点
初始时有1个节点节点间隔为277, 经过四叉树筛选,第5层剩余105个特征点
第6层图像切割成5行7列, 这层图像共有311个特征点
初始时有1个节点节点间隔为225, 经过四叉树筛选,第6层剩余87个特征点
第7层图像切割成4行6列, 这层图像共有163个特征点
初始时有1个节点节点间隔为182, 经过四叉树筛选,第7层剩余73个特征点
第8层图像切割成3行4列, 这层图像共有137个特征点
初始时有1个节点节点间隔为147, 经过四叉树筛选,第8层剩余4个特征点
*****************************************
共有951个特征点,现在开始添加描述子...
开始将原图层高斯模糊,正在模糊第1张图...
开始将原图层高斯模糊,正在模糊第2张图...
开始将原图层高斯模糊,正在模糊第3张图...
开始将原图层高斯模糊,正在模糊第4张图...
开始将原图层高斯模糊,正在模糊第5张图...
开始将原图层高斯模糊,正在模糊第6张图...
开始将原图层高斯模糊,正在模糊第7张图...
开始将原图层高斯模糊,正在模糊第8张图...
共找到了1000个特征点;

转载于:

《视觉slam十四讲》第七讲课后习题_hello我是小菜鸡的博客-CSDN博客_视觉14讲 课后习题 1.除了本书的ORB特征点,你还能找到哪些特征点?请说说SIFT或SURF的原理,并对比它们与ORB之间的优劣。除了ORB之外,一般还有还有SIFT、SURF、BRISK、AKAZE,这些都是在OpenCV中已经实现了的。SIFT算法,又称为尺度不变特征转换(Scale-invariant feature transform),大致方法是首先搜索所有尺度下图像的位置,通过高斯微分函数...https://blog.csdn.net/qq_17032807/article/details/84994607

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhiwei121/article/details/100941934
 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值