工件定位开发完成

This commit is contained in:
yiyi 2025-11-02 16:48:52 +08:00
parent 12585d2d2b
commit 81aefc448c
27 changed files with 567 additions and 395 deletions

View File

@ -52,6 +52,7 @@ struct DetectionResult {
QImage image;
std::vector<WorkpiecePosition> positions;
int cameraIndex = 1; // 相机索引默认为1第一个相机
int workpieceType = 0; // 工件类型1/2/3/4等
};
// 状态回调接口

View File

@ -70,7 +70,7 @@ public:
* @param pClient nullptr则发送给所有客户端
* @return 0--
*/
int SendDetectionResult(const DetectionResultData& result, const TCPClient* pClient = nullptr);
int SendDetectionResult(const QJsonObject& result, const TCPClient* pClient = nullptr);
/**
* @brief

View File

@ -11,56 +11,6 @@ DetectPresenter::~DetectPresenter()
}
void vzReadLaserScanPointFromFile_XYZ_vector(const char* fileName, std::vector<std::vector< SVzNL3DPosition>>& scanData)
{
std::ifstream inputFile(fileName);
std::string linedata;
if (inputFile.is_open() == false)
return;
std::vector< SVzNL3DPosition> a_line;
int ptIdx = 0;
while (getline(inputFile, linedata))
{
if (0 == strncmp("Line_", linedata.c_str(), 5))
{
int ptSize = (int)a_line.size();
if (ptSize > 0)
{
scanData.push_back(a_line);
}
a_line.clear();
ptIdx = 0;
}
else if (0 == strncmp("{", linedata.c_str(), 1))
{
float X, Y, Z;
int imageY = 0;
float leftX, leftY;
float rightX, rightY;
sscanf_s(linedata.c_str(), "{%f,%f,%f}-{%f,%f}-{%f,%f}", &X, &Y, &Z, &leftX, &leftY, &rightX, &rightY);
SVzNL3DPosition a_pt;
a_pt.pt3D.x = X;
a_pt.pt3D.y = Y;
a_pt.pt3D.z = Z;
a_pt.nPointIdx = ptIdx;
ptIdx++;
a_line.push_back(a_pt);
}
}
//last line
int ptSize = (int)a_line.size();
if (ptSize > 0)
{
scanData.push_back(a_line);
a_line.clear();
}
inputFile.close();
return;
}
int DetectPresenter::DetectWorkpiece(
int cameraIndex,
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines,
@ -94,20 +44,15 @@ int DetectPresenter::DetectWorkpiece(
dataLoader.SaveLaserScanData(fileName, laserLines, laserLines.size(), 0.0, 0, 0);
}
int nRet = SUCCESS;
// 转换为算法需要的XYZ格式
std::vector<std::vector<SVzNL3DPosition>> xyzData;
#if 0
int convertResult = dataLoader.ConvertToSVzNL3DPosition(laserLines, xyzData);
if (convertResult != SUCCESS || xyzData.empty()) {
LOG_WARNING("Failed to convert data to XYZ format or no XYZ data available\n");
return ERR_CODE(DEV_DATA_INVALID);
}
#else
vzReadLaserScanPointFromFile_XYZ_vector("C:\\project\\QT\\GrabBag\\TestData\\workpiece\\scanData_1_grid.txt", xyzData);
#endif
// 工件角点提取参数
SSX_BQworkpiecePara bqWorkpieceParam;
@ -162,8 +107,8 @@ int DetectPresenter::DetectWorkpiece(
SSG_planeCalibPara groundCalibPara;
if(cameraCalibParam){
memcpy(groundCalibPara.planeCalib, cameraCalibParam->planeCalib, sizeof(double) * 9);
groundCalibPara.planeHeight = cameraCalibParam->planeHeight;
memcpy(groundCalibPara.invRMatrix, cameraCalibParam->invRMatrix, sizeof(double) * 9);
groundCalibPara.planeHeight = cameraCalibParam->planeHeight;
} else {
// 使用默认单位矩阵
double identity[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
@ -174,20 +119,16 @@ int DetectPresenter::DetectWorkpiece(
if(debugParam.enableDebug && debugParam.printDetailLog)
{
if(cameraCalibParam){
LOG_INFO("Plane height: %.3f\n", cameraCalibParam->planeHeight);
LOG_INFO(" Plane calibration matrix: [%f, %f, %f; %f, %f, %f; %f, %f, %f]\n",
cameraCalibParam->planeCalib[0], cameraCalibParam->planeCalib[1], cameraCalibParam->planeCalib[2],
cameraCalibParam->planeCalib[3], cameraCalibParam->planeCalib[4], cameraCalibParam->planeCalib[5],
cameraCalibParam->planeCalib[6], cameraCalibParam->planeCalib[7], cameraCalibParam->planeCalib[8]);
LOG_INFO("Plane height: %.3f\n", groundCalibPara.planeHeight);
LOG_INFO(" Plane calibration matrix: [%f, %f, %f; %f, %f, %f; %f, %f, %f]\n",
groundCalibPara.planeCalib[0], groundCalibPara.planeCalib[1], groundCalibPara.planeCalib[2],
groundCalibPara.planeCalib[3], groundCalibPara.planeCalib[4], groundCalibPara.planeCalib[5],
groundCalibPara.planeCalib[6], groundCalibPara.planeCalib[7], groundCalibPara.planeCalib[8]);
LOG_INFO(" Plane invRMatrix matrix: [%f, %f, %f; %f, %f, %f; %f, %f, %f]\n",
cameraCalibParam->invRMatrix[0], cameraCalibParam->invRMatrix[1], cameraCalibParam->invRMatrix[2],
cameraCalibParam->invRMatrix[3], cameraCalibParam->invRMatrix[4], cameraCalibParam->invRMatrix[5],
cameraCalibParam->invRMatrix[6], cameraCalibParam->invRMatrix[7], cameraCalibParam->invRMatrix[8]);
} else {
LOG_WARNING("[Algo Thread] Camera calibration parameters not found for camera %d\n", cameraIndex);
}
LOG_INFO(" Plane invRMatrix matrix: [%f, %f, %f; %f, %f, %f; %f, %f, %f]\n",
groundCalibPara.invRMatrix[0], groundCalibPara.invRMatrix[1], groundCalibPara.invRMatrix[2],
groundCalibPara.invRMatrix[3], groundCalibPara.invRMatrix[4], groundCalibPara.invRMatrix[5],
groundCalibPara.invRMatrix[6], groundCalibPara.invRMatrix[7], groundCalibPara.invRMatrix[8]);
}
// 数据预处理:调平和去除地面(使用当前相机的调平参数)
@ -221,11 +162,15 @@ int DetectPresenter::DetectWorkpiece(
LOG_INFO("sx_BQ_getWorkpieceCorners: workpieceType=%d err=%d runtime=%.3fms\n", bqResult.workpieceType, errCode, oTimeUtils.GetElapsedTimeInMilliSec());
ERR_CODE_RETURN(errCode);
// 保存工件类型到检测结果
detectionResult.workpieceType = bqResult.workpieceType;
// 构建用于可视化的角点数组
// 按照逆时针顺序排列:左侧(从下到上) -> 顶部(从左到右) -> 右侧(从上到下) -> 底部(从右到左)
std::vector<std::vector<SVzNL3DPoint>> objOps;
std::vector<SVzNL3DPoint> allCorners;
// 添加左侧角点
// 1. 添加左侧角点(从下到上,逆时针)
for (int i = 0; i < 3; i++) {
SVzNL3DPoint pt;
pt.x = bqResult.corner_L[i].x;
@ -234,16 +179,7 @@ int DetectPresenter::DetectWorkpiece(
allCorners.push_back(pt);
}
// 添加右侧角点
for (int i = 0; i < 3; i++) {
SVzNL3DPoint pt;
pt.x = bqResult.corner_R[i].x;
pt.y = bqResult.corner_R[i].y;
pt.z = bqResult.corner_R[i].z;
allCorners.push_back(pt);
}
// 添加顶部角点
// 2. 添加顶部角点(从左到右,逆时针)
for (int i = 0; i < 3; i++) {
SVzNL3DPoint pt;
pt.x = bqResult.corner_T[i].x;
@ -252,7 +188,16 @@ int DetectPresenter::DetectWorkpiece(
allCorners.push_back(pt);
}
// 添加底部角点
// 3. 添加右侧角点(从上到下,逆时针)
for (int i = 0; i < 3; i++) {
SVzNL3DPoint pt;
pt.x = bqResult.corner_R[i].x;
pt.y = bqResult.corner_R[i].y;
pt.z = bqResult.corner_R[i].z;
allCorners.push_back(pt);
}
// 4. 添加底部角点(从右到左,逆时针)
for (int i = 0; i < 3; i++) {
SVzNL3DPoint pt;
pt.x = bqResult.corner_B[i].x;
@ -264,7 +209,7 @@ int DetectPresenter::DetectWorkpiece(
objOps.push_back(allCorners);
// 从点云数据生成投影图像
detectionResult.image = PointCloudImageUtils::GeneratePointCloudImage(xyzData, objOps);
detectionResult.image = PointCloudImageUtils::GeneratePointCloudRetPointImage(xyzData, objOps);
// 转换检测结果为UI显示格式使用机械臂坐标系数据
for (size_t i = 0; i < objOps.size(); i++) {

View File

@ -17,7 +17,7 @@ int WorkpiecePresenter::InitTCPServer()
m_pTCPServer = new TCPServerProtocol();
// 从配置获取端口默认5020
int port = 5020;
int port = 7800;
if (m_vrConfig) {
// TODO: 从配置文件获取端口配置
// port = m_vrConfig->GetTCPPort();
@ -60,6 +60,9 @@ void WorkpiecePresenter::OnTCPConnectionChanged(bool connected)
} else {
m_pStatus->OnStatusUpdate("TCP客户端已断开");
}
// 更新机械臂连接状态
m_pStatus->OnRobotConnectionChanged(connected);
}
// 更新工作状态
@ -112,37 +115,71 @@ void WorkpiecePresenter::_SendDetectionResultToTCP(const DetectionResult& detect
LOG_DEBUG("Sending detection result to TCP clients, camera index: %d\n", cameraIndex);
// 构造TCP协议的检测结果数据
TCPServerProtocol::DetectionResultData tcpResult;
tcpResult.code = 0;
tcpResult.success = true;
tcpResult.message = "检测成功";
tcpResult.timestamp = QDateTime::currentMSecsSinceEpoch();
// 转换检测结果为JSON格式的3D坐标点数组
// 工件角点检测将12个角点左3、右3、上3、下3作为一组发送
// 创建主结果对象
QJsonObject resultObject;
// 添加工件类型
resultObject["type"] = detectionResult.workpieceType;
// 构造新的JSON格式type + L/T/R/B分组
try {
std::vector<QJsonObject> cornerPoints;
// 遍历所有检测到的角点位置
for (const auto& position : detectionResult.positions) {
QJsonObject point;
point["x"] = position.x;
point["y"] = position.y;
point["z"] = position.z;
point["roll"] = position.roll;
point["pitch"] = position.pitch;
point["yaw"] = position.yaw;
cornerPoints.push_back(point);
// 检查角点数量是否为12个左3、顶3、右3、底3
if (detectionResult.positions.size() != 12) {
LOG_WARNING("Expected 12 corner points, but got %zu points\n", detectionResult.positions.size());
// 如果点数不对,仍然发送,但可能数据不完整
}
if (!cornerPoints.empty()) {
tcpResult.result.push_back(cornerPoints);
LOG_INFO("Prepared %zu corner points for TCP transmission\n", cornerPoints.size());
resultObject["success"] = true;
// 角点索引说明根据DetectPresenter.cpp中的顺序
// 索引 0-2: 左侧 (L) - 从下到上
// 索引 3-5: 顶部 (T) - 从左到右
// 索引 6-8: 右侧 (R) - 从上到下
// 索引 9-11: 底部 (B) - 从右到左
// 构造左侧角点 (L)
QJsonObject L;
for (int i = 0; i < 3 && i < (int)detectionResult.positions.size(); i++) {
QJsonArray point;
point.append(detectionResult.positions[i].x);
point.append(detectionResult.positions[i].y);
point.append(detectionResult.positions[i].z);
L[QString("P%1").arg(i + 1)] = point;
}
resultObject["L"] = L;
// 构造顶部角点 (T)
QJsonObject T;
for (int i = 0; i < 3 && (i + 3) < (int)detectionResult.positions.size(); i++) {
QJsonArray point;
point.append(detectionResult.positions[i + 3].x);
point.append(detectionResult.positions[i + 3].y);
point.append(detectionResult.positions[i + 3].z);
T[QString("P%1").arg(i + 1)] = point;
}
resultObject["T"] = T;
// 构造右侧角点 (R)
QJsonObject R;
for (int i = 0; i < 3 && (i + 6) < (int)detectionResult.positions.size(); i++) {
QJsonArray point;
point.append(detectionResult.positions[i + 6].x);
point.append(detectionResult.positions[i + 6].y);
point.append(detectionResult.positions[i + 6].z);
R[QString("P%1").arg(i + 1)] = point;
}
resultObject["R"] = R;
// 构造底部角点 (B)
QJsonObject B;
for (int i = 0; i < 3 && (i + 9) < (int)detectionResult.positions.size(); i++) {
QJsonArray point;
point.append(detectionResult.positions[i + 9].x);
point.append(detectionResult.positions[i + 9].y);
point.append(detectionResult.positions[i + 9].z);
B[QString("P%1").arg(i + 1)] = point;
}
resultObject["B"] = B;
// 发送结果
int sendResult = m_pTCPServer->SendDetectionResult(tcpResult);
int sendResult = m_pTCPServer->SendDetectionResult(resultObject);
if (sendResult == 0) {
LOG_INFO("Detection result sent to TCP clients successfully\n");
} else {
@ -150,12 +187,6 @@ void WorkpiecePresenter::_SendDetectionResultToTCP(const DetectionResult& detect
}
} catch (const std::exception& e) {
LOG_ERROR("Exception while preparing TCP detection result: %s\n", e.what());
// 发送错误响应
tcpResult.code = -1;
tcpResult.success = false;
tcpResult.message = "检测结果处理异常";
tcpResult.result.clear();
m_pTCPServer->SendDetectionResult(tcpResult);
m_pTCPServer->SendDetectionResult(resultObject);
}
}

View File

@ -78,33 +78,15 @@ void TCPServerProtocol::Deinitialize()
}
}
int TCPServerProtocol::SendDetectionResult(const DetectionResultData& result, const TCPClient* pClient)
int TCPServerProtocol::SendDetectionResult(const QJsonObject& result, const TCPClient* pClient)
{
if (!m_pTCPServer || !m_bServerRunning) {
LOG_ERROR("TCP server is not running\n");
return -1;
}
// 构造JSON响应
QJsonObject response;
response["code"] = result.code;
response["success"] = result.success;
response["message"] = result.message;
response["timestamp"] = result.timestamp;
// 构造结果数组
QJsonArray resultArray;
for (const auto& weldLine : result.result) {
QJsonArray lineArray;
for (const auto& point : weldLine) {
lineArray.append(point);
}
resultArray.append(lineArray);
}
response["result"] = resultArray;
// 转换为JSON字符串
QJsonDocument doc(response);
QJsonDocument doc(result);
QByteArray jsonData = doc.toJson(QJsonDocument::Compact);
// 发送数据
@ -248,12 +230,8 @@ void TCPServerProtocol::HandleStartDetectionCommand(const TCPClient* pClient, qi
void TCPServerProtocol::SendErrorResponse(const TCPClient* pClient, int code, const QString& message, qint64 timestamp)
{
DetectionResultData errorResult;
errorResult.code = code;
errorResult.success = false;
errorResult.message = message;
errorResult.timestamp = timestamp;
errorResult.result.clear(); // 错误时返回空结果
QJsonObject resultObject;
resultObject["success"] = true;
SendDetectionResult(errorResult, pClient);
SendDetectionResult(resultObject, pClient);
}

View File

@ -837,6 +837,15 @@ int WorkpiecePresenter::_DetectTask()
return ERR_CODE(DEV_DATA_INVALID);
}
// 检查检测处理器是否已初始化
if (!m_pDetectPresenter) {
LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n");
if (m_pStatus) {
m_pStatus->OnStatusUpdate("检测处理器未初始化");
}
return ERR_CODE(DEV_NOT_FIND);
}
// 2. 准备算法输入数据
unsigned int lineNum = 0;
lineNum = m_detectionDataCache.size();

View File

@ -36,6 +36,7 @@ INCLUDEPATH += $$PWD/../../../VrEyeDevice/Inc
INCLUDEPATH += $$PWD/../../../VrNets/TCPServer/Inc
INCLUDEPATH += $$PWD/../../../CloudUtils/Inc
INCLUDEPATH += $$PWD/../../../QtUtils/Inc
# 源文件
SOURCES += \
@ -91,15 +92,18 @@ win32 {
win32:CONFIG(debug, debug|release) {
LIBS += -L../../../VrUtils/debug -lVrUtils
LIBS += -L../../../CloudUtils/debug -lCloudUtils
LIBS += -L../../../QtUtils/debug -lQtUtils
LIBS += -L../WorkpieceConfig/debug -lWorkpieceConfig
LIBS += -L../../../VrEyeDevice/debug -lVrEyeDevice
LIBS += -L../../../Module/ModbusTCPServer/debug -lModbusTCPServer
LIBS += -L../../../Module/ShareMem/debug -lShareMem
LIBS += -L../../../VrNets/debug -lVrModbus
LIBS += -L../../../VrNets/debug -lVrTcpServer
}else:win32:CONFIG(release, debug|release){
LIBS += -L../../../VrUtils/release -lVrUtils
LIBS += -L../../../CloudUtils/release -lCloudUtils
LIBS += -L../../../QtUtils/release -lQtUtils
LIBS += -L../WorkpieceConfig/release -lWorkpieceConfig
LIBS += -L../../../VrEyeDevice/release -lVrEyeDevice
LIBS += -L../../../Module/ModbusTCPServer/release -lModbusTCPServer
@ -116,6 +120,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../Module/ShareMem -lShareMem
LIBS += -L../../../VrNets -lVrTcpServer
LIBS += -L../../../CloudUtils -lCloudUtils
LIBS += -L../../../QtUtils -lQtUtils
LIBS += -L../../../VrUtils -lVrUtils
# 添加系统库依赖

View File

@ -9,6 +9,7 @@
#include <QtCore/QFile>
#include <cstring>
#include "PathManager.h"
#include "StyledMessageBox.h"
#include "VrLog.h"
@ -28,7 +29,7 @@ DialogAlgoarg::DialogAlgoarg(IVrConfig* vrConfig, QWidget *parent)
// 检查配置文件路径是否有效
if (m_configFilePath.isEmpty()) {
QMessageBox::critical(this, "错误", "无法获取配置文件路径!");
StyledMessageBox::critical(this, "错误", "无法获取配置文件路径!");
return;
}
@ -36,9 +37,9 @@ DialogAlgoarg::DialogAlgoarg(IVrConfig* vrConfig, QWidget *parent)
LoadConfigToUI();
} catch (const std::exception& e) {
QMessageBox::critical(this, "初始化错误", QString("对话框初始化失败: %1").arg(e.what()));
StyledMessageBox::critical(this, "初始化错误", QString("对话框初始化失败: %1").arg(e.what()));
} catch (...) {
QMessageBox::critical(this, "初始化错误", "对话框初始化失败!(未知错误)");
StyledMessageBox::critical(this, "初始化错误", "对话框初始化失败!(未知错误)");
}
}
@ -50,7 +51,7 @@ DialogAlgoarg::~DialogAlgoarg()
void DialogAlgoarg::LoadConfigToUI()
{
if (!m_vrConfig) {
QMessageBox::critical(this, "错误", "配置对象未初始化!");
StyledMessageBox::critical(this, "错误", "配置对象未初始化!");
return;
}
@ -60,7 +61,7 @@ void DialogAlgoarg::LoadConfigToUI()
if (ret != LOAD_CONFIG_SUCCESS) {
// 配置文件加载失败,使用默认参数
LOG_WARNING("Failed to load config file (error code: %d), using default parameters\n", ret);
QMessageBox::information(this, "提示",
StyledMessageBox::information(this, "提示",
QString("配置文件加载失败(错误代码: %1将使用默认参数显示").arg(ret));
// 使用 IVrConfig.h 中定义的默认值初始化 ConfigResult
@ -83,7 +84,7 @@ void DialogAlgoarg::LoadConfigToUI()
} catch (const std::exception& e) {
LOG_ERROR("Exception in LoadConfigToUI: %s\n", e.what());
QMessageBox::warning(this, "警告",
StyledMessageBox::warning(this, "警告",
QString("加载配置时发生异常: %1\n将使用默认参数显示").arg(e.what()));
// 发生异常时也使用默认参数
@ -95,7 +96,7 @@ void DialogAlgoarg::LoadConfigToUI()
LoadGrowParamToUI(algoParams.growParam);
} catch (...) {
LOG_ERROR("Unknown exception in LoadConfigToUI\n");
QMessageBox::warning(this, "警告", "加载配置文件失败(未知错误),将使用默认参数显示");
StyledMessageBox::warning(this, "警告", "加载配置文件失败(未知错误),将使用默认参数显示");
// 发生未知异常时也使用默认参数
m_configData = ConfigResult();
@ -157,22 +158,22 @@ bool DialogAlgoarg::SaveConfigFromUI()
// 保存各个参数组
if (!SaveWorkpieceParamFromUI(algoParams.workpieceParam)) {
QMessageBox::warning(this, "错误", "工件参数输入有误,请检查!");
StyledMessageBox::warning(this, "错误", "工件参数输入有误,请检查!");
return false;
}
if (!SaveFilterParamFromUI(algoParams.filterParam)) {
QMessageBox::warning(this, "错误", "滤波参数输入有误,请检查!");
StyledMessageBox::warning(this, "错误", "滤波参数输入有误,请检查!");
return false;
}
if (!SaveCornerParamFromUI(algoParams.cornerParam)) {
QMessageBox::warning(this, "错误", "拐角参数输入有误,请检查!");
StyledMessageBox::warning(this, "错误", "拐角参数输入有误,请检查!");
return false;
}
if (!SaveGrowParamFromUI(algoParams.growParam)) {
QMessageBox::warning(this, "错误", "生长参数输入有误,请检查!");
StyledMessageBox::warning(this, "错误", "生长参数输入有误,请检查!");
return false;
}
@ -260,10 +261,10 @@ bool DialogAlgoarg::SaveGrowParamFromUI(VrTreeGrowParam& param)
void DialogAlgoarg::on_btn_camer_ok_clicked()
{
if (SaveConfigFromUI()) {
QMessageBox::information(this, "成功", "配置保存成功!");
StyledMessageBox::information(this, "成功", "配置保存成功!");
accept();
} else {
QMessageBox::warning(this, "失败", "配置保存失败,请检查文件权限或参数输入!");
StyledMessageBox::warning(this, "失败", "配置保存失败,请检查文件权限或参数输入!");
}
}

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>659</width>
<height>400</height>
<height>448</height>
</rect>
</property>
<property name="windowTitle">
@ -58,14 +58,14 @@
<string>相机调平</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QPushButton" name="btn_apply">
<property name="geometry">
<rect>
<x>180</x>
<y>350</y>
<y>390</y>
<width>101</width>
<height>38</height>
</rect>
@ -86,7 +86,7 @@
<property name="geometry">
<rect>
<x>390</x>
<y>350</y>
<y>390</y>
<width>111</width>
<height>38</height>
</rect>
@ -128,7 +128,7 @@ background-color: rgb(47, 48, 52);</string>
<x>140</x>
<y>100</y>
<width>401</width>
<height>241</height>
<height>281</height>
</rect>
</property>
<property name="font">
@ -146,7 +146,7 @@ padding: 5px;</string>
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
<set>Qt::AlignCenter</set>
</property>
</widget>
</widget>

View File

@ -30,6 +30,10 @@
#include <QDir>
#include <QGraphicsView>
#include "WorkpiecePresenter.h"
#include <QProcess>
#include <QSettings>
#include <QFile>
#include <QTextStream>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
@ -55,6 +59,9 @@ MainWindow::MainWindow(QWidget *parent)
// 设置状态栏颜色和padding
statusBar()->setStyleSheet("QStatusBar { color: rgb(239, 241, 245); padding: 20px; }");
// 设置CPU序列号显示左下角
setupCPUSerialDisplay();
// 在状态栏右侧添加版本信息(包含编译时间)
// 使用VrSimpleLog.h中的宏定义构建编译时间
QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7")
@ -817,7 +824,7 @@ int MainWindow::getAvailableHeight()
if (screen) {
QRect availableGeometry = screen->availableGeometry();
// 额外预留20px底部边距
return availableGeometry.height() - 28;
return availableGeometry.height() - 30;
}
// 如果无法获取屏幕信息,返回默认值
@ -852,14 +859,13 @@ void MainWindow::setupSingleCameraLayout()
ui->detect_result_list->setWrapping(true); // 允许换行
// 单相机模式设置gridSizegridHeight = 207
int gridWidth = 275;
int gridHeight = 206;
int gridWidth = 183;
int gridHeight = 154;
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
// 恢复设备状态widget的原始布局
m_deviceStatusWidget->setCameraCount(1);
// 日志: 位于右下方,动态计算高度
int detectLogTop = 850;
int detectLogHeight = availableHeight - detectLogTop;
@ -1028,5 +1034,78 @@ bool MainWindow::saveDetectionDataToFile(const QString& filePath, int cameraInde
}
}
// 获取CPU序列号跨平台实现
QString MainWindow::getCPUSerialNumber()
{
QString serialNumber = "UNKNOWN";
#ifdef _WIN32
// Windows平台使用WMI获取CPU序列号
QProcess process;
process.start("wmic", QStringList() << "cpu" << "get" << "ProcessorId");
process.waitForFinished();
QString output = QString::fromLocal8Bit(process.readAllStandardOutput());
QStringList lines = output.split('\n', Qt::SkipEmptyParts);
// 第一行是标题"ProcessorId",第二行是实际的序列号
if (lines.size() >= 2) {
serialNumber = lines[1].trimmed();
}
// 如果WMIC失败尝试读取注册表备用方案
if (serialNumber.isEmpty() || serialNumber == "UNKNOWN") {
QSettings settings("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", QSettings::NativeFormat);
serialNumber = settings.value("ProcessorNameString", "UNKNOWN").toString();
// 从处理器名称中提取唯一标识
if (serialNumber != "UNKNOWN") {
// 使用处理器名称的哈希作为替代
serialNumber = QString("WIN_%1").arg(QString::number(qHash(serialNumber), 16).toUpper());
}
}
#else
// Linux/ARM Ubuntu平台从/proc/cpuinfo读取
QFile file("/proc/cpuinfo");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString line;
// 首先尝试查找Serial字段
while (!in.atEnd()) {
line = in.readLine();
if (line.contains("Serial", Qt::CaseInsensitive)) {
QStringList parts = line.split(':');
if (parts.size() >= 2) {
serialNumber = parts[1].trimmed();
break;
}
}
}
file.close();
}
#endif
// 转换为大写
return serialNumber.toUpper();
}
// 设置CPU序列号显示
void MainWindow::setupCPUSerialDisplay()
{
return;
QString cpuSerial = getCPUSerialNumber();
// 创建CPU序列号标签
m_cpuSerialLabel = new QLabel(QString("编号: %1").arg(cpuSerial));
m_cpuSerialLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 16px; margin-left: 16px;");
// 添加到状态栏左侧
statusBar()->addWidget(m_cpuSerialLabel);
LOG_INFO("CPU Serial Number: %s\n", cpuSerial.toStdString().c_str());
}

View File

@ -141,5 +141,12 @@ private:
void setupContextMenu();
void showContextMenu(const QPoint& pos, QGraphicsView* view);
bool saveDetectionDataToFile(const QString& filePath, int cameraIndex);
// CPU序列号相关函数
QString getCPUSerialNumber();
void setupCPUSerialDisplay();
private:
QLabel* m_cpuSerialLabel = nullptr; // CPU序列号标签
};
#endif // MAINWINDOW_H

View File

@ -60,13 +60,14 @@ color: rgb(239, 241, 245);</string>
<rect>
<x>40</x>
<y>14</y>
<width>341</width>
<width>431</width>
<height>91</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>40</pointsize>
<pointsize>36</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
@ -74,7 +75,7 @@ color: rgb(239, 241, 245);</string>
background-color: rgba(255, 255, 255, 0);</string>
</property>
<property name="text">
<string>工件定位</string>
<string>3D视觉定位系统</string>
</property>
</widget>
<widget class="QListWidget" name="detect_result_list">
@ -353,7 +354,7 @@ background-color: rgba(255, 255, 255, 0);</string>
<x>0</x>
<y>0</y>
<width>1920</width>
<height>20</height>
<height>21</height>
</rect>
</property>
<property name="styleSheet">

View File

@ -28,7 +28,6 @@ void ResultItem::setResultData(int targetIndex, const WorkpiecePosition& positio
ui->result_x->setText(QString("%1").arg(position.x, 0, 'f', 2));
ui->result_y->setText(QString("%1").arg(position.y, 0, 'f', 2));
ui->result_z->setText(QString("%1").arg(position.z, 0, 'f', 2));
ui->result_rz->setText(QString("%1").arg(position.yaw, 0, 'f', 2));
}
@ -37,7 +36,7 @@ void ResultItem::setItemStyle()
// 只设置右侧和下侧边框作为格子间分隔线不修改背景和QLabel样式
this->setStyleSheet(
"ResultItem { "
" border: 6px solid #191A1C; " // 右侧边框
" border: 1px solid #191A1C; " // 右侧边框
" border-radius: 0px; " // 去掉圆角,让分隔线更清晰
"} "
);

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>272</width>
<height>200</height>
<width>180</width>
<height>152</height>
</rect>
</property>
<property name="windowTitle">
@ -20,10 +20,10 @@
<widget class="QLabel" name="result_id">
<property name="geometry">
<rect>
<x>48</x>
<y>13</y>
<width>161</width>
<height>29</height>
<x>50</x>
<y>15</y>
<width>121</width>
<height>24</height>
</rect>
</property>
<property name="font">
@ -32,7 +32,7 @@
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
@ -42,14 +42,14 @@ background-color: rgb(37, 38, 42);</string>
<string>目标</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>16</x>
<y>16</y>
<x>10</x>
<y>15</y>
<width>24</width>
<height>24</height>
</rect>
@ -59,225 +59,158 @@ background-color: rgb(37, 38, 42);</string>
background-color: rgba(255, 255, 255, 0);</string>
</property>
</widget>
<widget class="QLabel" name="label">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>59</x>
<y>43</y>
<width>58</width>
<height>30</height>
<x>20</x>
<y>50</y>
<width>61</width>
<height>91</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: rgb(37, 38, 42);</string>
</property>
<property name="text">
<string>坐标X</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>59</x>
<y>78</y>
<width>58</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
</property>
<property name="text">
<string>坐标X</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: rgb(37, 38, 42);</string>
</property>
<property name="text">
<string>坐标Y</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>33</x>
<y>148</y>
<width>84</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
</property>
<property name="text">
<string>坐标Y</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: rgb(37, 38, 42);</string>
</property>
<property name="text">
<string>旋转角RZ</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</property>
<property name="text">
<string>坐标Z</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QLabel" name="label_3">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>59</x>
<y>113</y>
<width>58</width>
<height>30</height>
<x>80</x>
<y>50</y>
<width>91</width>
<height>92</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: rgb(37, 38, 42);</string>
</property>
<property name="text">
<string>坐标Z</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="result_y">
<property name="geometry">
<rect>
<x>128</x>
<y>80</y>
<width>85</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="result_x">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
color: rgb(239, 241, 245);
border: 1px solid #3B3D47;
padding: 5px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="result_rz">
<property name="geometry">
<rect>
<x>128</x>
<y>150</y>
<width>85</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
padding: 2px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="result_y">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
color: rgb(239, 241, 245);
border: 1px solid #3B3D47;
padding: 5px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="result_x">
<property name="geometry">
<rect>
<x>128</x>
<y>45</y>
<width>85</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
padding: 2px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="result_z">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
color: rgb(239, 241, 245);
border: 1px solid #3B3D47;
padding: 5px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="result_z">
<property name="geometry">
<rect>
<x>128</x>
<y>115</y>
<width>85</width>
<height>30</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(59, 61, 71);
color: rgb(239, 241, 245);
border: 1px solid #3B3D47;
padding: 5px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
padding: 2px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>

View File

@ -29,6 +29,8 @@ public:
int maxTimeStamp,
int clockPerSecond);
int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData);
// 释放统一格式数据内存
void FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines);

View File

@ -29,6 +29,10 @@ public:
const std::vector<std::vector<SVzNL3DPoint>>& weldResults,
int imageWidth = 800, int imageHeight = 600);
// Workpiece点云和角点检测结果转图像 - 将角点画成圆点
static QImage GeneratePointCloudRetPointImage(const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
const std::vector<std::vector<SVzNL3DPoint>>& cornerPoints);
private:
// 定义线特征颜色和大小获取函数
static void GetLineFeatureStyle(int vType, int hType, int objId,

View File

@ -110,6 +110,7 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
SVzNL3DPosition* p3DPoints = static_cast<SVzNL3DPosition*>(sLaserData.p3DPoint);
SVzNL2DPosition* p2DPoints = static_cast<SVzNL2DPosition*>(sLaserData.p2DPoint);
_ParseLaserScanPoint(line, p3DPoints[nLaserPointIdx], p2DPoints[nLaserPointIdx]);
p3DPoints[nLaserPointIdx].nPointIdx = nLaserPointIdx;
nLaserPointIdx++;
}
}
@ -173,7 +174,7 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
float x = static_cast<float>(points[i].pt3D.x);
float y = static_cast<float>(points[i].pt3D.y);
float z = static_cast<float>(points[i].pt3D.z);
sw << "{ " << x << "," << y << "," << z << " }-";
sw << "{ " << std::fixed << std::setprecision(6) << x << "," << y << "," << z << " }-";
sw << "{ " << points2D[i].ptLeft2D.x << "," << points2D[i].ptLeft2D.y << " }-";
sw << "{ " << points2D[i].ptRight2D.x << "," << points2D[i].ptRight2D.y << " }" << std::endl;
}
@ -211,6 +212,60 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
}
}
int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition> > xyzData)
{
LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str());
if (xyzData.empty()) {
m_lastError = "Invalid input parameters for saving unified data";
LOG_ERROR("Invalid parameters for saving unified laser data\n");
return ERR_CODE(DEV_ARG_INVAILD);
}
try {
std::ofstream sw(fileName);
if (!sw.is_open()) {
m_lastError = "Cannot open file for writing: " + fileName;
LOG_ERROR("Cannot open file for writing: %s\n", fileName.c_str());
return ERR_CODE(FILE_ERR_WRITE);
}
// 写入文件头
sw << "LineNum:" << xyzData.size() << std::endl;
sw << "DataType: 0" << std::endl;
sw << "ScanSpeed:" << 0 << std::endl;
sw << "PointAdjust: 1" << std::endl;
sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl;
int index = 0;
// 写入每条扫描线数据
for (const auto& linePair : xyzData) {
sw << "Line_" << index++ << "_" << 0 << "_" << linePair.size() << std::endl;
// 根据数据类型写入点云数据
for(const auto& point : linePair){
// 写入XYZ格式数据
float x = static_cast<float>(point.pt3D.x);
float y = static_cast<float>(point.pt3D.y);
float z = static_cast<float>(point.pt3D.z);
sw << "{ "
<< std::fixed << std::setprecision(6) << x << ", "
<< std::fixed << std::setprecision(6) << y << ", "
<< std::fixed << std::setprecision(6) << z << " } - ";
sw << "{ 0, 0 } - { 0, 0 }" << std::endl;
}
}
sw.close();
return SUCCESS;
} catch (const std::exception& e) {
m_lastError = "Error saving unified file: " + std::string(e.what());
LOG_ERROR("Error saving unified laser data to file: %s\n", e.what());
return ERR_CODE(FILE_ERR_WRITE);
}
}
void LaserDataLoader::FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines)
{
LOG_DEBUG("Freeing unified laser scan data, line count: %zu\n", laserLines.size());

View File

@ -669,3 +669,123 @@ void PointCloudImageUtils::DrawLapWeldResults(QPainter& painter,
}
}
}
// Workpiece点云和角点检测结果转图像 - 将角点画成圆点
QImage PointCloudImageUtils::GeneratePointCloudRetPointImage(const std::vector<std::vector<SVzNL3DPosition>>& scanLines,
const std::vector<std::vector<SVzNL3DPoint>>& cornerPoints)
{
if (scanLines.empty()) {
return QImage();
}
// 固定图像尺寸,与其他函数保持一致
int imgRows = 992;
int imgCols = 1056;
int x_skip = 16;
int y_skip = 16;
// 计算点云范围
double xMin, xMax, yMin, yMax;
CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax);
// 检查范围是否有效
if (xMax <= xMin || yMax <= yMin) {
return QImage();
}
// 创建图像
QImage image(imgCols, imgRows, QImage::Format_RGB888);
image.fill(Qt::black);
QPainter painter(&image);
// 计算投影比例
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;
// 绘制点云数据
for (const auto& scanLine : scanLines) {
for (const auto& point : scanLine) {
if (point.pt3D.z < 1e-4) continue;
// 解析点索引信息
int vType = point.nPointIdx & 0xff;
int hType = vType >> 4;
int objId = (point.nPointIdx >> 16) & 0xff;
vType = vType & 0x0f;
// 根据线特征类型确定颜色和大小
QColor pointColor;
int pointSize = 1;
GetLineFeatureStyle(vType, hType, objId, pointColor, pointSize);
// 计算图像坐标
int px = (int)((point.pt3D.x - xMin) / x_scale + x_skip);
int py = (int)((point.pt3D.y - yMin) / y_scale + y_skip);
if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) {
painter.setPen(QPen(pointColor, pointSize));
painter.drawPoint(px, py);
}
}
}
// 绘制角点作为圆点
if (!cornerPoints.empty()) {
// 定义不同组角点的颜色
QColor cornerColors[] = {
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(cornerColors) / sizeof(cornerColors[0]);
for (size_t i = 0; i < cornerPoints.size(); i++) {
const auto& cornerGroup = cornerPoints[i];
if (cornerGroup.empty()) continue;
QColor cornerColor = cornerColors[i % numColors];
// 绘制每个角点
for (size_t j = 0; j < cornerGroup.size(); j++) {
const SVzNL3DPoint& corner = cornerGroup[j];
// 计算图像坐标
int px = (int)((corner.x - xMin) / x_scale + x_skip);
int py = (int)((corner.y - yMin) / y_scale + y_skip);
if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) {
// 绘制圆点标记
int circleSize = 10; // 圆点直径
painter.setPen(QPen(cornerColor, 2));
painter.setBrush(QBrush(cornerColor));
painter.drawEllipse(px - circleSize/2, py - circleSize/2, circleSize, circleSize);
// 绘制角点编号,确保不超出图像边界
painter.setPen(QPen(Qt::white, 1));
QFont font("Arial", 16, QFont::Bold);
painter.setFont(font);
QString numberText = QString("%1").arg(j + 1);
painter.drawText(px + 8, py + 8, numberText);
}
}
}
}
return image;
}

View File

@ -10,3 +10,5 @@
//BQ_workpiece
#define SX_ERR_INVLD_VTREE_NUM -2001
#define SX_ERR_INVLD_HTREE_NUM -2002
#define SX_ERR_INVLD_EDGE_LINK_NUM -2003
#define SX_ERR_INVLD_CLOSES_PT -2004