#include <opencv2/opencv.hpp>
#include <iostream>
#include <fstream>
#include <vector>
// 生成PLY点云文件(二进制格式,兼容CloudCompare)
void savePointCloudToPLY(const cv::Mat & points, const cv::Mat & colors,
const std::string & filename, double min_depth = 0.1, double max_depth = 10000.0) {
std::ofstream plyFile(filename, std::ios::binary);
if (!plyFile.is_open()) {
std::cerr << "Error: Could not open PLY file for writing." << std::endl;
return;
}
// 统计有效点数量
int validCount = 0;
for (int y = 0; y < points.rows; y++) {
for (int x = 0; x < points.cols; x++) {
cv::Vec3f point = points.at<cv::Vec3f>(y, x);
// 检查深度值是否在有效范围内且不是无穷大
if (point[2] > min_depth && point[2] < max_depth &&
!std::isinf(point[0]) && !std::isinf(point[1]) && !std::isinf(point[2])) {
validCount++;
}
}
}
// 写入PLY头(二进制格式)
plyFile << "ply\n";
plyFile << "format binary_little_endian 1.0\n";
plyFile << "element vertex " << validCount << "\n";
plyFile << "property float x\n";
plyFile << "property float y\n";
plyFile << "property float z\n";
plyFile << "property uchar red\n";
plyFile << "property uchar green\n";
plyFile << "property uchar blue\n";
plyFile << "end_header\n";
// 写入点云数据(二进制)
for (int y = 0; y < points.rows; y++) {
for (int x = 0; x < points.cols; x++) {
cv::Vec3f point = points.at<cv::Vec3f>(y, x);
cv::Vec3b color = colors.at<cv::Vec3b>(y, x);
// 过滤无效点
if (point[2] > min_depth && point[2] < max_depth &&
!std::isinf(point[0]) && !std::isinf(point[1]) && !std::isinf(point[2])) {
// 写入坐标(小端序浮点数)
plyFile.write(reinterpret_cast<const char*>(&point[0]), sizeof(float));
plyFile.write(reinterpret_cast<const char*>(&point[1]), sizeof(float));
plyFile.write(reinterpret_cast<const char*>(&point[2]), sizeof(float));
// 写入颜色(注意OpenCV使用BGR顺序)
plyFile.write(reinterpret_cast<const char*>(&color[2]), sizeof(uint8_t)); // R
plyFile.write(reinterpret_cast<const char*>(&color[1]), sizeof(uint8_t)); // G
plyFile.write(reinterpret_cast<const char*>(&color[0]), sizeof(uint8_t)); // B
}
}
}
plyFile.close();
std::cout << "Saved " << validCount << " points to " << filename << std::endl;
}
int main() {
// 1. 定义投影矩阵(使用您提供的数据)
cv::Mat P1 = (cv::Mat_<double>(3, 4) <<
2791.1531638651541, -3164.2425130586262, -1932.4112841478736, 9203066124.8177795410156,
-3171.4539351046769, -2823.8486979863924, -2106.1412396703704, 11236470204.1201114654541,
-0.0094942232932, -0.0067457239026, -0.9999321751664, 28815.1627210603910);
cv::Mat P2 = (cv::Mat_<double>(3, 4) <<
2791.1885344521997, -3164.2173819650006, -1928.6860517671789, 9202236641.5202198028564,
-3171.4539351046769, -2823.8486979863924, -2106.1412396703704, 11236470204.1201095581055,
-0.0094942232932, -0.0067457239026, -0.9999321751664, 28815.1627210603947);
// 2. 读取视差图和左影像(用于点云着色)
cv::Mat disparity = cv::imread("C:\\Users\\lzr15\\Desktop\\data1\\data1\\DispImg\\017ER030_017ER031_DISP_ET0.tif", cv::IMREAD_UNCHANGED); // 16位视差图
cv::Mat leftImage = cv::imread("C:\\Users\\lzr15\\Desktop\\data1\\data1\\EpiImg\\017ER030_017ER031_ET0\\017ER030_REC.tif"); // 左核线影像
if (disparity.empty() || leftImage.empty()) {
std::cerr << "Error: Could not read input images." << std::endl;
return -1;
}
// 3. 验证视差图数据
std::cout << "Disparity image size: " << disparity.size() << std::endl;
std::cout << "Disparity image type: " << disparity.type() << " (CV_16UC1=" << CV_16UC1 << ")" << std::endl;
// 检查视差图是否为16位格式
if (disparity.type() != CV_16UC1 && disparity.type() != CV_16SC1) {
std::cerr << "Warning: Disparity image is not 16-bit format. Results may be incorrect." << std::endl;
}
// 4. 计算重投影矩阵Q
cv::Mat K1, R1, t1, K2, R2, t2;
cv::decomposeProjectionMatrix(P1, K1, R1, t1);
cv::decomposeProjectionMatrix(P2, K2, R2, t2);
// 转换为非齐次坐标
t1 = t1.rowRange(0, 3) / t1.at<double>(3);
t2 = t2.rowRange(0, 3) / t2.at<double>(3);
// 计算基线长度(单位:世界坐标单位)
cv::Mat C1 = -R1.inv() * t1;
cv::Mat C2 = -R2.inv() * t2;
double Tx = cv::norm(C2 - C1);
// 提取内参参数
double fx = K1.at<double>(0, 0);
double fy = K1.at<double>(1, 1);
double cx = K1.at<double>(0, 2);
double cy = K1.at<double>(1, 2);
std::cout << "\nCamera Parameters:" << std::endl;
std::cout << " Focal length (fx, fy): " << fx << ", " << fy << std::endl;
std::cout << " Principal point (cx, cy): " << cx << ", " << cy << std::endl;
std::cout << " Baseline (Tx): " << Tx << " world units" << std::endl;
// 修改Q矩阵构造
double cx_right = K2.at<double>(0, 2); // 右相机主点x
cv::Mat Q = (cv::Mat_<double>(4, 4) <<
1.0, 0.0, 0.0, -cx,
0.0, 1.0, 0.0, -cy,
0.0, 0.0, 0.0, fx,
0.0, 0.0, -1.0 / Tx, (cx - cx_right) / Tx); // 关键修改
std::cout << "\nReprojection Matrix Q:" << std::endl;
std::cout << Q << std::endl;
// 6. 处理视差图 - 关键步骤!
cv::Mat disp_float;
// 修改视差图处理部分
double scale_factor = 1.0;
if (disparity.type() == CV_16UC1) {
scale_factor = 1.0 / 16.0;
}
else if (disparity.type() == CV_16SC1) {
scale_factor = 1.0 / 16.0;
}
else if (disparity.type() == CV_32FC1) { // 显式匹配32FC1
scale_factor = 1.0;
}
else if (disparity.type() == CV_32F) { // 后备匹配通用浮点
scale_factor = 1.0;
}
else {
std::cerr << "Warning: Unknown disparity format. Using default scaling." << std::endl;
scale_factor = 1.0 / 16.0;
}
disparity.convertTo(disp_float, CV_32F, scale_factor);
// 7. 计算3D点云
cv::Mat xyz;
cv::reprojectImageTo3D(disp_float, xyz, Q, false, CV_32F);
// 8. 提取深度图(Z通道)
std::vector<cv::Mat> channels;
cv::split(xyz, channels);
cv::Mat depth = channels[2];
// 修改深度图可视化部分
cv::Mat depthVis;
double minVal, maxVal;
// 创建掩码:有效点 = (深度>0) 且 (非无穷大)
cv::Mat validMask = (depth > 0) & (depth < std::numeric_limits<float>::infinity());
cv::Mat validMask8U;
validMask.convertTo(validMask8U, CV_8U);
// 仅对有效点计算深度范围
cv::minMaxLoc(depth, &minVal, &maxVal, nullptr, nullptr, validMask8U);
std::cout << "Valid Depth Range: min = " << minVal << ", max = " << maxVal << std::endl;
if (maxVal > minVal) {
// 归一化有效点到[0, 255]
cv::Mat temp = cv::Mat::zeros(depth.size(), CV_32F);
depth.copyTo(temp, validMask8U); // 仅复制有效点
temp.convertTo(depthVis, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
}
else {
depthVis = cv::Mat::zeros(depth.size(), CV_8U);
}
cv::imwrite("depth_map.png", depthVis);
// 10. 生成彩色点云(使用二进制PLY格式)
savePointCloudToPLY(xyz, leftImage, "point_cloud.ply");
std::cout << "\nProcessing completed." << std::endl;
std::cout << "Depth map saved as 'depth_map.png'" << std::endl;
std::cout << "Point cloud saved as 'point_cloud.ply'" << std::endl;
cv::waitKey(0);
return 0;
}这是一段生成深度图和三位点云的代码,但是生成的三维点云质量很差,你帮我重新写一段C++代码,运行出高精度的三位点云
最新发布