赞
踩
我们在第一张图片提取角点,然后用光流跟踪他们在第二张图片的位置。
采用3种方法:
1)OpenCV自带的LK光流;
2)基于高斯牛顿法的光流:因为光流可以看做一个优化问题,通过最小化灰度误差估计最优的像素偏移;
3)多层光流:使用图像金字塔。
cmake_minimum_required(VERSION 2.8) project(ch8) set(CMAKE_BUILD_TYPE "Release") add_definitions("-DENABLE_SSE") set(CMAKE_CXX_FLAGS "-std=c++14 -msse4") find_package(OpenCV REQUIRED) find_package(Sophus REQUIRED) find_package(Pangolin REQUIRED) include_directories( ${OpenCV_INCLUDE_DIRS} ${G2O_INCLUDE_DIRS} ${Sophus_INCLUDE_DIRS} "/usr/include/eigen3/" ${Pangolin_INCLUDE_DIRS} ) add_executable(optical_flow optical_flow.cpp) target_link_libraries(optical_flow ${OpenCV_LIBS} ${FMT_LIBRARIES} fmt) add_executable(direct_method direct_method.cpp) target_link_libraries(direct_method ${OpenCV_LIBS} ${Pangolin_LIBRARIES} ${FMT_LIBRARIES} fmt)
// // Created by Xiang on 2017/12/19. // #include <opencv2/opencv.hpp> #include <string> #include <chrono> #include <Eigen/Core> #include <Eigen/Dense> #include <opencv2/imgproc/types_c.h> using namespace std; using namespace cv; string file_1 = "/home/robot/桌面/slambook2-master/ch8/LK1.png"; // first image string file_2 = "/home/robot/桌面/slambook2-master/ch8/LK2.png"; // second image /// 光流跟踪器和接口Optical flow tracker and interface class OpticalFlowTracker { public: OpticalFlowTracker( const Mat &img1_, const Mat &img2_, const vector<KeyPoint> &kp1_, vector<KeyPoint> &kp2_, vector<bool> &success_, bool inverse_ = true, bool has_initial_ = false) : img1(img1_), img2(img2_), kp1(kp1_), kp2(kp2_), success(success_), inverse(inverse_), has_initial(has_initial_) {} void calculateOpticalFlow(const Range &range); private: const Mat &img1; const Mat &img2; const vector<KeyPoint> &kp1; vector<KeyPoint> &kp2; vector<bool> &success; bool inverse = true; bool has_initial = false; }; /**单层光流 * single level optical flow * @param [in] img1 the first image * @param [in] img2 the second image * @param [in] kp1 keypoints in img1 * @param [in|out] kp2 keypoints in img2, if empty, use initial guess in kp1 * @param [out] success true if a keypoint is tracked successfully * @param [in] inverse use inverse formulation? */ void OpticalFlowSingleLevel( const Mat &img1, const Mat &img2, const vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, vector<bool> &success, bool inverse = false, bool has_initial_guess = false ); /**多层光流,金字塔的比例默认设置为2 * multi level optical flow, scale of pyramid is set to 2 by default * the image pyramid will be create inside the function * @param [in] img1 the first pyramid * @param [in] img2 the second pyramid * @param [in] kp1 keypoints in img1 * @param [out] kp2 keypoints in img2 * @param [out] success true if a keypoint is tracked successfully * @param [in] inverse set true to enable inverse formulation */ void OpticalFlowMultiLevel( const Mat &img1, const Mat &img2, const vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, vector<bool> &success, bool inverse = false ); /**从参考图像中获取灰度值(双线性插值) * get a gray scale value from reference image (bi-linear interpolated) * @param img * @param x * @param y * @return the interpolated value of this pixel */ inline float GetPixelValue(const cv::Mat &img, float x, float y) { // boundary check if (x < 0) x = 0; if (y < 0) y = 0; if (x >= img.cols - 1) x = img.cols - 2; if (y >= img.rows - 1) y = img.rows - 2; float xx = x - floor(x); float yy = y - floor(y); int x_a1 = std::min(img.cols - 1, int(x) + 1); int y_a1 = std::min(img.rows - 1, int(y) + 1); return (1 - xx) * (1 - yy) * img.at<uchar>(y, x) + xx * (1 - yy) * img.at<uchar>(y, x_a1) + (1 - xx) * yy * img.at<uchar>(y_a1, x) + xx * yy * img.at<uchar>(y_a1, x_a1); } int main(int argc, char **argv) { // 1. 读取图像 Mat img1 = imread(file_1, 0); Mat img2 = imread(file_2, 0); // 2. 提取第一幅图的关键点,在此使用GFTT算法 vector<KeyPoint> kp1; Ptr<GFTTDetector> detector = GFTTDetector::create(500, 0.01, 20); // maximum 500 keypoints detector->detect(img1, kp1); // 3. 跟踪第二张图像中的这些关键点 // 1)首先在验证图片中使用单级LK vector<KeyPoint> kp2_single; vector<bool> success_single; OpticalFlowSingleLevel(img1, img2, kp1, kp2_single, success_single); // 2)然后测试多级LK vector<KeyPoint> kp2_multi; vector<bool> success_multi; chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); OpticalFlowMultiLevel(img1, img2, kp1, kp2_multi, success_multi, true); chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); auto time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1); cout << "optical flow by gauss-newton: " << time_used.count() << endl; // 3)使用opencv的光流进行验证 vector<Point2f> pt1, pt2; for (auto &kp: kp1) pt1.push_back(kp.pt); vector<uchar> status; vector<float> error; t1 = chrono::steady_clock::now(); cv::calcOpticalFlowPyrLK(img1, img2, pt1, pt2, status, error); t2 = chrono::steady_clock::now(); time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1); cout << "optical flow by opencv: " << time_used.count() << endl; // 4. 绘制这些函数的差异 Mat img2_single; cv::cvtColor(img2, img2_single, CV_GRAY2BGR); for (int i = 0; i < kp2_single.size(); i++) { if (success_single[i]) { cv::circle(img2_single, kp2_single[i].pt, 2, cv::Scalar(0, 250, 0), 2); cv::line(img2_single, kp1[i].pt, kp2_single[i].pt, cv::Scalar(0, 250, 0)); } } Mat img2_multi; cv::cvtColor(img2, img2_multi, CV_GRAY2BGR); for (int i = 0; i < kp2_multi.size(); i++) { if (success_multi[i]) { cv::circle(img2_multi, kp2_multi[i].pt, 2, cv::Scalar(0, 250, 0), 2); cv::line(img2_multi, kp1[i].pt, kp2_multi[i].pt, cv::Scalar(0, 250, 0)); } } Mat img2_CV; cv::cvtColor(img2, img2_CV, CV_GRAY2BGR); for (int i = 0; i < pt2.size(); i++) { if (status[i]) { cv::circle(img2_CV, pt2[i], 2, cv::Scalar(0, 250, 0), 2); cv::line(img2_CV, pt1[i], pt2[i], cv::Scalar(0, 250, 0)); } } cv::imshow("tracked single level", img2_single); cv::imshow("tracked multi level", img2_multi); cv::imshow("tracked by opencv", img2_CV); cv::waitKey(0); return 0; } // 单层光流 void OpticalFlowSingleLevel( const Mat &img1, const Mat &img2, const vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, vector<bool> &success, bool inverse, bool has_initial) { kp2.resize(kp1.size());//将第二个图像的特征点数量设置和第一个一样 success.resize(kp1.size()); OpticalFlowTracker tracker(img1, img2, kp1, kp2, success, inverse, has_initial); parallel_for_(Range(0, kp1.size()), std::bind(&OpticalFlowTracker::calculateOpticalFlow, &tracker, placeholders::_1));//计算光流 } // 计算光流 void OpticalFlowTracker::calculateOpticalFlow(const Range &range) { // parameters int half_patch_size = 4; int iterations = 10; for (size_t i = range.start; i < range.end; i++) { auto kp = kp1[i]; // 1. 设置dx dy double dx = 0, dy = 0; if (has_initial) { dx = kp2[i].pt.x - kp.pt.x; dy = kp2[i].pt.y - kp.pt.y; } double cost = 0, lastCost = 0; bool succ = true; // indicate if this point succeeded // 2. 高斯牛顿法 Eigen::Matrix2d H = Eigen::Matrix2d::Zero(); // hessian Eigen::Vector2d b = Eigen::Vector2d::Zero(); // bias Eigen::Vector2d J; // jacobian for (int iter = 0; iter < iterations; iter++) { //迭代10次 if (inverse == false) { H = Eigen::Matrix2d::Zero(); b = Eigen::Vector2d::Zero(); } else { // only reset b b = Eigen::Vector2d::Zero(); } cost = 0; // 计算 cost 和 jacobian for (int x = -half_patch_size; x < half_patch_size; x++) for (int y = -half_patch_size; y < half_patch_size; y++) { double error = GetPixelValue(img1, kp.pt.x + x, kp.pt.y + y) - GetPixelValue(img2, kp.pt.x + x + dx, kp.pt.y + y + dy);; // Jacobian if (inverse == false) { J = -1.0 * Eigen::Vector2d( 0.5 * (GetPixelValue(img2, kp.pt.x + dx + x + 1, kp.pt.y + dy + y) - GetPixelValue(img2, kp.pt.x + dx + x - 1, kp.pt.y + dy + y)), 0.5 * (GetPixelValue(img2, kp.pt.x + dx + x, kp.pt.y + dy + y + 1) - GetPixelValue(img2, kp.pt.x + dx + x, kp.pt.y + dy + y - 1)) ); } else if (iter == 0) { // 在逆模式中,J在所有迭代中保持不变 // 注意,当dx,dy更新时,这个J不会改变,所以我们可以存储它,只计算错误 // in inverse mode, J keeps same for all iterations // NOTE this J does not change when dx, dy is updated, so we can store it and only compute error J = -1.0 * Eigen::Vector2d( 0.5 * (GetPixelValue(img1, kp.pt.x + x + 1, kp.pt.y + y) - GetPixelValue(img1, kp.pt.x + x - 1, kp.pt.y + y)), 0.5 * (GetPixelValue(img1, kp.pt.x + x, kp.pt.y + y + 1) - GetPixelValue(img1, kp.pt.x + x, kp.pt.y + y - 1)) ); } // 计算 H, b and set cost; b += -error * J; cost += error * error; if (inverse == false || iter == 0) { // also update H H += J * J.transpose(); } } // 得到 H * 更新量 = b 的 更新量 Eigen::Vector2d update = H.ldlt().solve(b); if (std::isnan(update[0])) { // sometimes occurred when we have a black or white patch and H is irreversible cout << "update is nan" << endl; succ = false; break; } if (iter > 0 && cost > lastCost) { break; } // 更新 dx, dy dx += update[0]; dy += update[1]; lastCost = cost; succ = true; if (update.norm() < 1e-2) { // converge break; } } success[i] = succ; // 如果这个点成功就设置成功,反之 // 设置 kp2 kp2[i].pt = kp.pt + Point2f(dx, dy); } } // 多层光流 void OpticalFlowMultiLevel( const Mat &img1, const Mat &img2, const vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, vector<bool> &success, bool inverse) { // parameters int pyramids = 4; double pyramid_scale = 0.5; double scales[] = {1.0, 0.5, 0.25, 0.125}; // 构建金字塔 chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); vector<Mat> pyr1, pyr2; // image pyramids for (int i = 0; i < pyramids; i++) { if (i == 0) { // 第一个放入最底层 pyr1.push_back(img1); pyr2.push_back(img2); } else { // 其余要进行缩放 Mat img1_pyr, img2_pyr; cv::resize(pyr1[i - 1], img1_pyr, cv::Size(pyr1[i - 1].cols * pyramid_scale, pyr1[i - 1].rows * pyramid_scale)); cv::resize(pyr2[i - 1], img2_pyr, cv::Size(pyr2[i - 1].cols * pyramid_scale, pyr2[i - 1].rows * pyramid_scale)); pyr1.push_back(img1_pyr); pyr2.push_back(img2_pyr); } } chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); auto time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1); cout << "build pyramid time: " << time_used.count() << endl; // 特征点缩放 vector<KeyPoint> kp1_pyr, kp2_pyr; for (auto &kp:kp1) { auto kp_top = kp; kp_top.pt *= scales[pyramids - 1]; kp1_pyr.push_back(kp_top); kp2_pyr.push_back(kp_top); } // 对每层调用单层直接法 for (int level = pyramids - 1; level >= 0; level--) { // from coarse to fine success.clear(); t1 = chrono::steady_clock::now(); OpticalFlowSingleLevel(pyr1[level], pyr2[level], kp1_pyr, kp2_pyr, success, inverse, true); t2 = chrono::steady_clock::now(); auto time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1); cout << "track pyr " << level << " cost time: " << time_used.count() << endl; if (level > 0) { for (auto &kp: kp1_pyr) kp.pt /= pyramid_scale; for (auto &kp: kp2_pyr) kp.pt /= pyramid_scale; } } for (auto &kp: kp2_pyr) kp2.push_back(kp); }
build pyramid time: 0.001775
track pyr 3 cost time: 0.000317644
track pyr 2 cost time: 0.00025403
track pyr 1 cost time: 0.000238879
track pyr 0 cost time: 0.000227848
optical flow by gauss-newton: 0.00309121
optical flow by opencv: 0.00202506
求解直接法最后等价于求解一个优化问题,计算出雅克比矩阵,然后通过使用g2o或者Ceres这些优化库,也可以手写高斯牛顿法或者列文伯格-马夸尔特法,来计算增量,迭代求解。
这里我们使用手写高斯牛顿法,演示稀疏的直接法,分成两种方式:
1)单层直接法
2)多层直接法:图像金字塔,每层调用单层直接法
#include <opencv2/opencv.hpp> #include <sophus/se3.hpp> #include <boost/format.hpp> #include <pangolin/pangolin.h> #include <opencv2/imgproc/types_c.h> using namespace std; typedef vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> VecVector2d; // 相机内参 double fx = 718.856, fy = 718.856, cx = 607.1928, cy = 185.2157; // baseline double baseline = 0.573; // paths string left_file = "/home/robot/桌面/slambook2-master/ch8/left.png"; string disparity_file = "/home/robot/桌面/slambook2-master/ch8/disparity.png"; boost::format fmt_others("/home/robot/桌面/slambook2-master/ch8/%06d.png"); // other files // useful typedefs typedef Eigen::Matrix<double, 6, 6> Matrix6d; typedef Eigen::Matrix<double, 2, 6> Matrix26d; typedef Eigen::Matrix<double, 6, 1> Vector6d; /// class for accumulator jacobians in parallel class JacobianAccumulator { public: JacobianAccumulator( const cv::Mat &img1_, const cv::Mat &img2_, const VecVector2d &px_ref_, const vector<double> depth_ref_, Sophus::SE3d &T21_) : img1(img1_), img2(img2_), px_ref(px_ref_), depth_ref(depth_ref_), T21(T21_) { projection = VecVector2d(px_ref.size(), Eigen::Vector2d(0, 0)); } /// accumulate jacobians in a range void accumulate_jacobian(const cv::Range &range); /// get hessian matrix Matrix6d hessian() const { return H; } /// get bias Vector6d bias() const { return b; } /// get total cost double cost_func() const { return cost; } /// get projected points VecVector2d projected_points() const { return projection; } /// reset h, b, cost to zero void reset() { H = Matrix6d::Zero(); b = Vector6d::Zero(); cost = 0; } private: const cv::Mat &img1; const cv::Mat &img2; const VecVector2d &px_ref; const vector<double> depth_ref; Sophus::SE3d &T21; VecVector2d projection; // projected points std::mutex hessian_mutex; Matrix6d H = Matrix6d::Zero(); Vector6d b = Vector6d::Zero(); double cost = 0; }; /** * 使用多层直接法进行位姿态估计 * @param img1 * @param img2 * @param px_ref * @param depth_ref * @param T21 */ void DirectPoseEstimationMultiLayer( const cv::Mat &img1, const cv::Mat &img2, const VecVector2d &px_ref, const vector<double> depth_ref, Sophus::SE3d &T21 ); /** * 使用单层直接法进行位姿态估计 * @param img1 * @param img2 * @param px_ref * @param depth_ref * @param T21 */ void DirectPoseEstimationSingleLayer( const cv::Mat &img1, const cv::Mat &img2, const VecVector2d &px_ref, const vector<double> depth_ref, Sophus::SE3d &T21 ); // 双线性插值bilinear interpolation inline float GetPixelValue(const cv::Mat &img, float x, float y) { // 边界检查boundary check if (x < 0) x = 0; if (y < 0) y = 0; if (x >= img.cols) x = img.cols - 1; if (y >= img.rows) y = img.rows - 1; uchar *data = &img.data[int(y) * img.step + int(x)]; float xx = x - floor(x); float yy = y - floor(y); return float( (1 - xx) * (1 - yy) * data[0] + xx * (1 - yy) * data[1] + (1 - xx) * yy * data[img.step] + xx * yy * data[img.step + 1] ); } int main(int argc, char **argv) { // 1.图像读取 cv::Mat left_img = cv::imread(left_file, 0); cv::Mat disparity_img = cv::imread(disparity_file, 0);//rgb-d相机采集的数据 // 2. 让我们随机选取第一张图像中的像素,并在第一张图像的帧中生成一些3d点 cv::RNG rng; int nPoints = 2000; int boarder = 20; VecVector2d pixels_ref; vector<double> depth_ref; // 3. 在ref中生成像素并加载深度数据 for (int i = 0; i < nPoints; i++) { int x = rng.uniform(boarder, left_img.cols - boarder); // 不要拾取靠近边界的像素 int y = rng.uniform(boarder, left_img.rows - boarder); // 不要拾取靠近边界的像素 int disparity = disparity_img.at<uchar>(y, x); //从rgb-d相机采集的图像中提取某个像素点距离 // 根据这个式子计算深度,本人不太懂原理 double depth = fx * baseline / disparity; // 深度=焦距*基线/距离 depth_ref.push_back(depth); // 深度 pixels_ref.push_back(Eigen::Vector2d(x, y));// 像素 } // 4. 估计01-05.png的位姿 Sophus::SE3d T_cur_ref; for (int i = 1; i < 6; i++) { // 1~10 cv::Mat img = cv::imread((fmt_others % i).str(), 0); // 1)单层直接法 DirectPoseEstimationSingleLayer(left_img, img, pixels_ref, depth_ref, T_cur_ref); // 2)多层直接法 // DirectPoseEstimationMultiLayer(left_img, img, pixels_ref, depth_ref, T_cur_ref); } return 0; } void DirectPoseEstimationSingleLayer( const cv::Mat &img1, const cv::Mat &img2, const VecVector2d &px_ref, const vector<double> depth_ref, Sophus::SE3d &T21) { const int iterations = 10; double cost = 0, lastCost = 0; auto t1 = chrono::steady_clock::now(); JacobianAccumulator jaco_accu(img1, img2, px_ref, depth_ref, T21); // 1. 计算出H b 更新量,并更新到估计值上 for (int iter = 0; iter < iterations; iter++) { // 1) 构建Hdx=b jaco_accu.reset(); cv::parallel_for_(cv::Range(0, px_ref.size()), std::bind(&JacobianAccumulator::accumulate_jacobian, &jaco_accu, std::placeholders::_1)); // 上式中的accumulate_jacobian用来计算雅可比 Matrix6d H = jaco_accu.hessian(); Vector6d b = jaco_accu.bias(); // 2) 解决更新并进行估计 Vector6d update = H.ldlt().solve(b);; //求出dx T21 = Sophus::SE3d::exp(update) * T21; // 左乘更新 cost = jaco_accu.cost_func(); if (std::isnan(update[0])) { // 当有黑色或白色斑块并且H是不可逆的时候,有时发生 cout << "update is nan" << endl; break; } if (iter > 0 && cost > lastCost) { cout << "cost increased: " << cost << ", " << lastCost << endl; break; } if (update.norm() < 1e-3) { // converge break; } lastCost = cost; cout << "iteration: " << iter << ", cost: " << cost << endl; } // 2. 输出位姿和时间 cout << "T21 = \n" << T21.matrix() << endl; auto t2 = chrono::steady_clock::now(); auto time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1); cout << "单层直接法所用时间: " << time_used.count() << endl; // 3. 在此处绘制投影像素 cv::Mat img2_show; cv::cvtColor(img2, img2_show, CV_GRAY2BGR); VecVector2d projection = jaco_accu.projected_points(); for (size_t i = 0; i < px_ref.size(); ++i) { auto p_ref = px_ref[i]; auto p_cur = projection[i]; if (p_cur[0] > 0 && p_cur[1] > 0) { cv::circle(img2_show, cv::Point2f(p_cur[0], p_cur[1]), 2, cv::Scalar(0, 250, 0), 2); cv::line(img2_show, cv::Point2f(p_ref[0], p_ref[1]), cv::Point2f(p_cur[0], p_cur[1]), cv::Scalar(0, 250, 0)); } } cv::imshow("current", img2_show); cv::waitKey(); } void JacobianAccumulator::accumulate_jacobian(const cv::Range &range) { // 这里的误差和雅可比计算公式按照P220 // parameters const int half_patch_size = 1; int cnt_good = 0; Matrix6d hessian = Matrix6d::Zero(); Vector6d bias = Vector6d::Zero(); double cost_tmp = 0; for (size_t i = range.start; i < range.end; i++) { // 1. 计算第二幅图像中的投影P219的p1和p2 // point_ref表示第一张图片像素坐标P1 Eigen::Vector3d point_ref = depth_ref[i] * Eigen::Vector3d((px_ref[i][0] - cx) / fx, (px_ref[i][1] - cy) / fy, 1); // point_cur表示第二张图片的投影的下像素坐标P2 Eigen::Vector3d point_cur = T21 * point_ref; if (point_cur[2] < 0) // 深度<0无效 continue; // 2. 求出雅可比矩阵一面的一些参数u v float u = fx * point_cur[0] / point_cur[2] + cx, v = fy * point_cur[1] / point_cur[2] + cy; if (u < half_patch_size || u > img2.cols - half_patch_size || v < half_patch_size || v > img2.rows - half_patch_size) continue; // 求出雅可比矩阵一面的一些参数 X Y Z Z^2 1/Z 1/Z^2 projection[i] = Eigen::Vector2d(u, v); double X = point_cur[0], Y = point_cur[1], Z = point_cur[2], Z2 = Z * Z, Z_inv = 1.0 / Z, Z2_inv = Z_inv * Z_inv; cnt_good++; // 3. 计算误差和雅可比 for (int x = -half_patch_size; x <= half_patch_size; x++) for (int y = -half_patch_size; y <= half_patch_size; y++) { // 1)e=I1(p1)-I2(P2) P219 公式8.11 double error = GetPixelValue(img1, px_ref[i][0] + x, px_ref[i][1] + y) - GetPixelValue(img2, u + x, v + y); // 2)求出J Matrix26d J_pixel_xi; Eigen::Vector2d J_img_pixel; J_pixel_xi(0, 0) = fx * Z_inv; J_pixel_xi(0, 1) = 0; J_pixel_xi(0, 2) = -fx * X * Z2_inv; J_pixel_xi(0, 3) = -fx * X * Y * Z2_inv; J_pixel_xi(0, 4) = fx + fx * X * X * Z2_inv; J_pixel_xi(0, 5) = -fx * Y * Z_inv; J_pixel_xi(1, 0) = 0; J_pixel_xi(1, 1) = fy * Z_inv; J_pixel_xi(1, 2) = -fy * Y * Z2_inv; J_pixel_xi(1, 3) = -fy - fy * Y * Y * Z2_inv; J_pixel_xi(1, 4) = fy * X * Y * Z2_inv; J_pixel_xi(1, 5) = fy * X * Z_inv; J_img_pixel = Eigen::Vector2d( 0.5 * (GetPixelValue(img2, u + 1 + x, v + y) - GetPixelValue(img2, u - 1 + x, v + y)), 0.5 * (GetPixelValue(img2, u + x, v + 1 + y) - GetPixelValue(img2, u + x, v - 1 + y)) ); // total jacobian Vector6d J = -1.0 * (J_img_pixel.transpose() * J_pixel_xi).transpose(); // 3)利用高斯牛顿求出H * dx = b中的H b hessian += J * J.transpose(); bias += -error * J; cost_tmp += error * error; } } if (cnt_good) { // set hessian, bias and cost unique_lock<mutex> lck(hessian_mutex); H += hessian; b += bias; cost += cost_tmp / cnt_good; } } void DirectPoseEstimationMultiLayer( const cv::Mat &img1, const cv::Mat &img2, const VecVector2d &px_ref, const vector<double> depth_ref, Sophus::SE3d &T21) { // 1. 定义参数 int pyramids = 4; // 金字塔层数 double pyramid_scale = 0.5; //每层图片缩放倍率 double scales[] = {1.0, 0.5, 0.25, 0.125}; //每层内参缩放倍率 // 2. 构建金字塔 vector<cv::Mat> pyr1, pyr2; // image pyramids for (int i = 0; i < pyramids; i++) { if (i == 0) { //第一张直接放到金字塔底部 pyr1.push_back(img1); pyr2.push_back(img2); } else { //其余每层在上层的基础上缩放 cv::Mat img1_pyr, img2_pyr; cv::resize(pyr1[i - 1], img1_pyr, cv::Size(pyr1[i - 1].cols * pyramid_scale, pyr1[i - 1].rows * pyramid_scale)); cv::resize(pyr2[i - 1], img2_pyr, cv::Size(pyr2[i - 1].cols * pyramid_scale, pyr2[i - 1].rows * pyramid_scale)); pyr1.push_back(img1_pyr); pyr2.push_back(img2_pyr); } } // 3. 开始执行 double fxG = fx, fyG = fy, cxG = cx, cyG = cy; // 备份初始内参 for (int level = pyramids - 1; level >= 0; level--) { // 每层的关键点也要缩放 VecVector2d px_ref_pyr; for (auto &px: px_ref) { px_ref_pyr.push_back(scales[level] * px); } // 不同金字塔级别中的内参的缩放 fx = fxG * scales[level]; fy = fyG * scales[level]; cx = cxG * scales[level]; cy = cyG * scales[level]; // 每层用单层直接法计算 DirectPoseEstimationSingleLayer(pyr1[level], pyr2[level], px_ref_pyr, depth_ref, T21); } }
iteration: 0, cost: 4.0882e+06 iteration: 1, cost: 3.89309e+06 iteration: 2, cost: 3.80651e+06 iteration: 3, cost: 3.73133e+06 iteration: 4, cost: 3.66689e+06 iteration: 5, cost: 3.62699e+06 iteration: 6, cost: 3.60182e+06 cost increased: 3.60262e+06, 3.60182e+06 T21 = 0.999999 -0.000516409 0.000938655 -0.0107857 0.000513252 0.999994 0.00336124 -0.0199524 -0.000940385 -0.00336075 0.999994 -0.115379 0 0 0 1 单层直接法所用时间: 0.00370231 iteration: 0, cost: 4.51727e+06 iteration: 1, cost: 4.44097e+06 iteration: 2, cost: 4.40338e+06 iteration: 3, cost: 4.38437e+06 iteration: 4, cost: 4.36851e+06 iteration: 5, cost: 4.3375e+06 iteration: 6, cost: 4.31656e+06 iteration: 7, cost: 4.28746e+06 iteration: 8, cost: 4.28125e+06 cost increased: 4.29874e+06, 4.28125e+06 T21 = 1 -0.00023899 -0.000423502 0.0318101 0.000240825 0.999991 0.0043384 -0.0140133 0.000422461 -0.0043385 0.99999 -0.0616333 0 0 0 1 单层直接法所用时间: 0.00604826 iteration: 0, cost: 6.27375e+06 iteration: 1, cost: 6.21322e+06 iteration: 2, cost: 6.09078e+06 iteration: 3, cost: 5.94502e+06 iteration: 4, cost: 5.8199e+06 iteration: 5, cost: 5.7109e+06 iteration: 6, cost: 5.66204e+06 iteration: 7, cost: 5.63571e+06 iteration: 8, cost: 5.56997e+06 iteration: 9, cost: 5.12689e+06 T21 = 0.999966 -0.00781871 -0.00278991 0.0766677 0.00783866 0.999943 0.00721305 -0.0372605 0.00273336 -0.00723467 0.99997 -0.128187 0 0 0 1 单层直接法所用时间: 0.00568748 iteration: 0, cost: 6.13602e+06 iteration: 1, cost: 6.11936e+06 iteration: 2, cost: 6.09768e+06 iteration: 3, cost: 6.00533e+06 iteration: 4, cost: 6.00515e+06 iteration: 5, cost: 5.99342e+06 iteration: 6, cost: 5.9524e+06 iteration: 7, cost: 5.90819e+06 iteration: 8, cost: 5.89092e+06 iteration: 9, cost: 5.87545e+06 T21 = 0.999941 -0.0108718 0.000193771 0.0288788 0.0108699 0.999909 0.00799969 -0.0580119 -0.000280724 -0.00799711 0.999968 -0.256918 0 0 0 1 单层直接法所用时间: 0.00389448 iteration: 0, cost: 7.42899e+06 iteration: 1, cost: 7.412e+06 cost increased: 7.41608e+06, 7.412e+06 T21 = 0.999924 -0.0123424 -0.000713318 0.0351923 0.0123486 0.999878 0.00951657 -0.0727886 0.000595773 -0.00952466 0.999954 -0.231601 0 0 0 1 单层直接法所用时间: 0.00221729
2. 多层直接法:由于篇幅原因,这里只展示对第一张图片的金字塔多层优化,这里是4层金字塔。
iteration: 0, cost: 1.59493e+06 iteration: 1, cost: 652954 iteration: 2, cost: 263926 iteration: 3, cost: 166670 cost increased: 172304, 166670 T21 = 0.999991 0.00226062 0.00346118 -0.00358483 -0.00226947 0.999994 0.00255482 0.00317976 -0.00345538 -0.00256266 0.999991 -0.720607 0 0 0 1 单层直接法所用时间: 0.00162608 iteration: 0, cost: 178898 cost increased: 180789, 178898 T21 = 0.999989 0.00303756 0.00346181 0.00140036 -0.00304538 0.999993 0.00225747 0.00627627 -0.00345493 -0.00226798 0.999991 -0.72901 0 0 0 1 单层直接法所用时间: 0.00266667 iteration: 0, cost: 248601 iteration: 1, cost: 229994 T21 = 0.999991 0.00251351 0.00346626 -0.00271352 -0.00252162 0.999994 0.00233523 0.00243136 -0.00346037 -0.00234395 0.999991 -0.73472 0 0 0 1 单层直接法所用时间: 0.00587702 iteration: 0, cost: 348714 T21 = 0.999991 0.00248082 0.00343393 -0.00374045 -0.00248837 0.999994 0.00219446 0.0030455 -0.00342847 -0.00220298 0.999992 -0.732343 0 0 0 1 单层直接法所用时间: 0.00270028 iteration: 0, cost: 1.31076e+06 iteration: 1, cost: 918151 iteration: 2, cost: 604174 iteration: 3, cost: 384708 iteration: 4, cost: 269610 iteration: 5, cost: 235605 cost increased: 239882, 235605 T21 = 0.99997 0.0010374 0.00769571 0.00572471 -0.00107293 0.999989 0.00461427 0.000557331 -0.00769083 -0.00462238 0.99996 -1.46166 0 0 0 1 单层直接法所用时间: 0.00987296
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。