506 lines
16 KiB
C++
506 lines
16 KiB
C++
|
|
#include "DroneScrewCtrlPresenter.h"
|
|||
|
|
|
|||
|
|
#include <QPainter>
|
|||
|
|
#include <QFont>
|
|||
|
|
|
|||
|
|
DroneScrewCtrlPresenter::DroneScrewCtrlPresenter(QObject* parent)
|
|||
|
|
: QObject(parent)
|
|||
|
|
{
|
|||
|
|
m_pReconnectTimer = new QTimer(this);
|
|||
|
|
m_pReconnectTimer->setInterval(3000);
|
|||
|
|
connect(m_pReconnectTimer, &QTimer::timeout, this, &DroneScrewCtrlPresenter::onReconnectTimer);
|
|||
|
|
|
|||
|
|
connect(this, &DroneScrewCtrlPresenter::rtspFrameReady,
|
|||
|
|
this, &DroneScrewCtrlPresenter::onOverlayFrameReady,
|
|||
|
|
Qt::QueuedConnection);
|
|||
|
|
connect(this, &DroneScrewCtrlPresenter::detectionResultReady,
|
|||
|
|
this, &DroneScrewCtrlPresenter::onResultReceived,
|
|||
|
|
Qt::QueuedConnection);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
DroneScrewCtrlPresenter::~DroneScrewCtrlPresenter()
|
|||
|
|
{
|
|||
|
|
DeinitApp();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::ApplyConfig(const DroneScrewCtrlConfigResult& cfg)
|
|||
|
|
{
|
|||
|
|
m_cfg = cfg;
|
|||
|
|
if (m_bRunning.load())
|
|||
|
|
{
|
|||
|
|
DeinitApp();
|
|||
|
|
InitApp();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int DroneScrewCtrlPresenter::InitApp()
|
|||
|
|
{
|
|||
|
|
if (m_bRunning.load()) return 0;
|
|||
|
|
|
|||
|
|
// 1) 创建 WDRemoteReceiver 并注入回调(控制 + 结果 + 原始图)
|
|||
|
|
if (IWDRemoteReceiver::CreateInstance(&m_pRemote) != 0 || !m_pRemote)
|
|||
|
|
{
|
|||
|
|
m_bConnected = false;
|
|||
|
|
emit serverConnectionChanged(false);
|
|||
|
|
m_pReconnectTimer->start();
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
m_pRemote->SetEventCallback(
|
|||
|
|
[this](WDRemoteEventType ev, const std::string& msg) {
|
|||
|
|
this->onRemoteEvent(ev, msg);
|
|||
|
|
});
|
|||
|
|
m_pRemote->SetDetectionCallback(
|
|||
|
|
[this](const WDRemoteDetectionFrame& f) {
|
|||
|
|
this->onRemoteDetection(f);
|
|||
|
|
});
|
|||
|
|
// 检测模式下板一关闭 RTSP、改用 ZMQ 原始图通道发图;订阅之以显示检测结果图像
|
|||
|
|
m_pRemote->SetRawImageCallback(
|
|||
|
|
[this](const WDRemoteBinocularRawImage& img) {
|
|||
|
|
this->onRemoteRawImage(img);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 2) UDP 搜索设备 → 默认自动打开第一个;搜不到不再按固定 IP 直接交互。
|
|||
|
|
bool opened = false;
|
|||
|
|
std::vector<WDRemoteDeviceInfo> devices;
|
|||
|
|
if (m_pRemote->SearchDevices(devices, 500) == 0 && !devices.empty())
|
|||
|
|
{
|
|||
|
|
const WDRemoteDeviceInfo& d = devices.front();
|
|||
|
|
if (m_pRemote->Open(d.machineCode) == 0)
|
|||
|
|
{
|
|||
|
|
// 用发现到的设备信息更新连接参数,供 RTSP 拉流使用
|
|||
|
|
if (!d.serverIp.empty()) m_cfg.server.serverIp = d.serverIp;
|
|||
|
|
if (d.controlPort > 0) m_cfg.server.zmqControlPort = d.controlPort;
|
|||
|
|
if (d.resultPort > 0) m_cfg.server.zmqResultPort = d.resultPort;
|
|||
|
|
m_cfg.server.zmqRawImagePort = d.rawImagePort;
|
|||
|
|
if (!d.rtspUrl.empty()) m_streamUrl = QString::fromStdString(d.rtspUrl);
|
|||
|
|
opened = true;
|
|||
|
|
emit statusMessage(QStringLiteral("已自动打开设备 %1 (%2)")
|
|||
|
|
.arg(QString::fromStdString(d.deviceName),
|
|||
|
|
QString::fromStdString(d.serverIp)));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!opened)
|
|||
|
|
{
|
|||
|
|
emit statusMessage(QStringLiteral("未发现 DroneScrewServer,等待自动搜索"));
|
|||
|
|
m_bConnected = false;
|
|||
|
|
emit serverConnectionChanged(false);
|
|||
|
|
delete m_pRemote;
|
|||
|
|
m_pRemote = nullptr;
|
|||
|
|
m_pReconnectTimer->start();
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 拉流器等 start_stream 成功后再打开;地址以 Server get_info/discovery 返回为准。
|
|||
|
|
|
|||
|
|
m_bConnected = true;
|
|||
|
|
m_bRunning = true;
|
|||
|
|
emit serverConnectionChanged(true);
|
|||
|
|
emit statusMessage(QStringLiteral("已连接到服务器"));
|
|||
|
|
|
|||
|
|
// 4) 延迟从 Server 获取参数(相机参数、算法参数)并更新 UI
|
|||
|
|
QTimer::singleShot(200, this, [this]() {
|
|||
|
|
if (m_pRemote && m_bConnected.load())
|
|||
|
|
{
|
|||
|
|
WDRemoteServerInfo info = m_pRemote->GetServerInfo();
|
|||
|
|
if (info.ok)
|
|||
|
|
{
|
|||
|
|
updateStreamInfo(info);
|
|||
|
|
emit serverInfoReceived(info);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::DeinitApp()
|
|||
|
|
{
|
|||
|
|
if (m_pReconnectTimer && m_pReconnectTimer->isActive()) m_pReconnectTimer->stop();
|
|||
|
|
|
|||
|
|
releasePuller();
|
|||
|
|
|
|||
|
|
if (m_pRemote)
|
|||
|
|
{
|
|||
|
|
m_pRemote->Disconnect();
|
|||
|
|
delete m_pRemote;
|
|||
|
|
m_pRemote = nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
m_bRunning = false;
|
|||
|
|
m_bConnected = false;
|
|||
|
|
m_streamUrl.clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::onReconnectTimer()
|
|||
|
|
{
|
|||
|
|
if (m_bRunning.load() && m_bConnected.load()) { m_pReconnectTimer->stop(); return; }
|
|||
|
|
emit statusMessage(QStringLiteral("尝试重新连接..."));
|
|||
|
|
DeinitApp();
|
|||
|
|
if (InitApp() == 0) m_pReconnectTimer->stop();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::updateStreamInfo(const WDRemoteServerInfo& info)
|
|||
|
|
{
|
|||
|
|
if (!info.ok) return;
|
|||
|
|
if (!info.rtspUrl.empty())
|
|||
|
|
m_streamUrl = QString::fromStdString(info.rtspUrl).trimmed();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::releasePuller()
|
|||
|
|
{
|
|||
|
|
if (!m_pPuller) return;
|
|||
|
|
m_pPuller->Stop();
|
|||
|
|
m_pPuller->UnInit();
|
|||
|
|
delete m_pPuller;
|
|||
|
|
m_pPuller = nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::initPuller(const QString& streamUrl)
|
|||
|
|
{
|
|||
|
|
releasePuller();
|
|||
|
|
|
|||
|
|
const QString url = streamUrl.trimmed();
|
|||
|
|
if (url.isEmpty())
|
|||
|
|
{
|
|||
|
|
emit statusMessage(QStringLiteral("Server 未返回拉流地址"));
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!IVrFFMediaPuller::CreateObject(&m_pPuller) || !m_pPuller)
|
|||
|
|
{
|
|||
|
|
emit statusMessage(QStringLiteral("创建拉流器失败"));
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
VrFFPullConfig pc;
|
|||
|
|
pc.rtspUrl = url.toStdString();
|
|||
|
|
pc.useTcp = m_cfg.server.rtspUseTcp;
|
|||
|
|
pc.outputFormat = EFFPullPixelFormat::NV12;
|
|||
|
|
|
|||
|
|
m_pPuller->SetFrameCallback([this](const VrFFPulledFrame& f) {
|
|||
|
|
this->onRtspFrame(f);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const int ret = m_pPuller->Init(pc);
|
|||
|
|
if (ret != 0)
|
|||
|
|
{
|
|||
|
|
emit statusMessage(QStringLiteral("打开拉流地址失败: %1").arg(url));
|
|||
|
|
releasePuller();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::TriggerSingleDetection()
|
|||
|
|
{
|
|||
|
|
return m_pRemote && m_pRemote->RequestSingleDetection() == 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::StartLiveStream()
|
|||
|
|
{
|
|||
|
|
if (!m_pRemote) return false;
|
|||
|
|
releasePuller();
|
|||
|
|
m_pRemote->StopWork(WDRemoteWorkMode::Detection);
|
|||
|
|
bool ok = m_pRemote->StartWork(WDRemoteWorkMode::LiveStream) == 0;
|
|||
|
|
if (ok)
|
|||
|
|
{
|
|||
|
|
WDRemoteServerInfo info = m_pRemote->GetServerInfo();
|
|||
|
|
if (info.ok)
|
|||
|
|
{
|
|||
|
|
updateStreamInfo(info);
|
|||
|
|
emit serverInfoReceived(info);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!initPuller(m_streamUrl))
|
|||
|
|
{
|
|||
|
|
m_pRemote->StopWork(WDRemoteWorkMode::LiveStream);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const int startRet = m_pPuller ? m_pPuller->Start() : -1;
|
|||
|
|
if (startRet != 0)
|
|||
|
|
{
|
|||
|
|
emit statusMessage(QStringLiteral("启动拉流失败: %1").arg(m_streamUrl));
|
|||
|
|
releasePuller();
|
|||
|
|
m_pRemote->StopWork(WDRemoteWorkMode::LiveStream);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
emit statusMessage(QStringLiteral("Server 已开流: %1").arg(m_streamUrl));
|
|||
|
|
}
|
|||
|
|
return ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::StopLiveStream()
|
|||
|
|
{
|
|||
|
|
if (!m_pRemote) return false;
|
|||
|
|
// 停止 RTSP 拉流
|
|||
|
|
releasePuller();
|
|||
|
|
bool ok = m_pRemote->StopWork(WDRemoteWorkMode::LiveStream) == 0;
|
|||
|
|
if (ok) emit statusMessage(QStringLiteral("Server 已关流"));
|
|||
|
|
return ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::StartDetection()
|
|||
|
|
{
|
|||
|
|
if (!m_pRemote) return false;
|
|||
|
|
// 检测模式下停止 RTSP 拉流(改用 ZMQ 原始图像通道)
|
|||
|
|
releasePuller();
|
|||
|
|
m_pRemote->StopWork(WDRemoteWorkMode::LiveStream);
|
|||
|
|
|
|||
|
|
// 使用单目传输模式:仅接收左目图像,减少网络带宽消耗
|
|||
|
|
m_pRemote->SetDetectMode("mono");
|
|||
|
|
|
|||
|
|
bool ok = m_pRemote->StartWork(WDRemoteWorkMode::Detection) == 0;
|
|||
|
|
if (ok) emit statusMessage(QStringLiteral("Server 已开始检测(单目传输)"));
|
|||
|
|
return ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::StopDetection()
|
|||
|
|
{
|
|||
|
|
if (!m_pRemote) return false;
|
|||
|
|
bool ok = m_pRemote->StopWork(WDRemoteWorkMode::Detection) == 0;
|
|||
|
|
if (ok) emit statusMessage(QStringLiteral("Server 已停止检测"));
|
|||
|
|
return ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::StopAll()
|
|||
|
|
{
|
|||
|
|
if (!m_pRemote) return false;
|
|||
|
|
// 停止 RTSP 拉流
|
|||
|
|
releasePuller();
|
|||
|
|
// 两种模式都停掉,保证服务端回到空闲(幂等)
|
|||
|
|
m_pRemote->StopWork(WDRemoteWorkMode::Detection, 1000);
|
|||
|
|
m_pRemote->StopWork(WDRemoteWorkMode::LiveStream, 1000);
|
|||
|
|
emit statusMessage(QStringLiteral("Server 已停止(实时/检测)"));
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::SetServerExposure(double exposureTime)
|
|||
|
|
{
|
|||
|
|
return m_pRemote && m_pRemote->SetExposure(exposureTime) == 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::SetServerGain(double gain)
|
|||
|
|
{
|
|||
|
|
return m_pRemote && m_pRemote->SetGain(gain) == 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewCtrlPresenter::PushAlgoParams(const DroneScrewAlgoUiParams& params)
|
|||
|
|
{
|
|||
|
|
if (!m_pRemote) return false;
|
|||
|
|
WDRemoteAlgoParams p;
|
|||
|
|
p.scoreThreshold = params.scoreThreshold;
|
|||
|
|
p.nmsThreshold = params.nmsThreshold;
|
|||
|
|
p.inputWidth = params.inputWidth;
|
|||
|
|
p.inputHeight = params.inputHeight;
|
|||
|
|
p.modelType = params.modelType;
|
|||
|
|
p.modelPath = params.modelPath;
|
|||
|
|
return m_pRemote->SetAlgoParams(p) == 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// =====================================================================
|
|||
|
|
// WDRemoteReceiver 回调
|
|||
|
|
// =====================================================================
|
|||
|
|
void DroneScrewCtrlPresenter::onRemoteDetection(const WDRemoteDetectionFrame& f)
|
|||
|
|
{
|
|||
|
|
CtrlDetectionFrame frame = toCtrlFrame(f);
|
|||
|
|
{
|
|||
|
|
QMutexLocker lk(&m_lastResultMutex);
|
|||
|
|
m_lastResult = frame;
|
|||
|
|
m_bHasResult = true;
|
|||
|
|
}
|
|||
|
|
emit detectionResultReady(frame);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::onRemoteRawImage(const WDRemoteBinocularRawImage& img)
|
|||
|
|
{
|
|||
|
|
// 检测模式下板一通过 ZMQ 原始图通道发来左目图像(此时 RTSP 已关闭)。
|
|||
|
|
// 转为 QImage,走与 RTSP 相同的叠加+显示通路(onOverlayFrameReady 会叠加检测框)。
|
|||
|
|
if (!img.leftData || img.leftWidth <= 0 || img.leftHeight <= 0) return;
|
|||
|
|
|
|||
|
|
QImage q;
|
|||
|
|
switch (img.leftPixelFormat)
|
|||
|
|
{
|
|||
|
|
case WDRemotePixelFormat::MONO8:
|
|||
|
|
q = QImage(reinterpret_cast<const uchar*>(img.leftData),
|
|||
|
|
img.leftWidth, img.leftHeight, img.leftStride,
|
|||
|
|
QImage::Format_Grayscale8).copy();
|
|||
|
|
break;
|
|||
|
|
case WDRemotePixelFormat::RGB8:
|
|||
|
|
q = QImage(reinterpret_cast<const uchar*>(img.leftData),
|
|||
|
|
img.leftWidth, img.leftHeight, img.leftStride,
|
|||
|
|
QImage::Format_RGB888).copy();
|
|||
|
|
break;
|
|||
|
|
case WDRemotePixelFormat::BGR8:
|
|||
|
|
q = QImage(reinterpret_cast<const uchar*>(img.leftData),
|
|||
|
|
img.leftWidth, img.leftHeight, img.leftStride,
|
|||
|
|
QImage::Format_RGB888).rgbSwapped();
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (!q.isNull()) emit rtspFrameReady(q);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::onRemoteEvent(WDRemoteEventType ev, const std::string& msg)
|
|||
|
|
{
|
|||
|
|
switch (ev)
|
|||
|
|
{
|
|||
|
|
case WDRemoteEventType::CONNECTED:
|
|||
|
|
m_bConnected = true;
|
|||
|
|
emit serverConnectionChanged(true);
|
|||
|
|
emit statusMessage(QStringLiteral("控制通道已连接"));
|
|||
|
|
break;
|
|||
|
|
case WDRemoteEventType::DISCONNECTED:
|
|||
|
|
m_bConnected = false;
|
|||
|
|
emit serverConnectionChanged(false);
|
|||
|
|
emit statusMessage(QStringLiteral("控制通道已断开"));
|
|||
|
|
if (m_bRunning.load()) m_pReconnectTimer->start();
|
|||
|
|
break;
|
|||
|
|
case WDRemoteEventType::CONNECTION_ERROR:
|
|||
|
|
m_bConnected = false;
|
|||
|
|
emit serverConnectionChanged(false);
|
|||
|
|
emit statusMessage(QStringLiteral("连接错误: %1").arg(QString::fromStdString(msg)));
|
|||
|
|
if (m_bRunning.load()) m_pReconnectTimer->start();
|
|||
|
|
break;
|
|||
|
|
case WDRemoteEventType::REQUEST_TIMEOUT:
|
|||
|
|
emit statusMessage(QStringLiteral("请求超时"));
|
|||
|
|
break;
|
|||
|
|
default: break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
CtrlDetectionFrame DroneScrewCtrlPresenter::toCtrlFrame(const WDRemoteDetectionFrame& f)
|
|||
|
|
{
|
|||
|
|
CtrlDetectionFrame frame;
|
|||
|
|
frame.frameId = static_cast<qint64>(f.frameId);
|
|||
|
|
frame.timestampUs = f.timestampUs;
|
|||
|
|
frame.imageWidth = f.imageWidth;
|
|||
|
|
frame.imageHeight = f.imageHeight;
|
|||
|
|
frame.success = f.success;
|
|||
|
|
frame.errorCode = f.errorCode;
|
|||
|
|
frame.message = QString::fromStdString(f.message);
|
|||
|
|
frame.boxes.reserve(f.boxes.size());
|
|||
|
|
for (const auto& b : f.boxes)
|
|||
|
|
{
|
|||
|
|
CtrlDetectionBox cb;
|
|||
|
|
cb.classId = b.classId;
|
|||
|
|
cb.score = b.score;
|
|||
|
|
cb.x = b.x;
|
|||
|
|
cb.y = b.y;
|
|||
|
|
cb.width = b.width;
|
|||
|
|
cb.height = b.height;
|
|||
|
|
frame.boxes.push_back(cb);
|
|||
|
|
}
|
|||
|
|
frame.distances.reserve(f.distances.size());
|
|||
|
|
for (const auto& d : f.distances)
|
|||
|
|
{
|
|||
|
|
CtrlDetectionDistance cd;
|
|||
|
|
cd.fromId = d.fromId;
|
|||
|
|
cd.toId = d.toId;
|
|||
|
|
cd.distanceMm = d.distanceMm;
|
|||
|
|
frame.distances.push_back(cd);
|
|||
|
|
}
|
|||
|
|
return frame;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// =====================================================================
|
|||
|
|
// RTSP
|
|||
|
|
// =====================================================================
|
|||
|
|
void DroneScrewCtrlPresenter::onRtspFrame(const VrFFPulledFrame& frame)
|
|||
|
|
{
|
|||
|
|
if (!frame.data || frame.width == 0 || frame.height == 0) return;
|
|||
|
|
|
|||
|
|
QImage img;
|
|||
|
|
if (frame.format == EFFPullPixelFormat::NV12)
|
|||
|
|
{
|
|||
|
|
img = QImage(static_cast<const uchar*>(frame.data),
|
|||
|
|
frame.width, frame.height, frame.stride,
|
|||
|
|
QImage::Format_Grayscale8).copy();
|
|||
|
|
}
|
|||
|
|
else if (frame.format == EFFPullPixelFormat::RGBA32)
|
|||
|
|
{
|
|||
|
|
img = QImage(static_cast<const uchar*>(frame.data),
|
|||
|
|
frame.width, frame.height, frame.stride,
|
|||
|
|
QImage::Format_RGBA8888).copy();
|
|||
|
|
}
|
|||
|
|
else if (frame.format == EFFPullPixelFormat::BGR24)
|
|||
|
|
{
|
|||
|
|
img = QImage(static_cast<const uchar*>(frame.data),
|
|||
|
|
frame.width, frame.height, frame.stride,
|
|||
|
|
QImage::Format_BGR888).copy();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
emit rtspFrameReady(img);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::onOverlayFrameReady(const QImage& img)
|
|||
|
|
{
|
|||
|
|
QImage frame = img;
|
|||
|
|
CtrlDetectionFrame det;
|
|||
|
|
bool hasDet = false;
|
|||
|
|
{
|
|||
|
|
QMutexLocker lk(&m_lastResultMutex);
|
|||
|
|
hasDet = m_bHasResult;
|
|||
|
|
if (hasDet) det = m_lastResult;
|
|||
|
|
}
|
|||
|
|
if (hasDet) overlayDetectionBoxes(frame, det);
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
QMutexLocker lk(&m_lastImgMutex);
|
|||
|
|
m_lastDisplay = frame;
|
|||
|
|
}
|
|||
|
|
if (m_pReceiver) m_pReceiver->OnRtspFrame(frame);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::onResultReceived(const CtrlDetectionFrame& result)
|
|||
|
|
{
|
|||
|
|
if (m_pReceiver) m_pReceiver->OnDetectionResult(result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewCtrlPresenter::overlayDetectionBoxes(QImage& img, const CtrlDetectionFrame& frame)
|
|||
|
|
{
|
|||
|
|
if (!m_cfg.display.drawBoxes || frame.boxes.empty()) return;
|
|||
|
|
|
|||
|
|
QPainter p(&img);
|
|||
|
|
QPen pen(QColor(0, 220, 0), m_cfg.display.boxThickness);
|
|||
|
|
p.setPen(pen);
|
|||
|
|
QFont f = p.font(); f.setPointSize(10); p.setFont(f);
|
|||
|
|
|
|||
|
|
const double scaleX = (frame.imageWidth > 0)
|
|||
|
|
? static_cast<double>(img.width()) / frame.imageWidth
|
|||
|
|
: 1.0;
|
|||
|
|
const double scaleY = (frame.imageHeight > 0)
|
|||
|
|
? static_cast<double>(img.height()) / frame.imageHeight
|
|||
|
|
: 1.0;
|
|||
|
|
|
|||
|
|
for (const auto& b : frame.boxes)
|
|||
|
|
{
|
|||
|
|
QRect r(static_cast<int>(b.x * scaleX),
|
|||
|
|
static_cast<int>(b.y * scaleY),
|
|||
|
|
static_cast<int>(b.width * scaleX),
|
|||
|
|
static_cast<int>(b.height * scaleY));
|
|||
|
|
p.drawRect(r);
|
|||
|
|
|
|||
|
|
if (m_cfg.display.drawScores || m_cfg.display.drawClassId)
|
|||
|
|
{
|
|||
|
|
QString label;
|
|||
|
|
if (m_cfg.display.drawClassId) label += QString("cls=%1 ").arg(b.classId);
|
|||
|
|
if (m_cfg.display.drawScores) label += QString("%1").arg(b.score, 0, 'f', 2);
|
|||
|
|
p.drawText(r.topLeft() + QPoint(0, -3), label);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QImage DroneScrewCtrlPresenter::GetLastDisplayImage() const
|
|||
|
|
{
|
|||
|
|
QMutexLocker lk(&m_lastImgMutex);
|
|||
|
|
return m_lastDisplay;
|
|||
|
|
}
|