diff --git a/.gitignore b/.gitignore index 1caf9a4..51db085 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ Release/ build/ build-*/ *-build-*/ +**/TestData # IDE 配置文件 .vscode/ diff --git a/CloudUtils/Inc/PointCloudImageUtils.h b/CloudUtils/Inc/PointCloudImageUtils.h index ab3c760..43b8402 100644 --- a/CloudUtils/Inc/PointCloudImageUtils.h +++ b/CloudUtils/Inc/PointCloudImageUtils.h @@ -94,12 +94,16 @@ public: // 点云灰度图生成 - 灰色着色 + 自适应点大小填充扫描线间隙(通用接口) static QImage GenerateHeatmapImage( - const std::vector>& scanLines); + const std::vector>& scanLines, + double rotateX_deg = 0.0, + double rotateY_deg = 0.0); // 孔洞检测图像生成 - 热力图底图 + 孔洞标记 static QImage GenerateHoleDetectionImage( const std::vector>& scanLines, - const std::vector& holes); + const std::vector& holes, + double rotateX_deg = 0.0, + double rotateY_deg = 0.0); private: // 定义线特征颜色和大小获取函数 diff --git a/CloudUtils/Src/PointCloudImageUtils.cpp b/CloudUtils/Src/PointCloudImageUtils.cpp index 20151b4..7b0e8f8 100644 --- a/CloudUtils/Src/PointCloudImageUtils.cpp +++ b/CloudUtils/Src/PointCloudImageUtils.cpp @@ -1333,21 +1333,57 @@ int PointCloudImageUtils::GenerateDepthImage( } QImage PointCloudImageUtils::GenerateHeatmapImage( - const std::vector>& scanLines) + const std::vector>& scanLines, + double rotateX_deg, + double rotateY_deg) { if (scanLines.empty()) { return QImage(); } + // 预计算旋转参数 + double cosX = cos(rotateX_deg * PI / 180.0); + double sinX = sin(rotateX_deg * PI / 180.0); + double cosY = cos(rotateY_deg * PI / 180.0); + double sinY = sin(rotateY_deg * PI / 180.0); + bool needRotate = (fabs(rotateX_deg) > 1e-6 || fabs(rotateY_deg) > 1e-6); + + // 旋转点的 lambda:先绕X旋转,再绕Y旋转 + auto rotatePoint = [&](double x, double y, double z, double& rx, double& ry, double& rz) { + // 绕X旋转 + double y1 = y * cosX - z * sinX; + double z1 = y * sinX + z * cosX; + // 绕Y旋转 + rx = x * cosY + z1 * sinY; + ry = y1; + rz = -x * sinY + z1 * cosY; + }; + // 固定图像尺寸 int imgRows = 992; int imgCols = 1056; int x_skip = 50; int y_skip = 50; - // 计算 XY 范围(复用已有方法) + // 计算 XY 范围(考虑旋转后的坐标) double xMin, xMax, yMin, yMax; - CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + if (!needRotate) { + CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + } else { + xMin = yMin = std::numeric_limits::max(); + xMax = yMax = -std::numeric_limits::max(); + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + double rx, ry, rz; + rotatePoint(point.pt3D.x, point.pt3D.y, point.pt3D.z, rx, ry, rz); + if (rx < xMin) xMin = rx; + if (rx > xMax) xMax = rx; + if (ry < yMin) yMin = ry; + if (ry > yMax) yMax = ry; + } + } + } if (xMax <= xMin || yMax <= yMin) { return QImage(); @@ -1385,8 +1421,19 @@ QImage PointCloudImageUtils::GenerateHeatmapImage( for (const auto& point : scanLine) { if (point.pt3D.z < 1e-4) continue; - int px = (int)((point.pt3D.x - xMin) / x_scale + x_skip); - int py = (int)((point.pt3D.y - yMin) / y_scale + y_skip); + double projX, projY; + if (needRotate) { + double rx, ry, rz; + rotatePoint(point.pt3D.x, point.pt3D.y, point.pt3D.z, rx, ry, rz); + projX = rx; + projY = ry; + } else { + projX = point.pt3D.x; + projY = point.pt3D.y; + } + + int px = (int)((projX - xMin) / x_scale + x_skip); + int py = (int)((projY - yMin) / y_scale + y_skip); if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { painter.fillRect(px, py, pointSize, pointSize, grayColor); @@ -1399,23 +1446,56 @@ QImage PointCloudImageUtils::GenerateHeatmapImage( QImage PointCloudImageUtils::GenerateHoleDetectionImage( const std::vector>& scanLines, - const std::vector& holes) + const std::vector& holes, + double rotateX_deg, + double rotateY_deg) { - // 先生成热力图底图 - QImage image = GenerateHeatmapImage(scanLines); + // 先生成热力图底图(透传旋转参数) + QImage image = GenerateHeatmapImage(scanLines, rotateX_deg, rotateY_deg); if (image.isNull() || holes.empty()) { return image; } + // 预计算旋转参数(用于孔洞中心坐标变换) + double cosX = cos(rotateX_deg * PI / 180.0); + double sinX = sin(rotateX_deg * PI / 180.0); + double cosY = cos(rotateY_deg * PI / 180.0); + double sinY = sin(rotateY_deg * PI / 180.0); + bool needRotate = (fabs(rotateX_deg) > 1e-6 || fabs(rotateY_deg) > 1e-6); + + auto rotatePoint = [&](double x, double y, double z, double& rx, double& ry, double& rz) { + double y1 = y * cosX - z * sinX; + double z1 = y * sinX + z * cosX; + rx = x * cosY + z1 * sinY; + ry = y1; + rz = -x * sinY + z1 * cosY; + }; + // 固定图像尺寸(与 GenerateHeatmapImage 一致) int imgRows = 992; int imgCols = 1056; int x_skip = 50; int y_skip = 50; - // 重新计算投影参数(与底图一致) + // 重新计算投影参数(与底图一致,需要旋转后的范围) double xMin, xMax, yMin, yMax; - CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + if (!needRotate) { + CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + } else { + xMin = yMin = std::numeric_limits::max(); + xMax = yMax = -std::numeric_limits::max(); + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + double rx, ry, rz; + rotatePoint(point.pt3D.x, point.pt3D.y, point.pt3D.z, rx, ry, rz); + if (rx < xMin) xMin = rx; + if (rx > xMax) xMax = rx; + if (ry < yMin) yMin = ry; + if (ry > yMax) yMax = ry; + } + } + } double y_rows = (double)(imgRows - y_skip * 2); double x_cols = (double)(imgCols - x_skip * 2); @@ -1441,8 +1521,19 @@ QImage PointCloudImageUtils::GenerateHoleDetectionImage( continue; } - int px = (int)((hole.center.x - xMin) / x_scale + x_skip); - int py = (int)((hole.center.y - yMin) / y_scale + y_skip); + double projX, projY; + if (needRotate) { + double rx, ry, rz; + rotatePoint(hole.center.x, hole.center.y, hole.center.z, rx, ry, rz); + projX = rx; + projY = ry; + } else { + projX = hole.center.x; + projY = hole.center.y; + } + + int px = (int)((projX - xMin) / x_scale + x_skip); + int py = (int)((projY - yMin) / y_scale + y_skip); if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { // 将物理半径转换为像素半径 diff --git a/CloudView/Src/CloudViewMainWindow.cpp b/CloudView/Src/CloudViewMainWindow.cpp index 9566ec0..b7e261a 100644 --- a/CloudView/Src/CloudViewMainWindow.cpp +++ b/CloudView/Src/CloudViewMainWindow.cpp @@ -248,6 +248,7 @@ QWidget* CloudViewMainWindow::createViewToolbar() {":/common/resource/view_right.png", "右视", 180.0f, -90.0f, 0.0f}, {":/common/resource/view_top.png", "俯视 (XZ面)", 90.0f, 0.0f, 0.0f}, {":/common/resource/view_bottom.png", "仰视", -90.0f, 0.0f, 0.0f}, + {":/common/resource/view_robot.png", "机械臂", -90.0f, 90.0f, 0.0f}, }; int btnSize = 32; diff --git a/CloudView/Version.md b/CloudView/Version.md new file mode 100644 index 0000000..0a16e60 --- /dev/null +++ b/CloudView/Version.md @@ -0,0 +1,7 @@ +# 1.1.3 +- 修复崩溃 +- 修复选点序号不对 +- 视图修改为左侧 + +# 1.1.2 +- 线回撤选中