输入坐标可直接显示;点云生成图像增加功能
This commit is contained in:
parent
7631f8aadc
commit
5e978eaea4
@ -99,11 +99,13 @@ public:
|
||||
double rotateY_deg = 0.0);
|
||||
|
||||
// 孔洞检测图像生成 - 热力图底图 + 孔洞标记
|
||||
// useZGradient: 为true时根据点云Z值范围生成灰度渐变底图,否则使用统一灰色底图
|
||||
static QImage GenerateHoleDetectionImage(
|
||||
const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
||||
const std::vector<HoleMarkerInfo>& holes,
|
||||
double rotateX_deg = 0.0,
|
||||
double rotateY_deg = 0.0);
|
||||
double rotateY_deg = 0.0,
|
||||
bool useZGradient = false);
|
||||
|
||||
private:
|
||||
// 定义线特征颜色和大小获取函数
|
||||
|
||||
@ -1448,10 +1448,135 @@ QImage PointCloudImageUtils::GenerateHoleDetectionImage(
|
||||
const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
||||
const std::vector<HoleMarkerInfo>& holes,
|
||||
double rotateX_deg,
|
||||
double rotateY_deg)
|
||||
double rotateY_deg,
|
||||
bool useZGradient)
|
||||
{
|
||||
// 先生成热力图底图(透传旋转参数)
|
||||
QImage image = GenerateHeatmapImage(scanLines, rotateX_deg, rotateY_deg);
|
||||
QImage image;
|
||||
|
||||
if (useZGradient) {
|
||||
// 根据Z值范围生成灰度渐变底图
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
// 计算 XY 范围和 Z 范围
|
||||
double xMin, xMax, yMin, yMax;
|
||||
double zMin = std::numeric_limits<double>::max();
|
||||
double zMax = -std::numeric_limits<double>::max();
|
||||
|
||||
if (!needRotate) {
|
||||
CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax);
|
||||
for (const auto& scanLine : scanLines) {
|
||||
for (const auto& point : scanLine) {
|
||||
if (point.pt3D.z < 1e-4) continue;
|
||||
if (point.pt3D.z < zMin) zMin = point.pt3D.z;
|
||||
if (point.pt3D.z > zMax) zMax = point.pt3D.z;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
xMin = yMin = std::numeric_limits<double>::max();
|
||||
xMax = yMax = -std::numeric_limits<double>::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 (point.pt3D.z < zMin) zMin = point.pt3D.z;
|
||||
if (point.pt3D.z > zMax) zMax = point.pt3D.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xMax <= xMin || yMax <= yMin) {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
double y_rows = (double)(imgRows - y_skip * 2);
|
||||
double x_cols = (double)(imgCols - x_skip * 2);
|
||||
double x_scale = (xMax - xMin) / x_cols;
|
||||
double y_scale = (yMax - yMin) / y_rows;
|
||||
if (x_scale < y_scale)
|
||||
x_scale = y_scale;
|
||||
else
|
||||
y_scale = x_scale;
|
||||
|
||||
// 自适应点大小
|
||||
int lineCount = static_cast<int>(scanLines.size());
|
||||
int pointSize = 2;
|
||||
if (lineCount > 1) {
|
||||
double lineSpacing = (yMax - yMin) / (lineCount - 1);
|
||||
pointSize = (int)std::ceil(lineSpacing / x_scale);
|
||||
if (pointSize < 2) pointSize = 2;
|
||||
if (pointSize > 6) pointSize = 6;
|
||||
}
|
||||
|
||||
// 创建图像
|
||||
image = QImage(imgCols, imgRows, QImage::Format_RGB888);
|
||||
image.fill(Qt::black);
|
||||
|
||||
QPainter painter(&image);
|
||||
|
||||
double zRange = zMax - zMin;
|
||||
for (const auto& scanLine : scanLines) {
|
||||
for (const auto& point : scanLine) {
|
||||
if (point.pt3D.z < 1e-4) continue;
|
||||
|
||||
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) {
|
||||
// Z值映射到灰度:zMin → 0(黑), zMax → 255(白)
|
||||
int gray = 0;
|
||||
if (zRange > 1e-6) {
|
||||
gray = (int)(255.0 * (point.pt3D.z - zMin) / zRange);
|
||||
if (gray < 0) gray = 0;
|
||||
if (gray > 255) gray = 255;
|
||||
}
|
||||
painter.fillRect(px, py, pointSize, pointSize, QColor(gray, gray, gray));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 使用统一灰色底图
|
||||
image = GenerateHeatmapImage(scanLines, rotateX_deg, rotateY_deg);
|
||||
}
|
||||
|
||||
if (image.isNull() || holes.empty()) {
|
||||
return image;
|
||||
}
|
||||
|
||||
@ -303,6 +303,9 @@ private:
|
||||
QLineEdit* m_editRz2;
|
||||
QPushButton* m_btnShowPose2;
|
||||
|
||||
// 姿态坐标轴缩放
|
||||
QLineEdit* m_editPoseScale;
|
||||
|
||||
// 欧拉角旋转顺序选择
|
||||
QComboBox* m_comboEulerOrder;
|
||||
|
||||
|
||||
@ -596,6 +596,16 @@ QGroupBox* CloudViewMainWindow::createMeasureGroup()
|
||||
connect(m_editRy2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose2);
|
||||
connect(m_editRz2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose2);
|
||||
|
||||
// 姿态坐标轴缩放输入
|
||||
QHBoxLayout* scaleLayout = new QHBoxLayout();
|
||||
scaleLayout->setSpacing(5);
|
||||
scaleLayout->addWidget(new QLabel("坐标轴长度:", group));
|
||||
m_editPoseScale = new QLineEdit("10.0", group);
|
||||
m_editPoseScale->setMaximumWidth(60);
|
||||
scaleLayout->addWidget(m_editPoseScale);
|
||||
scaleLayout->addStretch();
|
||||
layout->addLayout(scaleLayout);
|
||||
|
||||
// 分隔线
|
||||
QFrame* line3 = new QFrame(group);
|
||||
line3->setFrameShape(QFrame::HLine);
|
||||
@ -1588,8 +1598,9 @@ void CloudViewMainWindow::onShowPose1()
|
||||
return;
|
||||
}
|
||||
|
||||
// 固定大小为10
|
||||
float scale = 10.0f;
|
||||
// 读取坐标轴缩放
|
||||
float scale = m_editPoseScale->text().toFloat(&ok);
|
||||
if (!ok || scale <= 0) scale = 25.0f;
|
||||
|
||||
// 清除之前的姿态点
|
||||
m_glWidget->clearPosePoints();
|
||||
@ -1661,8 +1672,9 @@ void CloudViewMainWindow::onShowPose2()
|
||||
return;
|
||||
}
|
||||
|
||||
// 固定大小为10
|
||||
float scale = 10.0f;
|
||||
// 读取坐标轴缩放
|
||||
float scale = m_editPoseScale->text().toFloat(&ok);
|
||||
if (!ok || scale <= 0) scale = 25.0f;
|
||||
|
||||
// 清除之前的姿态点
|
||||
m_glWidget->clearPosePoints();
|
||||
@ -2023,12 +2035,6 @@ void CloudViewMainWindow::onViewAnglesChanged(float rotX, float rotY, float rotZ
|
||||
|
||||
void CloudViewMainWindow::onPoint1CoordChanged()
|
||||
{
|
||||
auto selectedPoints = m_glWidget->getSelectedPoints();
|
||||
if (selectedPoints.isEmpty() || !selectedPoints[0].valid) {
|
||||
QMessageBox::warning(this, "提示", "请先选择点1");
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取编辑框中的坐标
|
||||
bool ok = true;
|
||||
float x = m_editPoint1X->text().toFloat(&ok);
|
||||
@ -2049,17 +2055,16 @@ void CloudViewMainWindow::onPoint1CoordChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新选中点的坐标
|
||||
m_glWidget->updateSelectedPointCoord(0, x, y, z);
|
||||
// 直接设置选中点坐标(无论是否已有鼠标选点)
|
||||
m_glWidget->setSelectedPointCoord(0, x, y, z);
|
||||
updateSelectedPointsDisplay();
|
||||
|
||||
// 如果启用了测距且有两个点,重新计算距离
|
||||
if (m_glWidget->isMeasureDistanceEnabled() && selectedPoints.size() >= 2 && selectedPoints[1].valid) {
|
||||
auto updatedPoints = m_glWidget->getSelectedPoints();
|
||||
if (updatedPoints.size() >= 2) {
|
||||
if (m_glWidget->isMeasureDistanceEnabled() && updatedPoints.size() >= 2 && updatedPoints[1].valid) {
|
||||
float distance = m_glWidget->calculateDistance(updatedPoints[0], updatedPoints[1]);
|
||||
m_lblDistance->setText(QString("%1 mm").arg(distance, 0, 'f', 3));
|
||||
statusBar()->showMessage(QString("点1坐标已更新,距离: %1 mm").arg(distance, 0, 'f', 3));
|
||||
}
|
||||
} else {
|
||||
statusBar()->showMessage(QString("点1坐标已更新为 (%1, %2, %3)").arg(x, 0, 'f', 3).arg(y, 0, 'f', 3).arg(z, 0, 'f', 3));
|
||||
}
|
||||
@ -2069,12 +2074,6 @@ void CloudViewMainWindow::onPoint1CoordChanged()
|
||||
|
||||
void CloudViewMainWindow::onPoint2CoordChanged()
|
||||
{
|
||||
auto selectedPoints = m_glWidget->getSelectedPoints();
|
||||
if (selectedPoints.size() < 2 || !selectedPoints[1].valid) {
|
||||
QMessageBox::warning(this, "提示", "请先选择点2");
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取编辑框中的坐标
|
||||
bool ok = true;
|
||||
float x = m_editPoint2X->text().toFloat(&ok);
|
||||
@ -2095,17 +2094,16 @@ void CloudViewMainWindow::onPoint2CoordChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新选中点的坐标
|
||||
m_glWidget->updateSelectedPointCoord(1, x, y, z);
|
||||
// 直接设置选中点坐标(无论是否已有鼠标选点)
|
||||
m_glWidget->setSelectedPointCoord(1, x, y, z);
|
||||
updateSelectedPointsDisplay();
|
||||
|
||||
// 如果启用了测距,重新计算距离
|
||||
if (m_glWidget->isMeasureDistanceEnabled()) {
|
||||
auto updatedPoints = m_glWidget->getSelectedPoints();
|
||||
if (updatedPoints.size() >= 2) {
|
||||
if (m_glWidget->isMeasureDistanceEnabled() && updatedPoints.size() >= 2 && updatedPoints[0].valid) {
|
||||
float distance = m_glWidget->calculateDistance(updatedPoints[0], updatedPoints[1]);
|
||||
m_lblDistance->setText(QString("%1 mm").arg(distance, 0, 'f', 3));
|
||||
statusBar()->showMessage(QString("点2坐标已更新,距离: %1 mm").arg(distance, 0, 'f', 3));
|
||||
}
|
||||
} else {
|
||||
statusBar()->showMessage(QString("点2坐标已更新为 (%1, %2, %3)").arg(x, 0, 'f', 3).arg(y, 0, 'f', 3).arg(z, 0, 'f', 3));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user