修改结果显示和存储结果

This commit is contained in:
杰仔 2026-07-02 10:57:32 +08:00
parent d3e6b4d0f3
commit 4004807396
15 changed files with 197 additions and 43 deletions

View File

@ -17,6 +17,8 @@ struct CtrlDetectionBox
int y{0};
int width{0};
int height{0};
bool hasPhysicalHeight{false};
float physicalHeightMm{0.0f};
};
/**

View File

@ -758,6 +758,8 @@ CtrlDetectionFrame DroneScrewCtrlPresenter::toCtrlFrame(const WDRemoteDetectionF
cb.y = b.y;
cb.width = b.width;
cb.height = b.height;
cb.hasPhysicalHeight = b.hasPhysicalHeight;
cb.physicalHeightMm = b.physicalHeightMm;
frame.boxes.push_back(cb);
}
frame.distances.reserve(f.distances.size());

View File

@ -3,8 +3,8 @@
#define DRONESCREWCTRL_APP_NAME "无人机螺杆控制端"
#define DRONESCREWCTRL_VERSION_STRING "1.0.0"
#define DRONESCREWCTRL_BUILD_STRING "3"
#define DRONESCREWCTRL_FULL_VERSION_STRING "V1.0.0_3"
#define DRONESCREWCTRL_BUILD_STRING "4"
#define DRONESCREWCTRL_FULL_VERSION_STRING "V1.0.0_4"
inline const char* GetDroneScrewCtrlFullVersion() { return DRONESCREWCTRL_FULL_VERSION_STRING; }

View File

@ -355,7 +355,7 @@ void MainWindow::OnDetectionResult(const CtrlDetectionFrame& result)
upsertResultItem(row++, head, static_cast<int>(i), ResultItem::DisplayMode::Distance);
}
}
else if (result.boxes.empty())
else if (result.boxes.empty() && result.distances.empty())
{
const QString head = QStringLiteral("帧#%1 精准目标 0")
.arg(result.frameId);
@ -371,6 +371,14 @@ void MainWindow::OnDetectionResult(const CtrlDetectionFrame& result)
.arg(static_cast<int>(result.boxes.size()));
upsertResultItem(row++, head, static_cast<int>(i), ResultItem::DisplayMode::Precision);
}
for (size_t i = 0; i < result.distances.size(); ++i)
{
const QString head = QStringLiteral("帧#%1 杆间距 %2/%3")
.arg(result.frameId)
.arg(static_cast<int>(i + 1))
.arg(static_cast<int>(result.distances.size()));
upsertResultItem(row++, head, static_cast<int>(i), ResultItem::DisplayMode::PrecisionDistance);
}
}
trimResultItems(row);

View File

@ -37,6 +37,11 @@ QString oneDistanceText(const CtrlDetectionDistance& d)
return QStringLiteral("%1m").arg(meters, 0, 'f', 3);
}
QString oneDistanceMmText(const CtrlDetectionDistance& d)
{
return QStringLiteral("%1 mm").arg(d.distanceMm, 0, 'f', 2);
}
QString distanceText(const CtrlDetectionFrame& frame, int targetIndex)
{
if (targetIndex >= 0 && targetIndex < static_cast<int>(frame.distances.size()))
@ -50,6 +55,19 @@ QString distanceText(const CtrlDetectionFrame& frame, int targetIndex)
: distanceParts.join(" ");
}
QString distanceMmText(const CtrlDetectionFrame& frame, int targetIndex)
{
if (targetIndex >= 0 && targetIndex < static_cast<int>(frame.distances.size()))
return oneDistanceMmText(frame.distances[static_cast<size_t>(targetIndex)]);
QStringList distanceParts;
for (size_t i = 0; i < frame.distances.size(); ++i)
distanceParts << oneDistanceMmText(frame.distances[i]);
return distanceParts.isEmpty()
? QStringLiteral("未测到距离")
: distanceParts.join(" ");
}
QString distanceIndexText(const CtrlDetectionFrame& frame, int targetIndex)
{
const int total = static_cast<int>(frame.distances.size());
@ -60,6 +78,22 @@ QString distanceIndexText(const CtrlDetectionFrame& frame, int targetIndex)
}
return QStringLiteral("-");
}
QString precisionDistanceIndexText(const CtrlDetectionFrame& frame, int targetIndex)
{
const int total = static_cast<int>(frame.distances.size());
if (targetIndex >= 0 && targetIndex < total)
{
const CtrlDetectionDistance& d = frame.distances[static_cast<size_t>(targetIndex)];
if (d.fromId >= 0 && d.toId >= 0)
return QStringLiteral("%1-%2").arg(d.fromId + 1).arg(d.toId + 1);
if (d.fromId >= 0)
return QString::number(d.fromId + 1);
if (d.toId >= 0)
return QString::number(d.toId + 1);
}
return QStringLiteral("-");
}
}
ResultItem::ResultItem(QWidget* parent)
@ -110,15 +144,23 @@ void ResultItem::setResultData(int targetIndex,
DisplayMode mode)
{
const bool precisionMode = (mode == DisplayMode::Precision);
const bool precisionDistanceMode = (mode == DisplayMode::PrecisionDistance);
setResultTextStyle(ui);
setMinimumHeight(precisionMode ? 104 : 108);
setMinimumHeight((precisionMode || precisionDistanceMode) ? 104 : 108);
if (ui->lb_id_t)
ui->lb_id_t->setText(QStringLiteral("ID"));
if (ui->lb_status_t)
ui->lb_status_t->setText(mode == DisplayMode::Distance
? QStringLiteral("距离")
: QStringLiteral("状态"));
{
if (mode == DisplayMode::Distance)
ui->lb_status_t->setText(QStringLiteral("距离"));
else if (precisionDistanceMode)
ui->lb_status_t->setText(QStringLiteral("杆间距"));
else if (precisionMode)
ui->lb_status_t->setText(QStringLiteral("物理高度"));
else
ui->lb_status_t->setText(QStringLiteral("状态"));
}
if (ui->label_id)
{
@ -126,6 +168,10 @@ void ResultItem::setResultData(int targetIndex,
ui->label_id->setText(QString("%1/%2")
.arg(targetIndex + 1)
.arg(static_cast<int>(frame.boxes.size())));
else if (precisionDistanceMode)
{
ui->label_id->setText(precisionDistanceIndexText(frame, targetIndex));
}
else if (mode == DisplayMode::Distance)
{
ui->label_id->setText(distanceIndexText(frame, targetIndex));
@ -134,10 +180,16 @@ void ResultItem::setResultData(int targetIndex,
ui->label_id->setText(QStringLiteral("0/0"));
}
if (mode == DisplayMode::Distance)
if (mode == DisplayMode::Distance || precisionDistanceMode)
{
if (ui->label_status)
ui->label_status->setText(frame.success ? distanceText(frame, targetIndex) : failedText(frame));
{
ui->label_status->setText(frame.success
? (precisionDistanceMode
? distanceMmText(frame, targetIndex)
: distanceText(frame, targetIndex))
: failedText(frame));
}
return;
}
@ -146,11 +198,19 @@ void ResultItem::setResultData(int targetIndex,
if (targetIndex >= 0 && targetIndex < static_cast<int>(frame.boxes.size()))
{
const CtrlDetectionBox& b = frame.boxes[static_cast<size_t>(targetIndex)];
const QString text = QStringLiteral("score=%1 w=%2 h=%3")
.arg(b.score, 0, 'f', 3)
.arg(b.width)
.arg(b.height);
ui->label_status->setText(text);
if (b.hasPhysicalHeight)
{
ui->label_status->setText(QStringLiteral("%1 mm")
.arg(b.physicalHeightMm, 0, 'f', 2));
}
else
{
const QString text = QStringLiteral("score=%1 w=%2 h=%3")
.arg(b.score, 0, 'f', 3)
.arg(b.width)
.arg(b.height);
ui->label_status->setText(text);
}
}
else if (!frame.success)
{

View File

@ -14,7 +14,8 @@ public:
enum class DisplayMode
{
Distance,
Precision
Precision,
PrecisionDistance
};
explicit ResultItem(QWidget* parent = nullptr);

View File

@ -700,6 +700,18 @@ void DroneScrewAlgoStub::appendRoiBox(const StereoBoltModuleRoiC& roi,
result.boxes.push_back(box);
}
void DroneScrewAlgoStub::appendBoltBox(const StereoBoltModuleBoltC& bolt,
DroneScrewResult& result) const
{
DroneScrewBox box;
if (mapRectifiedLeftRoi(bolt.left_roi, result.imageWidth, result.imageHeight, box))
{
box.hasPhysicalHeight = true;
box.physicalHeightMm = static_cast<float>(bolt.height_mm);
result.boxes.push_back(box);
}
}
int DroneScrewAlgoStub::Init(const DroneScrewAlgoParams& params)
{
std::lock_guard<std::mutex> lk(m_mutex);
@ -899,7 +911,7 @@ int DroneScrewAlgoStub::Detect(const DroneScrewInputImage& leftImage,
if (out.data.n_bolts > 0 && out.data.bolts)
{
for (int i = 0; i < out.data.n_bolts; ++i)
appendRoiBox(out.data.bolts[i].left_roi, result);
appendBoltBox(out.data.bolts[i], result);
}
if (out.data.n_adjacent_distances > 0 && out.data.adjacent_distances)
{

View File

@ -47,6 +47,8 @@ private:
DroneScrewBox& box) const;
void appendRoiBox(const StereoBoltModuleRoiC& roi,
DroneScrewResult& result) const;
void appendBoltBox(const StereoBoltModuleBoltC& bolt,
DroneScrewResult& result) const;
#endif
DroneScrewAlgoParams m_params;

View File

@ -16,6 +16,8 @@ struct DroneScrewBox
int y{0};
int width{0};
int height{0};
bool hasPhysicalHeight{false};
float physicalHeightMm{0.0f};
};
/**

View File

@ -69,7 +69,6 @@ constexpr int kDetectPipelineDistance = 1;
constexpr const char* kImageSaveRootDir = "/home/cat";
constexpr size_t kMaxImageSaveQueueDepth = 100;
constexpr size_t kImageSaveWorkerCount = 4;
constexpr int kDistanceImageSaveStride = 3;
const char* mvsSdkErrorName(int code)
{
@ -253,6 +252,70 @@ void logDetectionResultFixed(const char* tag, int frameNo, const DroneScrewResul
}
}
QString savedImageFileName(unsigned long long index, const QString& prefix)
{
return QStringLiteral("%1_%2Image.png").arg(index).arg(prefix);
}
bool saveDetectionResultText(const QString& dirPath,
unsigned long long imageIndex,
const DroneScrewResult& result)
{
const QString filePath = QDir(dirPath).filePath(
QStringLiteral("%1_result.txt").arg(imageIndex));
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
LOG_WARN("[SAVE] result save open failed: %s\n",
filePath.toStdString().c_str());
return false;
}
QTextStream out(&file);
out << "index:" << static_cast<qulonglong>(imageIndex) << '\n';
out << "leftImage:" << savedImageFileName(imageIndex, QStringLiteral("left")) << '\n';
out << "rightImage:" << savedImageFileName(imageIndex, QStringLiteral("right")) << '\n';
out << "frameId:" << static_cast<qulonglong>(result.frameId) << '\n';
out << "timestampUs:" << static_cast<qlonglong>(result.timestampUs) << '\n';
out << "success:" << (result.success ? 1 : 0) << '\n';
out << "errorCode:" << result.errorCode << '\n';
out << "message:" << QString::fromStdString(result.message) << '\n';
int physicalHeightCount = 0;
for (const auto& b : result.boxes)
{
if (b.hasPhysicalHeight)
++physicalHeightCount;
}
out << "physicalHeightCount:" << physicalHeightCount << '\n';
int physicalHeightIndex = 1;
for (const auto& b : result.boxes)
{
if (!b.hasPhysicalHeight)
continue;
out << "physicalHeight" << physicalHeightIndex << "Id:" << physicalHeightIndex << '\n';
out << "physicalHeight" << physicalHeightIndex << "Mm:"
<< QString::number(b.physicalHeightMm, 'f', 3) << '\n';
++physicalHeightIndex;
}
out << "rodSpacingCount:" << static_cast<int>(result.distances.size()) << '\n';
for (size_t i = 0; i < result.distances.size(); ++i)
{
const auto& d = result.distances[i];
const int index = static_cast<int>(i + 1);
out << "rodSpacing" << index << "FromId:" << (d.fromId + 1) << '\n';
out << "rodSpacing" << index << "ToId:" << (d.toId + 1) << '\n';
out << "rodSpacing" << index << "Mm:"
<< QString::number(d.distanceMm, 'f', 3) << '\n';
}
out.flush();
if (out.status() != QTextStream::Ok)
return false;
return true;
}
double clampCameraFloatFeature(IMvsDevice* camera,
const char* role,
const char* featureName,
@ -1814,9 +1877,9 @@ int DroneScrewServerPresenter::stopDetectionWork()
stopGpioTriggerLoop();
m_bThreadExit = true;
stopImageSaveThread();
if (m_detectThread.joinable())
m_detectThread.join();
stopImageSaveThread();
m_bIsDetecting = false;
m_detectPipelineMode = kDetectPipelinePrecision;
m_activeTriggerFps = static_cast<int>(kPrecisionFrameRate);
@ -1909,7 +1972,6 @@ int DroneScrewServerPresenter::startLiveStream()
m_bLiveStreaming = true;
m_bThreadExit = false;
m_bIsDetecting = true;
startImageSaveThread(imageSaveModeName());
startGpioTriggerLoop();
m_detectThread = std::thread(&DroneScrewServerPresenter::detectThreadFunc, this);
emit statusChanged(QStringLiteral("开始实时传图"));
@ -1935,9 +1997,9 @@ int DroneScrewServerPresenter::stopLiveStream()
m_bRtspStarted = false;
stopGpioTriggerLoop();
m_bThreadExit = true;
stopImageSaveThread();
if (m_detectThread.joinable())
m_detectThread.join();
stopImageSaveThread();
m_bIsDetecting = false;
m_detectPipelineMode = kDetectPipelinePrecision;
m_activeTriggerFps = static_cast<int>(kPrecisionFrameRate);
@ -2374,7 +2436,7 @@ void DroneScrewServerPresenter::startImageSaveThread(const QString& modeName)
void DroneScrewServerPresenter::stopImageSaveThread()
{
const bool shouldJoin = !m_imageSaveThreads.empty();
size_t droppedPending = 0;
size_t pendingJobs = 0;
{
std::lock_guard<std::mutex> lk(m_imageSaveMutex);
if (!shouldJoin)
@ -2384,16 +2446,14 @@ void DroneScrewServerPresenter::stopImageSaveThread()
m_imageSaveThreadExit = false;
return;
}
droppedPending = m_imageSaveQueue.size();
if (droppedPending > 0)
m_imageSaveQueue.clear();
pendingJobs = m_imageSaveQueue.size();
m_imageSaveThreadExit = true;
}
if (droppedPending > 0)
if (pendingJobs > 0)
{
LOG_WARN("[SAVE] stop requested, drop pending save jobs=%zu\n",
droppedPending);
LOG_INFO("[SAVE] stop requested, flush pending save jobs=%zu\n",
pendingJobs);
}
m_imageSaveCv.notify_all();
@ -2414,6 +2474,7 @@ void DroneScrewServerPresenter::stopImageSaveThread()
void DroneScrewServerPresenter::enqueueImageSave(const MvsImageData& leftImg,
const MvsImageData& rightImg,
const DroneScrewResult& result,
unsigned long long index)
{
auto dropIfQueueFull = [this, index]() -> bool {
@ -2460,6 +2521,8 @@ void DroneScrewServerPresenter::enqueueImageSave(const MvsImageData& leftImg,
ImageSaveJob job;
job.index = index;
job.result = result;
job.hasResult = true;
if (!copyMono8(leftImg, job.left) || !copyMono8(rightImg, job.right))
{
LOG_WARN("[SAVE] skip image save: unsupported image pair index=%llu left=%ux%u pf=0x%x right=%ux%u pf=0x%x\n",
@ -2494,14 +2557,10 @@ void DroneScrewServerPresenter::imageSaveThreadFunc(int workerIndex)
return m_imageSaveThreadExit.load() || !m_imageSaveQueue.empty();
});
if (m_imageSaveThreadExit.load())
{
m_imageSaveQueue.clear();
break;
}
if (m_imageSaveQueue.empty())
{
if (m_imageSaveThreadExit.load())
break;
continue;
}
@ -2523,7 +2582,7 @@ void DroneScrewServerPresenter::imageSaveThreadFunc(int workerIndex)
static_cast<int>(img.width),
QImage::Format_Grayscale8);
const QString filePath = QDir(job.dirPath).filePath(
QStringLiteral("%1_%2Image.png").arg(job.index).arg(prefix));
savedImageFileName(job.index, prefix));
if (!q.save(filePath, "PNG"))
{
LOG_WARN("[SAVE] image save failed: %s\n",
@ -2532,9 +2591,9 @@ void DroneScrewServerPresenter::imageSaveThreadFunc(int workerIndex)
};
saveMono8(job.left, QStringLiteral("left"));
if (m_imageSaveThreadExit.load())
break;
saveMono8(job.right, QStringLiteral("right"));
if (job.hasResult)
saveDetectionResultText(job.dirPath, job.index, job.result);
}
LOG_DEBUG("[SAVE] worker #%d stopped\n", workerIndex);
}
@ -2908,12 +2967,6 @@ void DroneScrewServerPresenter::detectThreadFunc()
haveProcessedFrame = true;
lastProcessedLeftFrameId = leftImg.frameID;
lastProcessedRightFrameId = rightImg.frameID;
const bool distanceSaveMode =
(m_detectPipelineMode.load() == kDetectPipelineDistance);
const bool shouldSaveFrame =
!distanceSaveMode || ((frameCnt % kDistanceImageSaveStride) == 0);
if (shouldSaveFrame)
enqueueImageSave(leftImg, rightImg, ++savedFrameCnt);
// Publish the display frame before detection work. detectMode only controls raw transport.
if (m_bRawPubEnabled.load())
@ -3138,6 +3191,9 @@ void DroneScrewServerPresenter::detectThreadFunc()
result.success ? 1 : 0, result.errorCode,
result.boxes.size(), result.distances.size());
if (!distanceMode)
enqueueImageSave(leftImg, rightImg, result, ++savedFrameCnt);
emit detectionResult(result);
LOG_DEBUG("[DETECT] frame #%d after detectionResult emit\n", frameCnt + 1);

View File

@ -239,6 +239,7 @@ private:
void imageSaveThreadFunc(int workerIndex);
void enqueueImageSave(const MvsImageData& leftImg,
const MvsImageData& rightImg,
const DroneScrewResult& result,
unsigned long long index);
QString imageSaveModeName() const;
@ -258,6 +259,8 @@ private:
unsigned long long index{0};
ImageSaveBuffer left;
ImageSaveBuffer right;
DroneScrewResult result;
bool hasResult{false};
};
// 双目相机设备

View File

@ -163,6 +163,8 @@ void fillDetectionJson(const DroneScrewResult& result, QJsonObject& obj)
jb["y"] = b.y;
jb["w"] = b.width;
jb["h"] = b.height;
if (b.hasPhysicalHeight)
jb["physicalHeightMm"] = static_cast<double>(b.physicalHeightMm);
boxes.append(jb);
}
obj["boxes"] = boxes;

View File

@ -8,6 +8,6 @@
#define DRONESCREWSERVER_COMPANY_NAME "VisionTech"
#define DRONESCREWSERVER_COPYRIGHT "Copyright (C) 2026"
#define DRONESCREWSERVER_VERSION_STRING "1.0.0"
#define DRONESCREWSERVER_VERSION_BUILD "3"
#define DRONESCREWSERVER_VERSION_BUILD "4"
#endif // DRONESCREW_VERSION_H

View File

@ -55,6 +55,8 @@ struct WDRemoteDetectionBox
int y{0};
int width{0};
int height{0};
bool hasPhysicalHeight{false};
float physicalHeightMm{0.0f};
};
/**

View File

@ -1297,6 +1297,8 @@ bool WDRemoteReceiver::parseDetectionJson(const std::string& jsonStr,
box.y = b.get("y", 0 ).asInt();
box.width = b.get("w", 0 ).asInt();
box.height = b.get("h", 0 ).asInt();
box.hasPhysicalHeight = b.isMember("physicalHeightMm");
box.physicalHeightMm = static_cast<float>(b.get("physicalHeightMm", 0.0).asDouble());
outFrame.boxes.push_back(box);
}
}