#include "DetectPresenter.h" #include #include #include #include #include "AapgsModelClassifier.h" #include "PersionModelDetector.h" #include "SG_errCode.h" #include "planeLocalization_Export.h" namespace { struct PlaneLocalizationResult { SSX_planeInfo info{}; int errorCode = 0; QString errorMessage; }; bool IsFinitePoint(const SVzNL3DPoint& point) { return std::isfinite(point.x) && std::isfinite(point.y) && std::isfinite(point.z); } bool IsPersonDetection(const PersionModelDetector::Detection& detection) { const QString label = detection.label.trimmed(); return label.compare(QStringLiteral("person"), Qt::CaseInsensitive) == 0 || (label.isEmpty() && detection.classId == 0); } bool ConvertCloudToScanLines( const RsCloudData& cloud, std::vector>& scanLines, QString& errorMessage) { scanLines.clear(); errorMessage.clear(); scanLines.reserve(cloud.size()); size_t validPointCount = 0; for (const auto& item : cloud) { const EVzResultDataType dataType = item.first; const SVzLaserLineData& sourceLine = item.second; if (sourceLine.nPointCount < 0 || (sourceLine.nPointCount > 0 && !sourceLine.p3DPoint)) { errorMessage = QStringLiteral("点云包含无效扫描线"); return false; } if (dataType != keResultDataType_PointXYZI && dataType != keResultDataType_Position) { errorMessage = QStringLiteral("停机算法不支持点云类型:%1") .arg(static_cast(dataType)); return false; } std::vector targetLine; targetLine.resize(static_cast(sourceLine.nPointCount)); if (dataType == keResultDataType_PointXYZI) { const auto* points = static_cast(sourceLine.p3DPoint); for (int pointIndex = 0; pointIndex < sourceLine.nPointCount; ++pointIndex) { SVzNL3DPosition& target = targetLine[static_cast(pointIndex)]; target.nPointIdx = pointIndex; target.pt3D.x = static_cast(points[pointIndex].x); target.pt3D.y = static_cast(points[pointIndex].y); target.pt3D.z = static_cast(points[pointIndex].z); if (!IsFinitePoint(target.pt3D)) { target.pt3D = { 0.0, 0.0, 0.0 }; } else if (std::fabs(target.pt3D.x) > 1e-6 || std::fabs(target.pt3D.y) > 1e-6 || std::fabs(target.pt3D.z) > 1e-6) { ++validPointCount; } } } else { const auto* points = static_cast(sourceLine.p3DPoint); for (int pointIndex = 0; pointIndex < sourceLine.nPointCount; ++pointIndex) { targetLine[static_cast(pointIndex)] = points[pointIndex]; SVzNL3DPoint& point = targetLine[static_cast(pointIndex)].pt3D; if (!IsFinitePoint(point)) { point = { 0.0, 0.0, 0.0 }; } else if (std::fabs(point.x) > 1e-6 || std::fabs(point.y) > 1e-6 || std::fabs(point.z) > 1e-6) { ++validPointCount; } } } scanLines.push_back(std::move(targetLine)); } if (scanLines.empty() || validPointCount == 0) { errorMessage = QStringLiteral("点云不包含有效三维点"); return false; } return true; } SSX_planeParkingParam BuildParkingParam(const VrPlaneParkingParam& source) { SSX_planeParkingParam target{}; target.parkingPoint = { source.parkingPointX, source.parkingPointY, source.parkingPointZ }; target.guideLinePoint = { source.guideLinePointX, source.guideLinePointY, source.guideLinePointZ }; target.guidingRange = source.guidingRange; target.parkingRange = source.parkingRange; target.distFromNoseToWheel = source.distFromNoseToWheel; return target; } SSG_planeCalibPara BuildGroundCalibrationParam( const VrPlaneGroundCalibrationParam& source) { SSG_planeCalibPara target{}; for (int i = 0; i < 9; ++i) { target.planeCalib[i] = source.planeCalib[i]; target.invRMatrix[i] = source.invRMatrix[i]; } target.planeHeight = source.planeHeight; return target; } SSG_treeGrowParam BuildTreeGrowParam(const VrPlaneTreeGrowParam& source) { SSG_treeGrowParam target{}; target.yDeviation_max = source.yDeviationMax; target.zDeviation_max = source.zDeviationMax; target.maxLineSkipNum = source.maxLineSkipNum; target.maxSkipDistance = source.maxSkipDistance; target.minLTypeTreeLen = source.minLTypeTreeLen; target.minVTypeTreeLen = source.minVTypeTreeLen; return target; } PlaneLocalizationResult LocalizePlane(const RsCloudData& cloud, const VrAlgorithmParams& algorithmParams) { PlaneLocalizationResult result; std::vector> scanLines; if (!ConvertCloudToScanLines(cloud, scanLines, result.errorMessage)) { result.errorCode = cloud.empty() ? SG_ERR_3D_DATA_NULL : SG_ERR_3D_DATA_INVLD; return result; } std::vector> debugData; int algorithmError = 0; result.info = wd_planeLocalization( scanLines, BuildGroundCalibrationParam(algorithmParams.groundCalibrationParam), BuildParkingParam(algorithmParams.planeParkingParam), BuildTreeGrowParam(algorithmParams.treeGrowParam), debugData, &algorithmError); result.errorCode = algorithmError; if (algorithmError == SX_ERR_NO_PLANE_FOUND) { result.errorMessage = QStringLiteral("未找到有效飞机目标"); } else if (algorithmError == SX_ERR_NOSEPOINT_FAIL) { result.errorMessage = QStringLiteral("飞机机鼻定位失败"); } else if (algorithmError == SX_ERR_ENDINE_FAIL) { result.errorMessage = QStringLiteral("飞机主体特征提取失败"); } else if (algorithmError != 0) { result.errorMessage = QStringLiteral("飞机定位算法错误:%1") .arg(algorithmError); } else if (!std::isfinite(result.info.distance) || !std::isfinite(result.info.deviation) || !std::isfinite(result.info.dirAngle_deg) || !IsFinitePoint(result.info.nosePoint) || !IsFinitePoint(result.info.axis)) { result.errorCode = SG_ERR_3D_DATA_INVLD; result.errorMessage = QStringLiteral("飞机定位算法返回非有限数值"); } return result; } bool IsPlaneLostError(int errorCode) { return errorCode == SX_ERR_NO_PLANE_FOUND || errorCode == SX_ERR_NOSEPOINT_FAIL || errorCode == SX_ERR_ENDINE_FAIL || errorCode == SG_ERR_3D_DATA_NULL; } QString GuideText(ParkingGuideState state) { switch (state) { case ParkingGuideState::DockingStarted: return QStringLiteral("START"); case ParkingGuideState::Capturing: return QStringLiteral("CAPTURE"); case ParkingGuideState::Tracking: return QStringLiteral("TRACKING"); case ParkingGuideState::ApproachRate: return QStringLiteral("APPROACH"); case ParkingGuideState::CenterLineAligned: return QStringLiteral("CENTER"); case ParkingGuideState::Slow: return QStringLiteral("SLOW"); case ParkingGuideState::AzimuthGuidance: return QStringLiteral("AZIMUTH"); case ParkingGuideState::StopPositionReached: return QStringLiteral("STOP"); case ParkingGuideState::DockingCompleted: return QStringLiteral("OK"); case ParkingGuideState::Overshot: return QStringLiteral("TOOFAR"); case ParkingGuideState::StoppedShort: return QStringLiteral("STOP SHORT"); case ParkingGuideState::SlowAircraftLost: return QStringLiteral("SLOW+LOST"); case ParkingGuideState::AircraftVerificationFailed: return QStringLiteral("STOP+IDFAIL"); case ParkingGuideState::TooFast: return QStringLiteral("STOPTOOFAST"); case ParkingGuideState::EmergencyStop: return QStringLiteral("STOP"); case ParkingGuideState::ChocksOn: return QStringLiteral("CHOCK ON"); case ParkingGuideState::SystemError: return QStringLiteral("ERROR"); default: return QStringLiteral("WAIT"); } } void SetGuideState(ParkingSpaceGuideInfo& info, ParkingGuideState state) { info.guideStateCode = static_cast(state); info.guideText = GuideText(state); } ParkingSpaceGuideInfo MakeLastKnownInfo( const ParkingGuideAlgorithmState& state) { ParkingSpaceGuideInfo info; const QString selectedModelType = state.selectedModelType.trimmed(); const QString recognizedModelType = state.recognizedModelType.trimmed(); info.expectedModelType = selectedModelType; info.modelType = recognizedModelType.isEmpty() ? QStringLiteral("未知") : recognizedModelType; info.modelMatched = state.modelVerified; info.modelVerificationSupported = state.modelVerificationSupported; info.personInChockRegion = state.personInChockRegion; info.chocksConfirmed = state.chocksConfirmed; info.personCount = state.personCount; info.personConfidence = state.personConfidence; info.distance = state.lastDistance; info.lateralOffset = state.lastDeviation; info.angle = state.lastAngle; info.aircraftSpeed = state.filteredApproachSpeed / 1000.0; info.confidence = std::isfinite(state.modelConfidence) ? state.modelConfidence : 0.0; return info; } } DetectPresenter::DetectPresenter() : m_modelClassifier(std::make_unique()) , m_personDetector(std::make_unique()) { } DetectPresenter::~DetectPresenter() = default; bool ModelRecognitionResult::IsVerified(double minimumConfidence) const { if (!std::isfinite(minimumConfidence)) { return false; } const QString normalizedModel = modelType.trimmed(); const bool isUnknown = normalizedModel == QStringLiteral("未知") || normalizedModel.compare(QStringLiteral("unknown"), Qt::CaseInsensitive) == 0; const double threshold = (std::max)(0.0, minimumConfidence); return verified && !normalizedModel.isEmpty() && !isUnknown && std::isfinite(confidence) && confidence >= threshold; } QString DetectPresenter::GetAlgoVersion() { const char* planeVersion = wd_PlaneLocalizationVersion(); return QStringLiteral( "planeLocalization %1 / AAPGS_model 1.0.0 / Persion_model 1.0.0") .arg(planeVersion ? QString::fromLocal8Bit(planeVersion) : QStringLiteral("未知")); } int DetectPresenter::DetectAirplanePresence(const RsCloudData& cloud, const VrAlgorithmParams& algorithmParams, AirplanePresenceResult& result) { result = AirplanePresenceResult(); const PlaneLocalizationResult localization = LocalizePlane(cloud, algorithmParams); if (localization.errorCode == 0) { result.state = AirplanePresenceState::Detected; result.message = QStringLiteral("检测到飞机,距离停机点 %1 mm") .arg(localization.info.distance, 0, 'f', 1); return 0; } if (IsPlaneLostError(localization.errorCode)) { result.state = AirplanePresenceState::NotDetected; result.message = localization.errorMessage.trimmed().isEmpty() ? QStringLiteral("未检测到飞机") : localization.errorMessage; return 0; } result.state = AirplanePresenceState::ViewBlocked; result.message = localization.errorMessage.trimmed().isEmpty() ? QStringLiteral("飞机检测视野或点云异常") : localization.errorMessage; return 0; } int DetectPresenter::DetectParkingSpaceGuide(const RsCloudData& cloud, const VrAlgorithmParams& algorithmParams, qint64 frameTimestampMs, const ParkingGuideAlgorithmState& previousState, DetectionResult& result, ParkingGuideAlgorithmControl& control) { result = DetectionResult(); control = ParkingGuideAlgorithmControl(); control.nextState = previousState; result.cameraIndex = 1; result.errorCode = 0; const VrGuideDecisionParam& guideParam = algorithmParams.guideDecisionParam; const VrParkingProcessParam& processParam = algorithmParams.processParam; const VrModelRecognitionParam& modelParam = algorithmParams.modelRecognitionParam; const PlaneLocalizationResult localization = LocalizePlane(cloud, algorithmParams); control.planeLocalizationErrorCode = localization.errorCode; control.planeLocalizationMessage = localization.errorMessage; if (localization.errorCode != 0) { ParkingGuideAlgorithmState& next = control.nextState; next.errorFrameCount = (std::max)(0, previousState.errorFrameCount) + 1; const bool planeLost = IsPlaneLostError(localization.errorCode); next.lostFrameCount = planeLost ? (std::max)(0, previousState.lostFrameCount) + 1 : 0; ParkingSpaceGuideInfo errorInfo = MakeLastKnownInfo(previousState); const int errorThreshold = (std::max)(1, processParam.errorFrameThreshold); if (next.errorFrameCount < errorThreshold) { const ParkingGuideState retainedState = previousState.hasMeasurement ? ParkingGuideStateFromCode(previousState.lastGuideStateCode) : ParkingGuideState::Waiting; SetGuideState(errorInfo, retainedState == ParkingGuideState::Unknown ? ParkingGuideState::Waiting : retainedState); result.message = previousState.hasMeasurement ? QStringLiteral( "距离 %1 mm,横向偏差 %2 mm,航向角 %3°,接近速度 %4 m/s") .arg(errorInfo.distance, 0, 'f', 1) .arg(errorInfo.lateralOffset, 0, 'f', 1) .arg(errorInfo.angle, 0, 'f', 2) .arg(errorInfo.aircraftSpeed, 0, 'f', 1) : QStringLiteral("等待飞机定位"); } else { SetGuideState(errorInfo, planeLost ? ParkingGuideState::SlowAircraftLost : ParkingGuideState::SystemError); errorInfo.hasException = true; result.errorCode = localization.errorCode; next.stoppedFrameCount = 0; next.stoppedShortFrameCount = 0; next.stopPositionReached = false; next.dockingCompleted = false; next.completedHoldFrameCount = 0; result.message = localization.errorMessage.trimmed().isEmpty() ? QStringLiteral("停机引导定位失败") : localization.errorMessage; } result.parkingSpaceInfoList.push_back(errorInfo); return 0; } if (previousState.hasMeasurement) { const double distanceChange = std::fabs( localization.info.distance - previousState.lastDistance); const double lateralOffsetChange = std::fabs( localization.info.deviation - previousState.lastDeviation); const double distanceThreshold = (std::max)( 0.0, processParam.distanceChangeThreshold); const double lateralOffsetThreshold = (std::max)( 0.0, processParam.lateralOffsetChangeThreshold); if (distanceChange > distanceThreshold || lateralOffsetChange > lateralOffsetThreshold) { ParkingSpaceGuideInfo retainedInfo = MakeLastKnownInfo(previousState); const ParkingGuideState retainedState = ParkingGuideStateFromCode(previousState.lastGuideStateCode); SetGuideState(retainedInfo, retainedState == ParkingGuideState::Unknown ? ParkingGuideState::Waiting : retainedState); result.parkingSpaceInfoList.push_back(retainedInfo); result.message = QStringLiteral( "检测数据突变已忽略:距离变化 %1 mm(阈值 %2 mm,%3),横向偏移变化 %4 mm(阈值 %5 mm,%6)") .arg(distanceChange, 0, 'f', 1) .arg(distanceThreshold, 0, 'f', 1) .arg(distanceChange > distanceThreshold ? QStringLiteral("超阈值") : QStringLiteral("未超阈值")) .arg(lateralOffsetChange, 0, 'f', 1) .arg(lateralOffsetThreshold, 0, 'f', 1) .arg(lateralOffsetChange > lateralOffsetThreshold ? QStringLiteral("超阈值") : QStringLiteral("未超阈值")); return 0; } } ParkingGuideAlgorithmState& next = control.nextState; next.errorFrameCount = 0; next.lostFrameCount = 0; next.successfulFrameCount = (std::max)(0, previousState.successfulFrameCount) + 1; double approachSpeed = previousState.filteredApproachSpeed; if (previousState.hasMeasurement && previousState.errorFrameCount == 0 && frameTimestampMs > previousState.lastTimestampMs) { const double elapsedSeconds = static_cast(frameTimestampMs - previousState.lastTimestampMs) / 1000.0; const double rawApproachSpeed = (previousState.lastDistance - localization.info.distance) / elapsedSeconds; const double alpha = std::clamp(processParam.speedFilterAlpha, 0.0, 1.0); approachSpeed = alpha * rawApproachSpeed + (1.0 - alpha) * previousState.filteredApproachSpeed; } else if (!previousState.hasMeasurement || previousState.errorFrameCount > 0) { approachSpeed = 0.0; } if (!std::isfinite(approachSpeed)) { approachSpeed = 0.0; } next.hasMeasurement = true; next.lastTimestampMs = frameTimestampMs; next.lastDistance = localization.info.distance; next.lastDeviation = localization.info.deviation; next.lastAngle = localization.info.dirAngle_deg; next.lastNoseX = localization.info.nosePoint.x; next.lastNoseY = localization.info.nosePoint.y; next.lastNoseZ = localization.info.nosePoint.z; next.filteredApproachSpeed = approachSpeed; ParkingSpaceGuidePosition position; position.x = localization.info.nosePoint.x; position.y = localization.info.nosePoint.y; position.z = localization.info.nosePoint.z; position.roll = 0.0; position.pitch = 0.0; position.yaw = localization.info.dirAngle_deg; result.positions.push_back(position); ParkingSpaceGuideInfo info = MakeLastKnownInfo(next); info.hasException = false; const double stopTolerance = (std::max)(0.0, processParam.stopDistanceTolerance); const double overshootDistance = (std::max)(stopTolerance, processParam.overshootDistance); const double slowDistance = (std::max)(stopTolerance, processParam.slowDistance); const double approachStartDistance = (std::max)(slowDistance, processParam.approachStartDistance); const double captureStartDistance = (std::max)(approachStartDistance, processParam.captureStartDistance); const double dockingStartDistance = (std::max)(captureStartDistance, processParam.dockingStartDistance); const double stoppedSpeed = (std::max)(0.0, processParam.stoppedSpeedThreshold); const double maxApproachSpeed = (std::max)(0.0, processParam.maxApproachSpeed); const bool hasSpeedBaseline = previousState.hasMeasurement && previousState.errorFrameCount == 0; const bool isStopped = hasSpeedBaseline && std::fabs(approachSpeed) <= stoppedSpeed; const bool isAtStopPosition = std::fabs(localization.info.distance) <= stopTolerance; ParkingGuideState guideState = ParkingGuideState::Tracking; // Overshoot must win over a previously reached stop position. The // completion latch is only valid while the aircraft remains in the stop // tolerance; otherwise the process must resume guidance. if (localization.info.distance <= -overshootDistance) { next.stopPositionReached = false; next.dockingCompleted = false; next.stoppedFrameCount = 0; next.stoppedShortFrameCount = 0; next.completedHoldFrameCount = 0; next.centerLineAligned = false; guideState = ParkingGuideState::Overshot; } else if (previousState.dockingCompleted && isAtStopPosition && isStopped) { next.dockingCompleted = true; guideState = ParkingGuideState::DockingCompleted; } else if (isAtStopPosition) { next.dockingCompleted = false; next.stoppedShortFrameCount = 0; if (!previousState.stopPositionReached) { next.stopPositionReached = true; next.completedHoldFrameCount = 0; } if (isStopped) { next.stoppedFrameCount = (std::max)(0, previousState.stoppedFrameCount) + 1; } else { next.stoppedFrameCount = 0; } next.completedHoldFrameCount = (std::max)(0, previousState.completedHoldFrameCount) + 1; const int stableFrames = (std::max)(1, processParam.stopStableFrames); const int completedFrames = (std::max)(1, processParam.completedHoldFrames); if (next.stoppedFrameCount >= stableFrames && next.completedHoldFrameCount >= completedFrames) { next.dockingCompleted = true; guideState = ParkingGuideState::DockingCompleted; } else { guideState = ParkingGuideState::StopPositionReached; } } else { next.stopPositionReached = false; next.dockingCompleted = false; next.stoppedFrameCount = 0; next.stoppedShortFrameCount = 0; next.centerLineAligned = false; next.completedHoldFrameCount = 0; const double stoppedShortDistance = (std::max)(stopTolerance, processParam.stoppedShortMinDistance); if (previousState.hasMeasurement && next.successfulFrameCount > 4 && isStopped && localization.info.distance >= stoppedShortDistance) { next.stoppedShortFrameCount = (std::max)(0, previousState.stoppedShortFrameCount) + 1; } else { next.stoppedShortFrameCount = 0; } if (next.stoppedShortFrameCount >= (std::max)(1, processParam.stoppedShortStableFrames)) { guideState = ParkingGuideState::StoppedShort; } else if (localization.info.distance >= 0.0 && localization.info.distance <= slowDistance && hasSpeedBaseline && maxApproachSpeed > 0.0 && approachSpeed >= maxApproachSpeed) { guideState = ParkingGuideState::Slow; } else if (localization.info.distance > slowDistance && hasSpeedBaseline && maxApproachSpeed > 0.0 && approachSpeed >= maxApproachSpeed) { guideState = ParkingGuideState::TooFast; } else if (localization.info.distance >= 0.0 && localization.info.distance <= approachStartDistance && (std::fabs(localization.info.deviation) > (std::max)(0.0, guideParam.lateralTolerance) || std::fabs(localization.info.dirAngle_deg) > (std::max)(0.0, guideParam.angleTolerance))) { next.centerLineAligned = false; guideState = ParkingGuideState::AzimuthGuidance; } else if (localization.info.distance >= 0.0 && localization.info.distance <= approachStartDistance) { next.centerLineAligned = true; guideState = ParkingGuideState::CenterLineAligned; } else if (localization.info.distance <= captureStartDistance && localization.info.distance > approachStartDistance) { next.centerLineAligned = false; guideState = ParkingGuideState::Capturing; } else if (localization.info.distance <= dockingStartDistance && localization.info.distance > captureStartDistance) { next.centerLineAligned = false; guideState = ParkingGuideState::DockingStarted; } else if (localization.info.distance > dockingStartDistance) { next.centerLineAligned = false; guideState = ParkingGuideState::Waiting; } else { next.centerLineAligned = false; guideState = ParkingGuideState::Tracking; } } SetGuideState(info, guideState); next.lastGuideStateCode = info.guideStateCode; result.parkingSpaceInfoList.push_back(info); result.message = QStringLiteral( "距离 %1 mm,横向偏差 %2 mm,航向角 %3°,接近速度 %4 m/s") .arg(info.distance, 0, 'f', 1) .arg(info.lateralOffset, 0, 'f', 1) .arg(info.angle, 0, 'f', 2) .arg(info.aircraftSpeed, 0, 'f', 1); const double modelVerifyDistance = modelParam.modelVerifyDistance; const int maxRecognitionAttempts = (std::max)(1, modelParam.maxRecognitionAttempts); const bool isInModelVerifyRange = modelVerifyDistance >= 0.0 && info.distance >= 0.0 && info.distance <= modelVerifyDistance; const ParkingGuideState measuredState = ParkingGuideStateFromCode(info.guideStateCode); const bool mustPreserveMeasuredState = measuredState == ParkingGuideState::Overshot; const bool hasSelectedModel = !previousState.selectedModelType.trimmed().isEmpty(); const bool canAttemptRecognition = !hasSelectedModel || previousState.modelRecognitionAttempts < maxRecognitionAttempts; if (isInModelVerifyRange && !previousState.modelRecognitionComplete && !previousState.modelRecognitionInProgress && canAttemptRecognition) { control.needModelRecognition = true; control.recognitionContext = previousState.selectedModelType.trimmed().toUtf8(); } else if (isInModelVerifyRange && !previousState.modelRecognitionComplete && !previousState.modelRecognitionInProgress && hasSelectedModel && !mustPreserveMeasuredState) { ParkingSpaceGuideInfo& failedInfo = result.parkingSpaceInfoList.back(); failedInfo.hasException = true; failedInfo.guideStateCode = static_cast(ParkingGuideState::AircraftVerificationFailed); failedInfo.guideText = GuideText(ParkingGuideState::AircraftVerificationFailed); control.nextState.lastGuideStateCode = failedInfo.guideStateCode; result.message = QStringLiteral("机型二次验证失败"); } return 0; } int DetectPresenter::RecognizeModel2D(const QImage& frame, const QByteArray& recognitionContext, ModelRecognitionResult& result) { result = ModelRecognitionResult(); if (!m_modelClassifier) { result.message = QStringLiteral("AAPGS机型识别器未初始化"); return -1; } AapgsModelClassifier::Classification classification; QString errorMessage; if (!m_modelClassifier->Classify(frame, classification, errorMessage)) { result.message = errorMessage.trimmed().isEmpty() ? QStringLiteral("AAPGS机型识别失败") : errorMessage.trimmed(); return -1; } result.recognitionComplete = true; result.verificationSupported = true; result.modelType = classification.modelType; result.confidence = classification.confidence; const QString expectedModelType = QString::fromUtf8(recognitionContext).trimmed(); const QString recognizedModelType = result.modelType.trimmed(); if (expectedModelType.isEmpty()) { result.message = QStringLiteral("自动识别机型:%1") .arg(recognizedModelType); return 0; } if (recognizedModelType.compare(expectedModelType, Qt::CaseInsensitive) != 0) { result.message = QStringLiteral("机型验证不一致:选择%1,识别%2") .arg(expectedModelType, recognizedModelType.isEmpty() ? QStringLiteral("未知") : recognizedModelType); return 0; } result.verified = true; return 0; } int DetectPresenter::DetectPersonInRegion2D( const QImage& frame, const VrPersonDetectionParam& personParam, PersonDetectionResult& result) { result = PersonDetectionResult(); if (!m_personDetector || frame.isNull()) { result.message = QStringLiteral("人员检测图像或检测器不可用"); return -1; } const double imageWidth = static_cast(frame.width()); const double imageHeight = static_cast(frame.height()); // The caller has already cropped the configured chock ROI. Detection and // result coordinates therefore use the cropped image as their full area. result.roi = QRectF(0.0, 0.0, imageWidth, imageHeight); PersionModelDetector::Analysis analysis; QString errorMessage; if (!m_personDetector->Analyze(frame, analysis, errorMessage)) { result.message = errorMessage.trimmed().isEmpty() ? QStringLiteral("Persion人员检测失败") : errorMessage.trimmed(); return -1; } const double threshold = std::clamp(personParam.confidenceThreshold, 0.0, 1.0); for (const PersionModelDetector::Detection& detection : analysis.detections) { if (!IsPersonDetection(detection) || detection.confidence < threshold) { continue; } const QRectF box = detection.boundingBox.intersected( QRectF(0.0, 0.0, imageWidth, imageHeight)); if (box.isEmpty()) { continue; } result.personBoxes.push_back(box); ++result.personCount; result.confidence = (std::max)(result.confidence, detection.confidence); } result.personInRegion = result.personCount > 0; result.message = result.personInRegion ? QStringLiteral("轮挡区域检测到人员") : QStringLiteral("等待人员进入轮挡区域"); return 0; }