/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2010-2012, Willow Garage, Inc. * Copyright (c) 2012-, Open Perception, 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 the copyright holder(s) 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. * * $Id: pfh.hpp 5027 2012-03-12 03:10:45Z rusu $ * */ #ifndef PCL_FEATURES_IMPL_ESF_H_ #define PCL_FEATURES_IMPL_ESF_H_ #include #include #include #include #include // for time ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::ESFEstimation::computeESF ( PointCloudIn &pc, std::vector &hist) { const int binsize = 64; unsigned int sample_size = 20000; // @TODO: Replace with c++ stdlib uniform_random_generator srand (static_cast (time (nullptr))); const auto maxindex = pc.size (); std::vector d2v, d1v, d3v, wt_d3; std::vector wt_d2; d1v.reserve (sample_size); d2v.reserve (sample_size * 3); d3v.reserve (sample_size); wt_d2.reserve (sample_size * 3); wt_d3.reserve (sample_size); float h_in[binsize] = {0}; float h_out[binsize] = {0}; float h_mix[binsize] = {0}; float h_mix_ratio[binsize] = {0}; float h_a3_in[binsize] = {0}; float h_a3_out[binsize] = {0}; float h_a3_mix[binsize] = {0}; float h_d3_in[binsize] = {0}; float h_d3_out[binsize] = {0}; float h_d3_mix[binsize] = {0}; float ratio=0.0; float pih = static_cast(M_PI) / 2.0f; float a,b,c,s; int th1,th2,th3; int vxlcnt = 0; int pcnt1,pcnt2,pcnt3; for (std::size_t nn_idx = 0; nn_idx < sample_size; ++nn_idx) { // get a new random point int index1 = rand()%maxindex; int index2 = rand()%maxindex; int index3 = rand()%maxindex; if (index1==index2 || index1 == index3 || index2 == index3) { nn_idx--; continue; } Eigen::Vector4f p1 = pc[index1].getVector4fMap (); Eigen::Vector4f p2 = pc[index2].getVector4fMap (); Eigen::Vector4f p3 = pc[index3].getVector4fMap (); // A3 Eigen::Vector4f v21 (p2 - p1); Eigen::Vector4f v31 (p3 - p1); Eigen::Vector4f v23 (p2 - p3); a = v21.norm (); b = v31.norm (); c = v23.norm (); s = (a+b+c) * 0.5f; if (s * (s-a) * (s-b) * (s-c) <= 0.001f) { nn_idx--; continue; } v21.normalize (); v31.normalize (); v23.normalize (); //TODO: .dot gives nan's th1 = static_cast (pcl_round (std::acos (std::abs (v21.dot (v31))) / pih * (binsize-1))); th2 = static_cast (pcl_round (std::acos (std::abs (v23.dot (v31))) / pih * (binsize-1))); th3 = static_cast (pcl_round (std::acos (std::abs (v23.dot (v21))) / pih * (binsize-1))); if (th1 < 0 || th1 >= binsize) { nn_idx--; continue; } if (th2 < 0 || th2 >= binsize) { nn_idx--; continue; } if (th3 < 0 || th3 >= binsize) { nn_idx--; continue; } // D2 d2v.push_back (pcl::euclideanDistance (pc[index1], pc[index2])); d2v.push_back (pcl::euclideanDistance (pc[index1], pc[index3])); d2v.push_back (pcl::euclideanDistance (pc[index2], pc[index3])); int vxlcnt_sum = 0; int p_cnt = 0; // IN, OUT, MIXED, Ratio line tracing, index1->index2 { const int xs = p1[0] < 0.0? static_cast(std::floor(p1[0])+GRIDSIZE_H): static_cast(std::ceil(p1[0])+GRIDSIZE_H-1); const int ys = p1[1] < 0.0? static_cast(std::floor(p1[1])+GRIDSIZE_H): static_cast(std::ceil(p1[1])+GRIDSIZE_H-1); const int zs = p1[2] < 0.0? static_cast(std::floor(p1[2])+GRIDSIZE_H): static_cast(std::ceil(p1[2])+GRIDSIZE_H-1); const int xt = p2[0] < 0.0? static_cast(std::floor(p2[0])+GRIDSIZE_H): static_cast(std::ceil(p2[0])+GRIDSIZE_H-1); const int yt = p2[1] < 0.0? static_cast(std::floor(p2[1])+GRIDSIZE_H): static_cast(std::ceil(p2[1])+GRIDSIZE_H-1); const int zt = p2[2] < 0.0? static_cast(std::floor(p2[2])+GRIDSIZE_H): static_cast(std::ceil(p2[2])+GRIDSIZE_H-1); wt_d2.push_back (this->lci (xs, ys, zs, xt, yt, zt, ratio, vxlcnt, pcnt1)); if (wt_d2.back () == 2) h_mix_ratio[static_cast (pcl_round (ratio * (binsize-1)))]++; vxlcnt_sum += vxlcnt; p_cnt += pcnt1; } // IN, OUT, MIXED, Ratio line tracing, index1->index3 { const int xs = p1[0] < 0.0? static_cast(std::floor(p1[0])+GRIDSIZE_H): static_cast(std::ceil(p1[0])+GRIDSIZE_H-1); const int ys = p1[1] < 0.0? static_cast(std::floor(p1[1])+GRIDSIZE_H): static_cast(std::ceil(p1[1])+GRIDSIZE_H-1); const int zs = p1[2] < 0.0? static_cast(std::floor(p1[2])+GRIDSIZE_H): static_cast(std::ceil(p1[2])+GRIDSIZE_H-1); const int xt = p3[0] < 0.0? static_cast(std::floor(p3[0])+GRIDSIZE_H): static_cast(std::ceil(p3[0])+GRIDSIZE_H-1); const int yt = p3[1] < 0.0? static_cast(std::floor(p3[1])+GRIDSIZE_H): static_cast(std::ceil(p3[1])+GRIDSIZE_H-1); const int zt = p3[2] < 0.0? static_cast(std::floor(p3[2])+GRIDSIZE_H): static_cast(std::ceil(p3[2])+GRIDSIZE_H-1); wt_d2.push_back (this->lci (xs, ys, zs, xt, yt, zt, ratio, vxlcnt, pcnt2)); if (wt_d2.back () == 2) h_mix_ratio[static_cast(pcl_round (ratio * (binsize-1)))]++; vxlcnt_sum += vxlcnt; p_cnt += pcnt2; } // IN, OUT, MIXED, Ratio line tracing, index2->index3 { const int xs = p2[0] < 0.0? static_cast(std::floor(p2[0])+GRIDSIZE_H): static_cast(std::ceil(p2[0])+GRIDSIZE_H-1); const int ys = p2[1] < 0.0? static_cast(std::floor(p2[1])+GRIDSIZE_H): static_cast(std::ceil(p2[1])+GRIDSIZE_H-1); const int zs = p2[2] < 0.0? static_cast(std::floor(p2[2])+GRIDSIZE_H): static_cast(std::ceil(p2[2])+GRIDSIZE_H-1); const int xt = p3[0] < 0.0? static_cast(std::floor(p3[0])+GRIDSIZE_H): static_cast(std::ceil(p3[0])+GRIDSIZE_H-1); const int yt = p3[1] < 0.0? static_cast(std::floor(p3[1])+GRIDSIZE_H): static_cast(std::ceil(p3[1])+GRIDSIZE_H-1); const int zt = p3[2] < 0.0? static_cast(std::floor(p3[2])+GRIDSIZE_H): static_cast(std::ceil(p3[2])+GRIDSIZE_H-1); wt_d2.push_back (this->lci (xs,ys,zs,xt,yt,zt,ratio,vxlcnt,pcnt3)); if (wt_d2.back () == 2) h_mix_ratio[static_cast(pcl_round(ratio * (binsize-1)))]++; vxlcnt_sum += vxlcnt; p_cnt += pcnt3; } // D3 ( herons formula ) d3v.push_back (std::sqrt (std::sqrt (s * (s-a) * (s-b) * (s-c)))); if (vxlcnt_sum <= 21) { wt_d3.push_back (0); h_a3_out[th1] += static_cast (pcnt3) / 32.0f; h_a3_out[th2] += static_cast (pcnt1) / 32.0f; h_a3_out[th3] += static_cast (pcnt2) / 32.0f; } else if (p_cnt - vxlcnt_sum < 4) { h_a3_in[th1] += static_cast (pcnt3) / 32.0f; h_a3_in[th2] += static_cast (pcnt1) / 32.0f; h_a3_in[th3] += static_cast (pcnt2) / 32.0f; wt_d3.push_back (1); } else { h_a3_mix[th1] += static_cast (pcnt3) / 32.0f; h_a3_mix[th2] += static_cast (pcnt1) / 32.0f; h_a3_mix[th3] += static_cast (pcnt2) / 32.0f; wt_d3.push_back (static_cast (vxlcnt_sum) / static_cast (p_cnt)); } } // Normalizing, get max float maxd2 = 0; float maxd3 = 0; for (std::size_t nn_idx = 0; nn_idx < sample_size; ++nn_idx) { // get max of Dx if (d2v[nn_idx] > maxd2) maxd2 = d2v[nn_idx]; if (d2v[sample_size + nn_idx] > maxd2) maxd2 = d2v[sample_size + nn_idx]; if (d2v[sample_size*2 +nn_idx] > maxd2) maxd2 = d2v[sample_size*2 +nn_idx]; if (d3v[nn_idx] > maxd3) maxd3 = d3v[nn_idx]; } // Normalize and create histogram int index; for (std::size_t nn_idx = 0; nn_idx < sample_size; ++nn_idx) { if (wt_d3[nn_idx] >= 0.999) // IN { index = static_cast(pcl_round (d3v[nn_idx] / maxd3 * (binsize-1))); if (index >= 0 && index < binsize) h_d3_in[index]++; } else { if (wt_d3[nn_idx] <= 0.001) // OUT { index = static_cast(pcl_round (d3v[nn_idx] / maxd3 * (binsize-1))); if (index >= 0 && index < binsize) h_d3_out[index]++ ; } else { index = static_cast(pcl_round (d3v[nn_idx] / maxd3 * (binsize-1))); if (index >= 0 && index < binsize) h_d3_mix[index]++; } } } //normalize and create histogram for (std::size_t nn_idx = 0; nn_idx < d2v.size(); ++nn_idx ) { if (wt_d2[nn_idx] == 0) h_in[static_cast(pcl_round (d2v[nn_idx] / maxd2 * (binsize-1)))]++ ; if (wt_d2[nn_idx] == 1) h_out[static_cast(pcl_round (d2v[nn_idx] / maxd2 * (binsize-1)))]++; if (wt_d2[nn_idx] == 2) h_mix[static_cast(pcl_round (d2v[nn_idx] / maxd2 * (binsize-1)))]++ ; } //float weights[10] = {1, 1, 1, 1, 1, 1, 1, 1 , 1 , 1}; float weights[10] = {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f}; hist.reserve (binsize * 10); for (const float &i : h_a3_in) hist.push_back (i * weights[0]); for (const float &i : h_a3_out) hist.push_back (i * weights[1]); for (const float &i : h_a3_mix) hist.push_back (i * weights[2]); for (const float &i : h_d3_in) hist.push_back (i * weights[3]); for (const float &i : h_d3_out) hist.push_back (i * weights[4]); for (const float &i : h_d3_mix) hist.push_back (i * weights[5]); for (const float &i : h_in) hist.push_back (i*0.5f * weights[6]); for (const float &i : h_out) hist.push_back (i * weights[7]); for (const float &i : h_mix) hist.push_back (i * weights[8]); for (const float &i : h_mix_ratio) hist.push_back (i*0.5f * weights[9]); float sm = 0; for (const float &i : hist) sm += i; for (float &i : hist) i /= sm; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::ESFEstimation::lci ( const int x1, const int y1, const int z1, const int x2, const int y2, const int z2, float &ratio, int &incnt, int &pointcount) { int voxelcount = 0; int voxel_in = 0; int act_voxel[3]; act_voxel[0] = x1; act_voxel[1] = y1; act_voxel[2] = z1; int x_inc, y_inc, z_inc; int dx = x2 - x1; int dy = y2 - y1; int dz = z2 - z1; if (dx < 0) x_inc = -1; else x_inc = 1; int l = std::abs (dx); if (dy < 0) y_inc = -1 ; else y_inc = 1; int m = std::abs (dy); if (dz < 0) z_inc = -1 ; else z_inc = 1; int n = std::abs (dz); int dx2 = 2 * l; int dy2 = 2 * m; int dz2 = 2 * n; if ((l >= m) & (l >= n)) { int err_1 = dy2 - l; int err_2 = dz2 - l; for (int i = 1; i(lut_[act_voxel[0]][act_voxel[1]][act_voxel[2]] == 1); if (err_1 > 0) { act_voxel[1] += y_inc; err_1 -= dx2; } if (err_2 > 0) { act_voxel[2] += z_inc; err_2 -= dx2; } err_1 += dy2; err_2 += dz2; act_voxel[0] += x_inc; } } else if ((m >= l) & (m >= n)) { int err_1 = dx2 - m; int err_2 = dz2 - m; for (int i=1; i(lut_[act_voxel[0]][act_voxel[1]][act_voxel[2]] == 1); if (err_1 > 0) { act_voxel[0] += x_inc; err_1 -= dy2; } if (err_2 > 0) { act_voxel[2] += z_inc; err_2 -= dy2; } err_1 += dx2; err_2 += dz2; act_voxel[1] += y_inc; } } else { int err_1 = dy2 - n; int err_2 = dx2 - n; for (int i=1; i(lut_[act_voxel[0]][act_voxel[1]][act_voxel[2]] == 1); if (err_1 > 0) { act_voxel[1] += y_inc; err_1 -= dz2; } if (err_2 > 0) { act_voxel[0] += x_inc; err_2 -= dz2; } err_1 += dy2; err_2 += dx2; act_voxel[2] += z_inc; } } voxelcount++; voxel_in += static_cast(lut_[act_voxel[0]][act_voxel[1]][act_voxel[2]] == 1); incnt = voxel_in; pointcount = voxelcount; if (voxel_in >= voxelcount-1) return (0); if (voxel_in <= 7) return (1); ratio = static_cast(voxel_in) / static_cast(voxelcount); return (2); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::ESFEstimation::voxelize9 (PointCloudIn &cluster) { for (const auto& point: cluster) { int xx = point.x<0.0? static_cast(std::floor(point.x)+GRIDSIZE_H) : static_cast(std::ceil(point.x)+GRIDSIZE_H-1); int yy = point.y<0.0? static_cast(std::floor(point.y)+GRIDSIZE_H) : static_cast(std::ceil(point.y)+GRIDSIZE_H-1); int zz = point.z<0.0? static_cast(std::floor(point.z)+GRIDSIZE_H) : static_cast(std::ceil(point.z)+GRIDSIZE_H-1); for (int x = -1; x < 2; x++) for (int y = -1; y < 2; y++) for (int z = -1; z < 2; z++) { int xi = xx + x; int yi = yy + y; int zi = zz + z; if (yi >= GRIDSIZE || xi >= GRIDSIZE || zi>=GRIDSIZE || yi < 0 || xi < 0 || zi < 0) { ; } else this->lut_[xi][yi][zi] = 1; } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::ESFEstimation::cleanup9 (PointCloudIn &cluster) { for (const auto& point: cluster) { int xx = point.x<0.0? static_cast(std::floor(point.x)+GRIDSIZE_H) : static_cast(std::ceil(point.x)+GRIDSIZE_H-1); int yy = point.y<0.0? static_cast(std::floor(point.y)+GRIDSIZE_H) : static_cast(std::ceil(point.y)+GRIDSIZE_H-1); int zz = point.z<0.0? static_cast(std::floor(point.z)+GRIDSIZE_H) : static_cast(std::ceil(point.z)+GRIDSIZE_H-1); for (int x = -1; x < 2; x++) for (int y = -1; y < 2; y++) for (int z = -1; z < 2; z++) { int xi = xx + x; int yi = yy + y; int zi = zz + z; if (yi >= GRIDSIZE || xi >= GRIDSIZE || zi>=GRIDSIZE || yi < 0 || xi < 0 || zi < 0) { ; } else this->lut_[xi][yi][zi] = 0; } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::ESFEstimation::scale_points_unit_sphere ( const pcl::PointCloud &pc, float scalefactor, Eigen::Vector4f& centroid) { pcl::compute3DCentroid (pc, centroid); pcl::demeanPointCloud (pc, centroid, local_cloud_); float max_distance = 0; pcl::PointXYZ cog (0, 0, 0); for (const auto& point: local_cloud_) { float d = pcl::euclideanDistance(cog,point); if (d > max_distance) max_distance = d; } float scale_factor = 1.0f / max_distance * scalefactor; Eigen::Affine3f matrix = Eigen::Affine3f::Identity(); matrix.scale (scale_factor); pcl::transformPointCloud (local_cloud_, local_cloud_, matrix); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::ESFEstimation::compute (PointCloudOut &output) { if (!Feature::initCompute ()) { output.width = output.height = 0; output.clear (); return; } // Copy the header output.header = input_->header; // Resize the output dataset // Important! We should only allocate precisely how many elements we will need, otherwise // we risk at pre-allocating too much memory which could lead to bad_alloc // (see http://dev.pointclouds.org/issues/657) output.width = output.height = 1; output.is_dense = input_->is_dense; output.resize (1); // Perform the actual feature computation computeFeature (output); Feature::deinitCompute (); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::ESFEstimation::computeFeature (PointCloudOut &output) { Eigen::Vector4f xyz_centroid; std::vector hist; scale_points_unit_sphere (*surface_, static_cast(GRIDSIZE_H), xyz_centroid); this->voxelize9 (local_cloud_); this->computeESF (local_cloud_, hist); this->cleanup9 (local_cloud_); // We only output _1_ signature output.resize (1); output.width = 1; output.height = 1; for (std::size_t d = 0; d < hist.size (); ++d) output[0].histogram[d] = hist[d]; } #define PCL_INSTANTIATE_ESFEstimation(T,OutT) template class PCL_EXPORTS pcl::ESFEstimation; #endif // PCL_FEATURES_IMPL_ESF_H_