├── rgb-image.ppm ├── disparity-image.pgm ├── Q.xml ├── README.md └── main.cpp /rgb-image.ppm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkornel/Reproject-Image-To-3D/HEAD/rgb-image.ppm -------------------------------------------------------------------------------- /disparity-image.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bkornel/Reproject-Image-To-3D/HEAD/disparity-image.pgm -------------------------------------------------------------------------------- /Q.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4 5 | 4 6 |
d
7 | 8 | 1. 0. 0. -2.9615028381347656e+02 0. 1. 0. -2.3373317337036133e+02 0. 9 | 0. 0. 5.6446880931501073e+02 0. 0. -1.1340974198400260e-01 10 | 4.1658568844268817e+00
11 |
12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reproject-Image-To-3D 2 | Comparing a OpenCV's [`reprojectImageTo3D()`][1] to my own 3 | 4 | // Reproject image to 3D 5 | void customReproject(const cv::Mat& disparity, const cv::Mat& Q, cv::Mat& out3D) 6 | { 7 | CV_Assert(disparity.type() == CV_32F && !disparity.empty()); 8 | CV_Assert(Q.type() == CV_32F && Q.cols == 4 && Q.rows == 4); 9 | 10 | // 3-channel matrix for containing the reprojected 3D world coordinates 11 | out3D = cv::Mat::zeros(disparity.size(), CV_32FC3); 12 | 13 | // Getting the interesting parameters from Q, everything else is zero or one 14 | float Q03 = Q.at(0, 3); 15 | float Q13 = Q.at(1, 3); 16 | float Q23 = Q.at(2, 3); 17 | float Q32 = Q.at(3, 2); 18 | float Q33 = Q.at(3, 3); 19 | 20 | // Transforming a single-channel disparity map to a 3-channel image representing a 3D surface 21 | for (int i = 0; i < disparity.rows; i++) 22 | { 23 | const float* disp_ptr = disparity.ptr(i); 24 | cv::Vec3f* out3D_ptr = out3D.ptr(i); 25 | 26 | for (int j = 0; j < disparity.cols; j++) 27 | { 28 | const float pw = 1.0f / (disp_ptr[j] * Q32 + Q33); 29 | 30 | cv::Vec3f& point = out3D_ptr[j]; 31 | point[0] = (static_cast(j)+Q03) * pw; 32 | point[1] = (static_cast(i)+Q13) * pw; 33 | point[2] = Q23 * pw; 34 | } 35 | } 36 | } 37 | 38 | 39 | Disparity map and Q matrix is from [Martin Peris' Blog][2]. 40 | 41 | [1]: http://docs.opencv.org/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#reprojectimageto3d 42 | [2]: http://blog.martinperis.com/2012/01/3d-reconstruction-with-opencv-and-point.html 43 | 44 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void customReproject(const cv::Mat& disparity, const cv::Mat& Q, cv::Mat& out3D) 11 | { 12 | CV_Assert(disparity.type() == CV_32F && !disparity.empty()); 13 | CV_Assert(Q.type() == CV_32F && Q.cols == 4 && Q.rows == 4); 14 | 15 | out3D = cv::Mat::zeros(disparity.size(), CV_32FC3); 16 | 17 | float Q03 = Q.at(0, 3); 18 | float Q13 = Q.at(1, 3); 19 | float Q23 = Q.at(2, 3); 20 | float Q32 = Q.at(3, 2); 21 | float Q33 = Q.at(3, 3); 22 | 23 | for (int i = 0; i < disparity.rows; i++) 24 | { 25 | const float* disp_ptr = disparity.ptr(i); 26 | cv::Vec3f* out3D_ptr = out3D.ptr(i); 27 | 28 | for (int j = 0; j < disparity.cols; j++) 29 | { 30 | const float pw = 1.0f / (disp_ptr[j] * Q32 + Q33); 31 | 32 | cv::Vec3f& point = out3D_ptr[j]; 33 | point[0] = (static_cast(j)+Q03) * pw; 34 | point[1] = (static_cast(i)+Q13) * pw; 35 | point[2] = Q23 * pw; 36 | } 37 | } 38 | } 39 | 40 | void save(const cv::Mat& image3D, const std::string& fileName) 41 | { 42 | CV_Assert(image3D.type() == CV_32FC3 && !image3D.empty()); 43 | CV_Assert(!fileName.empty()); 44 | 45 | std::ofstream outFile(fileName); 46 | 47 | if (!outFile.is_open()) 48 | { 49 | std::cerr << "ERROR: Could not open " << fileName << std::endl; 50 | return; 51 | } 52 | 53 | for (int i = 0; i < image3D.rows; i++) 54 | { 55 | const cv::Vec3f* image3D_ptr = image3D.ptr(i); 56 | 57 | for (int j = 0; j < image3D.cols; j++) 58 | { 59 | outFile << image3D_ptr[j][0] << " " << image3D_ptr[j][1] << " " << image3D_ptr[j][2] << std::endl; 60 | } 61 | } 62 | 63 | outFile.close(); 64 | } 65 | 66 | int main(int argc, char* argv[]) 67 | { 68 | // Loading Q Matrix 69 | cv::FileStorage fs("Q.xml", cv::FileStorage::READ); 70 | 71 | if (!fs.isOpened()) 72 | { 73 | std::cerr << "ERROR: Could not read Q.xml" << std::endl; 74 | return 1; 75 | } 76 | 77 | cv::Mat Q; 78 | 79 | fs["Q"] >> Q; 80 | Q.convertTo(Q, CV_32F); 81 | fs.release(); 82 | 83 | // If size of Q is not 4x4 exit 84 | if (Q.cols != 4 || Q.rows != 4) 85 | { 86 | std::cerr << "ERROR: Q is not 4x4)" << std::endl; 87 | return 1; 88 | } 89 | 90 | // Loading disparity image 91 | cv::Mat disparity = cv::imread("disparity-image.pgm", cv::IMREAD_GRAYSCALE); 92 | if (disparity.empty()) 93 | { 94 | std::cerr << "ERROR: Could not read disparity-image.pgm" << std::endl; 95 | return 1; 96 | } 97 | 98 | // Conversion of the disparity map to 32F before reprojecting to 3D 99 | // NOTE: also take care to do not scale twice the disparity 100 | disparity.convertTo(disparity, CV_32F, 1.0 / 16.0); 101 | 102 | // Reproject image to 3D by OpenCV 103 | cv::Mat image3DOCV; 104 | cv::reprojectImageTo3D(disparity, image3DOCV, Q, false, CV_32F); 105 | 106 | // Reproject image to 3D by our own method 107 | cv::Mat image3D; 108 | customReproject(disparity, Q, image3D); 109 | 110 | save(image3D, "custom.xyz"); 111 | save(image3D, "opencv.xyz"); 112 | 113 | return 0; 114 | } 115 | --------------------------------------------------------------------------------