/
klischa
/
AstraScanner2
Обзор
Документация
Войти
/
klischa
/
AstraScanner2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/gui/SettingsDialog.cpp
828 строк
40 KB
k k
fix: mirror only applies to RGB, not depth
15 июл 2026, 13:52
15 июл 2026, 13:52
b1b1c50
Код
Авторство
О чём код?
#include "SettingsDialog.h" #include "HelpDialog.h" #include "../settings/SettingsManager.h" #include <QCheckBox> #include <QComboBox> #include <QDialogButtonBox> #include <QDoubleSpinBox> #include <QFileDialog> #include <QFormLayout> #include <QGroupBox> #include <QHBoxLayout> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QSlider> #include <QSpinBox> #include <QTabWidget> #include <QVBoxLayout> namespace { // Дефолты продублированы здесь, чтобы «Reset to defaults» не зависел от // состояния QSettings. При изменении дефолтов в SettingsManager.cpp эти // значения нужно обновить синхронно. constexpr int kDefaultScanTimeout = 300; constexpr double kDefaultVoxelLeaf = 0.002; constexpr int kDefaultFrameSkip = 3; constexpr double kDefaultDepthMin = 0.1; constexpr double kDefaultDepthMax = 10.0; constexpr bool kDefaultColorCamera = true; constexpr int kDefaultSorMeanK = 50; constexpr double kDefaultSorStddev = 1.0; constexpr double kDefaultRorRadius = 0.02; constexpr int kDefaultRorMinNeighbors = 5; constexpr double kDefaultIcpMaxCorr = 0.05; constexpr int kDefaultIcpMaxIter = 50; constexpr bool kDefaultIcpSkipNonConv = false; constexpr double kDefaultIcpVoxelOut = 0.0; constexpr int kDefaultPoissonDepth = 9; constexpr double kDefaultPoissonPointWt = 4.0; constexpr double kDefaultPoissonSamples = 1.5; constexpr double kDefaultPoissonNormalRad = 0.01; constexpr int kDefaultPoissonKNearest = 20; constexpr bool kDefaultNeuralIcpConfirmEnabled = true; constexpr double kDefaultNeuralIcpConfirmMaxCorr = 0.03; constexpr int kDefaultNeuralIcpConfirmMaxIter = 20; constexpr double kDefaultNeuralIcpConfirmMaxFitness = 0.0025; constexpr double kDefaultNeuralIcpCorrectionT = 0.03; constexpr double kDefaultNeuralIcpCorrectionRot = 6.0; } // namespace SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent) { setWindowTitle("Настройки → Параметры"); setModal(true); buildUi(); loadFromSettings(); resize(640, 560); } void SettingsDialog::buildUi() { auto *outer = new QVBoxLayout(this); auto *tabs = new QTabWidget(this); outer->addWidget(tabs); auto addFieldHelpRow = [this](QFormLayout *form, const QString &label, QWidget *field, const QString &anchor, const QString &tooltip) { auto *container = new QWidget(this); auto *layout = new QHBoxLayout(container); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(6); layout->addWidget(field, 1); auto *helpBtn = new QPushButton("?", container); helpBtn->setFixedWidth(28); helpBtn->setToolTip(tooltip); layout->addWidget(helpBtn); form->addRow(label, container); connect(helpBtn, &QPushButton::clicked, this, [this, anchor]() { onShowHelpSection(anchor); }); }; // ---- Вкладка «Сканирование» ---- auto *scanPage = new QWidget(this); auto *scanForm = new QFormLayout(scanPage); m_scanTimeout = new QSpinBox(this); m_scanTimeout->setRange(10, 3600); m_scanTimeout->setSuffix(" сек"); m_scanTimeout->setToolTip("Жёсткий лимит длительности сессии сканирования."); scanForm->addRow("Таймаут сканирования:", m_scanTimeout); m_voxelLeaf = new QDoubleSpinBox(this); m_voxelLeaf->setDecimals(4); m_voxelLeaf->setRange(0.0001, 0.05); m_voxelLeaf->setSingleStep(0.0005); m_voxelLeaf->setSuffix(" м"); m_voxelLeaf->setToolTip("Размер ячейки voxel-grid (используется в Magic Wand и «большой» децимации)."); addFieldHelpRow(scanForm, "Voxel leaf size:", m_voxelLeaf, QStringLiteral("voxel-leaf-size"), QStringLiteral("Открыть справку по параметру voxel leaf size")); m_frameSkip = new QSpinBox(this); m_frameSkip->setRange(1, 30); m_frameSkip->setToolTip("Каждый N-й кадр добавляется в облако. Больше = меньше точек, но быстрее."); addFieldHelpRow(scanForm, "Frame skip:", m_frameSkip, QStringLiteral("frame-skip"), QStringLiteral("Открыть справку по параметру frame skip")); m_depthMin = new QDoubleSpinBox(this); m_depthMin->setDecimals(2); m_depthMin->setRange(0.01, 15.0); m_depthMin->setSingleStep(0.05); m_depthMin->setSuffix(" м"); m_depthMin->setToolTip("Минимальная дистанция захвата точек (ближе — отбрасывается)."); scanForm->addRow("Мин. дистанция:", m_depthMin); m_depthMax = new QDoubleSpinBox(this); m_depthMax->setDecimals(2); m_depthMax->setRange(0.1, 30.0); m_depthMax->setSingleStep(0.5); m_depthMax->setSuffix(" м"); m_depthMax->setToolTip("Максимальная дистанция захвата точек (дальше — отбрасывается)."); scanForm->addRow("Макс. дистанция:", m_depthMax); // Связываем спинбоксы: min не может превысить max и наоборот. connect(m_depthMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, [this](double val) { m_depthMax->setMinimum(val + 0.01); }); connect(m_depthMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, [this](double val) { m_depthMin->setMaximum(val - 0.01); }); m_colorCamera = new QCheckBox("Включить цветную камеру (RGB)", this); m_colorCamera->setToolTip("Если выключено, захват идёт только по depth-сенсору; точки будут белыми."); scanForm->addRow("", m_colorCamera); m_mirrorRgb = new QCheckBox("Зеркалировать RGB-превью", this); m_mirrorRgb->setToolTip("Отражает RGB изображение по горизонтали. Depth-камера остаётся оригинальной."); scanForm->addRow("", m_mirrorRgb); tabs->addTab(scanPage, "Сканирование"); // ---- Вкладка «Фильтры» ---- auto *filtersPage = new QWidget(this); auto *filtersLayout = new QVBoxLayout(filtersPage); auto *sorGroup = new QGroupBox("SOR (Statistical Outlier Removal)", filtersPage); auto *sorForm = new QFormLayout(sorGroup); m_sorMeanK = new QSpinBox(this); m_sorMeanK->setRange(5, 500); sorForm->addRow("meanK:", m_sorMeanK); m_sorStddev = new QDoubleSpinBox(this); m_sorStddev->setRange(0.1, 10.0); m_sorStddev->setSingleStep(0.1); sorForm->addRow("stddev mul:", m_sorStddev); filtersLayout->addWidget(sorGroup); auto *rorGroup = new QGroupBox("ROR (Radius Outlier Removal)", filtersPage); auto *rorForm = new QFormLayout(rorGroup); m_rorRadius = new QDoubleSpinBox(this); m_rorRadius->setDecimals(4); m_rorRadius->setRange(0.001, 0.5); m_rorRadius->setSingleStep(0.001); m_rorRadius->setSuffix(" м"); rorForm->addRow("Radius:", m_rorRadius); m_rorMinNb = new QSpinBox(this); m_rorMinNb->setRange(1, 200); rorForm->addRow("Min neighbors:", m_rorMinNb); filtersLayout->addWidget(rorGroup); filtersLayout->addStretch(); tabs->addTab(filtersPage, "Фильтры"); // ---- Вкладка «ICP-регистрация» ---- auto *icpPage = new QWidget(this); auto *icpForm = new QFormLayout(icpPage); { auto *icpHelpRow = new QHBoxLayout(); auto *icpHelpBtn = new QPushButton("?", this); icpHelpBtn->setFixedWidth(28); icpHelpBtn->setToolTip("Открыть справку по настройке ICP"); icpHelpRow->addWidget(new QLabel("Справка по ICP:", this)); icpHelpRow->addWidget(icpHelpBtn); icpHelpRow->addStretch(); icpForm->addRow(icpHelpRow); connect(icpHelpBtn, &QPushButton::clicked, this, [this]() { onShowHelpSection(QStringLiteral("icp-settings")); }); } m_icpMaxCorr = new QDoubleSpinBox(this); m_icpMaxCorr->setDecimals(4); m_icpMaxCorr->setRange(0.001, 1.0); m_icpMaxCorr->setSingleStep(0.005); m_icpMaxCorr->setSuffix(" м"); m_icpMaxCorr->setToolTip("maxCorrespondenceDistance — порог для сопоставления точек."); addFieldHelpRow(icpForm, "Max correspondence distance:", m_icpMaxCorr, QStringLiteral("icp-max-correspondence"), QStringLiteral("Открыть справку по параметру ICP max correspondence distance")); m_icpMaxIter = new QSpinBox(this); m_icpMaxIter->setRange(1, 500); icpForm->addRow("Max iterations:", m_icpMaxIter); m_icpSkipNonConv = new QCheckBox("Пропускать сканы, на которых ICP не сошёлся", this); icpForm->addRow("", m_icpSkipNonConv); m_icpVoxelOut = new QDoubleSpinBox(this); m_icpVoxelOut->setDecimals(4); m_icpVoxelOut->setRange(0.0, 0.05); m_icpVoxelOut->setSingleStep(0.001); m_icpVoxelOut->setSuffix(" м"); m_icpVoxelOut->setToolTip("Размер ячейки voxel-grid для финальной децимации. 0 = без децимации."); icpForm->addRow("Voxel leaf (output):", m_icpVoxelOut); tabs->addTab(icpPage, "ICP"); // ---- Вкладка «Poisson» ---- auto *poissonPage = new QWidget(this); auto *poissonForm = new QFormLayout(poissonPage); { auto *poissonHelpRow = new QHBoxLayout(); auto *poissonHelpBtn = new QPushButton("?", this); poissonHelpBtn->setFixedWidth(28); poissonHelpBtn->setToolTip("Открыть справку по Poisson-реконструкции и нормалям"); poissonHelpRow->addWidget(new QLabel("Справка по Poisson:", this)); poissonHelpRow->addWidget(poissonHelpBtn); poissonHelpRow->addStretch(); poissonForm->addRow(poissonHelpRow); connect(poissonHelpBtn, &QPushButton::clicked, this, [this]() { onShowHelpSection(QStringLiteral("poisson-settings")); }); } m_poissonDepth = new QSpinBox(this); m_poissonDepth->setRange(5, 12); m_poissonDepth->setToolTip("Глубина октодерева. 8≈256^3, 9≈512^3, 10≈1024^3."); addFieldHelpRow(poissonForm, "Depth:", m_poissonDepth, QStringLiteral("poisson-depth"), QStringLiteral("Открыть справку по параметру Poisson depth")); m_poissonPointWeight = new QDoubleSpinBox(this); m_poissonPointWeight->setRange(0.0, 20.0); m_poissonPointWeight->setSingleStep(0.5); addFieldHelpRow(poissonForm, "Point weight:", m_poissonPointWeight, QStringLiteral("poisson-point-weight"), QStringLiteral("Открыть справку по параметру Poisson point weight")); m_poissonSamples = new QDoubleSpinBox(this); m_poissonSamples->setRange(1.0, 20.0); m_poissonSamples->setSingleStep(0.5); poissonForm->addRow("Samples/node:", m_poissonSamples); m_poissonNormalRadius = new QDoubleSpinBox(this); m_poissonNormalRadius->setDecimals(4); m_poissonNormalRadius->setRange(0.0, 0.1); m_poissonNormalRadius->setSingleStep(0.001); m_poissonNormalRadius->setSuffix(" м"); m_poissonNormalRadius->setToolTip("0 → использовать k-nearest (см. поле k)."); addFieldHelpRow(poissonForm, "Normal radius:", m_poissonNormalRadius, QStringLiteral("poisson-normal-radius"), QStringLiteral("Открыть справку по параметру Poisson normal radius")); m_poissonKNearest = new QSpinBox(this); m_poissonKNearest->setRange(5, 200); poissonForm->addRow("k (nearest):", m_poissonKNearest); tabs->addTab(poissonPage, "Poisson"); // ---- Вкладка «Пути» ---- auto *pathsPage = new QWidget(this); auto *pathsLayout = new QFormLayout(pathsPage); auto *projectsRow = new QHBoxLayout(); m_projectsDir = new QLineEdit(this); m_browseProjects = new QPushButton("Обзор…", this); projectsRow->addWidget(m_projectsDir); projectsRow->addWidget(m_browseProjects); pathsLayout->addRow("Директория проектов:", projectsRow); auto *exportRow = new QHBoxLayout(); m_exportDir = new QLineEdit(this); m_browseExport = new QPushButton("Обзор…", this); exportRow->addWidget(m_exportDir); exportRow->addWidget(m_browseExport); pathsLayout->addRow("Директория экспорта:", exportRow); tabs->addTab(pathsPage, "Пути"); // ---- Вкладка "Трекинг" ---- auto *trackingPage = new QWidget(this); auto *trackingForm = new QFormLayout(trackingPage); m_markerSizeMm = new QDoubleSpinBox(this); m_markerSizeMm->setDecimals(1); m_markerSizeMm->setRange(5.0, 50.0); m_markerSizeMm->setSingleStep(1.0); m_markerSizeMm->setSuffix(" мм"); m_markerSizeMm->setToolTip("Физический размер маркера в миллиметрах."); trackingForm->addRow("Размер маркера:", m_markerSizeMm); m_markerInitCount = new QSpinBox(this); m_markerInitCount->setRange(3, 20); m_markerInitCount->setToolTip("Минимальное количество маркеров для инициализации карты."); trackingForm->addRow("Мин. маркеров для инициализации:", m_markerInitCount); // Параметры детектора маркеров (для отладки) auto *detectorGroup = new QGroupBox("Параметры детектора маркеров (для отладки)", trackingPage); auto *detectorLayout = new QFormLayout(detectorGroup); m_markerMinArea = new QDoubleSpinBox(this); m_markerMinArea->setDecimals(1); m_markerMinArea->setRange(1.0, 10000.0); m_markerMinArea->setSingleStep(10.0); m_markerMinArea->setSuffix(" px²"); m_markerMinArea->setToolTip("Минимальная площадь blob'а в пикселях²"); detectorLayout->addRow("Min Area:", m_markerMinArea); m_markerMaxArea = new QDoubleSpinBox(this); m_markerMaxArea->setDecimals(1); m_markerMaxArea->setRange(1.0, 10000.0); m_markerMaxArea->setSingleStep(10.0); m_markerMaxArea->setSuffix(" px²"); m_markerMaxArea->setToolTip("Максимальная площадь blob'а в пикселях²"); detectorLayout->addRow("Max Area:", m_markerMaxArea); m_markerMinCircularity = new QDoubleSpinBox(this); m_markerMinCircularity->setDecimals(3); m_markerMinCircularity->setRange(0.1, 1.0); m_markerMinCircularity->setSingleStep(0.05); m_markerMinCircularity->setToolTip("Минимальная круглость (1.0 = идеальный круг)"); detectorLayout->addRow("Min Circularity:", m_markerMinCircularity); m_markerMinConvexity = new QDoubleSpinBox(this); m_markerMinConvexity->setDecimals(3); m_markerMinConvexity->setRange(0.1, 1.0); m_markerMinConvexity->setSingleStep(0.05); m_markerMinConvexity->setToolTip("Минимальная выпуклость"); detectorLayout->addRow("Min Convexity:", m_markerMinConvexity); m_markerMinInertiaRatio = new QDoubleSpinBox(this); m_markerMinInertiaRatio->setDecimals(3); m_markerMinInertiaRatio->setRange(0.1, 1.0); m_markerMinInertiaRatio->setSingleStep(0.05); m_markerMinInertiaRatio->setToolTip("Минимальное соотношение осей (1.0 = круг, 0.0 = линия)"); detectorLayout->addRow("Min Inertia Ratio:", m_markerMinInertiaRatio); trackingForm->addRow(detectorGroup); // Исправление #14: чекбокс для скрытия меток маркеров m_showMarkersChk = new QCheckBox("Показывать метки маркеров на RGB-изображении", trackingPage); m_showMarkersChk->setToolTip("Если выключено, изображение с детектированными маркерами не будет отображаться в интерфейсе"); trackingForm->addRow(m_showMarkersChk); // Исправление #21: чекбокс для использования CUDA в нейросети m_useCudaForNeural = new QCheckBox("Использовать CUDA для нейросетевого трекинга", trackingPage); m_useCudaForNeural->setToolTip("Включить CUDA (GPU) для ускорения инференса нейросети. Требуется совместимая видеокарта с CUDA"); trackingForm->addRow(m_useCudaForNeural); auto *neuralIcpGroup = new QGroupBox("Neural + ICP confirm", trackingPage); auto *neuralIcpLayout = new QFormLayout(neuralIcpGroup); { auto *neuralHelpRow = new QHBoxLayout(); auto *neuralHelpBtn = new QPushButton("?", neuralIcpGroup); neuralHelpBtn->setFixedWidth(28); neuralHelpBtn->setToolTip("Открыть справку по нейросетевому трекингу и ICP confirm"); neuralHelpRow->addWidget(new QLabel("Справка по Neural + ICP:", neuralIcpGroup)); neuralHelpRow->addWidget(neuralHelpBtn); neuralHelpRow->addStretch(); neuralIcpLayout->addRow(neuralHelpRow); connect(neuralHelpBtn, &QPushButton::clicked, this, [this]() { onShowHelpSection(QStringLiteral("neural-icp")); }); } m_neuralIcpConfirmEnabled = new QCheckBox("Включить ICP confirm/refinement после нейросети", neuralIcpGroup); m_neuralIcpConfirmEnabled->setToolTip("Если включено, neural pose проходит дополнительное подтверждение и уточнение через короткий ICP с initial guess от нейросети."); neuralIcpLayout->addRow("", m_neuralIcpConfirmEnabled); m_neuralIcpConfirmMaxCorr = new QDoubleSpinBox(neuralIcpGroup); m_neuralIcpConfirmMaxCorr->setDecimals(4); m_neuralIcpConfirmMaxCorr->setRange(0.001, 0.1); m_neuralIcpConfirmMaxCorr->setSingleStep(0.001); m_neuralIcpConfirmMaxCorr->setSuffix(" м"); m_neuralIcpConfirmMaxCorr->setToolTip("Максимальная дистанция соответствия для ICP confirm. Меньше = строже, больше = устойчивее к шуму."); neuralIcpLayout->addRow("ICP max correspondence distance:", m_neuralIcpConfirmMaxCorr); m_neuralIcpConfirmMaxIter = new QSpinBox(neuralIcpGroup); m_neuralIcpConfirmMaxIter->setRange(1, 100); m_neuralIcpConfirmMaxIter->setToolTip("Максимальное число итераций короткого ICP confirm-stage."); neuralIcpLayout->addRow("ICP max iterations:", m_neuralIcpConfirmMaxIter); m_neuralIcpConfirmMaxFitness = new QDoubleSpinBox(neuralIcpGroup); m_neuralIcpConfirmMaxFitness->setDecimals(6); m_neuralIcpConfirmMaxFitness->setRange(0.000001, 0.05); m_neuralIcpConfirmMaxFitness->setSingleStep(0.0005); m_neuralIcpConfirmMaxFitness->setToolTip("Максимально допустимый fitness ICP confirm. Меньше = строже."); addFieldHelpRow(neuralIcpLayout, "ICP max fitness:", m_neuralIcpConfirmMaxFitness, QStringLiteral("neural-icp-fitness"), QStringLiteral("Открыть справку по параметру ICP max fitness в neural confirm")); m_neuralIcpCorrectionTranslation = new QDoubleSpinBox(neuralIcpGroup); m_neuralIcpCorrectionTranslation->setDecimals(4); m_neuralIcpCorrectionTranslation->setRange(0.001, 0.2); m_neuralIcpCorrectionTranslation->setSingleStep(0.001); m_neuralIcpCorrectionTranslation->setSuffix(" м"); m_neuralIcpCorrectionTranslation->setToolTip("Насколько ICP может исправить neural pose по трансляции. Если нужно больше — neural proposal считается подозрительным."); addFieldHelpRow(neuralIcpLayout, "Max ICP correction translation:", m_neuralIcpCorrectionTranslation, QStringLiteral("neural-icp-correction-translation"), QStringLiteral("Открыть справку по допустимой коррекции трансляции после neural pose")); m_neuralIcpCorrectionRotation = new QDoubleSpinBox(neuralIcpGroup); m_neuralIcpCorrectionRotation->setDecimals(2); m_neuralIcpCorrectionRotation->setRange(0.1, 45.0); m_neuralIcpCorrectionRotation->setSingleStep(0.5); m_neuralIcpCorrectionRotation->setSuffix(" °"); m_neuralIcpCorrectionRotation->setToolTip("Насколько ICP может довернуть neural pose. Если превышено — pose отклоняется."); addFieldHelpRow(neuralIcpLayout, "Max ICP correction rotation:", m_neuralIcpCorrectionRotation, QStringLiteral("neural-icp-correction-rotation"), QStringLiteral("Открыть справку по допустимой коррекции угла после neural pose")); trackingForm->addRow(neuralIcpGroup); // Выбор типа детектора маркеров (Circular или CCTag) auto *markerDetectorGroup = new QGroupBox("Тип детектора маркеров", trackingPage); auto *markerDetectorLayout = new QFormLayout(markerDetectorGroup); m_markerDetectorType = new QComboBox(this); m_markerDetectorType->addItem("Circular", "Circular"); #ifdef ASTRA_ENABLE_CCTAG m_markerDetectorType->addItem("CCTag", "CCTag"); #endif m_markerDetectorType->setToolTip("Выберите детектор маркеров: Circular (простой) или CCTag (с идентификацией)"); markerDetectorLayout->addRow("Тип детектора:", m_markerDetectorType); // CCTag параметры (вложенные в группу с отступом) auto *cctagGroup = new QGroupBox("Параметры CCTag", markerDetectorGroup); auto *cctagLayout = new QFormLayout(cctagGroup); cctagLayout->setContentsMargins(10, 5, 10, 5); // Отступы для вложенной группы m_cctagNumRings = new QSpinBox(cctagGroup); m_cctagNumRings->setRange(3, 4); m_cctagNumRings->setSingleStep(1); m_cctagNumRings->setToolTip("Количество колец маркера CCTag (3 или 4)"); cctagLayout->addRow("Количество колец:", m_cctagNumRings); m_cctagMinIdentProba = new QDoubleSpinBox(cctagGroup); m_cctagMinIdentProba->setDecimals(6); m_cctagMinIdentProba->setRange(1e-6, 1.0); m_cctagMinIdentProba->setSingleStep(1e-6); m_cctagMinIdentProba->setToolTip("Минимальная вероятность идентификации (1e-6 ... 1.0)"); cctagLayout->addRow("Min Ident Probability:", m_cctagMinIdentProba); markerDetectorLayout->addRow(cctagGroup); trackingForm->addRow(markerDetectorGroup); // Новый переключатель режима трекинга // m_trackingModeCombo = new QComboBox(this); // Удалено: элемент управления для выбора режима трекинга // m_trackingModeCombo->addItem("ICP", "icp"); // Удалено: элемент управления для выбора режима трекинга // m_trackingModeCombo->addItem("Маркеры", "marker_based"); // Удалено: элемент управления для выбора режима трекинга // m_trackingModeCombo->addItem("Нейросеть", "neural_based"); // Удалено: элемент управления для выбора режима трекинга // trackingForm->addRow("Режим трекинга:", m_trackingModeCombo); // Удалено: элемент управления для выбора режима трекинга tabs->addTab(trackingPage, "Трекинг"); // ---- Вкладка "Mesh Processing (PR #2)" ---- auto *meshPage = new QWidget(this); auto *meshForm = new QFormLayout(meshPage); m_meshSimplification = new QSlider(Qt::Horizontal, meshPage); m_meshSimplification->setRange(10, 90); m_meshSimplification->setToolTip("Процент упрощения меша (10-90%). 50% = уменьшение полигонов в 2 раза."); m_meshSimplificationValue = new QLabel("50%", meshPage); meshForm->addRow("Упрощение меша (%):", m_meshSimplification); // Исправление #13: обновление label при изменении слайдера connect(m_meshSimplification, &QSlider::valueChanged, this, [this](int value) { m_meshSimplificationValue->setText(QString::number(value) + "%"); }); m_meshHoleFilling = new QDoubleSpinBox(this); m_meshHoleFilling->setDecimals(3); m_meshHoleFilling->setRange(0.001, 0.5); m_meshHoleFilling->setSingleStep(0.005); m_meshHoleFilling->setSuffix(" м"); m_meshHoleFilling->setToolTip("Максимальный размер дыры для заполнения (в метрах). Более крупные дыры не будут заполнены."); meshForm->addRow("Макс. дыра для закрытия:", m_meshHoleFilling); tabs->addTab(meshPage, "Mesh PR#2"); // ---- Кнопки ---- m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::RestoreDefaults, this); outer->addWidget(m_buttons); connect(m_buttons->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &SettingsDialog::onApply); connect(m_buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &SettingsDialog::onResetDefaults); connect(m_buttons, &QDialogButtonBox::accepted, this, &SettingsDialog::onAccepted); connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_browseProjects, &QPushButton::clicked, this, &SettingsDialog::onBrowseProjectsDir); connect(m_browseExport, &QPushButton::clicked, this, &SettingsDialog::onBrowseExportDir); // Подключаем изменение типа детектора для скрытия/показа CCTag параметров connect(m_markerDetectorType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) { bool isCctag = (m_markerDetectorType->itemData(index).toString() == "CCTag"); m_cctagNumRings->setVisible(isCctag); m_cctagMinIdentProba->setVisible(isCctag); }); connect(m_neuralIcpConfirmEnabled, &QCheckBox::toggled, this, [this](bool enabled) { m_neuralIcpConfirmMaxCorr->setEnabled(enabled); m_neuralIcpConfirmMaxIter->setEnabled(enabled); m_neuralIcpConfirmMaxFitness->setEnabled(enabled); m_neuralIcpCorrectionTranslation->setEnabled(enabled); m_neuralIcpCorrectionRotation->setEnabled(enabled); }); // Подключаем чекбокс показа маркеров (для бага #14) connect(m_showMarkersChk, &QCheckBox::stateChanged, this, [this](int state) { Q_UNUSED(state); // Здесь можно добавить логику для обновления UI }); } void SettingsDialog::loadFromSettings() { const SettingsManager &s = SettingsManager::instance(); m_scanTimeout->setValue(s.scanTimeoutSec()); m_voxelLeaf->setValue(s.voxelLeafSize()); m_frameSkip->setValue(s.frameSkip()); m_depthMin->setValue(s.depthMin()); m_depthMax->setValue(s.depthMax()); m_colorCamera->setChecked(s.colorCameraEnabled()); m_mirrorRgb->setChecked(s.mirrorRgb()); m_sorMeanK->setValue(s.sorMeanK()); m_sorStddev->setValue(s.sorStddevMul()); m_rorRadius->setValue(s.rorRadius()); m_rorMinNb->setValue(s.rorMinNeighbors()); m_icpMaxCorr->setValue(s.icpMaxCorrespondenceDistance()); m_icpMaxIter->setValue(s.icpMaxIterations()); m_icpSkipNonConv->setChecked(s.icpSkipNonConverged()); m_icpVoxelOut->setValue(s.icpVoxelLeafOut()); m_poissonDepth->setValue(s.poissonDepth()); m_poissonPointWeight->setValue(s.poissonPointWeight()); m_poissonSamples->setValue(s.poissonSamplesPerNode()); m_poissonNormalRadius->setValue(s.poissonNormalRadius()); m_poissonKNearest->setValue(s.poissonKNearest()); m_projectsDir->setText(s.projectsDirectory()); m_exportDir->setText(s.lastExportDirectory()); m_markerSizeMm->setValue(s.markerSizeMm()); m_markerInitCount->setValue(s.markerInitCount()); m_markerMinArea->setValue(s.markerMinArea()); m_markerMaxArea->setValue(s.markerMaxArea()); m_markerMinCircularity->setValue(s.markerMinCircularity()); m_markerMinConvexity->setValue(s.markerMinConvexity()); m_markerMinInertiaRatio->setValue(s.markerMinInertiaRatio()); // Исправление #14: загрузка чекбокса показа меток маркеров m_showMarkersChk->setChecked(s.showMarkers()); // Загрузка настройки использования CUDA для нейросети m_useCudaForNeural->setChecked(s.useCudaForNeural()); m_neuralIcpConfirmEnabled->setChecked(s.neuralIcpConfirmEnabled()); m_neuralIcpConfirmMaxCorr->setValue(s.neuralIcpConfirmMaxCorrespondenceDistance()); m_neuralIcpConfirmMaxIter->setValue(s.neuralIcpConfirmMaxIterations()); m_neuralIcpConfirmMaxFitness->setValue(s.neuralIcpConfirmMaxFitness()); m_neuralIcpCorrectionTranslation->setValue(s.neuralIcpConfirmMaxCorrectionTranslation()); m_neuralIcpCorrectionRotation->setValue(s.neuralIcpConfirmMaxCorrectionRotationDeg()); // Загружаем тип детектора и CCTag параметры QString detectorType = s.markerDetectorType(); int detectorIndex = m_markerDetectorType->findData(detectorType); if (detectorIndex >= 0) { m_markerDetectorType->setCurrentIndex(detectorIndex); } else { m_markerDetectorType->setCurrentIndex(0); // По умолчанию Circular } // Устанавливаем видимость CCTag параметров по фактическому выбранному пункту bool isCctag = (m_markerDetectorType->currentData().toString() == "CCTag"); m_cctagNumRings->setVisible(isCctag); m_cctagMinIdentProba->setVisible(isCctag); m_cctagNumRings->setValue(s.cctagNumRings()); m_cctagMinIdentProba->setValue(s.cctagMinIdentProba()); const bool icpConfirmEnabled = m_neuralIcpConfirmEnabled->isChecked(); m_neuralIcpConfirmMaxCorr->setEnabled(icpConfirmEnabled); m_neuralIcpConfirmMaxIter->setEnabled(icpConfirmEnabled); m_neuralIcpConfirmMaxFitness->setEnabled(icpConfirmEnabled); m_neuralIcpCorrectionTranslation->setEnabled(icpConfirmEnabled); m_neuralIcpCorrectionRotation->setEnabled(icpConfirmEnabled); // Загружаем настройки PR #2 float simplValue = s.meshSimplificationTargetReduction(); m_meshSimplification->setValue(static_cast<int>(simplValue * 100)); m_meshSimplificationValue->setText(QString::number(static_cast<int>(simplValue * 100)) + "%"); m_meshHoleFilling->setValue(s.meshHoleFillingMaxHoleSize()); // // Загружаем режим трекинга // ProjectManager::TrackingMode mode = SettingsManager::instance().trackingMode(); // if (mode == ProjectManager::TrackingMode::ICP) { // m_trackingModeCombo->setCurrentIndex(0); // } else if (mode == ProjectManager::TrackingMode::MarkerBased) { // m_trackingModeCombo->setCurrentIndex(1); // } else if (mode == ProjectManager::TrackingMode::NeuralBased) { // m_trackingModeCombo->setCurrentIndex(2); // } } void SettingsDialog::saveToSettings() { SettingsManager &s = SettingsManager::instance(); s.setScanTimeoutSec(m_scanTimeout->value()); s.setVoxelLeafSize(m_voxelLeaf->value()); s.setFrameSkip(m_frameSkip->value()); s.setDepthMin(m_depthMin->value()); s.setDepthMax(m_depthMax->value()); s.setColorCameraEnabled(m_colorCamera->isChecked()); s.setMirrorRgb(m_mirrorRgb->isChecked()); s.setSorMeanK(m_sorMeanK->value()); s.setSorStddevMul(m_sorStddev->value()); s.setRorRadius(m_rorRadius->value()); s.setRorMinNeighbors(m_rorMinNb->value()); s.setIcpMaxCorrespondenceDistance(m_icpMaxCorr->value()); s.setIcpMaxIterations(m_icpMaxIter->value()); s.setIcpSkipNonConverged(m_icpSkipNonConv->isChecked()); s.setIcpVoxelLeafOut(m_icpVoxelOut->value()); s.setPoissonDepth(m_poissonDepth->value()); s.setPoissonPointWeight(m_poissonPointWeight->value()); s.setPoissonSamplesPerNode(m_poissonSamples->value()); s.setPoissonNormalRadius(m_poissonNormalRadius->value()); s.setPoissonKNearest(m_poissonKNearest->value()); s.setProjectsDirectory(m_projectsDir->text()); s.setLastExportDirectory(m_exportDir->text()); s.setMarkerSizeMm(m_markerSizeMm->value()); s.setMarkerInitCount(m_markerInitCount->value()); s.setMarkerMinArea(m_markerMinArea->value()); s.setMarkerMaxArea(m_markerMaxArea->value()); s.setMarkerMinCircularity(m_markerMinCircularity->value()); s.setMarkerMinConvexity(m_markerMinConvexity->value()); s.setMarkerMinInertiaRatio(m_markerMinInertiaRatio->value()); // Исправление #14: сохранение чекбокса показа меток маркеров s.setShowMarkers(m_showMarkersChk->isChecked()); // Сохранение настройки использования CUDA для нейросети s.setUseCudaForNeural(m_useCudaForNeural->isChecked()); s.setNeuralIcpConfirmEnabled(m_neuralIcpConfirmEnabled->isChecked()); s.setNeuralIcpConfirmMaxCorrespondenceDistance(m_neuralIcpConfirmMaxCorr->value()); s.setNeuralIcpConfirmMaxIterations(m_neuralIcpConfirmMaxIter->value()); s.setNeuralIcpConfirmMaxFitness(m_neuralIcpConfirmMaxFitness->value()); s.setNeuralIcpConfirmMaxCorrectionTranslation(m_neuralIcpCorrectionTranslation->value()); s.setNeuralIcpConfirmMaxCorrectionRotationDeg(m_neuralIcpCorrectionRotation->value()); // Сохраняем тип детектора и CCTag параметры QString detectorType = m_markerDetectorType->itemData(m_markerDetectorType->currentIndex()).toString(); s.setMarkerDetectorType(detectorType); s.setCctagNumRings(m_cctagNumRings->value()); s.setCctagMinIdentProba(static_cast<float>(m_cctagMinIdentProba->value())); // Сохраняем настройки PR #2 float simplValue = m_meshSimplification->value() / 100.0f; s.setMeshSimplificationTargetReduction(simplValue); s.setMeshHoleFillingMaxHoleSize(static_cast<float>(m_meshHoleFilling->value())); // // Сохраняем режим трекинга // int index = m_trackingModeCombo->currentIndex(); // ProjectManager::TrackingMode mode = ProjectManager::TrackingMode::ICP; // if (index == 1) { // mode = ProjectManager::TrackingMode::MarkerBased; // } else if (index == 2) { // mode = ProjectManager::TrackingMode::NeuralBased; // } // s.setTrackingMode(mode); s.sync(); } void SettingsDialog::onApply() { saveToSettings(); } void SettingsDialog::onAccepted() { saveToSettings(); accept(); } void SettingsDialog::onResetDefaults() { m_scanTimeout->setValue(kDefaultScanTimeout); m_voxelLeaf->setValue(kDefaultVoxelLeaf); m_frameSkip->setValue(kDefaultFrameSkip); m_depthMin->setValue(kDefaultDepthMin); m_depthMax->setValue(kDefaultDepthMax); m_colorCamera->setChecked(kDefaultColorCamera); m_sorMeanK->setValue(kDefaultSorMeanK); m_sorStddev->setValue(kDefaultSorStddev); m_rorRadius->setValue(kDefaultRorRadius); m_rorMinNb->setValue(kDefaultRorMinNeighbors); m_icpMaxCorr->setValue(kDefaultIcpMaxCorr); m_icpMaxIter->setValue(kDefaultIcpMaxIter); m_icpSkipNonConv->setChecked(kDefaultIcpSkipNonConv); m_icpVoxelOut->setValue(kDefaultIcpVoxelOut); m_poissonDepth->setValue(kDefaultPoissonDepth); m_poissonPointWeight->setValue(kDefaultPoissonPointWt); m_poissonSamples->setValue(kDefaultPoissonSamples); m_poissonNormalRadius->setValue(kDefaultPoissonNormalRad); m_poissonKNearest->setValue(kDefaultPoissonKNearest); // Пути не сбрасываем — это пользовательский выбор, а не «дефолт», // сохранённый в исходниках. // Настройки трекинга m_markerSizeMm->setValue(20.0); m_markerInitCount->setValue(5); // Сбрасываем тип детектора к рабочему дефолту: CCTag, если собран, // иначе Circular (он не подходит для MarkerBased pose, но доступен как детектор). #ifdef ASTRA_ENABLE_CCTAG const int defaultDetectorIndex = m_markerDetectorType->findData("CCTag"); m_markerDetectorType->setCurrentIndex(defaultDetectorIndex >= 0 ? defaultDetectorIndex : 0); #else m_markerDetectorType->setCurrentIndex(0); #endif m_cctagNumRings->setValue(3); m_cctagMinIdentProba->setValue(0.000001); // 1e-6 // Сбрасываем настройки PR #2 m_meshSimplification->setValue(50); // 50% упрощения m_meshSimplificationValue->setText("50%"); m_meshHoleFilling->setValue(0.05); // 5 см // Сбрасываем чекбокс показа меток (по умолчанию включено) m_showMarkersChk->setChecked(true); // Сбрасываем чекбокс CUDA (по умолчанию выключен) m_useCudaForNeural->setChecked(false); m_neuralIcpConfirmEnabled->setChecked(kDefaultNeuralIcpConfirmEnabled); m_neuralIcpConfirmMaxCorr->setValue(kDefaultNeuralIcpConfirmMaxCorr); m_neuralIcpConfirmMaxIter->setValue(kDefaultNeuralIcpConfirmMaxIter); m_neuralIcpConfirmMaxFitness->setValue(kDefaultNeuralIcpConfirmMaxFitness); m_neuralIcpCorrectionTranslation->setValue(kDefaultNeuralIcpCorrectionT); m_neuralIcpCorrectionRotation->setValue(kDefaultNeuralIcpCorrectionRot); // // Сбрасываем режим трекинга на ICP // m_trackingModeCombo->setCurrentIndex(0); } void SettingsDialog::onBrowseProjectsDir() { const QString dir = QFileDialog::getExistingDirectory( this, "Директория проектов", m_projectsDir->text()); if (!dir.isEmpty()) m_projectsDir->setText(dir); } void SettingsDialog::onBrowseExportDir() { const QString dir = QFileDialog::getExistingDirectory( this, "Директория экспорта", m_exportDir->text()); if (!dir.isEmpty()) m_exportDir->setText(dir); } void SettingsDialog::onShowHelpSection(const QString &anchor) { if (!m_helpDialog) { m_helpDialog = new HelpDialog(this); m_helpDialog->setAttribute(Qt::WA_DeleteOnClose, false); } m_helpDialog->show(); m_helpDialog->raise(); m_helpDialog->activateWindow(); m_helpDialog->openSection(anchor); }