CVPR:A Two-point Method for PTZ Camera Calibration in Sports的C++程序分析(2)
首先声明一些变量,
const char * model_file = "/Users/jimmy/Desktop/BTDT_ptz_soccer/model/seq2_model.txt";
const char * ptz_feature_folder = "/Users/jimmy/Desktop/BTDT_ptz_soccer/soccer_data/train_data/seq1_ptz_sift_inlier/*.mat";
const int max_check = 8;
const double reprojection_error_threshold = 2.0;
const double distance_threshold = 0.2;
const int sample_number = 32;
const char * save_file = "result.mat";
其中,model_file指txt文件seq2_model的存放路径,该文档记录了随机森林的训练结果。ptz_feature_folder指用来做预测的图片的存放路径。然后加载随机森林模型model。
BTDTRegressor model;
bool is_read = model.load(model_file);
assert(is_read);
if (maxTreeNum == -1) {
maxTreeNum = model.treeNum();
}
printf("use %d trees in the test\n", maxTreeNum);
做初始化操作,把用来做预测的图片路径存放到vector<string>型的feature_files中。MatrixXd型变量gt_ptz_all指测试集所有图像对应的Pan,Tilt,focal的真值,而MatrixXd型变量estimated_ptz_all指测试集所有图像对应的Pan,Tilt,focal的估计值。最后我们要计算真值和估计值之间的偏差。
// read testing examples
vector<string> feature_files;
CvxUtil::readFilenames(ptz_feature_folder, feature_files);
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;
printf("principal point is fixed at %lf %lf\n", pp.x(), pp.y());
printf("inlier reprojection error is %lf pixels\n", param.reprojection_error_threshold_);
Eigen::MatrixXd gt_ptz_all(feature_files.size(), 3);
Eigen::MatrixXd estimated_ptz_all(feature_files.size(), 3);
接下来进入一个循环,即对测试集的每一帧图像都会做预测评估的处理。
int index = 0;
for (const string &file_name: feature_files){ //.. }
先看这个循环的第一块,
vector<btdtr_ptz_util::PTZSample> samples;
Eigen::Vector3f ptz;
btdtr_ptz_util::generatePTZSampleWithFeature(file_name.c_str(), pp.cast<float>(), ptz, samples);
Eigen::Vector3d ptz_gt(ptz.x(), ptz.y(), ptz.z());
printf("feature number is %lu\n", samples.size());
vector<Eigen::Vector2d> image_points;
vector<vector<Eigen::Vector2d> > candidate_pan_tilt;
Eigen::Vector3d estimated_ptz(0, 0, 0);
首先应该弄清楚samples是什么。它是vector<btdtr_ptz_util::PTZSample>型的变量,刚刚被初始化。那么PTZSample是什么呢?根据btdtr_ptz_util的头文件,可以看见PTZSample的定义,发现它分为,像素点位置,相机拍摄像素点所在的图像时的Pan,以及Tilt角(这是真值,待会要与估计值向减求误差的),以及像素点SIFT描述子信息。
class PTZSample
{
public:
Eigen::Vector2f loc_; // 2D location (x, y)
Eigen::VectorXf pan_tilt_; // Pan, tilt parameter in world coordinate, label
Eigen::VectorXf descriptor_; // image patch descriptor, feature
PTZSample() {
pan_tilt_ = Eigen::VectorXf::Zero(2, 1);
}
};
那么函数generatePTZSampleWithFeature作用是什么?
void generatePTZSampleWithFeature(const char * feature_ptz_file_name,
const Eigen::Vector2f& pp,
Eigen::Vector3f & ptz,
vector<PTZSample> & samples)
{
assert(feature_ptz_file_name);
vector<Eigen::VectorXf> locations;
vector<Eigen::VectorXf> features;
readPTZFeatureLocationAndDescriptors(feature_ptz_file_name, ptz, locations, features);
for (int i = 0; i<locations.size(); i++) {
PTZSample s;
Eigen::Vector2f pan_tilt;
s.loc_[0] = locations[i][0];
s.loc_[1] = locations[i][1];
EigenX::pointPanTilt(pp, ptz, s.loc_, pan_tilt);
s.pan_tilt_[0] = pan_tilt[0];
s.pan_tilt_[1] = pan_tilt[1];
s.descriptor_ = features[i];
samples.push_back(s);
}
}
从上述代码可以发现这个非常简单。从feature_ptz_file_name这个文件读取像素点的相关信息。一共有location.size()个像素点。而每一个像素点都有它们的PT角信息,SIFT描述子信息。而它们一一存入vector<btdtr_ptz_util::PTZSample>类型的变量samples中。而readPTZFeatureLocationAndDescriptors这个函数的意义非常直接,不必分析。
函数generatePTZSampleWithFeature已经分析清楚了(说白了就是把真值ground truth罗列出来,与之后的估计值向减,把估计误差算出来)。再来分析for循环第一块的其他声明变量。为了方便观看,再次抄在下面。
Eigen::Vector3d ptz_gt(ptz.x(), ptz.y(), ptz.z());
printf("feature number is %lu\n", samples.size());
vector<Eigen::Vector2d> image_points;
vector<vector<Eigen::Vector2d> > candidate_pan_tilt;
Eigen::Vector3d estimated_ptz(0, 0, 0);
Vector3d型变量ptz_gt(即ground truth)指相机拍摄这一帧图画的pan角,tilt角,focal的真值。
vector<Vector2d>型变量image_points指这一帧图像中选中的像素点的像素位置。
至于vector<vector<Vector2d>>型变量candidate_pan_tilt指随机森林的预测值,在上一讲也提及过,并不是惟一的。所以要列出一个候补名单,然后用RANSAC做筛选,得到最后的估计值estimated_ptz。
最后罗嗦一句,变量的命名尤为重要,不仅方便看代码的人,也方便写代码的人!