CVPR:A Two-point Method for PTZ Camera Calibration in Sports的C++程序分析(4)

CVPR:A Two-point Method for PTZ Camera Calibration in Sports的C++程序分析(4)

接着之前的讨论,开始看函数panTilt2Point(),

    Eigen::Vector2d panTilt2Point(const Eigen::Vector2d& pp,
                                  const Eigen::Vector3d& ptz,
                                  const Eigen::Vector2d& point_pan_tilt)
    {
        double delta_pan  = (point_pan_tilt[0] - ptz[0]) * M_PI/180.0;
        double delta_tilt = (point_pan_tilt[1] - ptz[1]) * M_PI/180.0;
        double fl = ptz[2];
        double delta_x = fl * tan(delta_pan);
        double delta_y = fl * tan(delta_tilt);
        
        Eigen::Vector2d point(pp.x() + delta_x, pp.y() - delta_y); // oppositive direction of y
        return point;
    }

这个函数主要是根据预测的PT角来来求解变量rp,变量rp的定义在这篇论文的Eq.(5)。如果不读论文,姑且接受这个变量好了。

那么,对每一个特征点的每一个预测值,都可以产生一个rp,而rp是一个2×1的列向量。可能不好理解。

举个例子,如果这幅图像有8个特征点,并且每个特征点都有5个预测值,而每个预测值都可以产生一个rp,所以一共有8×5=40个rp,记为rp_i_j,i=1,2,3,...,8,j=1,2,3..,5

其实,对于真值Pan_true,和Tilt_true,在这幅图像的8个特征点,分别生成rp_true_1, rp_true_2,..., rp_true_8.

看到最后,就会发现:这个找最优解的问题其实就是一个最近邻问题,别慌,慢慢来

好了,对于这幅图像的第一个特征点,现在有rp_1_1, ..., rp_1_8 这八个值,它们哪一个跟 rp_true_1最近呢?因为rp是2×1的列向量,所以这里的距离指的是欧氏距离。经过排序,前三名分别是rp_1_3, rp_1_8, rp_1_4。这说明rp_1_3相关联的PT_1_3接近真值。

这只是第一个特征点。还有7个需要查看。每一次都需要排序,找到前三名。我们只要盯着预测值中上榜率最高的就行啦。原理是这样的。

讨论完这个辅助函数之后,就直接看这个工程中最为核心的函数preemptiveRANSACOneToMany(),它的函数原型是,

    bool preemptiveRANSACOneToMany(const vector<Eigen::Vector2d> & image_points,
                                   const vector<vector<Eigen::Vector2d> > & candidate_pan_tilt,
                                   const Eigen::Vector2d& pp,
                                   const PTZPreemptiveRANSACParameter & param,
                                   Eigen::Vector3d & ptz,
                                   bool verbose)

至于变量candidate_pan_tilt为什么是vector<vector<Eigen::vector2d> >这我就不多说了,前面已经介绍了。pp是相机的中心点,如果相机分辨率是1024×768,那么pp等于(512, 384), param 存放RANSAC,关于迭代以及阈值的变量,都写在这里了,

    Eigen::Vector2d pp(1280.0/2.0, 720.0/2.0);
    ptz_pose_opt::PTZPreemptiveRANSACParameter param;
    param.reprojection_error_threshold_ = reprojection_error_threshold;
    param.sample_number_ = sample_number;

而vector2d型变量ptz是输出结果

来看这个函数的第一部分,

        assert(image_points.size() == candidate_pan_tilt.size());
        if (image_points.size() <= 12) {
            return false;
        }
        
        const int num_iteration = 1024;
        const int K = 512;
        const int N = (int)image_points.size();
        const int B = param.sample_number_;
        double threshold = param.reprojection_error_threshold_;

除了一些常规赋值,报警外,可以发现:一副图像至少需要12个点,用来做RANSAC,否则就会报错; 变量N指代这一帧图像点的数量

再往下看,一个小循环的第一部分

        // step 1: sample hyperthesis
        vector<Hypothesis> hypotheses;
        for (int i = 0; i<num_iteration; i++) {
            int k1 = 0;
            int k2 = 0;
            do{
                k1 = rand()%N;
                k2 = rand()%N;
            }while (k1 == k2);

          // not end....

       }

看注释可以发现,这是在sample hypothesis,中文就是“假设抽样”,白话一点就是,随机地抽取一些假设。可能还是听不太明白。可以这样讲。假设一副图像用SIFT采集到20个特征点,相当于袋子里放了二十个球,每次从这里面拿两个出来; 然后把这两个球再放进去,再随机拿出两个,就这样重复num_iteration次,这里num_iteration等于1024

现在,从放有N个球的袋子里随机拿两个后,得以初始化一些变量,

            const Eigen::Vector2d pan_tilt1 = candidate_pan_tilt[k1][0];
            const Eigen::Vector2d pan_tilt2 = candidate_pan_tilt[k2][0];
            const Eigen::Vector2d point1 = image_points[k1];
            const Eigen::Vector2d point2 = image_points[k2];
            Eigen::Vector3d ptz;            

然后,对这一次抽样进行一次判断,如果合格,就把它存起来,

            bool is_valid = EigenX::ptzFromTwoPoints(pan_tilt1, pan_tilt2, point1, point2, pp, ptz);
            if (is_valid) {
                Hypothesis hp;
                hp.ptz_ = ptz;
                hypotheses.push_back(hp);
            }
            else {
                if (verbose) {
                    printf("warning: estimate ptz from two points failed.\n");
                }
                
            }
            if (hypotheses.size() > K) {
                if (verbose) {
                    printf("initialization repeat %d times\n", i);
                }
                break;
            }
        }

这样的操作重复num_iteration次,这里num_iteration等于1024,因此一共抽取了1024个假设抽样。

在进行sample hypotheses之后,进行下一步,计算pan角,tilt角以及focal

        // step 2: optimize pan, tilt, focal length
        while (hypotheses.size() > 1) { 

                 //...........

        }

先看step2,这个大循环的第一部分,

            // sample random set
            vector<Eigen::Vector2d> sampled_image_pts;
            vector<vector<Eigen::Vector2d> > sampled_pan_tilt;  // one camera point may have multiple pan, tilt correspondences
            vector<int> sampled_indices;
            for (int i =0; i<B; i++) {
                int index = rand()%N;
                sampled_image_pts.push_back(image_points[index]);
                sampled_pan_tilt.push_back(candidate_pan_tilt[index]);
                sampled_indices.push_back(index);
            }

这个注释的意思前面已经讲过,就是在一帧图像中,一个特征点对应多个随机森里预测值。B = param.sample_number_,那么sample_number_在头文件中这样定义,它等于32

即假设一帧图像有40个特征点,随机抽取32次(不排除重复,这应该是一个bug),每次抽取之后,记录它的像素坐标,相应的随机森林多个预测值,以及该特征点的index(即它是这40个特征点的第多少多少位)

下一次,再分析后面的代码



猜你喜欢

转载自blog.csdn.net/qq_39732684/article/details/80396822
PTZ