From aff0e79777ab31bc11077c03c7f640d54f3d6506 Mon Sep 17 00:00:00 2001 From: Daniel Girardeau-Montaut Date: Mon, 26 Feb 2024 23:59:59 +0100 Subject: [PATCH] Improve the syntax of ScalarFieldTools::computeCellGaussianFilter --- include/ScalarFieldTools.h | 15 +++--- src/ScalarFieldTools.cpp | 104 ++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 68 deletions(-) diff --git a/include/ScalarFieldTools.h b/include/ScalarFieldTools.h index 7bf4307..d72500d 100644 --- a/include/ScalarFieldTools.h +++ b/include/ScalarFieldTools.h @@ -71,16 +71,15 @@ namespace CCCoreLib DgmOctree* theOctree = nullptr); //! Computes a spatial gaussian filter on a scalar field associated to a point cloud - /** The "amplitutde" of the gaussian filter must be precised (sigma). - As 99% of the gaussian distribution is between -3*sigma and +3*sigma - around the mean value, this filter will only look for neighbouring - points (around each point) in a sphere of radius 3*sigma. - It also permits to use the filter as a bilateral filter. Where the wights are computed also considering the - distance of the neighbor's scalar value from the current point scalar value. (weighted with gaussian as distances are) - Warning: this method assumes the input scalar field is different from output. + /** The "amplitutde" of the Gaussian filter must be specified (sigma). + As 99% of the Gaussian distribution is between -3*sigma and +3*sigma around the mean value, + this filter will only look for neighbors within a sphere of radius 3*sigma. + One can also use the filter as a Bilateral filter. In this case the weights are computed considering the + difference of the neighbors SF values with the current point SF value (also following a Gaussian distribution). + Warning: this method assumes the input scalar field is different from the output one. \param sigma filter variance \param theCloud a point cloud (associated to scalar values) - \param sigmaSF the sigma for the bilateral filter. when different than -1 turns the gaussian filter into a bilateral filter + \param sigmaSF if strictly positive, the variance for the Bilateral filter \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) \param theOctree the octree, if it has already been computed \return success diff --git a/src/ScalarFieldTools.cpp b/src/ScalarFieldTools.cpp index f1cc3b1..a562eee 100644 --- a/src/ScalarFieldTools.cpp +++ b/src/ScalarFieldTools.cpp @@ -278,9 +278,8 @@ bool ScalarFieldTools::applyScalarFieldGaussianFilter(PointCoordinateType sigma, progressCb->update(0); } - void* additionalParameters[2] = { reinterpret_cast(&sigma), - reinterpret_cast(&sigmaSF) - }; + void* additionalParameters[2] { reinterpret_cast(&sigma), + reinterpret_cast(&sigmaSF) }; bool success = true; @@ -298,10 +297,9 @@ bool ScalarFieldTools::applyScalarFieldGaussianFilter(PointCoordinateType sigma, return success; } -//FONCTION "CELLULAIRE" DE CALCUL DU FILTRE GAUSSIEN (PAR PROJECTION SUR LE PLAN AUX MOINDRES CARRES) -//DETAIL DES PARAMETRES ADDITIONNELS (2) : -// [0] -> (PointCoordinateType*) sigma : gauss function sigma -// [1] -> (PointCoordinateType*) sigmaSF : used when in "bilateral modality" - if -1 pure gaussian filtering is performed +// Additional parameters: +// [0] -> (PointCoordinateType*) sigma: Gaussian filter sigma +// [1] -> (PointCoordinateType*) sigmaSF: Bilateral filter sigma (if > 0) bool ScalarFieldTools::computeCellGaussianFilter( const DgmOctree::octreeCell& cell, void** additionalParameters, NormalizedProgress* nProgress/*=nullptr*/) @@ -312,7 +310,7 @@ bool ScalarFieldTools::computeCellGaussianFilter( const DgmOctree::octreeCell& c //we use only the squared value of sigma PointCoordinateType sigma2 = 2*sigma*sigma; - PointCoordinateType radius = 3*sigma; //2.5 sigma > 99% + PointCoordinateType radius = 3*sigma; //3*sigma > 99.7% //we use only the squared value of sigmaSF PointCoordinateType sigmaSF2 = 2*sigmaSF*sigmaSF; @@ -348,73 +346,59 @@ bool ScalarFieldTools::computeCellGaussianFilter( const DgmOctree::octreeCell& c const GenericIndexedCloudPersist* cloud = cell.points->getAssociatedCloud(); - //Pure Gaussian Filtering - if (sigmaSF == -1) + bool bilateralFilter = (sigmaSF > 0); + + for (unsigned i = 0; i < n; ++i) //for each point in cell { - for (unsigned i = 0; i < n; ++i) //for each point in cell + ScalarType queryValue = 0; + if (bilateralFilter) { - //we get the points inside a spherical neighbourhood (radius: '3*sigma') - cell.points->getPoint(i,nNSS.queryPoint); - //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)! - unsigned k = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS,radius,false); + queryValue = cell.points->getPointScalarValue(i); //scalar of the query point - //each point adds a contribution weighted by its distance to the sphere center - it = nNSS.pointsInNeighbourhood.begin(); - double meanValue = 0.0; - double wSum = 0.0; - for (unsigned j = 0; j < k; ++j, ++it) + // check that the query SF value is valid, otherwise no need to compute anything + if (!ScalarField::ValidValue(queryValue)) { - double weight = exp(-(it->squareDistd) / sigma2); //PDF: -exp(-(x-mu)^2/(2*sigma^2)) - ScalarType val = cloud->getPointScalarValue(it->pointIndex); - //scalar value must be valid - if (ScalarField::ValidValue(val)) - { - meanValue += static_cast(val) * weight; - wSum += weight; - } + cell.points->setPointScalarValue(i, NAN_VALUE); + continue; } + } - ScalarType newValue = (wSum > 0.0 ? static_cast(meanValue / wSum) : NAN_VALUE); - - cell.points->setPointScalarValue(i, newValue); + //we get the points inside a spherical neighbourhood (radius: '3*sigma') + cell.points->getPoint(i, nNSS.queryPoint); + //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)! + unsigned k = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS, radius, false); - if (nProgress && !nProgress->oneStep()) - return false; - } - } - //Bilateral Filtering using the second sigma parameters on values (when given) - else - { - for (unsigned i = 0; i < n; ++i) //for each point in cell + //each point adds a contribution weighted by its distance to the sphere center + it = nNSS.pointsInNeighbourhood.begin(); + double meanValue = 0.0; + double wSum = 0.0; + for (unsigned j = 0; j < k; ++j, ++it) { - ScalarType queryValue = cell.points->getPointScalarValue(i); //scalar of the query point + double weight = exp(-(it->squareDistd) / sigma2); //PDF: -exp(-(x-mu)^2/(2*sigma^2)) - //we get the points inside a spherical neighbourhood (radius: '3*sigma') - cell.points->getPoint(i, nNSS.queryPoint); - //warning: there may be more points at the end of nNSS.pointsInNeighbourhood than the actual nearest neighbors (k)! - unsigned k = cell.parentOctree->findNeighborsInASphereStartingFromCell(nNSS, radius, false); + ScalarType val = cloud->getPointScalarValue(it->pointIndex); + + //scalar value must be valid + if (!ScalarField::ValidValue(val)) + { + continue; + } - //each point adds a contribution weighted by its distance to the sphere center - it = nNSS.pointsInNeighbourhood.begin(); - double meanValue = 0.0; - double wSum = 0.0; - for (unsigned j = 0; j < k; ++j, ++it) + if (bilateralFilter) { - ScalarType val = cloud->getPointScalarValue(it->pointIndex); ScalarType dSF = queryValue - val; - double weight = exp(-(it->squareDistd) / sigma2) * exp(-(dSF*dSF) / sigmaSF2); - //scalar value must be valid - if (ScalarField::ValidValue(val)) - { - meanValue += static_cast(val) * weight; - wSum += weight; - } + weight *= exp(-(dSF*dSF) / sigmaSF2); } - cell.points->setPointScalarValue(i, wSum > 0.0 ? static_cast(meanValue / wSum) : NAN_VALUE); + meanValue += val * weight; + wSum += weight; + } + + cell.points->setPointScalarValue(i, wSum != 0.0 ? static_cast(meanValue / wSum) : NAN_VALUE); - if (nProgress && !nProgress->oneStep()) - return false; + if (nProgress && !nProgress->oneStep()) + { + return false; } }