232 lines
8.7 KiB
C++
232 lines
8.7 KiB
C++
|
|
#include "PointCloudImageUtils.h"
|
|||
|
|
#include <QPainter>
|
|||
|
|
#include <cmath>
|
|||
|
|
#include <algorithm>
|
|||
|
|
#include <limits>
|
|||
|
|
#include "VrLog.h"
|
|||
|
|
|
|||
|
|
#include "VrTimeUtils.h"
|
|||
|
|
|
|||
|
|
#ifndef PI
|
|||
|
|
#define PI 3.14159265358979323846
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
QImage PointCloudImageUtils::GeneratePointCloudImage(const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
|||
|
|
const std::vector<std::vector<SVzNL3DPoint>>& detectionResults,
|
|||
|
|
int imageWidth, int imageHeight)
|
|||
|
|
{
|
|||
|
|
if (scanLines.empty() || imageWidth <= 0 || imageHeight <= 0) {
|
|||
|
|
LOG_WARNING("Invalid input parameters: scanLines.size()=%d, imageWidth=%d, imageHeight=%d\n",
|
|||
|
|
scanLines.size(), imageWidth, imageHeight);
|
|||
|
|
return QImage();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 快速计算点云范围
|
|||
|
|
double xMin, xMax, yMin, yMax;
|
|||
|
|
if (!CalculateRangeFast(scanLines, xMin, xMax, yMin, yMax)) {
|
|||
|
|
LOG_WARNING("No valid points found in scan lines\n");
|
|||
|
|
return QImage();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查范围是否有效
|
|||
|
|
if (xMax <= xMin || yMax <= yMin) {
|
|||
|
|
LOG_WARNING("Invalid point cloud range: xRange=%.2f, yRange=%.2f\n", xMax - xMin, yMax - yMin);
|
|||
|
|
return QImage();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建图像
|
|||
|
|
QImage image(imageWidth, imageHeight, QImage::Format_RGB888);
|
|||
|
|
if (image.isNull()) {
|
|||
|
|
LOG_ERROR("Failed to create QImage with size %dx%d\n", imageWidth, imageHeight);
|
|||
|
|
return QImage();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
image.fill(Qt::black);
|
|||
|
|
|
|||
|
|
// 直接绘制到图像数据,避免QPainter开销
|
|||
|
|
DrawPointCloudDirect(image, scanLines, xMin, xMax, yMin, yMax, imageWidth, imageHeight);
|
|||
|
|
|
|||
|
|
// 绘制检测结果(如果需要)
|
|||
|
|
if (!detectionResults.empty()) {
|
|||
|
|
QPainter painter(&image);
|
|||
|
|
DrawLapWeldResults(painter, detectionResults, xMin, xMax, yMin, yMax, imageWidth, imageHeight);
|
|||
|
|
}
|
|||
|
|
return image;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool PointCloudImageUtils::CalculateRangeFast(const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
|||
|
|
double& xMin, double& xMax,
|
|||
|
|
double& yMin, double& yMax)
|
|||
|
|
{
|
|||
|
|
// 使用局部变量减少内存写入
|
|||
|
|
double localXMin = std::numeric_limits<double>::max();
|
|||
|
|
double localXMax = std::numeric_limits<double>::lowest();
|
|||
|
|
double localYMin = std::numeric_limits<double>::max();
|
|||
|
|
double localYMax = std::numeric_limits<double>::lowest();
|
|||
|
|
|
|||
|
|
bool hasValidPoints = false;
|
|||
|
|
|
|||
|
|
// 使用const引用减少拷贝开销
|
|||
|
|
for (const auto& scanLine : scanLines) {
|
|||
|
|
const SVzNL3DPosition* points = scanLine.data();
|
|||
|
|
const size_t count = scanLine.size();
|
|||
|
|
|
|||
|
|
// 使用指针遍历,减少边界检查
|
|||
|
|
for (size_t i = 0; i < count; ++i) {
|
|||
|
|
const auto& point = points[i];
|
|||
|
|
|
|||
|
|
// ARM兼容的Z值检查 - 避免位操作
|
|||
|
|
if (point.pt3D.z < 1e-4) continue;
|
|||
|
|
|
|||
|
|
hasValidPoints = true;
|
|||
|
|
|
|||
|
|
// 使用局部变量缓存坐标值
|
|||
|
|
const double x = point.pt3D.x;
|
|||
|
|
const double y = point.pt3D.y;
|
|||
|
|
|
|||
|
|
// 分支预测友好的比较 - 使用条件运算符
|
|||
|
|
localXMin = (x < localXMin) ? x : localXMin;
|
|||
|
|
localXMax = (x > localXMax) ? x : localXMax;
|
|||
|
|
localYMin = (y < localYMin) ? y : localYMin;
|
|||
|
|
localYMax = (y > localYMax) ? y : localYMax;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 最后一次性写入结果
|
|||
|
|
xMin = localXMin;
|
|||
|
|
xMax = localXMax;
|
|||
|
|
yMin = localYMin;
|
|||
|
|
yMax = localYMax;
|
|||
|
|
|
|||
|
|
return hasValidPoints;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void PointCloudImageUtils::DrawPointCloudDirect(QImage& image,
|
|||
|
|
const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
|||
|
|
double xMin, double xMax, double yMin, double yMax,
|
|||
|
|
int imageWidth, int imageHeight)
|
|||
|
|
{
|
|||
|
|
if (scanLines.empty()) return;
|
|||
|
|
|
|||
|
|
// 预计算缩放因子和偏移量
|
|||
|
|
const double xScale = imageWidth / (xMax - xMin);
|
|||
|
|
const double yScale = imageHeight / (yMax - yMin);
|
|||
|
|
|
|||
|
|
// 验证图像格式是否为RGB888
|
|||
|
|
if (image.format() != QImage::Format_RGB888) {
|
|||
|
|
LOG_WARNING("Image format is not RGB888, converting...\n");
|
|||
|
|
image = image.convertToFormat(QImage::Format_RGB888);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 直接访问图像数据
|
|||
|
|
uchar* imageData = image.bits();
|
|||
|
|
if (!imageData) {
|
|||
|
|
LOG_ERROR("Failed to get image data pointer\n");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const int bytesPerLine = image.bytesPerLine();
|
|||
|
|
const int maxX = imageWidth - 1;
|
|||
|
|
const int maxY = imageHeight - 1;
|
|||
|
|
|
|||
|
|
// 预计算常量
|
|||
|
|
constexpr uchar grayValue = 150;
|
|||
|
|
|
|||
|
|
for (const auto& scanLine : scanLines) {
|
|||
|
|
const SVzNL3DPosition* points = scanLine.data();
|
|||
|
|
const size_t count = scanLine.size();
|
|||
|
|
|
|||
|
|
// 使用指针遍历减少边界检查
|
|||
|
|
for (size_t i = 0; i < count; ++i) {
|
|||
|
|
const auto& point = points[i];
|
|||
|
|
|
|||
|
|
// ARM兼容的Z值检查 - 避免位操作
|
|||
|
|
if (point.pt3D.z < 1e-4) continue;
|
|||
|
|
|
|||
|
|
// 快速坐标转换
|
|||
|
|
const int px = static_cast<int>((point.pt3D.x - xMin) * xScale);
|
|||
|
|
const int py = static_cast<int>((point.pt3D.y - yMin) * yScale);
|
|||
|
|
|
|||
|
|
// 快速边界检查 - 使用无符号比较
|
|||
|
|
if (static_cast<unsigned>(px) <= static_cast<unsigned>(maxX) &&
|
|||
|
|
static_cast<unsigned>(py) <= static_cast<unsigned>(maxY)) {
|
|||
|
|
|
|||
|
|
// ARM安全的像素写入 - 确保内存对齐
|
|||
|
|
const size_t pixelOffset = static_cast<size_t>(py) * bytesPerLine + static_cast<size_t>(px) * 3;
|
|||
|
|
if (pixelOffset + 2 < static_cast<size_t>(image.sizeInBytes())) {
|
|||
|
|
uchar* pixel = imageData + pixelOffset;
|
|||
|
|
pixel[0] = grayValue; // R
|
|||
|
|
pixel[1] = grayValue; // G
|
|||
|
|
pixel[2] = grayValue; // B
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void PointCloudImageUtils::DrawLapWeldResults(QPainter& painter,
|
|||
|
|
const std::vector<std::vector<SVzNL3DPoint>>& weldResults,
|
|||
|
|
double xMin, double xMax, double yMin, double yMax,
|
|||
|
|
int imageWidth, int imageHeight)
|
|||
|
|
{
|
|||
|
|
if (weldResults.empty()) return;
|
|||
|
|
|
|||
|
|
double xScale = (xMax - xMin) / imageWidth;
|
|||
|
|
double yScale = (yMax - yMin) / imageHeight;
|
|||
|
|
|
|||
|
|
// 使用不同颜色绘制每条焊缝
|
|||
|
|
QColor weldColors[] = {
|
|||
|
|
QColor(255, 0, 0), // 红色
|
|||
|
|
QColor(0, 255, 0), // 绿色
|
|||
|
|
QColor(0, 0, 255), // 蓝色
|
|||
|
|
QColor(255, 255, 0), // 黄色
|
|||
|
|
QColor(255, 0, 255), // 紫色
|
|||
|
|
QColor(0, 255, 255), // 青色
|
|||
|
|
QColor(255, 128, 0), // 橙色
|
|||
|
|
QColor(128, 255, 0) // 浅绿色
|
|||
|
|
};
|
|||
|
|
int numColors = sizeof(weldColors) / sizeof(weldColors[0]);
|
|||
|
|
|
|||
|
|
for (size_t i = 0; i < weldResults.size(); i++) {
|
|||
|
|
const auto& weldLine = weldResults[i];
|
|||
|
|
if (weldLine.empty()) continue;
|
|||
|
|
|
|||
|
|
QColor weldColor = weldColors[i % numColors];
|
|||
|
|
painter.setPen(QPen(weldColor, 3));
|
|||
|
|
|
|||
|
|
// 绘制焊缝线段
|
|||
|
|
for (size_t j = 1; j < weldLine.size(); j++) {
|
|||
|
|
int px1 = (int)((weldLine[j-1].x - xMin) / xScale);
|
|||
|
|
int py1 = (int)((weldLine[j-1].y - yMin) / yScale);
|
|||
|
|
int px2 = (int)((weldLine[j].x - xMin) / xScale);
|
|||
|
|
int py2 = (int)((weldLine[j].y - yMin) / yScale);
|
|||
|
|
|
|||
|
|
if (px1 >= 0 && px1 < imageWidth && py1 >= 0 && py1 < imageHeight &&
|
|||
|
|
px2 >= 0 && px2 < imageWidth && py2 >= 0 && py2 < imageHeight) {
|
|||
|
|
painter.drawLine(px1, py1, px2, py2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 在起点和终点绘制标记
|
|||
|
|
if (!weldLine.empty()) {
|
|||
|
|
// 起点标记 - 圆形
|
|||
|
|
int startX = (int)((weldLine[0].x - xMin) / xScale);
|
|||
|
|
int startY = (int)((weldLine[0].y - yMin) / yScale);
|
|||
|
|
if (startX >= 0 && startX < imageWidth && startY >= 0 && startY < imageHeight) {
|
|||
|
|
painter.setPen(QPen(weldColor, 2));
|
|||
|
|
painter.setBrush(QBrush(weldColor));
|
|||
|
|
painter.drawEllipse(startX - 5, startY - 5, 10, 10);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 终点标记 - 方形
|
|||
|
|
int endX = (int)((weldLine.back().x - xMin) / xScale);
|
|||
|
|
int endY = (int)((weldLine.back().y - yMin) / yScale);
|
|||
|
|
if (endX >= 0 && endX < imageWidth && endY >= 0 && endY < imageHeight) {
|
|||
|
|
painter.setPen(QPen(weldColor, 2));
|
|||
|
|
painter.setBrush(QBrush(weldColor));
|
|||
|
|
painter.drawRect(endX - 4, endY - 4, 8, 8);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|