CVPR:A Two-point Method for PTZ Camera Calibration in Sports的C++程序分析(5)
今天接着分析,函数preemptiveRANSACOneToMany()的step2,
来看这一段代码的含义,
// count outliers as energy measurement
for (int i = 0; i<hypotheses.size(); i++) {
// check accuracy by project pan, tilt to image space
vector<vector<Eigen::Vector2d> > projected_pan_tilt = projectPanTilt(hypotheses[i].ptz_, pp, sampled_pan_tilt);
// check minimum distance from projected points to image coordinate
for (int j = 0; j<projected_pan_tilt.size(); j++) {
//.................
}
} // end of i
在分析这段代码前,回想一下,一共进行了两次抽样操作,第一次抽样得到Hypotheses集,第二次抽样得到一个random set。第一个for语句容易理解,就是遍历整个Hypotheses集,那么函数projectPanTilt()是什么意思呢?来看一下
// @brief project pan, tilt ray to image space
static vector<vector<Eigen::Vector2d> > projectPanTilt(const Eigen::Vector3d& ptz,
const Eigen::Vector2d& pp,
const vector<vector<Eigen::Vector2d> > & input_pan_tilt)
{
vector<vector<Eigen::Vector2d> > image_pts(input_pan_tilt.size());
for (int i = 0; i<input_pan_tilt.size(); i++) {
for (int j = 0; j<input_pan_tilt[i].size(); j++) {
Eigen::Vector2d point = cvx_pgl::panTilt2Point(pp, ptz, input_pan_tilt[i][j]);
image_pts[i].push_back(point);
}
}
return image_pts;
}
这里又需要了解一下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;
}
那么,vector<vector<Eigen::Vector2d> > projected_pan_tilt = projectPanTilt (hypotheses[i].ptz_, pp, sampled_pan_tilt);是什么意思呢?对于一个假设hypotheses[i],怎样验证这个假设是好是坏呢?
于是,sampled_pan_tilt就派上用场了,相当于测试集的作用
(PS:关于sampled_pan_tilt还有几句话要说。首先,sampled_pan_tilt由candidate_pan_tilt组成。
其次,candidate_pan_tilt是一个vector<vector<Eigen::Vector2d> >,即一帧图片有多个特征点,每个特征点又有多个预测值,再罗嗦一句,一帧图像特征点的数目等于candidate_pan_tilt的size() )
所以,hypotheses[i]作用在测试集sampled_pan_tilt上就得到一组结果projected_pan_tilt
那么,怎样根据hypotheses[i]作用的结果projected_pan_tilt来判断这个假设到底是好还是坏呢?
接着向下看,
// check minimum distance from projected points to image coordinate
for (int j = 0; j<projected_pan_tilt.size(); j++) {
double min_dis = threshold * 2;
int min_index = -1;
for (int k = 0; k<projected_pan_tilt[j].size(); k++) {
Eigen::Vector2d dif = sampled_image_pts[j] - projected_pan_tilt[j][k];
double dis = dif.norm();
if (dis < min_dis) {
min_dis = dis;
min_index = k;
}
} // end of k
if (min_dis > threshold) {
hypotheses[i].loss_ += 1.0;
}
else {
hypotheses[i].inlier_indices_.push_back(sampled_indices[j]);
hypotheses[i].inlier_candidate_pan_tilt_indices_.push_back(min_index);
}
} // end of j
其中,sampled_image_pts[j]指这一帧图像第j个特征点的像素坐标,那么double型变量dis就是重投影误差。最后两行代码是什么意思呢?(把最后两行代码弄懂有助于理解上面这一段代码,哈哈)
hypotheses[i].inlier_indices_.push_back(sampled_indices[j]);
hypotheses[i].inlier_candidate_pan_tilt_indices_.push_back(min_index);
hypotheses[i]作用在这帧图像的第j个特征点上。第j个特征点有M个预测值,其中第m个预测值与hypotheses[i]作用得到的第j个特征点的像素坐标。用这个估计的像素坐标减去这个点真实的像素坐标得到重投影误差。假设第m个预测值算得的重投影误差最小,那么min_index等于m
总之,上面的两个for语句,迭代变量 i 遍历hypotheses,迭代变量 j 遍历特征点
那么就接着向下看吧,
// remove half of the hypotheses
std::sort(hypotheses.begin(), hypotheses.end());
hypotheses.resize(hypotheses.size()/2);
看注释不难发现,这是要删除一半的假设集。一开始假设集有1024个,一次大循环后剩下512,第二次后剩下256,如此反复,最后只剩下1个,RANSAC做的就是这样的事情
可是,Hypotheses是一个类,这个类中定义了“大于”或者“小于”的符号吗?(不然没法使用sort)
这个是有的,
bool operator < (const Hypothesis & other) const
{
return loss_ < other.loss_;
}
可是loss指什么呢?在之前的讨论中,可以发现一个细节,就是
if (min_dis > threshold) {
hypotheses[i].loss_ += 1.0;
}
else {
hypotheses[i].inlier_indices_.push_back(sampled_indices[j]);
hypotheses[i].inlier_candidate_pan_tilt_indices_.push_back(min_index);
}
如果min_dis > threshold,那么就没有hypotheses[i]的赋值操作,相反,不但没有赋值,反而hypotheses[i]的loss变量加1。这是惩罚的意思。比如,按时完成作业,就会把你添加在“三好学生”候选名单里,如果没有写作业,就给你扣5分,大概就是这个意思。一个学期下来,有的学生是100分,有的学生十次没交作业,就只有50分。这个班假设只有一半的人可以评为少先队员,于是就用sort函数给全班排序,选一半的人出来。
假设集舍弃一半之后,还有一些操作,
// refine by inliers
for (int i = 0; i<hypotheses.size(); i++) {
//......................
}
下次再来看这个循环的第一部分,哈哈 :)