/
klischa
/
AstraScanner2
Обзор
Документация
Войти
/
klischa
/
AstraScanner2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/settings/SettingsManager.cpp
739 строк
22 KB
k k
refactor: capture subsystem overhaul
15 июл 2026, 11:17
15 июл 2026, 11:17
1123c30
Код
Авторство
О чём код?
#include "SettingsManager.h" #include <QCoreApplication> #include <QDir> #include <QStandardPaths> #include <algorithm> #include <cmath> #include <QMutexLocker> namespace { constexpr const char *kScanTimeout = "scan/timeoutSec"; constexpr const char *kVoxelLeaf = "scan/voxelLeafSize"; constexpr const char *kFrameSkip = "scan/frameSkip"; constexpr const char *kDepthMin = "scan/depthMin"; constexpr const char *kDepthMax = "scan/depthMax"; constexpr const char *kColorCamera = "scan/colorCameraEnabled"; constexpr const char *kMirrorRgb = "scan/mirrorRgb"; constexpr const char *kSorMeanK = "filters/sorMeanK"; constexpr const char *kSorStddev = "filters/sorStddevMul"; constexpr const char *kRorRadius = "filters/rorRadius"; constexpr const char *kRorMinNeighbors = "filters/rorMinNeighbors"; constexpr const char *kIcpMaxCorrDist = "icp/maxCorrespondenceDistance"; constexpr const char *kIcpMaxIter = "icp/maxIterations"; constexpr const char *kIcpSkipNonConv = "icp/skipNonConverged"; constexpr const char *kIcpVoxelOut = "icp/voxelLeafOut"; constexpr const char *kPoissonDepth = "poisson/depth"; constexpr const char *kPoissonPointWt = "poisson/pointWeight"; constexpr const char *kPoissonSamples = "poisson/samplesPerNode"; constexpr const char *kPoissonNormalRad = "poisson/normalRadius"; constexpr const char *kPoissonKNearest = "poisson/kNearest"; constexpr const char *kProjectsDir = "paths/projectsDirectory"; constexpr const char *kLastExportDir = "paths/lastExportDirectory"; // --- Постобработка (PR #1) --- constexpr const char *kBackgroundDistThresh = "background/distanceThreshold"; constexpr const char *kBackgroundMaxIter = "background/maxIterations"; constexpr const char *kSegClusterTol = "segmentation/clusterTolerance"; constexpr const char *kSegMinClusterSize = "segmentation/minClusterSize"; constexpr const char *kSegMaxClusterSize = "segmentation/maxClusterSize"; constexpr const char *kSmoothNumIter = "smoothing/numIterations"; constexpr const char *kSmoothConvergence = "smoothing/convergence"; // --- Постобработка (PR #2) --- constexpr const char *kMeshSimplificationReduction = "mesh_simplification/targetReduction"; constexpr const char *kMeshHoleFillingMaxHole = "mesh_hole_filling/maxHoleSize"; // --- Оптический трекинг по маркерам --- constexpr const char *kMarkerSizeMm = "marker_tracking/sizeMm"; constexpr const char *kMarkerInitCount = "marker_tracking/initCount"; // --- Режим трекинга --- constexpr const char *kTrackingMode = "tracking/mode"; constexpr const char *kUseCudaForNeural = "tracking/useCudaForNeural"; // --- Neural + ICP confirm --- constexpr const char *kNeuralIcpConfirmEnabled = "tracking/neuralIcpConfirmEnabled"; constexpr const char *kNeuralIcpConfirmMaxCorrDist = "tracking/neuralIcpConfirmMaxCorrespondenceDistance"; constexpr const char *kNeuralIcpConfirmMaxIterations = "tracking/neuralIcpConfirmMaxIterations"; constexpr const char *kNeuralIcpConfirmMaxFitness = "tracking/neuralIcpConfirmMaxFitness"; constexpr const char *kNeuralIcpConfirmMaxCorrectionT = "tracking/neuralIcpConfirmMaxCorrectionTranslation"; constexpr const char *kNeuralIcpConfirmMaxCorrectionRot = "tracking/neuralIcpConfirmMaxCorrectionRotationDeg"; inline double clampFinite(double value, double minValue, double maxValue, double fallback) { if (!std::isfinite(value)) value = fallback; return std::clamp(value, minValue, maxValue); } inline float clampFinite(float value, float minValue, float maxValue, float fallback) { if (!std::isfinite(value)) value = fallback; return std::clamp(value, minValue, maxValue); } } SettingsManager* SettingsManager::s_instance = nullptr; void SettingsManager::reset() { // Очищаем настройки на диске QSettings settings("AstraScanner", "AstraScanner"); settings.clear(); settings.sync(); // Уничтожаем синглтон, чтобы следующий вызов instance() создал новый destroyInstance(); } SettingsManager &SettingsManager::instance() { if (!s_instance) { s_instance = new SettingsManager(); } return *s_instance; } void SettingsManager::destroyInstance() { delete s_instance; s_instance = nullptr; } SettingsManager::SettingsManager() : m_settings("AstraScanner", "AstraScanner") { // Убеждаемся, что QCoreApplication заполнен: QSettings без явных // organization/application будет ругаться иначе. На всякий случай выставим. if (QCoreApplication::organizationName().isEmpty()) { QCoreApplication::setOrganizationName("AstraScanner"); } if (QCoreApplication::applicationName().isEmpty()) { QCoreApplication::setApplicationName("AstraScanner"); } } template <typename T> void SettingsManager::setValue(const QString &key, const T &value) { { QMutexLocker locker(&m_settingsMutex); m_settings.setValue(key, value); } emit settingsChanged(key); } int SettingsManager::scanTimeoutSec() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kScanTimeout, 300).toInt(); } void SettingsManager::setScanTimeoutSec(int seconds) { setValue(kScanTimeout, std::clamp(seconds, 10, 7200)); } double SettingsManager::voxelLeafSize() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kVoxelLeaf, 0.002).toDouble(); } void SettingsManager::setVoxelLeafSize(double meters) { setValue(kVoxelLeaf, clampFinite(meters, 0.0005, 0.05, 0.002)); } int SettingsManager::frameSkip() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kFrameSkip, 3).toInt(); } void SettingsManager::setFrameSkip(int n) { setValue(kFrameSkip, std::clamp(n, 1, 30)); } double SettingsManager::depthMin() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kDepthMin, 0.1).toDouble(); } void SettingsManager::setDepthMin(double meters) { double value = clampFinite(meters, 0.05, 5.0, 0.1); const double currentMax = depthMax(); if (value >= currentMax) value = std::max(0.05, currentMax - 0.05); setValue(kDepthMin, value); } double SettingsManager::depthMax() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kDepthMax, 10.0).toDouble(); } void SettingsManager::setDepthMax(double meters) { double value = clampFinite(meters, 0.1, 20.0, 10.0); const double currentMin = depthMin(); if (value <= currentMin) value = std::min(20.0, currentMin + 0.05); setValue(kDepthMax, value); } bool SettingsManager::colorCameraEnabled() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kColorCamera, true).toBool(); } void SettingsManager::setColorCameraEnabled(bool enabled) { setValue(kColorCamera, enabled); } bool SettingsManager::mirrorRgb() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kMirrorRgb, false).toBool(); } void SettingsManager::setMirrorRgb(bool enabled) { setValue(kMirrorRgb, enabled); } int SettingsManager::sorMeanK() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSorMeanK, 50).toInt(); } void SettingsManager::setSorMeanK(int k) { setValue(kSorMeanK, std::clamp(k, 3, 100)); } double SettingsManager::sorStddevMul() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSorStddev, 1.0).toDouble(); } void SettingsManager::setSorStddevMul(double m) { setValue(kSorStddev, clampFinite(m, 0.1, 10.0, 1.0)); } double SettingsManager::rorRadius() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kRorRadius, 0.02).toDouble(); } void SettingsManager::setRorRadius(double meters) { setValue(kRorRadius, clampFinite(meters, 0.001, 1.0, 0.02)); } int SettingsManager::rorMinNeighbors() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kRorMinNeighbors, 5).toInt(); } void SettingsManager::setRorMinNeighbors(int n) { setValue(kRorMinNeighbors, std::clamp(n, 1, 100)); } double SettingsManager::icpMaxCorrespondenceDistance() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kIcpMaxCorrDist, 0.05).toDouble(); } void SettingsManager::setIcpMaxCorrespondenceDistance(double meters) { setValue(kIcpMaxCorrDist, clampFinite(meters, 0.001, 1.0, 0.05)); } int SettingsManager::icpMaxIterations() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kIcpMaxIter, 50).toInt(); } void SettingsManager::setIcpMaxIterations(int n) { setValue(kIcpMaxIter, std::clamp(n, 1, 500)); } bool SettingsManager::icpSkipNonConverged() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kIcpSkipNonConv, false).toBool(); } void SettingsManager::setIcpSkipNonConverged(bool skip) { setValue(kIcpSkipNonConv, skip); } double SettingsManager::icpVoxelLeafOut() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kIcpVoxelOut, 0.0).toDouble(); } void SettingsManager::setIcpVoxelLeafOut(double meters) { setValue(kIcpVoxelOut, clampFinite(meters, 0.0, 0.1, 0.0)); } int SettingsManager::poissonDepth() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kPoissonDepth, 9).toInt(); } void SettingsManager::setPoissonDepth(int depth) { setValue(kPoissonDepth, std::clamp(depth, 6, 11)); } double SettingsManager::poissonPointWeight() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kPoissonPointWt, 4.0).toDouble(); } void SettingsManager::setPoissonPointWeight(double w) { setValue(kPoissonPointWt, clampFinite(w, 0.0, 20.0, 4.0)); } double SettingsManager::poissonSamplesPerNode() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kPoissonSamples, 1.5).toDouble(); } void SettingsManager::setPoissonSamplesPerNode(double n) { setValue(kPoissonSamples, clampFinite(n, 0.5, 10.0, 1.5)); } double SettingsManager::poissonNormalRadius() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kPoissonNormalRad, 0.01).toDouble(); } void SettingsManager::setPoissonNormalRadius(double meters) { // 0 оставляем допустимым значением: оно переключает оценку нормалей на k-nearest. setValue(kPoissonNormalRad, clampFinite(meters, 0.0, 0.2, 0.01)); } int SettingsManager::poissonKNearest() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kPoissonKNearest, 20).toInt(); } void SettingsManager::setPoissonKNearest(int k) { setValue(kPoissonKNearest, std::clamp(k, 3, 100)); } QString SettingsManager::projectsDirectory() const { QMutexLocker locker(&m_settingsMutex); // По умолчанию — ./projects относительно exe. Полный путь вычисляется // при первом обращении, чтобы пользователь мог переопределить через GUI. QString defaultDir = QDir(QCoreApplication::applicationDirPath()).filePath("projects"); return m_settings.value(kProjectsDir, defaultDir).toString(); } void SettingsManager::setProjectsDirectory(const QString &path) { const QString cleaned = QDir::cleanPath(path.trimmed()); if (!cleaned.isEmpty()) setValue(kProjectsDir, cleaned); } QString SettingsManager::lastExportDirectory() const { QMutexLocker locker(&m_settingsMutex); const QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); return m_settings.value(kLastExportDir, defaultDir).toString(); } void SettingsManager::setLastExportDirectory(const QString &path) { const QString cleaned = QDir::cleanPath(path.trimmed()); if (!cleaned.isEmpty()) setValue(kLastExportDir, cleaned); } // --- Постобработка (PR #1) --- double SettingsManager::backgroundDistanceThreshold() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kBackgroundDistThresh, 0.01).toDouble(); } void SettingsManager::setBackgroundDistanceThreshold(double meters) { setValue(kBackgroundDistThresh, clampFinite(meters, 0.001, 0.2, 0.01)); } int SettingsManager::backgroundMaxIterations() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kBackgroundMaxIter, 1000).toInt(); } void SettingsManager::setBackgroundMaxIterations(int n) { setValue(kBackgroundMaxIter, std::clamp(n, 10, 10000)); } float SettingsManager::segmentationClusterTolerance() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSegClusterTol, 0.02f).toFloat(); } void SettingsManager::setSegmentationClusterTolerance(float meters) { setValue(kSegClusterTol, clampFinite(meters, 0.001f, 0.5f, 0.02f)); } int SettingsManager::segmentationMinClusterSize() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSegMinClusterSize, 100).toInt(); } void SettingsManager::setSegmentationMinClusterSize(int n) { setValue(kSegMinClusterSize, std::clamp(n, 1, 1000000)); } int SettingsManager::segmentationMaxClusterSize() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSegMaxClusterSize, 250000).toInt(); } void SettingsManager::setSegmentationMaxClusterSize(int n) { setValue(kSegMaxClusterSize, std::clamp(n, 1, 10000000)); } int SettingsManager::smoothingNumIterations() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSmoothNumIter, 10).toInt(); } void SettingsManager::setSmoothingNumIterations(int n) { setValue(kSmoothNumIter, std::clamp(n, 0, 200)); } float SettingsManager::smoothingConvergence() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kSmoothConvergence, 0.01f).toFloat(); } void SettingsManager::setSmoothingConvergence(float value) { setValue(kSmoothConvergence, clampFinite(value, 1e-6f, 1.0f, 0.01f)); } // --- Постобработка (PR #2) --- float SettingsManager::meshSimplificationTargetReduction() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kMeshSimplificationReduction, 0.5f).toFloat(); } void SettingsManager::setMeshSimplificationTargetReduction(float value) { setValue(kMeshSimplificationReduction, clampFinite(value, 0.0f, 0.95f, 0.5f)); } float SettingsManager::meshHoleFillingMaxHoleSize() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kMeshHoleFillingMaxHole, 0.05f).toFloat(); } void SettingsManager::setMeshHoleFillingMaxHoleSize(float meters) { setValue(kMeshHoleFillingMaxHole, clampFinite(meters, 0.001f, 10.0f, 0.05f)); } // --- Оптический трекинг по маркерам --- double SettingsManager::markerSizeMm() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kMarkerSizeMm, 20.0).toDouble(); // Значение по умолчанию: 20 мм } void SettingsManager::setMarkerSizeMm(double mm) { setValue(kMarkerSizeMm, clampFinite(mm, 1.0, 200.0, 20.0)); } int SettingsManager::markerInitCount() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kMarkerInitCount, 5).toInt(); // Значение по умолчанию: 5 маркеров } void SettingsManager::setMarkerInitCount(int count) { setValue(kMarkerInitCount, std::clamp(count, 3, 50)); } // --- Параметры для настройки детектора маркеров (для отладки) --- float SettingsManager::markerMinArea() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("marker_tracking/minArea", 200.0f).toFloat(); // Значение по умолчанию: ~200 пикселей (радиус 8) } void SettingsManager::setMarkerMinArea(float area) { float value = clampFinite(area, 1.0f, 100000.0f, 200.0f); const float currentMax = markerMaxArea(); if (value > currentMax) value = currentMax; setValue("marker_tracking/minArea", value); } float SettingsManager::markerMaxArea() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("marker_tracking/maxArea", 2800.0f).toFloat(); // Значение по умолчанию: ~2800 пикселей (радиус 30) } void SettingsManager::setMarkerMaxArea(float area) { float value = clampFinite(area, 1.0f, 1000000.0f, 2800.0f); const float currentMin = markerMinArea(); if (value < currentMin) value = currentMin; setValue("marker_tracking/maxArea", value); } float SettingsManager::markerMinCircularity() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("marker_tracking/minCircularity", 0.85f).toFloat(); // Значение по умолчанию: 0.85 } void SettingsManager::setMarkerMinCircularity(float circularity) { setValue("marker_tracking/minCircularity", clampFinite(circularity, 0.0f, 1.0f, 0.85f)); } float SettingsManager::markerMinConvexity() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("marker_tracking/minConvexity", 0.9f).toFloat(); // Значение по умолчанию: 0.9 } void SettingsManager::setMarkerMinConvexity(float convexity) { setValue("marker_tracking/minConvexity", clampFinite(convexity, 0.0f, 1.0f, 0.9f)); } float SettingsManager::markerMinInertiaRatio() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("marker_tracking/minInertiaRatio", 0.7f).toFloat(); // Значение по умолчанию: 0.7 } void SettingsManager::setMarkerMinInertiaRatio(float inertia) { setValue("marker_tracking/minInertiaRatio", clampFinite(inertia, 0.0f, 1.0f, 0.7f)); } // --- CCTag параметры --- QString SettingsManager::markerDetectorType() const { QMutexLocker locker(&m_settingsMutex); #ifdef ASTRA_ENABLE_CCTAG constexpr const char *kDefaultDetector = "CCTag"; #else constexpr const char *kDefaultDetector = "Circular"; #endif return m_settings.value("marker_tracking/detectorType", kDefaultDetector).toString(); } void SettingsManager::setMarkerDetectorType(const QString& type) { const QString normalized = type.trimmed(); if (normalized == QStringLiteral("Circular")) { setValue("marker_tracking/detectorType", normalized); return; } #ifdef ASTRA_ENABLE_CCTAG if (normalized == QStringLiteral("CCTag")) { setValue("marker_tracking/detectorType", normalized); return; } setValue("marker_tracking/detectorType", QStringLiteral("CCTag")); #else setValue("marker_tracking/detectorType", QStringLiteral("Circular")); #endif } int SettingsManager::cctagNumRings() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("cctag/numRings", 3).toInt(); } void SettingsManager::setCctagNumRings(int rings) { setValue("cctag/numRings", rings == 4 ? 4 : 3); } float SettingsManager::cctagMinIdentProba() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("cctag/minIdentProba", 1e-6f).toFloat(); } void SettingsManager::setCctagMinIdentProba(float proba) { setValue("cctag/minIdentProba", clampFinite(proba, 1e-9f, 1.0f, 1e-6f)); } bool SettingsManager::showMarkers() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value("marker_tracking/showMarkers", true).toBool(); // По умолчанию включено } void SettingsManager::setShowMarkers(bool show) { setValue("marker_tracking/showMarkers", show); } TrackingMode SettingsManager::trackingMode() const { QMutexLocker locker(&m_settingsMutex); QString modeStr = m_settings.value(kTrackingMode, "icp").toString(); if (modeStr == "marker_based") { return TrackingMode::MarkerBased; } else if (modeStr == "neural_based") { return TrackingMode::NeuralBased; } else { return TrackingMode::ICP; } } void SettingsManager::setTrackingMode(TrackingMode mode) { QString modeStr = "icp"; if (mode == TrackingMode::MarkerBased) { modeStr = "marker_based"; } else if (mode == TrackingMode::NeuralBased) { modeStr = "neural_based"; } setValue(kTrackingMode, modeStr); } bool SettingsManager::useCudaForNeural() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kUseCudaForNeural, false).toBool(); } void SettingsManager::setUseCudaForNeural(bool use) { setValue(kUseCudaForNeural, use); } bool SettingsManager::neuralIcpConfirmEnabled() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kNeuralIcpConfirmEnabled, true).toBool(); } void SettingsManager::setNeuralIcpConfirmEnabled(bool enabled) { setValue(kNeuralIcpConfirmEnabled, enabled); } double SettingsManager::neuralIcpConfirmMaxCorrespondenceDistance() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kNeuralIcpConfirmMaxCorrDist, 0.03).toDouble(); } void SettingsManager::setNeuralIcpConfirmMaxCorrespondenceDistance(double meters) { setValue(kNeuralIcpConfirmMaxCorrDist, clampFinite(meters, 0.001, 1.0, 0.03)); } int SettingsManager::neuralIcpConfirmMaxIterations() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kNeuralIcpConfirmMaxIterations, 20).toInt(); } void SettingsManager::setNeuralIcpConfirmMaxIterations(int iterations) { setValue(kNeuralIcpConfirmMaxIterations, std::clamp(iterations, 1, 200)); } double SettingsManager::neuralIcpConfirmMaxFitness() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kNeuralIcpConfirmMaxFitness, 0.0025).toDouble(); } void SettingsManager::setNeuralIcpConfirmMaxFitness(double fitness) { setValue(kNeuralIcpConfirmMaxFitness, clampFinite(fitness, 1e-8, 1.0, 0.0025)); } double SettingsManager::neuralIcpConfirmMaxCorrectionTranslation() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kNeuralIcpConfirmMaxCorrectionT, 0.03).toDouble(); } void SettingsManager::setNeuralIcpConfirmMaxCorrectionTranslation(double meters) { setValue(kNeuralIcpConfirmMaxCorrectionT, clampFinite(meters, 0.0, 1.0, 0.03)); } double SettingsManager::neuralIcpConfirmMaxCorrectionRotationDeg() const { QMutexLocker locker(&m_settingsMutex); return m_settings.value(kNeuralIcpConfirmMaxCorrectionRot, 6.0).toDouble(); } void SettingsManager::setNeuralIcpConfirmMaxCorrectionRotationDeg(double degrees) { setValue(kNeuralIcpConfirmMaxCorrectionRot, clampFinite(degrees, 0.0, 180.0, 6.0)); } void SettingsManager::sync() { m_settings.sync(); }