在進行計算機視覺項目時,我們經常需要處理相機位姿的變換。最近,我在項目中遇到了一個看似簡單但實際上頗具挑戰性的問題:從 OpenCV 的 cv::Mat 格式轉換到 Eigen 庫的格式。這個過程中遇到了一些問題,但最終找到了一個穩健的解決方案。
問題描述: 我們有兩個表示相機位姿的 4x4 變換矩陣,格式為 cv::Mat。目標是計算這兩個位姿之間的變換,并提取出平移向量。
初始嘗試: 最初,我們嘗試直接從 cv::Mat 中提取平移向量:
Eigen::Vector3d transLast(mLastFrameTcw.at<double>(0, 3),mLastFrameTcw.at<double>(1, 3),mLastFrameTcw.at<double>(2, 3)
);
Eigen::Vector3d transCurrent(mCurrentFrameTcw.at<double>(0, 3),mCurrentFrameTcw.at<double>(1, 3),mCurrentFrameTcw.at<double>(2, 3)
);
TRANS_PRED = transCurrent - transLast;
遇到的問題: 針對簡單的工程,代碼運行完全沒有問題,但是放到復雜工程里面,代碼就會輸出莫名其妙的結果!!!!!這種方法可能會導致錯誤,原因如下:
- 直接訪問 cv::Mat 的元素可能不安全,特別是當矩陣的存儲格式不確定時。
- 這種方法忽略了旋轉部分的影響,可能導致計算結果不準確。
- 在某些情況下,可能會出現索引錯誤或類型不匹配的問題。
改進的解決方案: 經過多次嘗試和改進,我們最終采用了以下方法:
// 直接相減 cv::Mat
cv::Mat diff = mCurrentFrameTcw - mLastFrameTcw;// 將 cv::Mat 轉換為 Eigen 矩陣
Eigen::MatrixXd eigenDiff;
cv::cv2eigen(diff, eigenDiff);// 從 eigenDiff 的最后一列提取前三個元素賦值給 TRANS_PRED
TRANS_PRED = eigenDiff.block<3,1>(0, eigenDiff.cols()-1);
這個解決方案的優點:
- 使用 cv::Mat 的矩陣減法,保持了原始數據的完整性。
- 利用 OpenCV 提供的 cv2eigen 函數,安全地將 cv::Mat 轉換為 Eigen 矩陣。
- 使用 Eigen 的 block 操作,精確地提取所需的平移向量。
結論: 在處理計算機視覺中的坐標變換問題時,正確地在不同庫(如 OpenCV 和 Eigen)之間轉換數據格式是至關重要的。通過采用矩陣減法和適當的類型轉換,我們可以準確地計算相機位姿之間的變換。這個經驗教訓提醒我們,在處理不同庫之間的數據轉換時,要特別注意數據類型的一致性和操作的正確性。在future類似的問題中,我們可以借鑒這種方法,確保在不同數學庫之間進行安全和準確的數據轉換。
下面給出完整的代碼(注意:下面的兩個版本都可以用,只是區分兩個中哪個更魯棒!!!)
#include <iostream>
#include <iomanip>
#include <opencv2/core.hpp>
#include <Eigen/Dense>
#include <opencv2/core/eigen.hpp>void CalculateTransPred_Method1(const cv::Mat& mLastFrameTcw, const cv::Mat& mCurrentFrameTcw, Eigen::Vector3d& TRANS_PRED) {std::cout << "Method 1: Direct extraction from cv::Mat" << std::endl;Eigen::Vector3d transLast(mLastFrameTcw.at<double>(0, 3),mLastFrameTcw.at<double>(1, 3),mLastFrameTcw.at<double>(2, 3));Eigen::Vector3d transCurrent(mCurrentFrameTcw.at<double>(0, 3),mCurrentFrameTcw.at<double>(1, 3),mCurrentFrameTcw.at<double>(2, 3));std::cout << "transLast: " << transLast.transpose() << std::endl;std::cout << "transCurrent: " << transCurrent.transpose() << std::endl;TRANS_PRED = transCurrent - transLast;std::cout << "TRANS_PRED: " << TRANS_PRED.transpose() << std::endl;
}void CalculateTransPred_Method2(const cv::Mat& mLastFrameTcw, const cv::Mat& mCurrentFrameTcw, Eigen::Vector3d& TRANS_PRED) {std::cout << "\nMethod 2: Using cv::Mat subtraction and Eigen conversion" << std::endl;cv::Mat diff = mCurrentFrameTcw - mLastFrameTcw;Eigen::MatrixXd eigenDiff;cv::cv2eigen(diff, eigenDiff);std::cout << "Difference (diff) in Eigen::MatrixXd format:" << std::endl;std::cout << eigenDiff << std::endl;TRANS_PRED = eigenDiff.block<3,1>(0, eigenDiff.cols()-1);std::cout << "TRANS_PRED: " << TRANS_PRED.transpose() << std::endl;
}int main() {cv::Mat mLastFrameTcw = (cv::Mat_<double>(4, 4) -0.1642483, 0.094168551, -0.98191381, 0.90703607,0.0095526827, 0.99553794, 0.093877248, -0.038507219,0.98637277, 0.0060392693, -0.164415, -3.4207926,0, 0, 0, 1);cv::Mat mCurrentFrameTcw = (cv::Mat_<double>(4, 4) -0.16892175, 0.093616515, -0.98117346, 0.92409742,0.009967736, 0.99559039, 0.093275994, -0.039425559,0.98557907, 0.0059762667, -0.16911002, -3.4853551,0, 0, 0, 1);Eigen::Vector3d TRANS_PRED;std::cout << std::fixed << std::setprecision(6);std::cout << "mLastFrame.mTcw:" << std::endl;std::cout << mLastFrameTcw << std::endl;std::cout << "\nmCurrentFrame.mTcw:" << std::endl;std::cout << mCurrentFrameTcw << std::endl;CalculateTransPred_Method1(mLastFrameTcw, mCurrentFrameTcw, TRANS_PRED);CalculateTransPred_Method2(mLastFrameTcw, mCurrentFrameTcw, TRANS_PRED);return 0;
}