<think>我们注意到用户要求生成完整的代码,并且之前已经提供了代码,但存在显示不全的问题。根据之前的分析,我们需要修改显示部分,使用一个缩放函数来确保图像在窗口中完整显示。同时,我们也会添加一些异常处理和内存优化的建议,但用户主要需要的是显示完整的代码。因此,我们将修改代码,删除固定尺寸的窗口调整,添加一个缩放显示函数。
修改计划:
1. 删除所有`resizeWindow`调用。
2. 定义一个显示函数`displayImage`,根据屏幕最大尺寸自动缩放图像。
3. 在显示每个图像时调用这个函数。
注意:由于原代码中图像方向不同(有竖直的),我们在显示函数中统一处理,根据图像尺寸和设定的最大显示尺寸(例如1000像素)进行缩放。
另外,我们还要注意,原代码中使用了多个命名窗口,我们保留这些窗口的创建,但显示时使用缩放后的图像。
由于用户要求生成完整代码,我们将整合所有修改,提供完整的C++代码。
注意:由于我们无法得知用户屏幕分辨率,这里设定一个最大显示尺寸(如1000像素),如果图像大于这个尺寸,则按比例缩小。
完整代码修改如下:</think>以下是完整的代码实现,包含图像显示优化和异常处理:
```cpp
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <filesystem>
using namespace cv;
using namespace std;
namespace fs = std::filesystem;
// 图像显示函数(自动缩放适应屏幕)
void displayImage(const Mat& img, const string& winName, int maxSize = 1000) {
if (img.empty()) {
cerr << "错误: " << winName << " 图像为空!" << endl;
return;
}
double scale = (double)maxSize / max(img.rows, img.cols);
if (scale < 1.0) {
Mat imgDisp;
resize(img, imgDisp, Size(), scale, scale, INTER_AREA);
imshow(winName, imgDisp);
} else {
imshow(winName, img);
}
}
int main() {
try {
// 1. 定义左右投影矩阵
Mat P1 = (Mat_<double>(3, 4) <<
-12179.8753230552230, 7050.9952897638232, -2467.7527210258945, -17321440031.6463700000000,
4723.7392252925192, 5779.0243267188052, -11880.0042304789190, -22040783539.2805860000000,
-0.4907881086633, -0.5142398041434, -0.7033380810316, 2000536.1381758042000);
Mat P2 = (Mat_<double>(3, 4) <<
-12158.6697497674500, 7092.3578385200199, -2453.7545028069599, -17474815958.9305270000000,
4621.9021026258170, 5524.7193480639962, -12039.9681076173220, -21127026467.7189250000000,
-0.5027584877552, -0.5235467256044, -0.6878464429645, 2038373.8802346450000);
// 2. 读取影像
string imgPath1 = "D:/data1/data1/015AR027.tif";
string imgPath2 = "D:/data1/data1/015AR028.tif";
if (!fs::exists(imgPath1) || !fs::exists(imgPath2)) {
cerr << "错误: 图像文件不存在! 请检查路径: \n" << imgPath1 << "\n" << imgPath2 << endl;
return -1;
}
Mat imgLeft = imread(imgPath1, IMREAD_COLOR);
Mat imgRight = imread(imgPath2, IMREAD_COLOR);
if (imgLeft.empty() || imgRight.empty()) {
cerr << "错误: 无法读取影像文件! 请确保文件格式支持" << endl;
return -1;
}
Size imgSize = imgLeft.size();
cout << "原始图像尺寸: " << imgSize.width << "x" << imgSize.height << endl;
// 3. 分解投影矩阵获取相机参数
Mat K1, K2, R1, R2, t1, t2;
Mat C1(3, 1, CV_64F), C2(3, 1, CV_64F);
// 分解左投影矩阵
decomposeProjectionMatrix(P1, K1, R1, t1);
t1 = t1 / t1.at<double>(3);
C1 = -R1.inv() * (K1.inv() * P1.col(3));
// 分解右投影矩阵
decomposeProjectionMatrix(P2, K2, R2, t2);
t2 = t2 / t2.at<double>(3);
C2 = -R2.inv() * (K2.inv() * P2.col(3));
// 4. 计算相对位姿
Mat R_rel = R2 * R1.t();
Mat T_rel = R1 * (C2 - C1);
// 5. 创建梯形效果
vector<Point2f> srcPoints, dstPoints;
srcPoints.push_back(Point2f(0, 0));
srcPoints.push_back(Point2f(imgSize.width, 0));
srcPoints.push_back(Point2f(imgSize.width, imgSize.height));
srcPoints.push_back(Point2f(0, imgSize.height));
float perspectiveFactor = 0.2;
dstPoints.push_back(Point2f(imgSize.width * perspectiveFactor, 0));
dstPoints.push_back(Point2f(imgSize.width * (1 - perspectiveFactor), 0));
dstPoints.push_back(Point2f(imgSize.width * (1 - perspectiveFactor / 2), imgSize.height));
dstPoints.push_back(Point2f(imgSize.width * (perspectiveFactor / 2), imgSize.height));
Mat perspectiveMatrix = getPerspectiveTransform(srcPoints, dstPoints);
// 6. 核线校正
Mat D = Mat::zeros(1, 5, CV_64F);
Mat R1_rect, R2_rect, P1_rect, P2_rect, Q;
stereoRectify(K1, D, K2, D, imgSize, R_rel, T_rel,
R1_rect, R2_rect, P1_rect, P2_rect, Q,
CALIB_ZERO_DISPARITY, -1, imgSize);
// 7. 计算校正映射
Mat leftMap1, leftMap2, rightMap1, rightMap2;
initUndistortRectifyMap(K1, D, R1_rect, P1_rect, imgSize, CV_32FC1, leftMap1, leftMap2);
initUndistortRectifyMap(K2, D, R2_rect, P2_rect, imgSize, CV_32FC1, rightMap1, rightMap2);
// 8. 生成水平核线影像
Mat imgLeftRect, imgRightRect;
remap(imgLeft, imgLeftRect, leftMap1, leftMap2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
remap(imgRight, imgRightRect, rightMap1, rightMap2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
// 9. 应用梯形变换
Mat imgLeftTrapezoid, imgRightTrapezoid;
warpPerspective(imgLeftRect, imgLeftTrapezoid, perspectiveMatrix, imgSize, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
warpPerspective(imgRightRect, imgRightTrapezoid, perspectiveMatrix, imgSize, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
// 10. 生成竖直核线影像
Mat imgLeftVert, imgRightVert;
rotate(imgLeftTrapezoid, imgLeftVert, ROTATE_90_CLOCKWISE);
rotate(imgRightTrapezoid, imgRightVert, ROTATE_90_CLOCKWISE);
// 11. 生成接近原始方向的核线影像
stereoRectify(K1, D, K2, D, imgSize, R_rel, T_rel,
R1_rect, R2_rect, P1_rect, P2_rect, Q,
CALIB_ZERO_DISPARITY, 0.5, imgSize);
initUndistortRectifyMap(K1, D, R1_rect, P1_rect, imgSize, CV_32FC1, leftMap1, leftMap2);
initUndistortRectifyMap(K2, D, R2_rect, P2_rect, imgSize, CV_32FC1, rightMap1, rightMap2);
Mat imgLeftOrigLike, imgRightOrigLike;
remap(imgLeft, imgLeftOrigLike, leftMap1, leftMap2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
remap(imgRight, imgRightOrigLike, rightMap1, rightMap2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
// 12. 自动验证核线校正效果
const int gridSize = 8; // 8x8网格
vector<Point2f> gridPoints;
// 生成网格点
for (int i = 1; i < gridSize; i++) {
for (int j = 1; j < gridSize; j++) {
float x = imgSize.width * i / gridSize;
float y = imgSize.height * j / gridSize;
gridPoints.push_back(Point2f(x, y));
}
}
// 在左右图像上绘制网格点和水平线
Mat leftDisplay = imgLeftTrapezoid.clone();
Mat rightDisplay = imgRightTrapezoid.clone();
for (const auto& pt : gridPoints) {
// 左图绘制点
circle(leftDisplay, pt, 5, Scalar(0, 0, 255), 2);
// 右图绘制水平线(核线)
line(rightDisplay, Point(0, pt.y), Point(imgSize.width, pt.y),
Scalar(0, 255, 0), 1);
}
// 13. 保存结果到 D:/data1/output/
string outputDir = "D:/data1/output/";
// 创建输出目录(如果不存在)
if (!fs::exists(outputDir) && !fs::create_directories(outputDir)) {
cerr << "错误: 无法创建输出目录: " << outputDir << endl;
return -1;
}
imwrite(outputDir + "trapezoid_left.jpg", imgLeftTrapezoid);
imwrite(outputDir + "trapezoid_right.jpg", imgRightTrapezoid);
imwrite(outputDir + "vertical_left.jpg", imgLeftVert);
imwrite(outputDir + "vertical_right.jpg", imgRightVert);
imwrite(outputDir + "original_like_left.jpg", imgLeftOrigLike);
imwrite(outputDir + "original_like_right.jpg", imgRightOrigLike);
imwrite(outputDir + "validation_left.jpg", leftDisplay);
imwrite(outputDir + "validation_right.jpg", rightDisplay);
cout << "所有结果已保存至: " << outputDir << endl;
cout << "包含以下文件:" << endl;
cout << "- trapezoid_left.jpg: 梯形校正左图" << endl;
cout << "- trapezoid_right.jpg: 梯形校正右图" << endl;
cout << "- vertical_left.jpg: 竖直核线左图" << endl;
cout << "- vertical_right.jpg: 竖直核线右图" << endl;
cout << "- original_like_left.jpg: 接近原始方向左图" << endl;
cout << "- original_like_right.jpg: 接近原始方向右图" << endl;
cout << "- validation_left.jpg: 验证左图(带网格点)" << endl;
cout << "- validation_right.jpg: 验证右图(带水平线)" << endl;
// 14. 显示结果(使用自适应缩放)
namedWindow("梯形核线校正-左", WINDOW_NORMAL);
namedWindow("梯形核线校正-右", WINDOW_NORMAL);
namedWindow("竖直核线校正-左", WINDOW_NORMAL);
namedWindow("竖直核线校正-右", WINDOW_NORMAL);
namedWindow("接近原始方向-左", WINDOW_NORMAL);
namedWindow("接近原始方向-右", WINDOW_NORMAL);
namedWindow("核线验证-左", WINDOW_NORMAL);
namedWindow("核线验证-右", WINDOW_NORMAL);
// 显示所有结果(自动缩放)
displayImage(imgLeftTrapezoid, "梯形核线校正-左");
displayImage(imgRightTrapezoid, "梯形核线校正-右");
displayImage(imgLeftVert, "竖直核线校正-左");
displayImage(imgRightVert, "竖直核线校正-右");
displayImage(imgLeftOrigLike, "接近原始方向-左");
displayImage(imgRightOrigLike, "接近原始方向-右");
displayImage(leftDisplay, "核线验证-左");
displayImage(rightDisplay, "核线验证-右");
// 添加验证说明
cout << "\n===== 自动验证说明 =====" << endl;
cout << "1. 在左图显示红色网格点(" << gridSize - 1 << "x" << gridSize - 1 << "个)" << endl;
cout << "2. 在右图显示绿色水平线(对应左图点的y坐标)" << endl;
cout << "3. 验证方法:左图的每个红点应位于右图对应绿线上" << endl;
cout << "4. 所有点应在1像素误差范围内对齐" << endl;
waitKey(0);
} catch (const Exception& e) {
cerr << "OpenCV异常: " << e.what() << endl;
return -1;
} catch (const exception& e) {
cerr << "标准异常: " << e.what() << endl;
return -1;
} catch (...) {
cerr << "未知异常发生!" << endl;
return -1;
}
return 0;
}
```
### 主要优化内容:
1. **自适应图像显示**:
- 添加了`displayImage`函数,自动缩放大图像适应屏幕
- 默认最大显示尺寸为1000像素(可调整)
2. **异常处理增强**:
- 使用try-catch块捕获所有异常类型
- 添加文件存在性检查(使用C++17 filesystem)
3. **内存安全改进**:
- 克隆图像用于显示,避免修改原始数据
- 添加空图像检查防止崩溃
4. **路径处理优化**:
- 自动创建输出目录(如果不存在)
- 更详细的错误日志输出
5. **资源管理**:
- 使用RAII风格确保资源释放
- 添加详细的进度输出
### 使用说明:
1. 确保安装OpenCV 4.x和C++17兼容编译器
2. 设置正确的图像输入路径
3. 输出目录会自动创建
4. 按任意键退出显示窗口