456 lines
19 KiB
C++
Raw Permalink Normal View History

/*
* Software License Agreement (BSD License)
*
* Point Cloud Library (PCL) - www.pointclouds.org
* Copyright (c) 2010-2011, Willow Garage, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef PCL_SUSAN_IMPL_HPP_
#define PCL_SUSAN_IMPL_HPP_
#include <pcl/common/io.h> // for getFieldIndex
#include <pcl/keypoints/susan.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/integral_image_normal.h>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setNonMaxSupression (bool nonmax)
{
nonmax_ = nonmax;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setGeometricValidation (bool validate)
{
geometric_validation_ = validate;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setRadius (float radius)
{
search_radius_ = radius;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setDistanceThreshold (float distance_threshold)
{
distance_threshold_ = distance_threshold;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setAngularThreshold (float angular_threshold)
{
angular_threshold_ = angular_threshold;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setIntensityThreshold (float intensity_threshold)
{
intensity_threshold_ = intensity_threshold;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setNormals (const PointCloudNConstPtr &normals)
{
normals_ = normals;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setSearchSurface (const PointCloudInConstPtr &cloud)
{
surface_ = cloud;
normals_.reset (new pcl::PointCloud<NormalT>);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::setNumberOfThreads (unsigned int nr_threads)
{
threads_ = nr_threads;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
// pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::USAN (const PointInT& nucleus,
// const NormalT& nucleus_normal,
// const pcl::Indices& neighbors,
// const float& t,
// float& response,
// Eigen::Vector3f& centroid) const
// {
// float area = 0;
// response = 0;
// float x0 = nucleus.x;
// float y0 = nucleus.y;
// float z0 = nucleus.z;
// //xx xy xz yy yz zz
// std::vector<float> coefficients(6);
// memset (&coefficients[0], 0, sizeof (float) * 6);
// for (const auto& index : neighbors)
// {
// if (std::isfinite ((*normals_)[index].normal_x))
// {
// Eigen::Vector3f diff = (*normals_)[index].getNormal3fMap () - nucleus_normal.getNormal3fMap ();
// float c = diff.norm () / t;
// c = -1 * pow (c, 6.0);
// c = std::exp (c);
// Eigen::Vector3f xyz = (*surface_)[index].getVector3fMap ();
// centroid += c * xyz;
// area += c;
// coefficients[0] += c * (x0 - xyz.x) * (x0 - xyz.x);
// coefficients[1] += c * (x0 - xyz.x) * (y0 - xyz.y);
// coefficients[2] += c * (x0 - xyz.x) * (z0 - xyz.z);
// coefficients[3] += c * (y0 - xyz.y) * (y0 - xyz.y);
// coefficients[4] += c * (y0 - xyz.y) * (z0 - xyz.z);
// coefficients[5] += c * (z0 - xyz.z) * (z0 - xyz.z);
// }
// }
// if (area > 0)
// {
// centroid /= area;
// if (area < geometric_threshold)
// response = geometric_threshold - area;
// // Look for edge direction
// // X direction
// if ((coefficients[3]/coefficients[0]) < 1 && (coefficients[5]/coefficients[0]) < 1)
// direction = Eigen::Vector3f (1, 0, 0);
// else
// {
// // Y direction
// if ((coefficients[0]/coefficients[3]) < 1 && (coefficients[5]/coefficients[3]) < 1)
// direction = Eigen::Vector3f (0, 1, 0);
// else
// {
// // Z direction
// if ((coefficients[0]/coefficients[5]) < 1 && (coefficients[3]/coefficients[5]) < 1)
// direction = Eigen::Vector3f (0, 1, 0);
// // Diagonal edge
// else
// {
// //XY direction
// if ((coefficients[2]/coeffcients[1]) < 1 && (coeffcients[4]/coeffcients[1]) < 1)
// {
// if (coeffcients[1] > 0)
// direction = Eigen::Vector3f (1,1,0);
// else
// direction = Eigen::Vector3f (-1,1,0);
// }
// else
// {
// //XZ direction
// if ((coefficients[1]/coeffcients[2]) > 1 && (coeffcients[4]/coeffcients[2]) < 1)
// {
// if (coeffcients[2] > 0)
// direction = Eigen::Vector3f (1,0,1);
// else
// direction = Eigen::Vector3f (-1,0,1);
// }
// //YZ direction
// else
// {
// if (coeffcients[4] > 0)
// direction = Eigen::Vector3f (0,1,1);
// else
// direction = Eigen::Vector3f (0,-1,1);
// }
// }
// }
// }
// }
// // std::size_t max_index = std::distance (coefficients.begin (), max);
// // switch (max_index)
// // {
// // case 0 : direction = Eigen::Vector3f (1, 0, 0); break;
// // case 1 : direction = Eigen::Vector3f (1, 1, 0); break;
// // case 2 : direction = Eigen::Vector3f (1, 0, 1); break;
// // case 3 : direction = Eigen::Vector3f (0, 1, 0); break;
// // case 4 : direction = Eigen::Vector3f (0, 1, 1); break;
// // case 5 : direction = Eigen::Vector3f (0, 0, 1); break;
// // }
// }
// }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::initCompute ()
{
if (!Keypoint<PointInT, PointOutT>::initCompute ())
{
PCL_ERROR ("[pcl::%s::initCompute] init failed!\n", name_.c_str ());
return (false);
}
if (normals_->empty ())
{
PointCloudNPtr normals (new PointCloudN ());
normals->reserve (normals->size ());
if (!surface_->isOrganized ())
{
pcl::NormalEstimation<PointInT, NormalT> normal_estimation;
normal_estimation.setInputCloud (surface_);
normal_estimation.setRadiusSearch (search_radius_);
normal_estimation.compute (*normals);
}
else
{
IntegralImageNormalEstimation<PointInT, NormalT> normal_estimation;
normal_estimation.setNormalEstimationMethod (pcl::IntegralImageNormalEstimation<PointInT, NormalT>::SIMPLE_3D_GRADIENT);
normal_estimation.setInputCloud (surface_);
normal_estimation.setNormalSmoothingSize (5.0);
normal_estimation.compute (*normals);
}
normals_ = normals;
}
if (normals_->size () != surface_->size ())
{
PCL_ERROR ("[pcl::%s::initCompute] normals given, but the number of normals does not match the number of input points!\n", name_.c_str ());
return (false);
}
return (true);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::isWithinNucleusCentroid (const Eigen::Vector3f& nucleus,
const Eigen::Vector3f& centroid,
const Eigen::Vector3f& nc,
const PointInT& point) const
{
Eigen::Vector3f pc = centroid - point.getVector3fMap ();
Eigen::Vector3f pn = nucleus - point.getVector3fMap ();
Eigen::Vector3f pc_cross_nc = pc.cross (nc);
return ((pc_cross_nc.norm () <= tolerance_) && (pc.dot (nc) >= 0) && (pn.dot (nc) <= 0));
}
// template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
// pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::isValidQueryPoint3D (int point_index) const
// {
// return (isFinite (surface_->points [point_index]) &&
// isFinite (normals_->points [point_index]));
// }
// template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
// pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::isValidQueryPoint2D (int point_index) const
// {
// return (isFinite (surface_->points [point_index]));
// }
// template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
// pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::isWithinSusan2D (int nucleus, int neighbor) const
// {
// return (std::abs (intensity_ ((*surface_)[nucleus]) -
// intensity_ ((*surface_)[neighbor])) <= intensity_threshold_);
// }
// template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
// pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::isWithinSusan3D (int nucleus, int neighbor) const
// {
// Eigen::Vector3f nucleus_normal = normals_->point[nucleus].getVector3fMap ();
// return (1 - nucleus_normal.dot ((*normals_)[*index].getNormalVector3fMap ()) <= angular_threshold_);
// }
// template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> bool
// pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::isWithinSusanH (int nucleus, int neighbor) const
// {
// Eigen::Vector3f nucleus_normal = normals_->point[nucleus].getVector3fMap ();
// return ((1 - nucleus_normal.dot ((*normals_)[*index].getNormalVector3fMap ()) <= angular_threshold_) ||
// (std::abs (intensity_ ((*surface_)[nucleus]) - intensity_ ((*surface_)[neighbor])) <= intensity_threshold_));
// }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointInT, typename PointOutT, typename NormalT, typename IntensityT> void
pcl::SUSANKeypoint<PointInT, PointOutT, NormalT, IntensityT>::detectKeypoints (PointCloudOut &output)
{
typename pcl::PointCloud<PointOutT>::Ptr response (new pcl::PointCloud<PointOutT>);
response->reserve (surface_->size ());
// Check if the output has a "label" field
label_idx_ = pcl::getFieldIndex<PointOutT> ("label", out_fields_);
const auto input_size = static_cast<pcl::index_t> (input_->size ());
for (pcl::index_t point_index = 0; point_index < input_size; ++point_index)
{
const PointInT& point_in = input_->points [point_index];
const NormalT& normal_in = normals_->points [point_index];
if (!isFinite (point_in) || !isFinite (normal_in))
continue;
Eigen::Vector3f nucleus = point_in.getVector3fMap ();
Eigen::Vector3f nucleus_normal = normals_->points [point_index].getNormalVector3fMap ();
float nucleus_intensity = intensity_ (point_in);
pcl::Indices nn_indices;
std::vector<float> nn_dists;
tree_->radiusSearch (point_in, search_radius_, nn_indices, nn_dists);
float area = 0;
Eigen::Vector3f centroid = Eigen::Vector3f::Zero ();
// Exclude nucleus from the usan
std::vector<int> usan; usan.reserve (nn_indices.size () - 1);
for (const auto& index : nn_indices)
{
if ((index != point_index) && std::isfinite ((*normals_)[index].normal_x))
{
// if the point fulfill condition
if ((std::abs (nucleus_intensity - intensity_ ((*input_)[index])) <= intensity_threshold_) ||
(1 - nucleus_normal.dot ((*normals_)[index].getNormalVector3fMap ()) <= angular_threshold_))
{
++area;
centroid += (*input_)[index].getVector3fMap ();
usan.push_back (index);
}
}
}
float geometric_threshold = 0.5f * (static_cast<float> (nn_indices.size () - 1));
if ((area > 0) && (area < geometric_threshold))
{
// if no geometric validation required add the point to the response
if (!geometric_validation_)
{
PointOutT point_out;
point_out.getVector3fMap () = point_in.getVector3fMap ();
//point_out.intensity = geometric_threshold - area;
intensity_out_.set (point_out, geometric_threshold - area);
// if a label field is found use it to save the index
if (label_idx_ != -1)
{
// save the index in the cloud
std::uint32_t label = static_cast<std::uint32_t> (point_index);
memcpy (reinterpret_cast<char*> (&point_out) + out_fields_[label_idx_].offset,
&label, sizeof (std::uint32_t));
}
response->push_back (point_out);
}
else
{
centroid /= area;
Eigen::Vector3f dist = nucleus - centroid;
// Check the distance <= distance_threshold_
if (dist.norm () >= distance_threshold_)
{
// point is valid from distance point of view
Eigen::Vector3f nc = centroid - nucleus;
// Check the contiguity
auto usan_it = usan.cbegin ();
for (; usan_it != usan.cend (); ++usan_it)
{
if (!isWithinNucleusCentroid (nucleus, centroid, nc, (*input_)[*usan_it]))
break;
}
// All points within usan lies on the segment [nucleus centroid]
if (usan_it == usan.end ())
{
PointOutT point_out;
point_out.getVector3fMap () = point_in.getVector3fMap ();
// point_out.intensity = geometric_threshold - area;
intensity_out_.set (point_out, geometric_threshold - area);
// if a label field is found use it to save the index
if (label_idx_ != -1)
{
// save the index in the cloud
std::uint32_t label = static_cast<std::uint32_t> (point_index);
memcpy (reinterpret_cast<char*> (&point_out) + out_fields_[label_idx_].offset,
&label, sizeof (std::uint32_t));
}
response->push_back (point_out);
}
}
}
}
}
response->height = 1;
response->width = response->size ();
if (!nonmax_)
{
output = *response;
for (std::size_t i = 0; i < response->size (); ++i)
keypoints_indices_->indices.push_back (i);
// we don not change the denseness
output.is_dense = input_->is_dense;
}
else
{
output.clear ();
output.reserve (response->size());
for (pcl::index_t idx = 0; idx < static_cast<pcl::index_t> (response->size ()); ++idx)
{
const PointOutT& point_in = response->points [idx];
const NormalT& normal_in = normals_->points [idx];
//const float intensity = (*response)[idx].intensity;
const float intensity = intensity_out_ ((*response)[idx]);
if (!isFinite (point_in) || !isFinite (normal_in) || (intensity == 0))
continue;
pcl::Indices nn_indices;
std::vector<float> nn_dists;
tree_->radiusSearch (idx, search_radius_, nn_indices, nn_dists);
bool is_minima = true;
for (const auto& nn_index : nn_indices)
{
// if (intensity > (*response)[nn_index].intensity)
if (intensity > intensity_out_ ((*response)[nn_index]))
{
is_minima = false;
break;
}
}
if (is_minima)
{
output.push_back ((*response)[idx]);
keypoints_indices_->indices.push_back (idx);
}
}
output.height = 1;
output.width = output.size();
output.is_dense = true;
}
}
#define PCL_INSTANTIATE_SUSAN(T,U,N) template class PCL_EXPORTS pcl::SUSANKeypoint<T,U,N>;
#endif // #ifndef PCL_HARRIS_KEYPOINT_3D_IMPL_H_