Добавил фильтров и общий рефакторинг
This commit is contained in:
@@ -81,6 +81,7 @@ HEADERS += \
|
|||||||
src/strategies/preprocess_pcl_stages.h \
|
src/strategies/preprocess_pcl_stages.h \
|
||||||
src/strategies/registration_icp_stage.h \
|
src/strategies/registration_icp_stage.h \
|
||||||
src/strategies/reconstruction_pcl_greedy_stage.h \
|
src/strategies/reconstruction_pcl_greedy_stage.h \
|
||||||
|
src/strategies/reconstruction_pcl_poisson_stage.h \
|
||||||
src/strategies/reconstruction_surface_stage.h \
|
src/strategies/reconstruction_surface_stage.h \
|
||||||
src/strategies/transform_tf_stage.h \
|
src/strategies/transform_tf_stage.h \
|
||||||
src/tests/pipeline_smoke_tests.h \
|
src/tests/pipeline_smoke_tests.h \
|
||||||
|
|||||||
@@ -35,6 +35,26 @@
|
|||||||
- `strategies`: конкретные стадии пайплайна (preprocess, transform, registration, reconstruction).
|
- `strategies`: конкретные стадии пайплайна (preprocess, transform, registration, reconstruction).
|
||||||
- `factories`: сборка `PipelineExecutor` из plugin-id и профиля.
|
- `factories`: сборка `PipelineExecutor` из plugin-id и профиля.
|
||||||
|
|
||||||
|
## Pipeline Dashboard: UX и анализ цепочки
|
||||||
|
|
||||||
|
В dashboard реализованы инструменты для пошаговой настройки всей pipeline-цепочки (preprocess + reconstruction):
|
||||||
|
|
||||||
|
- **Категориальная палитра этапов**: фильтры сгруппированы по логике обработки
|
||||||
|
(`Обрезка -> NaN (предочистка) -> Условия/Индексы -> Шум -> Морфология -> Прореживание -> Сглаживание -> Нормали -> Реконструкция`), для каждого фильтра
|
||||||
|
доступны краткие подсказки.
|
||||||
|
- **Проверка порядка этапов**: при рискованной последовательности (например, когда
|
||||||
|
`OutlierRemoval` идет после `VoxelGrid`) показываются предупреждения и рекомендация.
|
||||||
|
- **Стартовые пресеты**: доступны шаблоны для типичных источников
|
||||||
|
(`LiDAR_scan`, `RGBD_camera`, `Synthetic_clean`) как отправная точка с возможностью
|
||||||
|
ручной донастройки.
|
||||||
|
- **Метрики между шагами**: показывается вклад каждого preprocess-этапа:
|
||||||
|
входные точки, выходные точки, сколько удалено и время шага в миллисекундах.
|
||||||
|
- **Единый редактор параметров**: и preprocess-стадии, и реконструкторы поверхности
|
||||||
|
настраиваются через одинаковые карточки и один диалог параметров.
|
||||||
|
|
||||||
|
Пер-шаговые метрики собираются на уровне `core::PipelineExecutor` и возвращаются через
|
||||||
|
`PipelineStats::preprocessStepMetrics`, чтобы данные были едиными для GUI и тестов.
|
||||||
|
|
||||||
### Профили выполнения
|
### Профили выполнения
|
||||||
|
|
||||||
- `desktop_debug`: дефолтный профиль для GUI/отладки.
|
- `desktop_debug`: дефолтный профиль для GUI/отладки.
|
||||||
@@ -44,16 +64,31 @@
|
|||||||
- `factories::pipeline::createPipelineExecutorForProfile("desktop_debug")`
|
- `factories::pipeline::createPipelineExecutorForProfile("desktop_debug")`
|
||||||
- `factories::pipeline::createPipelineExecutorForProfile("rpi4_runtime")`
|
- `factories::pipeline::createPipelineExecutorForProfile("rpi4_runtime")`
|
||||||
|
|
||||||
## Сборка
|
## Сборка и запуск в WSL
|
||||||
|
|
||||||
Пример для Qt 5.11:
|
Рабочий поток для этого проекта на Windows: собирать и запускать через WSL.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
qmake DotsToSirface.pro
|
cd /mnt/d/yakupov/Projects/DotsToSirface/build-wsl
|
||||||
make
|
qmake ../DotsToSirface.pro 'DEFINES+=PCL_ENABLED'
|
||||||
|
make -j4
|
||||||
|
LIBGL_ALWAYS_SOFTWARE=1 QT_QPA_PLATFORM=xcb ./DotsToSirface
|
||||||
```
|
```
|
||||||
|
|
||||||
Для Windows/MSVC используйте соответствующий `nmake`/`jom`.
|
Примечания:
|
||||||
|
- `DEFINES+=PCL_ENABLED` включает PCL-стадии пайплайна.
|
||||||
|
- `LIBGL_ALWAYS_SOFTWARE=1` снижает риски графических артефактов в WSLg.
|
||||||
|
- `QT_QPA_PLATFORM=xcb` используется как стабильный backend для Qt в WSL.
|
||||||
|
|
||||||
|
Перезапуск после правок:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pkill -f '^./DotsToSirface$' || true
|
||||||
|
cd /mnt/d/yakupov/Projects/DotsToSirface/build-wsl
|
||||||
|
qmake ../DotsToSirface.pro 'DEFINES+=PCL_ENABLED'
|
||||||
|
make -j4
|
||||||
|
LIBGL_ALWAYS_SOFTWARE=1 QT_QPA_PLATFORM=xcb ./DotsToSirface
|
||||||
|
```
|
||||||
|
|
||||||
## Smoke tests (отдельный runner)
|
## Smoke tests (отдельный runner)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"title": "MyImportedPreset",
|
||||||
|
"idValue": "user:my_imported_preset",
|
||||||
|
"stages": [
|
||||||
|
{
|
||||||
|
"id": "pcl_remove_nan",
|
||||||
|
"family": "preprocess",
|
||||||
|
"enabled": true,
|
||||||
|
"defaults": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pcl_statistical_outlier",
|
||||||
|
"family": "preprocess",
|
||||||
|
"enabled": true,
|
||||||
|
"defaults": "meanK=24,stddev=1.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pcl_voxel_grid",
|
||||||
|
"family": "preprocess",
|
||||||
|
"enabled": true,
|
||||||
|
"defaults": "leaf=0.02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pcl_greedy_triangulation",
|
||||||
|
"family": "reconstruction",
|
||||||
|
"enabled": true,
|
||||||
|
"defaults": "searchRadius=0.08,mu=2.5,maxNearest=100,maxSurfaceAngle=0.8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"createdAt": "2026-04-23T12:00:00Z",
|
||||||
|
"updatedAt": "2026-04-23T12:00:00Z"
|
||||||
|
}
|
||||||
@@ -1,18 +1,31 @@
|
|||||||
#include "pcl_point_cloud_adapter.h"
|
#include "pcl_point_cloud_adapter.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
#ifdef PCL_ENABLED
|
#ifdef PCL_ENABLED
|
||||||
#include <pcl/common/io.h>
|
#include <pcl/common/io.h>
|
||||||
#include <pcl/features/normal_3d.h>
|
#include <pcl/features/normal_3d.h>
|
||||||
|
#include <pcl/filters/approximate_voxel_grid.h>
|
||||||
|
#include <pcl/filters/farthest_point_sampling.h>
|
||||||
|
#include <pcl/filters/filter.h>
|
||||||
|
#include <pcl/filters/grid_minimum.h>
|
||||||
|
#include <pcl/filters/model_outlier_removal.h>
|
||||||
|
#include <pcl/filters/normal_space.h>
|
||||||
#include <pcl/filters/radius_outlier_removal.h>
|
#include <pcl/filters/radius_outlier_removal.h>
|
||||||
|
#include <pcl/filters/shadowpoints.h>
|
||||||
#include <pcl/filters/statistical_outlier_removal.h>
|
#include <pcl/filters/statistical_outlier_removal.h>
|
||||||
#include <pcl/filters/voxel_grid.h>
|
#include <pcl/filters/voxel_grid.h>
|
||||||
|
#include <pcl/filters/voxel_grid_covariance.h>
|
||||||
|
#include <pcl/filters/voxel_grid_label.h>
|
||||||
#include <pcl/kdtree/kdtree_flann.h>
|
#include <pcl/kdtree/kdtree_flann.h>
|
||||||
#include <pcl/point_cloud.h>
|
#include <pcl/point_cloud.h>
|
||||||
#include <pcl/point_types.h>
|
#include <pcl/point_types.h>
|
||||||
#include <pcl/search/kdtree.h>
|
#include <pcl/search/kdtree.h>
|
||||||
#include <pcl/surface/gp3.h>
|
#include <pcl/surface/gp3.h>
|
||||||
|
#include <pcl/surface/poisson.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace adapters
|
namespace adapters
|
||||||
@@ -42,6 +55,21 @@ QVector<core::Point3f> fromPclCloud(const ::pcl::PointCloud<::pcl::PointXYZ>::Pt
|
|||||||
}
|
}
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> fromIndices(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const std::vector<int> &indices)
|
||||||
|
{
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(static_cast<int>(indices.size()));
|
||||||
|
for (std::size_t i = 0; i < indices.size(); ++i) {
|
||||||
|
const int idx = indices[i];
|
||||||
|
if (idx >= 0 && idx < points.size()) {
|
||||||
|
out.push_back(points[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -50,6 +78,40 @@ QVector<core::Point3f> passThroughPoints(const QVector<core::Point3f> &points)
|
|||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> removeNaNFromPointCloud(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ> out;
|
||||||
|
std::vector<int> indices;
|
||||||
|
::pcl::removeNaNFromPointCloud(*in, out, indices);
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr outPtr(new ::pcl::PointCloud<::pcl::PointXYZ>(out));
|
||||||
|
removedCount = points.size() - static_cast<int>(out.size());
|
||||||
|
return fromPclCloud(outPtr);
|
||||||
|
#else
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
if (std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z)) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> removeNaNNormalsFromPointCloud(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
// In this project point type has no explicit normal fields; keep semantic as
|
||||||
|
// "drop points with non-finite geometry" to mimic removeNaNNormals behavior.
|
||||||
|
return removeNaNFromPointCloud(points, removedCount);
|
||||||
|
}
|
||||||
|
|
||||||
QVector<core::Point3f> applyVoxelGrid(
|
QVector<core::Point3f> applyVoxelGrid(
|
||||||
const QVector<core::Point3f> &points,
|
const QVector<core::Point3f> &points,
|
||||||
const float leafSize,
|
const float leafSize,
|
||||||
@@ -119,6 +181,547 @@ QVector<core::Point3f> applyRadiusOutlierRemoval(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyModelOutlierRemoval(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float threshold,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr out(new ::pcl::PointCloud<::pcl::PointXYZ>());
|
||||||
|
::pcl::ModelCoefficients coefficients;
|
||||||
|
coefficients.values.resize(4);
|
||||||
|
coefficients.values[0] = 0.0f;
|
||||||
|
coefficients.values[1] = 0.0f;
|
||||||
|
coefficients.values[2] = 1.0f;
|
||||||
|
coefficients.values[3] = 0.0f;
|
||||||
|
|
||||||
|
::pcl::ModelOutlierRemoval<::pcl::PointXYZ> filter;
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
filter.setModelCoefficients(coefficients);
|
||||||
|
filter.setThreshold(threshold);
|
||||||
|
filter.filter(*out);
|
||||||
|
removedCount = points.size() - static_cast<int>(out->size());
|
||||||
|
return fromPclCloud(out);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(threshold);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyShadowPointsRemoval(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float threshold,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
|
||||||
|
::pcl::search::KdTree<::pcl::PointXYZ>::Ptr tree(new ::pcl::search::KdTree<::pcl::PointXYZ>());
|
||||||
|
::pcl::PointCloud<::pcl::Normal>::Ptr normals(new ::pcl::PointCloud<::pcl::Normal>());
|
||||||
|
::pcl::NormalEstimation<::pcl::PointXYZ, ::pcl::Normal> n;
|
||||||
|
n.setInputCloud(in);
|
||||||
|
n.setSearchMethod(tree);
|
||||||
|
n.setKSearch(12);
|
||||||
|
n.compute(*normals);
|
||||||
|
|
||||||
|
::pcl::PointCloud<::pcl::PointNormal>::Ptr cloudWithNormals(new ::pcl::PointCloud<::pcl::PointNormal>());
|
||||||
|
::pcl::concatenateFields(*in, *normals, *cloudWithNormals);
|
||||||
|
|
||||||
|
::pcl::PointCloud<::pcl::PointNormal>::Ptr out(new ::pcl::PointCloud<::pcl::PointNormal>());
|
||||||
|
::pcl::ShadowPoints<::pcl::PointNormal, ::pcl::PointNormal> filter;
|
||||||
|
filter.setInputCloud(cloudWithNormals);
|
||||||
|
filter.setThreshold(threshold);
|
||||||
|
filter.filter(*out);
|
||||||
|
|
||||||
|
QVector<core::Point3f> filtered;
|
||||||
|
filtered.reserve(static_cast<int>(out->size()));
|
||||||
|
for (std::size_t i = 0; i < out->size(); ++i) {
|
||||||
|
const ::pcl::PointNormal &p = out->at(i);
|
||||||
|
filtered.push_back({p.x, p.y, p.z});
|
||||||
|
}
|
||||||
|
removedCount = points.size() - filtered.size();
|
||||||
|
return filtered;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(threshold);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyApproximateVoxelGrid(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float leafSize,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr out(new ::pcl::PointCloud<::pcl::PointXYZ>());
|
||||||
|
::pcl::ApproximateVoxelGrid<::pcl::PointXYZ> filter;
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
filter.setLeafSize(leafSize, leafSize, leafSize);
|
||||||
|
filter.filter(*out);
|
||||||
|
removedCount = points.size() - static_cast<int>(out->size());
|
||||||
|
return fromPclCloud(out);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(leafSize);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyVoxelGridCovariance(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float leafSize,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr out(new ::pcl::PointCloud<::pcl::PointXYZ>());
|
||||||
|
::pcl::VoxelGridCovariance<::pcl::PointXYZ> filter;
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
filter.setLeafSize(leafSize, leafSize, leafSize);
|
||||||
|
filter.filter(*out);
|
||||||
|
removedCount = points.size() - static_cast<int>(out->size());
|
||||||
|
return fromPclCloud(out);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(leafSize);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyVoxelGridLabel(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float leafSize,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZRGBL>::Ptr in(new ::pcl::PointCloud<::pcl::PointXYZRGBL>());
|
||||||
|
in->reserve(static_cast<std::size_t>(points.size()));
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
::pcl::PointXYZRGBL q;
|
||||||
|
q.x = p.x;
|
||||||
|
q.y = p.y;
|
||||||
|
q.z = p.z;
|
||||||
|
q.label = 1;
|
||||||
|
in->push_back(q);
|
||||||
|
}
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZRGBL>::Ptr out(new ::pcl::PointCloud<::pcl::PointXYZRGBL>());
|
||||||
|
::pcl::VoxelGridLabel filter;
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
filter.setLeafSize(leafSize, leafSize, leafSize);
|
||||||
|
filter.filter(*out);
|
||||||
|
|
||||||
|
QVector<core::Point3f> filtered;
|
||||||
|
filtered.reserve(static_cast<int>(out->size()));
|
||||||
|
for (std::size_t i = 0; i < out->size(); ++i) {
|
||||||
|
const ::pcl::PointXYZRGBL &q = out->at(i);
|
||||||
|
filtered.push_back({q.x, q.y, q.z});
|
||||||
|
}
|
||||||
|
removedCount = points.size() - filtered.size();
|
||||||
|
return filtered;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(leafSize);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyGridMinimum(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float resolution,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::GridMinimum<::pcl::PointXYZ> filter(resolution);
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
std::vector<int> indices;
|
||||||
|
filter.filter(indices);
|
||||||
|
QVector<core::Point3f> filtered = fromIndices(points, indices);
|
||||||
|
removedCount = points.size() - filtered.size();
|
||||||
|
return filtered;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(resolution);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyFarthestPointSampling(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const int sampleCount,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::FarthestPointSampling<::pcl::PointXYZ> filter;
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
filter.setSample(sampleCount);
|
||||||
|
std::vector<int> indices;
|
||||||
|
filter.filter(indices);
|
||||||
|
QVector<core::Point3f> filtered = fromIndices(points, indices);
|
||||||
|
removedCount = points.size() - filtered.size();
|
||||||
|
return filtered;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(sampleCount);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyNormalSpaceSampling(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const int sampleCount,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr in = toPclCloud(points);
|
||||||
|
::pcl::search::KdTree<::pcl::PointXYZ>::Ptr tree(new ::pcl::search::KdTree<::pcl::PointXYZ>());
|
||||||
|
::pcl::PointCloud<::pcl::Normal>::Ptr normals(new ::pcl::PointCloud<::pcl::Normal>());
|
||||||
|
::pcl::NormalEstimation<::pcl::PointXYZ, ::pcl::Normal> n;
|
||||||
|
n.setInputCloud(in);
|
||||||
|
n.setSearchMethod(tree);
|
||||||
|
n.setKSearch(12);
|
||||||
|
n.compute(*normals);
|
||||||
|
|
||||||
|
::pcl::NormalSpaceSampling<::pcl::PointXYZ, ::pcl::Normal> filter;
|
||||||
|
filter.setInputCloud(in);
|
||||||
|
filter.setNormals(normals);
|
||||||
|
filter.setBins(4, 4, 4);
|
||||||
|
filter.setSample(sampleCount);
|
||||||
|
std::vector<int> indices;
|
||||||
|
filter.filter(indices);
|
||||||
|
QVector<core::Point3f> filtered = fromIndices(points, indices);
|
||||||
|
removedCount = points.size() - filtered.size();
|
||||||
|
return filtered;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(sampleCount);
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applySamplingSurfaceNormal(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const int sampleCount,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
// PCL SamplingSurfaceNormal is not fully linked in this environment.
|
||||||
|
// Use NormalSpaceSampling as compatible normal-aware fallback.
|
||||||
|
return applyNormalSpaceSampling(points, sampleCount, removedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyPassThroughAxis(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const QString &axis,
|
||||||
|
const float minValue,
|
||||||
|
const float maxValue,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
const float v = (axis == "x") ? p.x : ((axis == "y") ? p.y : p.z);
|
||||||
|
if (v >= minValue && v <= maxValue) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyCropBoxBounds(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float minX,
|
||||||
|
const float minY,
|
||||||
|
const float minZ,
|
||||||
|
const float maxX,
|
||||||
|
const float maxY,
|
||||||
|
const float maxZ,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
if (p.x >= minX && p.x <= maxX
|
||||||
|
&& p.y >= minY && p.y <= maxY
|
||||||
|
&& p.z >= minZ && p.z <= maxZ) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyPlaneClipper3D(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float a,
|
||||||
|
const float b,
|
||||||
|
const float c,
|
||||||
|
const float d,
|
||||||
|
const bool keepPositiveSide,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
const float s = a * p.x + b * p.y + c * p.z + d;
|
||||||
|
if ((keepPositiveSide && s >= 0.0f) || (!keepPositiveSide && s <= 0.0f)) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyFrustumCulling(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float nearDistance,
|
||||||
|
const float farDistance,
|
||||||
|
const float hfovDeg,
|
||||||
|
const float vfovDeg,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const float hHalfTan = std::tan(hfovDeg * 0.5f * 3.1415926535f / 180.0f);
|
||||||
|
const float vHalfTan = std::tan(vfovDeg * 0.5f * 3.1415926535f / 180.0f);
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
if (p.z < nearDistance || p.z > farDistance) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const float maxX = p.z * hHalfTan;
|
||||||
|
const float maxY = p.z * vHalfTan;
|
||||||
|
if (p.x >= -maxX && p.x <= maxX && p.y >= -maxY && p.y <= maxY) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyConditionalZRange(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float zMin,
|
||||||
|
const float zMax,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
if (p.z >= zMin && p.z <= zMax) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyExtractIndicesNth(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const int keepEachNth,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const int step = qMax(1, keepEachNth);
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size() / step + 1);
|
||||||
|
for (int i = 0; i < points.size(); ++i) {
|
||||||
|
if (i % step == 0) {
|
||||||
|
out.push_back(points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyFunctorRadius(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float radiusMax,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const float r2 = radiusMax * radiusMax;
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
if (!std::isfinite(p.x) || !std::isfinite(p.y) || !std::isfinite(p.z)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const float d2 = p.x * p.x + p.y * p.y + p.z * p.z;
|
||||||
|
if (d2 <= r2) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyProjectInliersToPlane(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float a,
|
||||||
|
const float b,
|
||||||
|
const float c,
|
||||||
|
const float d,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const float n2 = a * a + b * b + c * c;
|
||||||
|
if (n2 <= 1e-8f) {
|
||||||
|
removedCount = 0;
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
const float t = (a * p.x + b * p.y + c * p.z + d) / n2;
|
||||||
|
out.push_back({p.x - a * t, p.y - b * t, p.z - c * t});
|
||||||
|
}
|
||||||
|
removedCount = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyNormalRefinementSmoothing(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float radius,
|
||||||
|
const int iterations,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const float r2 = radius * radius;
|
||||||
|
QVector<core::Point3f> current = points;
|
||||||
|
QVector<core::Point3f> next = points;
|
||||||
|
for (int it = 0; it < qMax(1, iterations); ++it) {
|
||||||
|
for (int i = 0; i < current.size(); ++i) {
|
||||||
|
float sx = 0.0f;
|
||||||
|
float sy = 0.0f;
|
||||||
|
float sz = 0.0f;
|
||||||
|
int count = 0;
|
||||||
|
for (int j = 0; j < current.size(); ++j) {
|
||||||
|
const float dx = current[j].x - current[i].x;
|
||||||
|
const float dy = current[j].y - current[i].y;
|
||||||
|
const float dz = current[j].z - current[i].z;
|
||||||
|
const float d2 = dx * dx + dy * dy + dz * dz;
|
||||||
|
if (d2 <= r2) {
|
||||||
|
sx += current[j].x;
|
||||||
|
sy += current[j].y;
|
||||||
|
sz += current[j].z;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
next[i] = {sx / count, sy / count, sz / count};
|
||||||
|
} else {
|
||||||
|
next[i] = current[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
removedCount = 0;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyBilateralSmoothing(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float sigmaSpatial,
|
||||||
|
const float sigmaRange,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const float s2 = qMax(1e-6f, sigmaSpatial * sigmaSpatial);
|
||||||
|
const float r2 = qMax(1e-6f, sigmaRange * sigmaRange);
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
for (int i = 0; i < points.size(); ++i) {
|
||||||
|
float wx = 0.0f;
|
||||||
|
float wy = 0.0f;
|
||||||
|
float wz = 0.0f;
|
||||||
|
float wsum = 0.0f;
|
||||||
|
for (int j = 0; j < points.size(); ++j) {
|
||||||
|
const float dx = points[j].x - points[i].x;
|
||||||
|
const float dy = points[j].y - points[i].y;
|
||||||
|
const float dz = points[j].z - points[i].z;
|
||||||
|
const float d2 = dx * dx + dy * dy + dz * dz;
|
||||||
|
const float spatial = std::exp(-d2 / (2.0f * s2));
|
||||||
|
const float range = std::exp(-(dz * dz) / (2.0f * r2));
|
||||||
|
const float w = spatial * range;
|
||||||
|
wx += points[j].x * w;
|
||||||
|
wy += points[j].y * w;
|
||||||
|
wz += points[j].z * w;
|
||||||
|
wsum += w;
|
||||||
|
}
|
||||||
|
if (wsum > 1e-6f) {
|
||||||
|
out.push_back({wx / wsum, wy / wsum, wz / wsum});
|
||||||
|
} else {
|
||||||
|
out.push_back(points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyConvolutionGaussian(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float sigma,
|
||||||
|
const int kernelSize,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const int half = qMax(1, kernelSize / 2);
|
||||||
|
const float s2 = qMax(1e-6f, sigma * sigma);
|
||||||
|
QVector<core::Point3f> out = points;
|
||||||
|
for (int i = 0; i < points.size(); ++i) {
|
||||||
|
float sx = 0.0f;
|
||||||
|
float sy = 0.0f;
|
||||||
|
float sz = 0.0f;
|
||||||
|
float sw = 0.0f;
|
||||||
|
const int begin = qMax(0, i - half);
|
||||||
|
const int end = qMin(points.size() - 1, i + half);
|
||||||
|
for (int j = begin; j <= end; ++j) {
|
||||||
|
const float d = static_cast<float>(j - i);
|
||||||
|
const float w = std::exp(-(d * d) / (2.0f * s2));
|
||||||
|
sx += points[j].x * w;
|
||||||
|
sy += points[j].y * w;
|
||||||
|
sz += points[j].z * w;
|
||||||
|
sw += w;
|
||||||
|
}
|
||||||
|
if (sw > 1e-6f) {
|
||||||
|
out[i] = {sx / sw, sy / sw, sz / sw};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Point3f> applyVoxelOcclusionEstimation(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const float leafSize,
|
||||||
|
const int minHits,
|
||||||
|
int &removedCount)
|
||||||
|
{
|
||||||
|
const float safeLeaf = qMax(1e-4f, leafSize);
|
||||||
|
std::unordered_map<long long, int> bins;
|
||||||
|
bins.reserve(static_cast<std::size_t>(points.size()));
|
||||||
|
auto keyFor = [safeLeaf](const core::Point3f &p) -> long long {
|
||||||
|
const int ix = static_cast<int>(std::floor(p.x / safeLeaf));
|
||||||
|
const int iy = static_cast<int>(std::floor(p.y / safeLeaf));
|
||||||
|
const int iz = static_cast<int>(std::floor(p.z / safeLeaf));
|
||||||
|
return (static_cast<long long>(ix) << 42)
|
||||||
|
^ (static_cast<long long>(iy) << 21)
|
||||||
|
^ static_cast<long long>(iz);
|
||||||
|
};
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
++bins[keyFor(p)];
|
||||||
|
}
|
||||||
|
QVector<core::Point3f> out;
|
||||||
|
out.reserve(points.size());
|
||||||
|
const int threshold = qMax(1, minHits);
|
||||||
|
for (const core::Point3f &p : points) {
|
||||||
|
if (bins[keyFor(p)] >= threshold) {
|
||||||
|
out.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removedCount = points.size() - out.size();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
QVector<core::Triangle> buildGreedyTriangles(
|
QVector<core::Triangle> buildGreedyTriangles(
|
||||||
const QVector<core::Point3f> &points,
|
const QVector<core::Point3f> &points,
|
||||||
const float searchRadius,
|
const float searchRadius,
|
||||||
@@ -182,5 +785,57 @@ QVector<core::Triangle> buildGreedyTriangles(
|
|||||||
return QVector<core::Triangle>();
|
return QVector<core::Triangle>();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<core::Triangle> buildPoissonTriangles(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const int depth,
|
||||||
|
const float samplesPerNode)
|
||||||
|
{
|
||||||
|
#ifdef PCL_ENABLED
|
||||||
|
if (points.size() < 4) {
|
||||||
|
return QVector<core::Triangle>();
|
||||||
|
}
|
||||||
|
|
||||||
|
::pcl::PointCloud<::pcl::PointXYZ>::Ptr cloud = toPclCloud(points);
|
||||||
|
::pcl::search::KdTree<::pcl::PointXYZ>::Ptr tree(new ::pcl::search::KdTree<::pcl::PointXYZ>());
|
||||||
|
|
||||||
|
::pcl::PointCloud<::pcl::Normal>::Ptr normals(new ::pcl::PointCloud<::pcl::Normal>());
|
||||||
|
::pcl::NormalEstimation<::pcl::PointXYZ, ::pcl::Normal> n;
|
||||||
|
n.setInputCloud(cloud);
|
||||||
|
n.setSearchMethod(tree);
|
||||||
|
n.setKSearch(20);
|
||||||
|
n.compute(*normals);
|
||||||
|
|
||||||
|
::pcl::PointCloud<::pcl::PointNormal>::Ptr cloudWithNormals(new ::pcl::PointCloud<::pcl::PointNormal>());
|
||||||
|
::pcl::concatenateFields(*cloud, *normals, *cloudWithNormals);
|
||||||
|
|
||||||
|
::pcl::Poisson<::pcl::PointNormal> poisson;
|
||||||
|
poisson.setDepth(depth);
|
||||||
|
poisson.setSamplesPerNode(samplesPerNode);
|
||||||
|
poisson.setInputCloud(cloudWithNormals);
|
||||||
|
|
||||||
|
::pcl::PolygonMesh mesh;
|
||||||
|
poisson.reconstruct(mesh);
|
||||||
|
|
||||||
|
QVector<core::Triangle> triangles;
|
||||||
|
triangles.reserve(static_cast<int>(mesh.polygons.size()));
|
||||||
|
for (std::size_t i = 0; i < mesh.polygons.size(); ++i) {
|
||||||
|
const ::pcl::Vertices &v = mesh.polygons[i];
|
||||||
|
if (v.vertices.size() < 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
triangles.push_back({
|
||||||
|
static_cast<int>(v.vertices[0]),
|
||||||
|
static_cast<int>(v.vertices[1]),
|
||||||
|
static_cast<int>(v.vertices[2])});
|
||||||
|
}
|
||||||
|
return triangles;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(points);
|
||||||
|
Q_UNUSED(depth);
|
||||||
|
Q_UNUSED(samplesPerNode);
|
||||||
|
return QVector<core::Triangle>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
} // namespace pcl
|
} // namespace pcl
|
||||||
} // namespace adapters
|
} // namespace adapters
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef PCL_POINT_CLOUD_ADAPTER_H
|
#ifndef PCL_POINT_CLOUD_ADAPTER_H
|
||||||
#define PCL_POINT_CLOUD_ADAPTER_H
|
#define PCL_POINT_CLOUD_ADAPTER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "../../core/point_cloud_types.h"
|
#include "../../core/point_cloud_types.h"
|
||||||
|
|
||||||
namespace adapters
|
namespace adapters
|
||||||
@@ -8,6 +10,12 @@ namespace adapters
|
|||||||
namespace pcl
|
namespace pcl
|
||||||
{
|
{
|
||||||
QVector<core::Point3f> passThroughPoints(const QVector<core::Point3f> &points);
|
QVector<core::Point3f> passThroughPoints(const QVector<core::Point3f> &points);
|
||||||
|
QVector<core::Point3f> removeNaNFromPointCloud(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> removeNaNNormalsFromPointCloud(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int &removedCount);
|
||||||
QVector<core::Point3f> applyVoxelGrid(
|
QVector<core::Point3f> applyVoxelGrid(
|
||||||
const QVector<core::Point3f> &points,
|
const QVector<core::Point3f> &points,
|
||||||
float leafSize,
|
float leafSize,
|
||||||
@@ -22,12 +30,122 @@ QVector<core::Point3f> applyRadiusOutlierRemoval(
|
|||||||
float radius,
|
float radius,
|
||||||
int minNeighbors,
|
int minNeighbors,
|
||||||
int &removedCount);
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyModelOutlierRemoval(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float threshold,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyShadowPointsRemoval(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float threshold,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyApproximateVoxelGrid(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float leafSize,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyVoxelGridCovariance(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float leafSize,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyVoxelGridLabel(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float leafSize,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyGridMinimum(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float resolution,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyFarthestPointSampling(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int sampleCount,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyNormalSpaceSampling(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int sampleCount,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applySamplingSurfaceNormal(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int sampleCount,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyPassThroughAxis(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
const QString &axis,
|
||||||
|
float minValue,
|
||||||
|
float maxValue,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyCropBoxBounds(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float minX,
|
||||||
|
float minY,
|
||||||
|
float minZ,
|
||||||
|
float maxX,
|
||||||
|
float maxY,
|
||||||
|
float maxZ,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyPlaneClipper3D(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float a,
|
||||||
|
float b,
|
||||||
|
float c,
|
||||||
|
float d,
|
||||||
|
bool keepPositiveSide,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyFrustumCulling(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float nearDistance,
|
||||||
|
float farDistance,
|
||||||
|
float hfovDeg,
|
||||||
|
float vfovDeg,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyConditionalZRange(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float zMin,
|
||||||
|
float zMax,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyExtractIndicesNth(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int keepEachNth,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyFunctorRadius(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float radiusMax,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyProjectInliersToPlane(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float a,
|
||||||
|
float b,
|
||||||
|
float c,
|
||||||
|
float d,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyNormalRefinementSmoothing(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float radius,
|
||||||
|
int iterations,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyBilateralSmoothing(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float sigmaSpatial,
|
||||||
|
float sigmaRange,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyConvolutionGaussian(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float sigma,
|
||||||
|
int kernelSize,
|
||||||
|
int &removedCount);
|
||||||
|
QVector<core::Point3f> applyVoxelOcclusionEstimation(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
float leafSize,
|
||||||
|
int minHits,
|
||||||
|
int &removedCount);
|
||||||
QVector<core::Triangle> buildGreedyTriangles(
|
QVector<core::Triangle> buildGreedyTriangles(
|
||||||
const QVector<core::Point3f> &points,
|
const QVector<core::Point3f> &points,
|
||||||
float searchRadius,
|
float searchRadius,
|
||||||
float mu,
|
float mu,
|
||||||
int maxNearestNeighbors,
|
int maxNearestNeighbors,
|
||||||
float maxSurfaceAngleRadians);
|
float maxSurfaceAngleRadians);
|
||||||
|
QVector<core::Triangle> buildPoissonTriangles(
|
||||||
|
const QVector<core::Point3f> &points,
|
||||||
|
int depth,
|
||||||
|
float samplesPerNode);
|
||||||
} // namespace pcl
|
} // namespace pcl
|
||||||
} // namespace adapters
|
} // namespace adapters
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,49 @@ struct PreprocessConfig
|
|||||||
double pclSorStdDevMul = 1.0;
|
double pclSorStdDevMul = 1.0;
|
||||||
float pclRorRadius = 0.08f;
|
float pclRorRadius = 0.08f;
|
||||||
int pclRorMinNeighbors = 4;
|
int pclRorMinNeighbors = 4;
|
||||||
|
float pclMorThreshold = 0.03f;
|
||||||
|
float pclShadowThreshold = 0.2f;
|
||||||
|
float pclApproxLeafSize = 0.03f;
|
||||||
|
float pclCovLeafSize = 0.04f;
|
||||||
|
float pclLabelLeafSize = 0.04f;
|
||||||
|
float pclGridMinimumResolution = 0.05f;
|
||||||
|
int pclFpsSampleCount = 800;
|
||||||
|
int pclNormalSpaceSampleCount = 800;
|
||||||
|
int pclSurfaceNormalSampleCount = 800;
|
||||||
|
QString pclPassAxis = "z";
|
||||||
|
float pclPassMin = -1.0f;
|
||||||
|
float pclPassMax = 1.0f;
|
||||||
|
float pclCropBoxMinX = -1.0f;
|
||||||
|
float pclCropBoxMinY = -1.0f;
|
||||||
|
float pclCropBoxMinZ = -1.0f;
|
||||||
|
float pclCropBoxMaxX = 1.0f;
|
||||||
|
float pclCropBoxMaxY = 1.0f;
|
||||||
|
float pclCropBoxMaxZ = 1.0f;
|
||||||
|
float pclClipPlaneA = 0.0f;
|
||||||
|
float pclClipPlaneB = 0.0f;
|
||||||
|
float pclClipPlaneC = 1.0f;
|
||||||
|
float pclClipPlaneD = 0.0f;
|
||||||
|
bool pclClipKeepPositive = true;
|
||||||
|
float pclFrustumNear = 0.1f;
|
||||||
|
float pclFrustumFar = 5.0f;
|
||||||
|
float pclFrustumHfovDeg = 70.0f;
|
||||||
|
float pclFrustumVfovDeg = 50.0f;
|
||||||
|
int pclExtractNth = 2;
|
||||||
|
float pclFunctorRadiusMax = 2.5f;
|
||||||
|
float pclConditionalZMin = -1.0f;
|
||||||
|
float pclConditionalZMax = 1.0f;
|
||||||
|
float pclProjectPlaneA = 0.0f;
|
||||||
|
float pclProjectPlaneB = 0.0f;
|
||||||
|
float pclProjectPlaneC = 1.0f;
|
||||||
|
float pclProjectPlaneD = 0.0f;
|
||||||
|
float pclNormalRefineRadius = 0.1f;
|
||||||
|
int pclNormalRefineIterations = 1;
|
||||||
|
float pclBilateralSigmaS = 0.08f;
|
||||||
|
float pclBilateralSigmaR = 0.05f;
|
||||||
|
float pclConvolutionKernelSigma = 0.08f;
|
||||||
|
int pclConvolutionKernelSize = 3;
|
||||||
|
float pclVoxelOccLeaf = 0.12f;
|
||||||
|
int pclVoxelOccMinHits = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TransformConfig
|
struct TransformConfig
|
||||||
@@ -39,6 +82,8 @@ struct ReconstructionConfig
|
|||||||
float pclGreedyMu = 2.5f;
|
float pclGreedyMu = 2.5f;
|
||||||
int pclGreedyMaxNearest = 100;
|
int pclGreedyMaxNearest = 100;
|
||||||
float pclGreedyMaxSurfaceAngle = 0.8f;
|
float pclGreedyMaxSurfaceAngle = 0.8f;
|
||||||
|
int pclPoissonDepth = 8;
|
||||||
|
float pclPoissonSamplesPerNode = 1.5f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RuntimeConfig
|
struct RuntimeConfig
|
||||||
|
|||||||
@@ -73,7 +73,17 @@ PipelineResult PipelineExecutor::run(const PointCloudFrame &input) const
|
|||||||
|
|
||||||
PointCloudFrame current = input;
|
PointCloudFrame current = input;
|
||||||
for (const std::shared_ptr<IPreprocessStage> &stage : m_preprocessStages) {
|
for (const std::shared_ptr<IPreprocessStage> &stage : m_preprocessStages) {
|
||||||
|
const int beforePoints = current.points.size();
|
||||||
|
QElapsedTimer stageTimer;
|
||||||
|
stageTimer.start();
|
||||||
current = stage->process(current, result.stats);
|
current = stage->process(current, result.stats);
|
||||||
|
PipelineStats::PreprocessStepMetric metric;
|
||||||
|
metric.stageId = stage->id();
|
||||||
|
metric.inputPoints = beforePoints;
|
||||||
|
metric.outputPoints = current.points.size();
|
||||||
|
metric.removedPoints = qMax(0, metric.inputPoints - metric.outputPoints);
|
||||||
|
metric.elapsedMs = stageTimer.elapsed();
|
||||||
|
result.stats.preprocessStepMetrics.push_back(metric);
|
||||||
}
|
}
|
||||||
for (const std::shared_ptr<ITransformStage> &stage : m_transformStages) {
|
for (const std::shared_ptr<ITransformStage> &stage : m_transformStages) {
|
||||||
current = stage->process(current, result.context, result.stats);
|
current = stage->process(current, result.context, result.stats);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class IPreprocessStage
|
|||||||
public:
|
public:
|
||||||
virtual ~IPreprocessStage() {}
|
virtual ~IPreprocessStage() {}
|
||||||
virtual PointCloudFrame process(const PointCloudFrame &input, PipelineStats &stats) const = 0;
|
virtual PointCloudFrame process(const PointCloudFrame &input, PipelineStats &stats) const = 0;
|
||||||
|
virtual QString id() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ITransformStage
|
class ITransformStage
|
||||||
|
|||||||
@@ -57,6 +57,15 @@ struct PipelineContext
|
|||||||
|
|
||||||
struct PipelineStats
|
struct PipelineStats
|
||||||
{
|
{
|
||||||
|
struct PreprocessStepMetric
|
||||||
|
{
|
||||||
|
QString stageId;
|
||||||
|
int inputPoints = 0;
|
||||||
|
int outputPoints = 0;
|
||||||
|
int removedPoints = 0;
|
||||||
|
qint64 elapsedMs = 0;
|
||||||
|
};
|
||||||
|
|
||||||
int inputPoints = 0;
|
int inputPoints = 0;
|
||||||
int afterPreprocessingPoints = 0;
|
int afterPreprocessingPoints = 0;
|
||||||
int outputTriangles = 0;
|
int outputTriangles = 0;
|
||||||
@@ -64,6 +73,7 @@ struct PipelineStats
|
|||||||
int removedDownsamplePoints = 0;
|
int removedDownsamplePoints = 0;
|
||||||
int detectedClusters = 1;
|
int detectedClusters = 1;
|
||||||
qint64 reconstructionMs = 0;
|
qint64 reconstructionMs = 0;
|
||||||
|
QVector<PreprocessStepMetric> preprocessStepMetrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PipelineResult
|
struct PipelineResult
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "../../strategies/preprocess_pcl_stages.h"
|
#include "../../strategies/preprocess_pcl_stages.h"
|
||||||
#include "../../strategies/registration_icp_stage.h"
|
#include "../../strategies/registration_icp_stage.h"
|
||||||
#include "../../strategies/reconstruction_pcl_greedy_stage.h"
|
#include "../../strategies/reconstruction_pcl_greedy_stage.h"
|
||||||
|
#include "../../strategies/reconstruction_pcl_poisson_stage.h"
|
||||||
#include "../../strategies/reconstruction_surface_stage.h"
|
#include "../../strategies/reconstruction_surface_stage.h"
|
||||||
#include "../../strategies/transform_tf_stage.h"
|
#include "../../strategies/transform_tf_stage.h"
|
||||||
|
|
||||||
@@ -67,6 +68,12 @@ core::PipelinePluginRegistry createDefaultPluginRegistry()
|
|||||||
registry.registerPreprocess("downsample_dense", [](const core::PipelineConfig &config) {
|
registry.registerPreprocess("downsample_dense", [](const core::PipelineConfig &config) {
|
||||||
return std::shared_ptr<core::IPreprocessStage>(new strategies::DownsampleDenseAreasStage(config.preprocess));
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::DownsampleDenseAreasStage(config.preprocess));
|
||||||
});
|
});
|
||||||
|
registry.registerPreprocess("pcl_remove_nan", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclRemoveNaNStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_remove_nan_normals", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclRemoveNaNNormalsStage(config.preprocess));
|
||||||
|
});
|
||||||
registry.registerPreprocess("pcl_voxel_grid", [](const core::PipelineConfig &config) {
|
registry.registerPreprocess("pcl_voxel_grid", [](const core::PipelineConfig &config) {
|
||||||
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclVoxelGridStage(config.preprocess));
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclVoxelGridStage(config.preprocess));
|
||||||
});
|
});
|
||||||
@@ -76,6 +83,84 @@ core::PipelinePluginRegistry createDefaultPluginRegistry()
|
|||||||
registry.registerPreprocess("pcl_radius_outlier", [](const core::PipelineConfig &config) {
|
registry.registerPreprocess("pcl_radius_outlier", [](const core::PipelineConfig &config) {
|
||||||
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclRadiusOutlierStage(config.preprocess));
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclRadiusOutlierStage(config.preprocess));
|
||||||
});
|
});
|
||||||
|
registry.registerPreprocess("pcl_model_outlier", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclModelOutlierStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_shadow_points", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclShadowPointsStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_approximate_voxel_grid", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclApproximateVoxelGridStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_voxel_grid_label", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclVoxelGridLabelStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_voxel_grid_covariance", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclVoxelGridCovarianceStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_grid_minimum", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclGridMinimumStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_farthest_point_sampling", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclFarthestPointSamplingStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_normal_space_sampling", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclNormalSpaceSamplingStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_sampling_surface_normal", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclSamplingSurfaceNormalStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_pass_through", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclPassThroughStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_crop_box", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclCropBoxStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_crop_hull", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclCropHullStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_frustum_culling", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclFrustumCullingStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_plane_clipper_3d", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclPlaneClipper3DStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_conditional_removal", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclConditionalRemovalStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_extract_indices", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclExtractIndicesStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_functor_filter", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclFunctorFilterStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_project_inliers", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclProjectInliersStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_normal_refinement", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclNormalRefinementStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_bilateral_filter", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclBilateralFilterStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_fast_bilateral_filter", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclFastBilateralFilterStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_fast_bilateral_filter_omp", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclFastBilateralFilterOmpStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_convolution", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclConvolutionStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_gaussian_kernel", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclGaussianKernelStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_gaussian_kernel_rgb", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclGaussianKernelRgbStage(config.preprocess));
|
||||||
|
});
|
||||||
|
registry.registerPreprocess("pcl_voxel_grid_occlusion", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IPreprocessStage>(new strategies::PclVoxelGridOcclusionStage(config.preprocess));
|
||||||
|
});
|
||||||
registry.registerTransform("tf_transform", [](const core::PipelineConfig &config) {
|
registry.registerTransform("tf_transform", [](const core::PipelineConfig &config) {
|
||||||
return std::shared_ptr<core::ITransformStage>(new strategies::TransformTfStage(config));
|
return std::shared_ptr<core::ITransformStage>(new strategies::TransformTfStage(config));
|
||||||
});
|
});
|
||||||
@@ -88,6 +173,9 @@ core::PipelinePluginRegistry createDefaultPluginRegistry()
|
|||||||
registry.registerReconstruction("pcl_greedy_triangulation", [](const core::PipelineConfig &config) {
|
registry.registerReconstruction("pcl_greedy_triangulation", [](const core::PipelineConfig &config) {
|
||||||
return std::shared_ptr<core::IReconstructionStage>(new strategies::PclGreedyReconstructionStage(config.reconstruction));
|
return std::shared_ptr<core::IReconstructionStage>(new strategies::PclGreedyReconstructionStage(config.reconstruction));
|
||||||
});
|
});
|
||||||
|
registry.registerReconstruction("pcl_poisson_reconstruction", [](const core::PipelineConfig &config) {
|
||||||
|
return std::shared_ptr<core::IReconstructionStage>(new strategies::PclPoissonReconstructionStage(config.reconstruction));
|
||||||
|
});
|
||||||
return registry;
|
return registry;
|
||||||
}
|
}
|
||||||
} // namespace pipeline
|
} // namespace pipeline
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ core::PointCloudFrame KeepLargestClusterStage::process(const core::PointCloudFra
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString KeepLargestClusterStage::id() const
|
||||||
|
{
|
||||||
|
return "keep_largest_cluster";
|
||||||
|
}
|
||||||
|
|
||||||
DownsampleDenseAreasStage::DownsampleDenseAreasStage(const core::PreprocessConfig &config)
|
DownsampleDenseAreasStage::DownsampleDenseAreasStage(const core::PreprocessConfig &config)
|
||||||
: m_config(config)
|
: m_config(config)
|
||||||
{
|
{
|
||||||
@@ -33,4 +38,9 @@ core::PointCloudFrame DownsampleDenseAreasStage::process(const core::PointCloudF
|
|||||||
m_config.downsampleCellScale);
|
m_config.downsampleCellScale);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString DownsampleDenseAreasStage::id() const
|
||||||
|
{
|
||||||
|
return "downsample_dense";
|
||||||
|
}
|
||||||
} // namespace strategies
|
} // namespace strategies
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class KeepLargestClusterStage : public core::IPreprocessStage
|
|||||||
public:
|
public:
|
||||||
explicit KeepLargestClusterStage(const core::PreprocessConfig &config = core::PreprocessConfig());
|
explicit KeepLargestClusterStage(const core::PreprocessConfig &config = core::PreprocessConfig());
|
||||||
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::PreprocessConfig m_config;
|
core::PreprocessConfig m_config;
|
||||||
@@ -21,6 +22,7 @@ class DownsampleDenseAreasStage : public core::IPreprocessStage
|
|||||||
public:
|
public:
|
||||||
explicit DownsampleDenseAreasStage(const core::PreprocessConfig &config = core::PreprocessConfig());
|
explicit DownsampleDenseAreasStage(const core::PreprocessConfig &config = core::PreprocessConfig());
|
||||||
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::PreprocessConfig m_config;
|
core::PreprocessConfig m_config;
|
||||||
|
|||||||
@@ -5,6 +5,36 @@
|
|||||||
|
|
||||||
namespace strategies
|
namespace strategies
|
||||||
{
|
{
|
||||||
|
core::PointCloudFrame PclRemoveNaNStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(m_config);
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::removeNaNFromPointCloud(input.points, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclRemoveNaNStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_remove_nan";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclRemoveNaNNormalsStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(m_config);
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::removeNaNNormalsFromPointCloud(input.points, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclRemoveNaNNormalsStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_remove_nan_normals";
|
||||||
|
}
|
||||||
|
|
||||||
core::PointCloudFrame PclVoxelGridStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
core::PointCloudFrame PclVoxelGridStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
{
|
{
|
||||||
core::PointCloudFrame output = input;
|
core::PointCloudFrame output = input;
|
||||||
@@ -17,6 +47,11 @@ core::PointCloudFrame PclVoxelGridStage::process(const core::PointCloudFrame &in
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PclVoxelGridStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_voxel_grid";
|
||||||
|
}
|
||||||
|
|
||||||
core::PointCloudFrame PclStatisticalOutlierStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
core::PointCloudFrame PclStatisticalOutlierStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
{
|
{
|
||||||
core::PointCloudFrame output = input;
|
core::PointCloudFrame output = input;
|
||||||
@@ -35,6 +70,11 @@ core::PointCloudFrame PclStatisticalOutlierStage::process(const core::PointCloud
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PclStatisticalOutlierStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_statistical_outlier";
|
||||||
|
}
|
||||||
|
|
||||||
core::PointCloudFrame PclRadiusOutlierStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
core::PointCloudFrame PclRadiusOutlierStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
{
|
{
|
||||||
core::PointCloudFrame output = input;
|
core::PointCloudFrame output = input;
|
||||||
@@ -50,4 +90,419 @@ core::PointCloudFrame PclRadiusOutlierStage::process(const core::PointCloudFrame
|
|||||||
stats.removedDownsamplePoints += removed;
|
stats.removedDownsamplePoints += removed;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PclRadiusOutlierStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_radius_outlier";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclModelOutlierStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyModelOutlierRemoval(
|
||||||
|
input.points,
|
||||||
|
m_config.pclMorThreshold,
|
||||||
|
removed);
|
||||||
|
#ifndef PCL_ENABLED
|
||||||
|
int clusters = 1;
|
||||||
|
output.points = algorithms::preprocess::keepLargestCluster(input.points, removed, clusters, m_config.clusterJoinDistanceScale);
|
||||||
|
#endif
|
||||||
|
stats.removedClusterPoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclModelOutlierStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_model_outlier";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclShadowPointsStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyShadowPointsRemoval(
|
||||||
|
input.points,
|
||||||
|
m_config.pclShadowThreshold,
|
||||||
|
removed);
|
||||||
|
#ifndef PCL_ENABLED
|
||||||
|
output.points = algorithms::preprocess::downsampleDenseAreas(input.points, removed, m_config.downsampleCellScale);
|
||||||
|
#endif
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclShadowPointsStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_shadow_points";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclApproximateVoxelGridStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyApproximateVoxelGrid(input.points, m_config.pclApproxLeafSize, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclApproximateVoxelGridStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_approximate_voxel_grid";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclVoxelGridLabelStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyVoxelGridLabel(input.points, m_config.pclLabelLeafSize, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclVoxelGridLabelStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_voxel_grid_label";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclVoxelGridCovarianceStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyVoxelGridCovariance(input.points, m_config.pclCovLeafSize, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclVoxelGridCovarianceStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_voxel_grid_covariance";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclGridMinimumStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyGridMinimum(input.points, m_config.pclGridMinimumResolution, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclGridMinimumStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_grid_minimum";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclFarthestPointSamplingStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyFarthestPointSampling(input.points, m_config.pclFpsSampleCount, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclFarthestPointSamplingStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_farthest_point_sampling";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclNormalSpaceSamplingStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyNormalSpaceSampling(input.points, m_config.pclNormalSpaceSampleCount, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclNormalSpaceSamplingStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_normal_space_sampling";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclSamplingSurfaceNormalStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applySamplingSurfaceNormal(input.points, m_config.pclSurfaceNormalSampleCount, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclSamplingSurfaceNormalStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_sampling_surface_normal";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclPassThroughStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyPassThroughAxis(
|
||||||
|
input.points,
|
||||||
|
m_config.pclPassAxis,
|
||||||
|
m_config.pclPassMin,
|
||||||
|
m_config.pclPassMax,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclPassThroughStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_pass_through";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclCropBoxStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyCropBoxBounds(
|
||||||
|
input.points,
|
||||||
|
m_config.pclCropBoxMinX,
|
||||||
|
m_config.pclCropBoxMinY,
|
||||||
|
m_config.pclCropBoxMinZ,
|
||||||
|
m_config.pclCropBoxMaxX,
|
||||||
|
m_config.pclCropBoxMaxY,
|
||||||
|
m_config.pclCropBoxMaxZ,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclCropBoxStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_crop_box";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclCropHullStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
// Current implementation reuses crop box bounds as a deterministic convex ROI fallback.
|
||||||
|
return PclCropBoxStage(m_config).process(input, stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclCropHullStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_crop_hull";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclFrustumCullingStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyFrustumCulling(
|
||||||
|
input.points,
|
||||||
|
m_config.pclFrustumNear,
|
||||||
|
m_config.pclFrustumFar,
|
||||||
|
m_config.pclFrustumHfovDeg,
|
||||||
|
m_config.pclFrustumVfovDeg,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclFrustumCullingStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_frustum_culling";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclPlaneClipper3DStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyPlaneClipper3D(
|
||||||
|
input.points,
|
||||||
|
m_config.pclClipPlaneA,
|
||||||
|
m_config.pclClipPlaneB,
|
||||||
|
m_config.pclClipPlaneC,
|
||||||
|
m_config.pclClipPlaneD,
|
||||||
|
m_config.pclClipKeepPositive,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclPlaneClipper3DStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_plane_clipper_3d";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclConditionalRemovalStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyConditionalZRange(
|
||||||
|
input.points,
|
||||||
|
m_config.pclConditionalZMin,
|
||||||
|
m_config.pclConditionalZMax,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclConditionalRemovalStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_conditional_removal";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclExtractIndicesStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyExtractIndicesNth(input.points, m_config.pclExtractNth, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclExtractIndicesStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_extract_indices";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclFunctorFilterStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyFunctorRadius(input.points, m_config.pclFunctorRadiusMax, removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclFunctorFilterStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_functor_filter";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclProjectInliersStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyProjectInliersToPlane(
|
||||||
|
input.points,
|
||||||
|
m_config.pclProjectPlaneA,
|
||||||
|
m_config.pclProjectPlaneB,
|
||||||
|
m_config.pclProjectPlaneC,
|
||||||
|
m_config.pclProjectPlaneD,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclProjectInliersStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_project_inliers";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclNormalRefinementStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyNormalRefinementSmoothing(
|
||||||
|
input.points,
|
||||||
|
m_config.pclNormalRefineRadius,
|
||||||
|
m_config.pclNormalRefineIterations,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclNormalRefinementStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_normal_refinement";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclBilateralFilterStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyBilateralSmoothing(
|
||||||
|
input.points,
|
||||||
|
m_config.pclBilateralSigmaS,
|
||||||
|
m_config.pclBilateralSigmaR,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclBilateralFilterStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_bilateral_filter";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclFastBilateralFilterStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
return PclBilateralFilterStage(m_config).process(input, stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclFastBilateralFilterStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_fast_bilateral_filter";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclFastBilateralFilterOmpStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
return PclBilateralFilterStage(m_config).process(input, stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclFastBilateralFilterOmpStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_fast_bilateral_filter_omp";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclConvolutionStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyConvolutionGaussian(
|
||||||
|
input.points,
|
||||||
|
m_config.pclConvolutionKernelSigma,
|
||||||
|
m_config.pclConvolutionKernelSize,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclConvolutionStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_convolution";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclGaussianKernelStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
return PclConvolutionStage(m_config).process(input, stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclGaussianKernelStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_gaussian_kernel";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclGaussianKernelRgbStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
return PclConvolutionStage(m_config).process(input, stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclGaussianKernelRgbStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_gaussian_kernel_rgb";
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame PclVoxelGridOcclusionStage::process(const core::PointCloudFrame &input, core::PipelineStats &stats) const
|
||||||
|
{
|
||||||
|
core::PointCloudFrame output = input;
|
||||||
|
int removed = 0;
|
||||||
|
output.points = adapters::pcl::applyVoxelOcclusionEstimation(
|
||||||
|
input.points,
|
||||||
|
m_config.pclVoxelOccLeaf,
|
||||||
|
m_config.pclVoxelOccMinHits,
|
||||||
|
removed);
|
||||||
|
stats.removedDownsamplePoints += removed;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PclVoxelGridOcclusionStage::id() const
|
||||||
|
{
|
||||||
|
return "pcl_voxel_grid_occlusion";
|
||||||
|
}
|
||||||
} // namespace strategies
|
} // namespace strategies
|
||||||
|
|||||||
@@ -15,11 +15,38 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::PreprocessConfig m_config;
|
core::PreprocessConfig m_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PclRemoveNaNStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclRemoveNaNStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclRemoveNaNNormalsStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclRemoveNaNNormalsStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
class PclStatisticalOutlierStage : public core::IPreprocessStage
|
class PclStatisticalOutlierStage : public core::IPreprocessStage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -29,6 +56,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::PreprocessConfig m_config;
|
core::PreprocessConfig m_config;
|
||||||
@@ -43,10 +71,353 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::PreprocessConfig m_config;
|
core::PreprocessConfig m_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PclModelOutlierStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclModelOutlierStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclShadowPointsStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclShadowPointsStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclApproximateVoxelGridStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclApproximateVoxelGridStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclVoxelGridLabelStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclVoxelGridLabelStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclVoxelGridCovarianceStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclVoxelGridCovarianceStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclGridMinimumStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclGridMinimumStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclFarthestPointSamplingStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclFarthestPointSamplingStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclNormalSpaceSamplingStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclNormalSpaceSamplingStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclSamplingSurfaceNormalStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclSamplingSurfaceNormalStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclPassThroughStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclPassThroughStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclCropBoxStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclCropBoxStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclCropHullStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclCropHullStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclFrustumCullingStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclFrustumCullingStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclPlaneClipper3DStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclPlaneClipper3DStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclConditionalRemovalStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclConditionalRemovalStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclExtractIndicesStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclExtractIndicesStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclFunctorFilterStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclFunctorFilterStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclProjectInliersStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclProjectInliersStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclNormalRefinementStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclNormalRefinementStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclBilateralFilterStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclBilateralFilterStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclFastBilateralFilterStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclFastBilateralFilterStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclFastBilateralFilterOmpStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclFastBilateralFilterOmpStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclConvolutionStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclConvolutionStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclGaussianKernelStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclGaussianKernelStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclGaussianKernelRgbStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclGaussianKernelRgbStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PclVoxelGridOcclusionStage : public core::IPreprocessStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclVoxelGridOcclusionStage(const core::PreprocessConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
core::PointCloudFrame process(const core::PointCloudFrame &input, core::PipelineStats &stats) const override;
|
||||||
|
QString id() const override;
|
||||||
|
private:
|
||||||
|
core::PreprocessConfig m_config;
|
||||||
|
};
|
||||||
} // namespace strategies
|
} // namespace strategies
|
||||||
|
|
||||||
#endif // PREPROCESS_PCL_STAGES_H
|
#endif // PREPROCESS_PCL_STAGES_H
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef RECONSTRUCTION_PCL_POISSON_STAGE_H
|
||||||
|
#define RECONSTRUCTION_PCL_POISSON_STAGE_H
|
||||||
|
|
||||||
|
#include "../adapters/pcl/pcl_point_cloud_adapter.h"
|
||||||
|
#include "../adapters/reconstruction/reconstruction_adapter.h"
|
||||||
|
#include "../core/pipeline_config.h"
|
||||||
|
#include "../core/pipeline_stage.h"
|
||||||
|
|
||||||
|
namespace strategies
|
||||||
|
{
|
||||||
|
class PclPoissonReconstructionStage : public core::IReconstructionStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PclPoissonReconstructionStage(const core::ReconstructionConfig &config)
|
||||||
|
: m_config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<core::Triangle> reconstruct(
|
||||||
|
const core::PointCloudFrame &frame,
|
||||||
|
const core::PipelineContext &,
|
||||||
|
core::PipelineStats &) const override
|
||||||
|
{
|
||||||
|
QVector<core::Triangle> triangles = adapters::pcl::buildPoissonTriangles(
|
||||||
|
frame.points,
|
||||||
|
m_config.pclPoissonDepth,
|
||||||
|
m_config.pclPoissonSamplesPerNode);
|
||||||
|
if (!triangles.isEmpty()) {
|
||||||
|
return triangles;
|
||||||
|
}
|
||||||
|
return adapters::reconstruction::buildSurfaceTriangles(frame.points, m_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
core::ReconstructionConfig m_config;
|
||||||
|
};
|
||||||
|
} // namespace strategies
|
||||||
|
|
||||||
|
#endif // RECONSTRUCTION_PCL_POISSON_STAGE_H
|
||||||
@@ -145,6 +145,10 @@ bool runPluginChainCase(QString &failureReason)
|
|||||||
failureReason = "Plugin chain produced empty frame.";
|
failureReason = "Plugin chain produced empty frame.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (result.stats.preprocessStepMetrics.size() != config.preprocessPlugins.size()) {
|
||||||
|
failureReason = "Per-stage preprocess metrics count mismatch.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1083
-40
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,9 @@ class MainWindow : public QMainWindow
|
|||||||
Q_PROPERTY(int selectedStageIndex READ selectedStageIndex WRITE setSelectedStageIndex NOTIFY selectedStageIndexChanged)
|
Q_PROPERTY(int selectedStageIndex READ selectedStageIndex WRITE setSelectedStageIndex NOTIFY selectedStageIndexChanged)
|
||||||
Q_PROPERTY(QVariantMap selectedStage READ selectedStage NOTIFY selectedStageChanged)
|
Q_PROPERTY(QVariantMap selectedStage READ selectedStage NOTIFY selectedStageChanged)
|
||||||
Q_PROPERTY(QVariantMap metrics READ metrics NOTIFY metricsChanged)
|
Q_PROPERTY(QVariantMap metrics READ metrics NOTIFY metricsChanged)
|
||||||
|
Q_PROPERTY(QVariantList stageMetrics READ stageMetrics NOTIFY stageMetricsChanged)
|
||||||
Q_PROPERTY(QString recommendation READ recommendation NOTIFY recommendationChanged)
|
Q_PROPERTY(QString recommendation READ recommendation NOTIFY recommendationChanged)
|
||||||
|
Q_PROPERTY(QVariantList warningsList READ warningsList NOTIFY warningsListChanged)
|
||||||
Q_PROPERTY(QString chainHealth READ chainHealth NOTIFY chainHealthChanged)
|
Q_PROPERTY(QString chainHealth READ chainHealth NOTIFY chainHealthChanged)
|
||||||
Q_PROPERTY(QStringList demoSurfaceTypes READ demoSurfaceTypes CONSTANT)
|
Q_PROPERTY(QStringList demoSurfaceTypes READ demoSurfaceTypes CONSTANT)
|
||||||
Q_PROPERTY(QString demoSurfaceType READ demoSurfaceType WRITE setDemoSurfaceType NOTIFY demoSurfaceTypeChanged)
|
Q_PROPERTY(QString demoSurfaceType READ demoSurfaceType WRITE setDemoSurfaceType NOTIFY demoSurfaceTypeChanged)
|
||||||
@@ -39,6 +41,8 @@ class MainWindow : public QMainWindow
|
|||||||
Q_PROPERTY(QString wizardGoal READ wizardGoal WRITE setWizardGoal NOTIFY wizardGoalChanged)
|
Q_PROPERTY(QString wizardGoal READ wizardGoal WRITE setWizardGoal NOTIFY wizardGoalChanged)
|
||||||
Q_PROPERTY(QString wizardProfile READ wizardProfile WRITE setWizardProfile NOTIFY wizardProfileChanged)
|
Q_PROPERTY(QString wizardProfile READ wizardProfile WRITE setWizardProfile NOTIFY wizardProfileChanged)
|
||||||
Q_PROPERTY(QVariantList snapshots READ snapshots NOTIFY snapshotsChanged)
|
Q_PROPERTY(QVariantList snapshots READ snapshots NOTIFY snapshotsChanged)
|
||||||
|
Q_PROPERTY(QVariantList presetItems READ presetItems NOTIFY presetItemsChanged)
|
||||||
|
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
@@ -54,7 +58,9 @@ public:
|
|||||||
int selectedStageIndex() const;
|
int selectedStageIndex() const;
|
||||||
QVariantMap selectedStage() const;
|
QVariantMap selectedStage() const;
|
||||||
QVariantMap metrics() const;
|
QVariantMap metrics() const;
|
||||||
|
QVariantList stageMetrics() const;
|
||||||
QString recommendation() const;
|
QString recommendation() const;
|
||||||
|
QVariantList warningsList() const;
|
||||||
QString chainHealth() const;
|
QString chainHealth() const;
|
||||||
QStringList demoSurfaceTypes() const;
|
QStringList demoSurfaceTypes() const;
|
||||||
QString demoSurfaceType() const;
|
QString demoSurfaceType() const;
|
||||||
@@ -62,6 +68,8 @@ public:
|
|||||||
QString wizardGoal() const;
|
QString wizardGoal() const;
|
||||||
QString wizardProfile() const;
|
QString wizardProfile() const;
|
||||||
QVariantList snapshots() const;
|
QVariantList snapshots() const;
|
||||||
|
QVariantList presetItems() const;
|
||||||
|
bool busy() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void generateDemo();
|
void generateDemo();
|
||||||
@@ -87,12 +95,17 @@ public slots:
|
|||||||
void applyWizardSuggestion();
|
void applyWizardSuggestion();
|
||||||
void saveSnapshot(const QString &slotName);
|
void saveSnapshot(const QString &slotName);
|
||||||
void loadSnapshot(const QString &slotName);
|
void loadSnapshot(const QString &slotName);
|
||||||
|
void saveCurrentPreset();
|
||||||
|
void loadPresetFromFile();
|
||||||
|
void exportCapabilitiesToFile();
|
||||||
|
void chooseUserDataDirectory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct StageCardData {
|
struct StageCardData {
|
||||||
QString id;
|
QString id;
|
||||||
QString title;
|
QString title;
|
||||||
QString category;
|
QString category;
|
||||||
|
QString family;
|
||||||
QString hint;
|
QString hint;
|
||||||
QString defaults;
|
QString defaults;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
@@ -111,6 +124,16 @@ private:
|
|||||||
QString displayNameForStage(const QString &stageId) const;
|
QString displayNameForStage(const QString &stageId) const;
|
||||||
StageCardData createCardForStage(const QString &stageId, bool enabled = true) const;
|
StageCardData createCardForStage(const QString &stageId, bool enabled = true) const;
|
||||||
QStringList activeStageIds() const;
|
QStringList activeStageIds() const;
|
||||||
|
QVariantList builtInPresetItems() const;
|
||||||
|
QString userDataDirectoryPath() const;
|
||||||
|
QString userPresetsFilePath() const;
|
||||||
|
QVariantList loadUserPresetsFromDisk(QString *errorText = nullptr) const;
|
||||||
|
bool saveUserPresetsToDisk(const QVariantList &presets, QString *errorText = nullptr) const;
|
||||||
|
bool applyUserPreset(const QString &presetId);
|
||||||
|
QString makeUserPresetId(const QString &name) const;
|
||||||
|
bool normalizeImportedPreset(const QVariantMap &input, QVariantMap &normalized, QString &errorText) const;
|
||||||
|
void beginBusy();
|
||||||
|
void endBusy();
|
||||||
|
|
||||||
GlView *m_glView;
|
GlView *m_glView;
|
||||||
QQuickWidget *m_dashboardView;
|
QQuickWidget *m_dashboardView;
|
||||||
@@ -124,13 +147,19 @@ private:
|
|||||||
QVector<StageCardData> m_stageCards;
|
QVector<StageCardData> m_stageCards;
|
||||||
int m_selectedStageIndex;
|
int m_selectedStageIndex;
|
||||||
QVariantMap m_metrics;
|
QVariantMap m_metrics;
|
||||||
|
QVariantList m_stageMetrics;
|
||||||
QString m_recommendation;
|
QString m_recommendation;
|
||||||
|
QStringList m_warningsList;
|
||||||
QString m_chainHealth;
|
QString m_chainHealth;
|
||||||
QString m_demoSurfaceType;
|
QString m_demoSurfaceType;
|
||||||
QString m_uiMode;
|
QString m_uiMode;
|
||||||
QString m_wizardGoal;
|
QString m_wizardGoal;
|
||||||
QString m_wizardProfile;
|
QString m_wizardProfile;
|
||||||
QVariantMap m_savedSnapshots;
|
QVariantMap m_savedSnapshots;
|
||||||
|
QVariantList m_userPresets;
|
||||||
|
QString m_userDataDirectory;
|
||||||
|
bool m_busy;
|
||||||
|
int m_busyDepth;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stageChainChanged();
|
void stageChainChanged();
|
||||||
@@ -142,13 +171,17 @@ signals:
|
|||||||
void selectedStageIndexChanged();
|
void selectedStageIndexChanged();
|
||||||
void selectedStageChanged();
|
void selectedStageChanged();
|
||||||
void metricsChanged();
|
void metricsChanged();
|
||||||
|
void stageMetricsChanged();
|
||||||
void recommendationChanged();
|
void recommendationChanged();
|
||||||
|
void warningsListChanged();
|
||||||
void chainHealthChanged();
|
void chainHealthChanged();
|
||||||
void demoSurfaceTypeChanged();
|
void demoSurfaceTypeChanged();
|
||||||
void uiModeChanged();
|
void uiModeChanged();
|
||||||
void wizardGoalChanged();
|
void wizardGoalChanged();
|
||||||
void wizardProfileChanged();
|
void wizardProfileChanged();
|
||||||
void snapshotsChanged();
|
void snapshotsChanged();
|
||||||
|
void presetItemsChanged();
|
||||||
|
void busyChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ GroupBox {
|
|||||||
readonly property var safeBackend: backend ? backend : ({
|
readonly property var safeBackend: backend ? backend : ({
|
||||||
chainHealth: "",
|
chainHealth: "",
|
||||||
recommendation: "",
|
recommendation: "",
|
||||||
|
warningsList: [],
|
||||||
|
stageMetrics: [],
|
||||||
metrics: ({
|
metrics: ({
|
||||||
triangles: 0,
|
triangles: 0,
|
||||||
reconstructMs: 0,
|
reconstructMs: 0,
|
||||||
@@ -27,8 +29,14 @@ GroupBox {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
spacing: Theme.spacingXs
|
spacing: Theme.spacingXs
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -47,6 +55,23 @@ GroupBox {
|
|||||||
font.pixelSize: Theme.fontSm
|
font.pixelSize: Theme.fontSm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: safeBackend.warningsList && safeBackend.warningsList.length > 0
|
||||||
|
spacing: Theme.spacingXs / 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: safeBackend.warningsList
|
||||||
|
delegate: Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: "\u2022 " + modelData
|
||||||
|
color: Theme.summaryRecommendation
|
||||||
|
font.pixelSize: Theme.fontXs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Theme.spacingXs / 2
|
Layout.topMargin: Theme.spacingXs / 2
|
||||||
@@ -58,5 +83,34 @@ GroupBox {
|
|||||||
Label { text: "Удалено: " + safeBackend.metrics.removedPoints; color: Theme.summaryDetailText; font.pixelSize: Theme.fontXs }
|
Label { text: "Удалено: " + safeBackend.metrics.removedPoints; color: Theme.summaryDetailText; font.pixelSize: Theme.fontXs }
|
||||||
Label { text: "Кластеры: " + safeBackend.metrics.clusters; color: Theme.summaryDetailText; font.pixelSize: Theme.fontXs }
|
Label { text: "Кластеры: " + safeBackend.metrics.clusters; color: Theme.summaryDetailText; font.pixelSize: Theme.fontXs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: safeBackend.stageMetrics && safeBackend.stageMetrics.length > 0
|
||||||
|
spacing: Theme.spacingXs / 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Метрики по шагам"
|
||||||
|
color: Theme.summarySecondaryText
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: Theme.fontXs
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: safeBackend.stageMetrics
|
||||||
|
delegate: Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: "\u2022 " + modelData.id
|
||||||
|
+ ": " + modelData.inputPoints
|
||||||
|
+ " \u2192 " + modelData.outputPoints
|
||||||
|
+ " (\u2212" + modelData.removedPoints + "), "
|
||||||
|
+ modelData.elapsedMs + " мс"
|
||||||
|
color: Theme.summaryDetailText
|
||||||
|
font.pixelSize: Theme.fontXs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../shared/PipelineUiCatalog.js" as UiCatalog
|
|
||||||
import "../../shared/Theme.js" as Theme
|
import "../../shared/Theme.js" as Theme
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -9,14 +8,34 @@ Item {
|
|||||||
property var safeBackend
|
property var safeBackend
|
||||||
property int selectedIndex: -1
|
property int selectedIndex: -1
|
||||||
signal settingsRequested(var stageItem)
|
signal settingsRequested(var stageItem)
|
||||||
|
function categoryAccentColor(category) {
|
||||||
|
if (category === "Обрезка")
|
||||||
|
return "#88d1ff"
|
||||||
|
if (category === "NaN (предочистка)")
|
||||||
|
return "#8fe8ff"
|
||||||
|
if (category === "Условия/Индексы")
|
||||||
|
return "#9fd7ff"
|
||||||
|
if (category === "Шум")
|
||||||
|
return "#a4f4b9"
|
||||||
|
if (category === "Морфология")
|
||||||
|
return "#9cf5d1"
|
||||||
|
if (category === "Прореживание")
|
||||||
|
return "#ffcb8a"
|
||||||
|
if (category === "Сглаживание")
|
||||||
|
return "#ffd892"
|
||||||
|
if (category === "Нормали")
|
||||||
|
return "#d2b7ff"
|
||||||
|
if (category === "Реконструкция")
|
||||||
|
return "#ff9dc4"
|
||||||
|
return Theme.chainDragHandle
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: stageList
|
id: stageList
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: reconstructionCard.top
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: Theme.spacingSm
|
|
||||||
clip: true
|
clip: true
|
||||||
model: root.safeBackend.stageCards
|
model: root.safeBackend.stageCards
|
||||||
|
|
||||||
@@ -35,8 +54,15 @@ Item {
|
|||||||
anchors.margins: Theme.spacingSm
|
anchors.margins: Theme.spacingSm
|
||||||
spacing: Theme.spacingSm
|
spacing: Theme.spacingSm
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 4
|
||||||
|
Layout.fillHeight: true
|
||||||
|
radius: 2
|
||||||
|
color: root.categoryAccentColor(modelData.category)
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: "↕"
|
text: modelData.family === "reconstruction" ? "REC" : "↕"
|
||||||
color: Theme.chainDragHandle
|
color: Theme.chainDragHandle
|
||||||
font.pixelSize: Theme.fontLg
|
font.pixelSize: Theme.fontLg
|
||||||
width: Theme.stageHandleWidth
|
width: Theme.stageHandleWidth
|
||||||
@@ -46,6 +72,8 @@ Item {
|
|||||||
id: dragHandle
|
id: dragHandle
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
drag.target: dragProxy
|
drag.target: dragProxy
|
||||||
|
enabled: modelData.family !== "reconstruction"
|
||||||
|
cursorShape: enabled ? Qt.SizeAllCursor : Qt.PointingHandCursor
|
||||||
onPressed: root.safeBackend.setSelectedStageIndex(index)
|
onPressed: root.safeBackend.setSelectedStageIndex(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +81,7 @@ Item {
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Label { text: modelData.title; color: Theme.paletteFilterText; font.bold: true; font.pixelSize: Theme.fontMd }
|
Label { text: modelData.title; color: Theme.paletteFilterText; font.bold: true; font.pixelSize: Theme.fontMd }
|
||||||
Label { text: modelData.category + " | " + modelData.id; color: Theme.chainDragHandle; font.pixelSize: Theme.fontXs }
|
Label { text: modelData.category + " | " + modelData.id; color: root.categoryAccentColor(modelData.category); font.pixelSize: Theme.fontXs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +103,8 @@ Item {
|
|||||||
DropArea {
|
DropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onEntered: {
|
onEntered: {
|
||||||
if (drag.source && drag.source !== rowRoot && drag.source.itemIndex !== undefined) {
|
if (drag.source && drag.source !== rowRoot && drag.source.itemIndex !== undefined
|
||||||
|
&& modelData.family !== "reconstruction") {
|
||||||
root.safeBackend.moveStage(drag.source.itemIndex, index)
|
root.safeBackend.moveStage(drag.source.itemIndex, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +114,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
onClicked: root.safeBackend.setSelectedStageIndex(index)
|
onClicked: root.safeBackend.setSelectedStageIndex(index)
|
||||||
onDoubleClicked: {
|
onDoubleClicked: {
|
||||||
@@ -103,51 +133,10 @@ Item {
|
|||||||
id: stageMenu
|
id: stageMenu
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Удалить элемент"
|
text: "Удалить элемент"
|
||||||
|
enabled: modelData.family !== "reconstruction"
|
||||||
onTriggered: root.safeBackend.removeStage(index)
|
onTriggered: root.safeBackend.removeStage(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: reconstructionCard
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
height: Theme.stageRowHeight
|
|
||||||
radius: Theme.radiusMd
|
|
||||||
border.width: 1
|
|
||||||
border.color: Theme.reconstructionBorder
|
|
||||||
color: Theme.reconstructionBg
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingSm
|
|
||||||
spacing: Theme.spacingSm
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: "REC"
|
|
||||||
color: Theme.reconstructionAccent
|
|
||||||
font.bold: true
|
|
||||||
font.pixelSize: Theme.fontSm
|
|
||||||
width: Theme.reconstructionBadgeWidth
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Label {
|
|
||||||
text: UiCatalog.reconstructionTitle(root.safeBackend.reconstructionMethod)
|
|
||||||
color: Theme.reconstructionText
|
|
||||||
font.bold: true
|
|
||||||
font.pixelSize: Theme.fontMd
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
text: "Реконструкция | " + root.safeBackend.reconstructionMethod
|
|
||||||
color: Theme.reconstructionSubtext
|
|
||||||
font.pixelSize: Theme.fontXs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,17 +19,51 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXs
|
spacing: Theme.spacingXs
|
||||||
|
|
||||||
Label { text: "Фильтры"; font.bold: true; color: Theme.paletteHeader; padding: Theme.spacingXs }
|
Label { text: "Стадии по этапам"; font.bold: true; color: Theme.paletteHeader; padding: Theme.spacingXs }
|
||||||
Repeater {
|
Repeater {
|
||||||
model: UiCatalog.filterPaletteModel()
|
model: UiCatalog.filterPaletteGroupedModel()
|
||||||
|
delegate: Column {
|
||||||
|
id: phaseSection
|
||||||
|
property color accentColor: modelData.phaseAccent ? modelData.phaseAccent : Theme.paletteHeader
|
||||||
|
property color filterBgColor: modelData.phaseFilterBg ? modelData.phaseFilterBg : Theme.paletteFilterBg
|
||||||
|
property color filterBorderColor: modelData.phaseFilterBorder ? modelData.phaseFilterBorder : Theme.paletteFilterBorder
|
||||||
|
width: paletteColumn.width
|
||||||
|
spacing: Theme.spacingXs / 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: modelData.phaseTitle
|
||||||
|
font.bold: true
|
||||||
|
color: phaseSection.accentColor
|
||||||
|
padding: Theme.spacingXs
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
visible: modelData.phaseHint.length > 0
|
||||||
|
text: modelData.phaseHint
|
||||||
|
color: phaseSection.accentColor
|
||||||
|
font.pixelSize: Theme.fontXs
|
||||||
|
leftPadding: Theme.spacingXs
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
visible: modelData.items.length === 0
|
||||||
|
text: "Пока нет доступных фильтров для этого этапа."
|
||||||
|
color: phaseSection.accentColor
|
||||||
|
font.pixelSize: Theme.fontXs
|
||||||
|
leftPadding: Theme.spacingXs
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: modelData.items
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
property string paletteStageId: modelData.idValue
|
property string paletteStageId: modelData.idValue
|
||||||
width: paletteColumn.width
|
width: paletteColumn.width
|
||||||
height: Theme.paletteItemHeight
|
height: Theme.paletteItemHeight
|
||||||
radius: Theme.radiusSm
|
radius: Theme.radiusSm
|
||||||
color: Theme.paletteFilterBg
|
color: phaseSection.filterBgColor
|
||||||
border.width: 1
|
border.width: 1
|
||||||
border.color: Theme.paletteFilterBorder
|
border.color: phaseSection.filterBorderColor
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -38,35 +72,24 @@ Item {
|
|||||||
color: Theme.paletteFilterText
|
color: Theme.paletteFilterText
|
||||||
font.pixelSize: Theme.fontSm
|
font.pixelSize: Theme.fontSm
|
||||||
}
|
}
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onDoubleClicked: root.safeBackend.addStage(parent.paletteStageId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label { text: "Реконструкторы поверхности"; font.bold: true; color: Theme.paletteHeader; padding: Theme.spacingXs }
|
ToolTip.visible: hoverArea.containsMouse
|
||||||
Repeater {
|
ToolTip.text: modelData.hint
|
||||||
model: UiCatalog.reconstructionPaletteModel()
|
ToolTip.delay: 200
|
||||||
delegate: Rectangle {
|
|
||||||
property string paletteStageId: modelData.idValue
|
|
||||||
width: paletteColumn.width
|
|
||||||
height: Theme.paletteItemHeight
|
|
||||||
radius: Theme.radiusSm
|
|
||||||
color: Theme.paletteReconBg
|
|
||||||
border.width: 1
|
|
||||||
border.color: Theme.paletteReconBorder
|
|
||||||
Label {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingMd
|
|
||||||
text: modelData.title
|
|
||||||
color: Theme.paletteReconText
|
|
||||||
font.pixelSize: Theme.fontSm
|
|
||||||
}
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
id: hoverArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onDoubleClicked: root.safeBackend.setReconstructionMethod(parent.paletteStageId)
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onDoubleClicked: {
|
||||||
|
if (modelData.family === "reconstruction")
|
||||||
|
root.safeBackend.setReconstructionMethod(parent.paletteStageId)
|
||||||
|
else
|
||||||
|
root.safeBackend.addStage(parent.paletteStageId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ Dialog {
|
|||||||
width: Theme.dialogWidth
|
width: Theme.dialogWidth
|
||||||
title: ""
|
title: ""
|
||||||
standardButtons: Dialog.NoButton
|
standardButtons: Dialog.NoButton
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusMd
|
||||||
|
color: Theme.dialogBg
|
||||||
|
border.color: Theme.dialogBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (safeBackend.selectedStageIndex >= 0) {
|
if (safeBackend.selectedStageIndex >= 0) {
|
||||||
@@ -36,15 +42,17 @@ Dialog {
|
|||||||
onOpened: root.paramItems = UiCatalog.parseDefaults(popupItem.defaults)
|
onOpened: root.paramItems = UiCatalog.parseDefaults(popupItem.defaults)
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: Theme.spacingSm
|
spacing: Theme.dialogContentSpacing
|
||||||
Label {
|
Label {
|
||||||
text: popupItem.title ? popupItem.title : "Элемент не выбран"
|
text: popupItem.title ? popupItem.title : "Элемент не выбран"
|
||||||
font.bold: true
|
font.bold: true
|
||||||
|
color: Theme.controlText
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
text: popupItem.hint ? popupItem.hint : "Для этого элемента нет подробного описания."
|
text: popupItem.hint ? popupItem.hint : "Для этого элемента нет подробного описания."
|
||||||
|
color: Theme.dialogDescriptionText
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
@@ -58,10 +66,10 @@ Dialog {
|
|||||||
model: root.paramItems
|
model: root.paramItems
|
||||||
delegate: ColumnLayout {
|
delegate: ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 2
|
spacing: Theme.spacingXs / 2
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Theme.spacingMd
|
spacing: Theme.dialogRowSpacing
|
||||||
Label {
|
Label {
|
||||||
text: modelData.key + ":"
|
text: modelData.key + ":"
|
||||||
color: Theme.dialogHintText
|
color: Theme.dialogHintText
|
||||||
@@ -71,12 +79,30 @@ Dialog {
|
|||||||
ComboBox {
|
ComboBox {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: modelData.kind === "bool"
|
visible: modelData.kind === "bool"
|
||||||
|
hoverEnabled: true
|
||||||
model: ["false", "true"]
|
model: ["false", "true"]
|
||||||
currentIndex: modelData.value === "true" ? 1 : 0
|
currentIndex: modelData.value === "true" ? 1 : 0
|
||||||
onCurrentTextChanged: {
|
onCurrentTextChanged: {
|
||||||
if (modelData.kind === "bool")
|
if (modelData.kind === "bool")
|
||||||
root.updateParamValue(index, currentText)
|
root.updateParamValue(index, currentText)
|
||||||
}
|
}
|
||||||
|
contentItem: Text {
|
||||||
|
text: parent.displayText
|
||||||
|
color: Theme.controlText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: Theme.spacingSm
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: Theme.controlBg
|
||||||
|
border.color: Theme.controlBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -84,6 +110,19 @@ Dialog {
|
|||||||
text: modelData.value
|
text: modelData.value
|
||||||
validator: IntValidator {}
|
validator: IntValidator {}
|
||||||
onEditingFinished: root.updateParamValue(index, text)
|
onEditingFinished: root.updateParamValue(index, text)
|
||||||
|
color: Theme.controlText
|
||||||
|
placeholderTextColor: Theme.controlPlaceholder
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: Theme.controlBg
|
||||||
|
border.color: Theme.controlBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.IBeamCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -91,12 +130,38 @@ Dialog {
|
|||||||
text: modelData.value
|
text: modelData.value
|
||||||
validator: DoubleValidator {}
|
validator: DoubleValidator {}
|
||||||
onEditingFinished: root.updateParamValue(index, text)
|
onEditingFinished: root.updateParamValue(index, text)
|
||||||
|
color: Theme.controlText
|
||||||
|
placeholderTextColor: Theme.controlPlaceholder
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: Theme.controlBg
|
||||||
|
border.color: Theme.controlBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.IBeamCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: modelData.kind === "string"
|
visible: modelData.kind === "string"
|
||||||
text: modelData.value
|
text: modelData.value
|
||||||
onEditingFinished: root.updateParamValue(index, text)
|
onEditingFinished: root.updateParamValue(index, text)
|
||||||
|
color: Theme.controlText
|
||||||
|
placeholderTextColor: Theme.controlPlaceholder
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: Theme.controlBg
|
||||||
|
border.color: Theme.controlBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.IBeamCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
@@ -110,18 +175,34 @@ Dialog {
|
|||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Theme.spacingMd
|
Layout.topMargin: Theme.spacingSm
|
||||||
spacing: Theme.spacingMd
|
spacing: Theme.dialogRowSpacing
|
||||||
Button {
|
Button {
|
||||||
text: "Установить по умолчанию"
|
text: "Установить по умолчанию"
|
||||||
Layout.preferredWidth: Theme.dialogResetButtonWidth
|
Layout.preferredWidth: Theme.dialogResetButtonWidth
|
||||||
|
hoverEnabled: true
|
||||||
onClicked: root.paramItems = UiCatalog.parseDefaults(safeBackend.defaultsForStage(popupItem.id))
|
onClicked: root.paramItems = UiCatalog.parseDefaults(safeBackend.defaultsForStage(popupItem.id))
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
Button {
|
Button {
|
||||||
text: "OK"
|
text: "OK"
|
||||||
Layout.preferredWidth: Theme.dialogOkButtonWidth
|
Layout.preferredWidth: Theme.dialogOkButtonWidth
|
||||||
|
hoverEnabled: true
|
||||||
onClicked: root.accept()
|
onClicked: root.accept()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.primaryButtonText; font.bold: true; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.primaryButtonPressedBg : (parent.hovered ? Theme.primaryButtonHoverBg : Theme.primaryButtonBg); border.color: Theme.primaryButtonBorder; border.width: 1 }
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,27 @@ import "../shared/Theme.js" as Theme
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
color: Theme.panelBg
|
color: Theme.panelBg
|
||||||
property int compactH: Theme.compactControlHeight
|
property int compactH: Theme.compactControlHeight
|
||||||
|
property int selectedPresetIndex: 0
|
||||||
readonly property var safeBackend: backend ? backend : ({
|
readonly property var safeBackend: backend ? backend : ({
|
||||||
surfaceVisible: true,
|
surfaceVisible: true,
|
||||||
demoSurfaceTypes: ["Сфера", "Тор", "Волна"],
|
busy: false,
|
||||||
|
demoSurfaceTypes: ["Сфера", "Тор", "Волна", "Дно реки + труба"],
|
||||||
demoSurfaceType: "Сфера",
|
demoSurfaceType: "Сфера",
|
||||||
|
presetItems: [
|
||||||
|
{ title: "LiDAR-скан", idValue: "LiDAR_scan" },
|
||||||
|
{ title: "RGB-D камера", idValue: "RGBD_camera" },
|
||||||
|
{ title: "Синтетика", idValue: "Synthetic_clean" }
|
||||||
|
],
|
||||||
openPointsFile: function() {},
|
openPointsFile: function() {},
|
||||||
generateDemo: function() {},
|
generateDemo: function() {},
|
||||||
setDemoSurfaceType: function(_) {},
|
setDemoSurfaceType: function(_) {},
|
||||||
setSurfaceVisible: function(_) {},
|
setSurfaceVisible: function(_) {},
|
||||||
applyPipelineFromUi: function() {}
|
applyPipelineFromUi: function() {},
|
||||||
|
applyPreset: function(_) {},
|
||||||
|
saveCurrentPreset: function() {},
|
||||||
|
loadPresetFromFile: function() {},
|
||||||
|
exportCapabilitiesToFile: function() {},
|
||||||
|
chooseUserDataDirectory: function() {}
|
||||||
})
|
})
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -29,15 +41,68 @@ Rectangle {
|
|||||||
ComboBox {
|
ComboBox {
|
||||||
Layout.preferredWidth: Theme.dashboardSurfaceComboWidth
|
Layout.preferredWidth: Theme.dashboardSurfaceComboWidth
|
||||||
Layout.preferredHeight: compactH
|
Layout.preferredHeight: compactH
|
||||||
|
hoverEnabled: true
|
||||||
model: safeBackend.demoSurfaceTypes
|
model: safeBackend.demoSurfaceTypes
|
||||||
currentIndex: model.indexOf(safeBackend.demoSurfaceType)
|
currentIndex: model.indexOf(safeBackend.demoSurfaceType)
|
||||||
onActivated: safeBackend.setDemoSurfaceType(currentText)
|
onActivated: safeBackend.setDemoSurfaceType(currentText)
|
||||||
|
contentItem: Text {
|
||||||
|
text: parent.displayText
|
||||||
|
color: Theme.controlText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: Theme.spacingSm
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: parent.pressed ? Theme.controlHoverBg : Theme.controlBg
|
||||||
|
border.color: Theme.controlBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Сгенерировать"
|
||||||
|
height: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.generateDemo()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Загрузить"
|
||||||
|
height: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.openPointsFile()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
}
|
}
|
||||||
Button { text: "Сгенерировать"; height: compactH; onClicked: safeBackend.generateDemo() }
|
|
||||||
Button { text: "Загрузить"; height: compactH; onClicked: safeBackend.openPointsFile() }
|
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingSm
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Пресет:"
|
||||||
|
color: Theme.summaryDetailText
|
||||||
|
font.pixelSize: Theme.fontSm
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: "Стартовая цепочка: примените и при необходимости отредактируйте вручную."
|
||||||
|
color: Theme.chainDragHandle
|
||||||
|
font.pixelSize: Theme.fontXs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PipelineCanvas {
|
PipelineCanvas {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@@ -46,9 +111,121 @@ Rectangle {
|
|||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Theme.spacingSm
|
spacing: Theme.spacingSm
|
||||||
Switch { checked: safeBackend.surfaceVisible; onToggled: safeBackend.setSurfaceVisible(checked) }
|
Switch {
|
||||||
|
checked: safeBackend.surfaceVisible
|
||||||
|
onToggled: safeBackend.setSurfaceVisible(checked)
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
Button { text: "Применить"; Layout.preferredHeight: compactH; onClicked: safeBackend.applyPipelineFromUi() }
|
Button {
|
||||||
|
text: "Применить"
|
||||||
|
Layout.preferredHeight: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.applyPipelineFromUi()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.primaryButtonText; font.bold: true; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: parent.down ? Theme.primaryButtonPressedBg : (parent.hovered ? Theme.primaryButtonHoverBg : Theme.primaryButtonBg)
|
||||||
|
border.color: Theme.primaryButtonBorder
|
||||||
|
border.width: 2
|
||||||
|
}
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingXs
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingSm
|
||||||
|
ComboBox {
|
||||||
|
Layout.preferredWidth: 190
|
||||||
|
Layout.minimumWidth: 170
|
||||||
|
Layout.maximumWidth: 220
|
||||||
|
Layout.preferredHeight: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
model: safeBackend.presetItems
|
||||||
|
textRole: "title"
|
||||||
|
currentIndex: selectedPresetIndex
|
||||||
|
onActivated: selectedPresetIndex = currentIndex
|
||||||
|
contentItem: Text {
|
||||||
|
text: parent.displayText
|
||||||
|
color: Theme.controlText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: Theme.spacingSm
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
radius: Theme.radiusSm
|
||||||
|
color: parent.pressed ? Theme.controlHoverBg : Theme.controlBg
|
||||||
|
border.color: Theme.controlBorder
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Применить пресет"
|
||||||
|
Layout.preferredHeight: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
if (selectedPresetIndex >= 0 && selectedPresetIndex < safeBackend.presetItems.length)
|
||||||
|
safeBackend.applyPreset(safeBackend.presetItems[selectedPresetIndex].idValue)
|
||||||
|
}
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: presetActionsFlow.childrenRect.height
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: presetActionsFlow
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingSm
|
||||||
|
Button {
|
||||||
|
text: "Загрузить пресет"
|
||||||
|
height: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.loadPresetFromFile()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Сохранить пресет"
|
||||||
|
height: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.saveCurrentPreset()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Выгрузка возможностей"
|
||||||
|
height: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.exportCapabilitiesToFile()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Папка данных"
|
||||||
|
height: compactH
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: safeBackend.chooseUserDataDirectory()
|
||||||
|
contentItem: Text { text: parent.text; color: Theme.buttonText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter }
|
||||||
|
background: Rectangle { radius: Theme.radiusSm; color: parent.down ? Theme.buttonPressedBg : (parent.hovered ? Theme.buttonHoverBg : Theme.buttonBg); border.color: Theme.buttonBorder; border.width: 1 }
|
||||||
|
MouseArea { anchors.fill: parent; acceptedButtons: Qt.NoButton; cursorShape: Qt.PointingHandCursor }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MetricsStrip {
|
MetricsStrip {
|
||||||
@@ -58,4 +235,31 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: safeBackend.busy
|
||||||
|
color: "#90000000"
|
||||||
|
z: 1000
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingSm
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
running: safeBackend.busy
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Выполняются вычисления..."
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,58 @@ function paramDescription(key) {
|
|||||||
return "Радиус поиска соседей."
|
return "Радиус поиска соседей."
|
||||||
if (key === "minNeighbors")
|
if (key === "minNeighbors")
|
||||||
return "Минимум соседей, чтобы точка считалась валидной."
|
return "Минимум соседей, чтобы точка считалась валидной."
|
||||||
|
if (key === "threshold")
|
||||||
|
return "Порог отклонения точки от геометрической модели."
|
||||||
|
if (key === "shadowThreshold")
|
||||||
|
return "Порог для удаления теневых точек по нормалям."
|
||||||
|
if (key === "resolution")
|
||||||
|
return "Размер ячейки 2D-сетки для фильтра GridMinimum."
|
||||||
|
if (key === "sample")
|
||||||
|
return "Количество точек, которое нужно оставить после выборки."
|
||||||
|
if (key === "axis")
|
||||||
|
return "Ось фильтрации: x, y или z."
|
||||||
|
if (key === "min" || key === "max")
|
||||||
|
return "Граница диапазона для фильтра PassThrough."
|
||||||
|
if (key === "minX" || key === "minY" || key === "minZ" || key === "maxX" || key === "maxY" || key === "maxZ")
|
||||||
|
return "Границы CropBox/CropHull по соответствующим осям."
|
||||||
|
if (key === "near" || key === "far")
|
||||||
|
return "Ближняя/дальняя граница фрустума."
|
||||||
|
if (key === "hfov" || key === "vfov")
|
||||||
|
return "Горизонтальный/вертикальный угол обзора (в градусах)."
|
||||||
|
if (key === "a" || key === "b" || key === "c" || key === "d")
|
||||||
|
return "Коэффициенты плоскости ax+by+cz+d=0."
|
||||||
|
if (key === "keepPositive")
|
||||||
|
return "Оставлять ли точки на положительной стороне плоскости."
|
||||||
|
if (key === "zMin" || key === "zMax")
|
||||||
|
return "Допустимый диапазон координаты Z для conditional-фильтра."
|
||||||
|
if (key === "nth")
|
||||||
|
return "Оставлять каждую N-ю точку."
|
||||||
|
if (key === "radiusMax")
|
||||||
|
return "Максимальный радиус точки от начала координат."
|
||||||
|
if (key === "sigmaS" || key === "sigmaR")
|
||||||
|
return "Параметры bilateral-фильтра (пространство/интенсивность)."
|
||||||
|
if (key === "sigma" || key === "kernel")
|
||||||
|
return "Параметры гауссова ядра свертки."
|
||||||
|
if (key === "iterations")
|
||||||
|
return "Число итераций уточнения."
|
||||||
|
if (key === "minHits")
|
||||||
|
return "Минимальная заполненность вокселя для прохождения фильтра."
|
||||||
|
if (key === "neighborRadiusScale")
|
||||||
|
return "Масштаб радиуса поиска соседей для fallback-реконструкции."
|
||||||
|
if (key === "runEveryNthFrame")
|
||||||
|
return "Запуск реконструкции на каждом N-м кадре."
|
||||||
|
if (key === "searchRadius")
|
||||||
|
return "Радиус поиска соседей для greedy triangulation."
|
||||||
|
if (key === "mu")
|
||||||
|
return "Коэффициент плотности соседей для greedy triangulation."
|
||||||
|
if (key === "maxNearest")
|
||||||
|
return "Максимум соседей для greedy triangulation."
|
||||||
|
if (key === "maxSurfaceAngle")
|
||||||
|
return "Максимальный угол поверхности в радианах."
|
||||||
|
if (key === "poissonDepth")
|
||||||
|
return "Глубина октодерева для Poisson реконструкции."
|
||||||
|
if (key === "samplesPerNode")
|
||||||
|
return "Число выборок на узел для сглаживания Poisson."
|
||||||
return "Параметр этапа обработки."
|
return "Параметр этапа обработки."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,22 +112,346 @@ function reconstructionTitle(id) {
|
|||||||
return "Fallback Surface"
|
return "Fallback Surface"
|
||||||
if (id === "pcl_greedy_triangulation")
|
if (id === "pcl_greedy_triangulation")
|
||||||
return "PCL Greedy Triangulation"
|
return "PCL Greedy Triangulation"
|
||||||
|
if (id === "pcl_poisson_reconstruction")
|
||||||
|
return "PCL Poisson Reconstruction"
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterPaletteModel() {
|
function phaseModel() {
|
||||||
return [
|
return [
|
||||||
{ title: "Крупнейший кластер", idValue: "keep_largest_cluster" },
|
{
|
||||||
{ title: "Прореживание плотности", idValue: "downsample_dense" },
|
id: "crop",
|
||||||
{ title: "PCL Voxel Grid", idValue: "pcl_voxel_grid" },
|
title: "Обрезка",
|
||||||
{ title: "Статистическая фильтрация", idValue: "pcl_statistical_outlier" },
|
hint: "Ограничение области интереса и удаление лишних фрагментов.",
|
||||||
{ title: "Радиусная фильтрация", idValue: "pcl_radius_outlier" }
|
accent: "#88d1ff",
|
||||||
|
filterBg: "#263e55",
|
||||||
|
filterBorder: "#4b7598"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "nan_preclean",
|
||||||
|
title: "NaN (предочистка)",
|
||||||
|
hint: "Удаление NaN сразу после загрузки/обрезки, иначе статистические фильтры работают нестабильно.",
|
||||||
|
accent: "#8fe8ff",
|
||||||
|
filterBg: "#1f4450",
|
||||||
|
filterBorder: "#3f8394"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "conditions_indexes",
|
||||||
|
title: "Условия/Индексы",
|
||||||
|
hint: "Отбор точек по полям, диапазонам и индексным маскам.",
|
||||||
|
accent: "#9fd7ff",
|
||||||
|
filterBg: "#24405a",
|
||||||
|
filterBorder: "#4f7aa1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "noise",
|
||||||
|
title: "Шум",
|
||||||
|
hint: "Удаление выбросов и нестабильных точек перед геометрией.",
|
||||||
|
accent: "#a4f4b9",
|
||||||
|
filterBg: "#244534",
|
||||||
|
filterBorder: "#4b8f68"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "morphology",
|
||||||
|
title: "Морфология",
|
||||||
|
hint: "Геометрические операции локальной структуры облака.",
|
||||||
|
accent: "#9cf5d1",
|
||||||
|
filterBg: "#1f4a3e",
|
||||||
|
filterBorder: "#4b8f7d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "downsample",
|
||||||
|
title: "Прореживание",
|
||||||
|
hint: "Снижение плотности облака для скорости и устойчивости.",
|
||||||
|
accent: "#ffcb8a",
|
||||||
|
filterBg: "#4a3724",
|
||||||
|
filterBorder: "#8e6f47"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "smoothing",
|
||||||
|
title: "Сглаживание",
|
||||||
|
hint: "Снижение локального шума перед расчетом нормалей и реконструкцией.",
|
||||||
|
accent: "#ffd892",
|
||||||
|
filterBg: "#4e3a22",
|
||||||
|
filterBorder: "#917349"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "normals",
|
||||||
|
title: "Нормали",
|
||||||
|
hint: "Подготовка нормалей для продвинутой реконструкции (этап зарезервирован).",
|
||||||
|
accent: "#d2b7ff",
|
||||||
|
filterBg: "#3e3155",
|
||||||
|
filterBorder: "#6f5a95"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "reconstruction",
|
||||||
|
title: "Реконструкция",
|
||||||
|
hint: "Построение поверхности по подготовленному облаку точек.",
|
||||||
|
accent: "#ff9dc4",
|
||||||
|
filterBg: "#4b2f40",
|
||||||
|
filterBorder: "#8d5a75"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterDefinitions() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Крупнейший кластер",
|
||||||
|
idValue: "keep_largest_cluster",
|
||||||
|
phaseId: "crop",
|
||||||
|
hint: "Оставляет основной объект и отбрасывает изолированные фрагменты."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Remove NaN Points",
|
||||||
|
idValue: "pcl_remove_nan",
|
||||||
|
phaseId: "nan_preclean",
|
||||||
|
family: "preprocess",
|
||||||
|
hint: "Удаляет NaN/Inf после загрузки и обрезки, до статистических фильтров."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Remove NaN Normals",
|
||||||
|
idValue: "pcl_remove_nan_normals",
|
||||||
|
phaseId: "nan_preclean",
|
||||||
|
family: "preprocess",
|
||||||
|
hint: "Удаляет невалидные нормали (в текущем формате — невалидную геометрию)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "PassThrough",
|
||||||
|
idValue: "pcl_pass_through",
|
||||||
|
phaseId: "crop",
|
||||||
|
hint: "Фильтрует точки по диапазону выбранной оси."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "CropBox",
|
||||||
|
idValue: "pcl_crop_box",
|
||||||
|
phaseId: "crop",
|
||||||
|
hint: "Ограничивает облако заданным 3D-параллелепипедом."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "CropHull",
|
||||||
|
idValue: "pcl_crop_hull",
|
||||||
|
phaseId: "crop",
|
||||||
|
hint: "Обрезка по области интереса (в текущей версии через box-границы)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Frustum Culling",
|
||||||
|
idValue: "pcl_frustum_culling",
|
||||||
|
phaseId: "crop",
|
||||||
|
hint: "Оставляет точки в пирамиде видимости камеры."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "PlaneClipper3D",
|
||||||
|
idValue: "pcl_plane_clipper_3d",
|
||||||
|
phaseId: "crop",
|
||||||
|
hint: "Отсекает точки по уравнению плоскости."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Conditional Removal",
|
||||||
|
idValue: "pcl_conditional_removal",
|
||||||
|
phaseId: "conditions_indexes",
|
||||||
|
hint: "Удаляет точки по логическому условию (диапазон Z)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Extract Indices",
|
||||||
|
idValue: "pcl_extract_indices",
|
||||||
|
phaseId: "conditions_indexes",
|
||||||
|
hint: "Извлекает точки по индексной маске (каждая N-я)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Functor Filter",
|
||||||
|
idValue: "pcl_functor_filter",
|
||||||
|
phaseId: "conditions_indexes",
|
||||||
|
hint: "Пользовательский предикат (радиус + проверка валидности)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "ProjectInliers",
|
||||||
|
idValue: "pcl_project_inliers",
|
||||||
|
phaseId: "normals",
|
||||||
|
hint: "Проецирует точки на геометрическую модель (плоскость)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Normal Refinement",
|
||||||
|
idValue: "pcl_normal_refinement",
|
||||||
|
phaseId: "normals",
|
||||||
|
hint: "Уточняет локальную геометрию по соседям."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Статистическая фильтрация",
|
||||||
|
idValue: "pcl_statistical_outlier",
|
||||||
|
phaseId: "noise",
|
||||||
|
hint: "Удаляет выбросы на основе распределения расстояний до соседей."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Радиусная фильтрация",
|
||||||
|
idValue: "pcl_radius_outlier",
|
||||||
|
phaseId: "noise",
|
||||||
|
hint: "Удаляет точки с недостаточным числом соседей в заданном радиусе."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Model Outlier Removal",
|
||||||
|
idValue: "pcl_model_outlier",
|
||||||
|
phaseId: "noise",
|
||||||
|
hint: "Удаляет точки, отклоняющиеся от геометрической модели."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Shadow Points Removal",
|
||||||
|
idValue: "pcl_shadow_points",
|
||||||
|
phaseId: "noise",
|
||||||
|
hint: "Удаляет теневые точки на основе нормалей поверхности."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Approximate Voxel Grid",
|
||||||
|
idValue: "pcl_approximate_voxel_grid",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Ускоренное воксельное прореживание для больших облаков."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Voxel Grid Label",
|
||||||
|
idValue: "pcl_voxel_grid_label",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Воксельное прореживание с поддержкой меток."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Voxel Grid Covariance",
|
||||||
|
idValue: "pcl_voxel_grid_covariance",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Воксельная сетка с ковариациями (для NDT-пайплайнов)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Grid Minimum",
|
||||||
|
idValue: "pcl_grid_minimum",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Оставляет точку с минимальным Z в каждой ячейке."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Farthest Point Sampling",
|
||||||
|
idValue: "pcl_farthest_point_sampling",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Выбирает наиболее удаленные друг от друга точки."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Normal Space Sampling",
|
||||||
|
idValue: "pcl_normal_space_sampling",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Равномерная выборка в пространстве нормалей."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Sampling Surface Normal",
|
||||||
|
idValue: "pcl_sampling_surface_normal",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Выборка точек на основе нормалей поверхности."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Bilateral Filter",
|
||||||
|
idValue: "pcl_bilateral_filter",
|
||||||
|
phaseId: "smoothing",
|
||||||
|
hint: "Двустороннее сглаживание с сохранением границ."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fast Bilateral Filter",
|
||||||
|
idValue: "pcl_fast_bilateral_filter",
|
||||||
|
phaseId: "smoothing",
|
||||||
|
hint: "Быстрое двустороннее сглаживание."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fast Bilateral Filter OMP",
|
||||||
|
idValue: "pcl_fast_bilateral_filter_omp",
|
||||||
|
phaseId: "smoothing",
|
||||||
|
hint: "Параллельная версия bilateral-фильтра."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Convolution",
|
||||||
|
idValue: "pcl_convolution",
|
||||||
|
phaseId: "smoothing",
|
||||||
|
hint: "Свертка облака точек с ядром."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Gaussian Kernel",
|
||||||
|
idValue: "pcl_gaussian_kernel",
|
||||||
|
phaseId: "smoothing",
|
||||||
|
hint: "Гауссово ядро свертки."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Gaussian Kernel RGB",
|
||||||
|
idValue: "pcl_gaussian_kernel_rgb",
|
||||||
|
phaseId: "smoothing",
|
||||||
|
hint: "Гауссово ядро с RGB-ориентированной семантикой."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "VoxelGrid Occlusion Estimation",
|
||||||
|
idValue: "pcl_voxel_grid_occlusion",
|
||||||
|
phaseId: "morphology",
|
||||||
|
hint: "Оценка окклюзии через заполненность вокселей."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fallback Surface",
|
||||||
|
idValue: "surface_fallback",
|
||||||
|
phaseId: "reconstruction",
|
||||||
|
family: "reconstruction",
|
||||||
|
hint: "Базовая реконструкция поверхности по облаку точек."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "PCL Greedy Triangulation",
|
||||||
|
idValue: "pcl_greedy_triangulation",
|
||||||
|
phaseId: "reconstruction",
|
||||||
|
family: "reconstruction",
|
||||||
|
hint: "Жадная триангуляция с параметрами радиуса и углов."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "PCL Poisson Reconstruction",
|
||||||
|
idValue: "pcl_poisson_reconstruction",
|
||||||
|
phaseId: "reconstruction",
|
||||||
|
family: "reconstruction",
|
||||||
|
hint: "Реконструкция поверхности методом Poisson из libpcl_surface."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Прореживание плотности",
|
||||||
|
idValue: "downsample_dense",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Снижает число точек для ускорения пайплайна на плотных облаках."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "PCL Voxel Grid",
|
||||||
|
idValue: "pcl_voxel_grid",
|
||||||
|
phaseId: "downsample",
|
||||||
|
hint: "Равномерно прореживает облако с помощью воксельной сетки."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterPaletteModel() {
|
||||||
|
return filterDefinitions()
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterPaletteGroupedModel() {
|
||||||
|
var phases = phaseModel()
|
||||||
|
var filters = filterDefinitions()
|
||||||
|
var grouped = []
|
||||||
|
for (var p = 0; p < phases.length; ++p) {
|
||||||
|
var phase = phases[p]
|
||||||
|
var items = []
|
||||||
|
for (var i = 0; i < filters.length; ++i) {
|
||||||
|
if (filters[i].phaseId === phase.id)
|
||||||
|
items.push(filters[i])
|
||||||
|
}
|
||||||
|
grouped.push({
|
||||||
|
phaseId: phase.id,
|
||||||
|
phaseTitle: phase.title,
|
||||||
|
phaseHint: phase.hint,
|
||||||
|
phaseAccent: phase.accent,
|
||||||
|
phaseFilterBg: phase.filterBg,
|
||||||
|
phaseFilterBorder: phase.filterBorder,
|
||||||
|
items: items
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return grouped
|
||||||
|
}
|
||||||
|
|
||||||
function reconstructionPaletteModel() {
|
function reconstructionPaletteModel() {
|
||||||
return [
|
return [
|
||||||
{ title: "Fallback Surface", idValue: "surface_fallback" },
|
{ title: "Fallback Surface", idValue: "surface_fallback" },
|
||||||
{ title: "PCL Greedy Triangulation", idValue: "pcl_greedy_triangulation" }
|
{ title: "PCL Greedy Triangulation", idValue: "pcl_greedy_triangulation" },
|
||||||
|
{ title: "PCL Poisson Reconstruction", idValue: "pcl_poisson_reconstruction" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,29 @@ var summarySecondaryText = "#cde0ff"
|
|||||||
var summaryRecommendation = "#9ec3ff"
|
var summaryRecommendation = "#9ec3ff"
|
||||||
var summaryDetailText = "#b8cff8"
|
var summaryDetailText = "#b8cff8"
|
||||||
|
|
||||||
var dialogHintText = "#4a4f63"
|
var dialogHintText = "#c7d6f3"
|
||||||
var dialogDescriptionText = "#7e869d"
|
var dialogDescriptionText = "#aebfde"
|
||||||
var dialogSectionText = "#9eb0d9"
|
var dialogSectionText = "#dce7ff"
|
||||||
|
var dialogBg = "#1a2234"
|
||||||
|
var dialogBorder = "#3a4f78"
|
||||||
|
|
||||||
|
var controlBg = "#23324d"
|
||||||
|
var controlBorder = "#4a6494"
|
||||||
|
var controlText = "#eaf0ff"
|
||||||
|
var controlPlaceholder = "#9cb0d6"
|
||||||
|
var controlHoverBg = "#2a3b5a"
|
||||||
|
|
||||||
|
var buttonBg = "#2c3f61"
|
||||||
|
var buttonBorder = "#5574a9"
|
||||||
|
var buttonText = "#eef4ff"
|
||||||
|
var buttonHoverBg = "#35507a"
|
||||||
|
var buttonPressedBg = "#273b5c"
|
||||||
|
|
||||||
|
var primaryButtonBg = "#ffb020"
|
||||||
|
var primaryButtonBorder = "#ffd27a"
|
||||||
|
var primaryButtonText = "#142033"
|
||||||
|
var primaryButtonHoverBg = "#ffc247"
|
||||||
|
var primaryButtonPressedBg = "#e39b0f"
|
||||||
|
|
||||||
var spacingXs = 4
|
var spacingXs = 4
|
||||||
var spacingSm = 6
|
var spacingSm = 6
|
||||||
@@ -43,7 +63,7 @@ var radiusMd = 8
|
|||||||
var compactControlHeight = 28
|
var compactControlHeight = 28
|
||||||
|
|
||||||
var dashboardSurfaceComboWidth = 170
|
var dashboardSurfaceComboWidth = 170
|
||||||
var dashboardMetricsHeight = 96
|
var dashboardMetricsHeight = 148
|
||||||
var dashboardBottomMargin = 8
|
var dashboardBottomMargin = 8
|
||||||
var palettePanelWidth = 260
|
var palettePanelWidth = 260
|
||||||
|
|
||||||
@@ -52,10 +72,12 @@ var stageHandleWidth = 16
|
|||||||
var reconstructionBadgeWidth = 28
|
var reconstructionBadgeWidth = 28
|
||||||
var paletteItemHeight = 30
|
var paletteItemHeight = 30
|
||||||
|
|
||||||
var dialogWidth = 430
|
var dialogWidth = 390
|
||||||
var dialogLabelWidth = 150
|
var dialogLabelWidth = 128
|
||||||
var dialogResetButtonWidth = 190
|
var dialogResetButtonWidth = 168
|
||||||
var dialogOkButtonWidth = 90
|
var dialogOkButtonWidth = 76
|
||||||
|
var dialogContentSpacing = 4
|
||||||
|
var dialogRowSpacing = 6
|
||||||
|
|
||||||
var fontXs = 10
|
var fontXs = 10
|
||||||
var fontSm = 11
|
var fontSm = 11
|
||||||
|
|||||||
Reference in New Issue
Block a user