/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * 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. * */ #pragma once #include #include #include // for rad2deg namespace pcl { template void Edge::detectEdgeSobel(pcl::PointCloud& output) { convolution_.setInputCloud(input_); pcl::PointCloud::Ptr kernel_x(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_x(new pcl::PointCloud); kernel_.setKernelType(kernel::SOBEL_X); kernel_.fetchKernel(*kernel_x); convolution_.setKernel(*kernel_x); convolution_.filter(*magnitude_x); pcl::PointCloud::Ptr kernel_y(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_y(new pcl::PointCloud); kernel_.setKernelType(kernel::SOBEL_Y); kernel_.fetchKernel(*kernel_y); convolution_.setKernel(*kernel_y); convolution_.filter(*magnitude_y); const int height = input_->height; const int width = input_->width; output.resize(height * width); output.height = height; output.width = width; for (std::size_t i = 0; i < output.size(); ++i) { output[i].magnitude_x = (*magnitude_x)[i].intensity; output[i].magnitude_y = (*magnitude_y)[i].intensity; output[i].magnitude = std::sqrt((*magnitude_x)[i].intensity * (*magnitude_x)[i].intensity + (*magnitude_y)[i].intensity * (*magnitude_y)[i].intensity); output[i].direction = std::atan2((*magnitude_y)[i].intensity, (*magnitude_x)[i].intensity); } } template void Edge::sobelMagnitudeDirection( const pcl::PointCloud& input_x, const pcl::PointCloud& input_y, pcl::PointCloud& output) { convolution_.setInputCloud(input_x.makeShared()); pcl::PointCloud::Ptr kernel_x(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_x(new pcl::PointCloud); kernel_.setKernelType(kernel::SOBEL_X); kernel_.fetchKernel(*kernel_x); convolution_.setKernel(*kernel_x); convolution_.filter(*magnitude_x); convolution_.setInputCloud(input_y.makeShared()); pcl::PointCloud::Ptr kernel_y(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_y(new pcl::PointCloud); kernel_.setKernelType(kernel::SOBEL_Y); kernel_.fetchKernel(*kernel_y); convolution_.setKernel(*kernel_y); convolution_.filter(*magnitude_y); const int height = input_x.height; const int width = input_x.width; output.resize(height * width); output.height = height; output.width = width; for (std::size_t i = 0; i < output.size(); ++i) { output[i].magnitude_x = (*magnitude_x)[i].intensity; output[i].magnitude_y = (*magnitude_y)[i].intensity; output[i].magnitude = std::sqrt((*magnitude_x)[i].intensity * (*magnitude_x)[i].intensity + (*magnitude_y)[i].intensity * (*magnitude_y)[i].intensity); output[i].direction = std::atan2((*magnitude_y)[i].intensity, (*magnitude_x)[i].intensity); } } template void Edge::detectEdgePrewitt(pcl::PointCloud& output) { convolution_.setInputCloud(input_); pcl::PointCloud::Ptr kernel_x(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_x(new pcl::PointCloud); kernel_.setKernelType(kernel::PREWITT_X); kernel_.fetchKernel(*kernel_x); convolution_.setKernel(*kernel_x); convolution_.filter(*magnitude_x); pcl::PointCloud::Ptr kernel_y(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_y(new pcl::PointCloud); kernel_.setKernelType(kernel::PREWITT_Y); kernel_.fetchKernel(*kernel_y); convolution_.setKernel(*kernel_y); convolution_.filter(*magnitude_y); const int height = input_->height; const int width = input_->width; output.resize(height * width); output.height = height; output.width = width; for (std::size_t i = 0; i < output.size(); ++i) { output[i].magnitude_x = (*magnitude_x)[i].intensity; output[i].magnitude_y = (*magnitude_y)[i].intensity; output[i].magnitude = std::sqrt((*magnitude_x)[i].intensity * (*magnitude_x)[i].intensity + (*magnitude_y)[i].intensity * (*magnitude_y)[i].intensity); output[i].direction = std::atan2((*magnitude_y)[i].intensity, (*magnitude_x)[i].intensity); } } template void Edge::detectEdgeRoberts(pcl::PointCloud& output) { convolution_.setInputCloud(input_); pcl::PointCloud::Ptr kernel_x(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_x(new pcl::PointCloud); kernel_.setKernelType(kernel::ROBERTS_X); kernel_.fetchKernel(*kernel_x); convolution_.setKernel(*kernel_x); convolution_.filter(*magnitude_x); pcl::PointCloud::Ptr kernel_y(new pcl::PointCloud); pcl::PointCloud::Ptr magnitude_y(new pcl::PointCloud); kernel_.setKernelType(kernel::ROBERTS_Y); kernel_.fetchKernel(*kernel_y); convolution_.setKernel(*kernel_y); convolution_.filter(*magnitude_y); const int height = input_->height; const int width = input_->width; output.resize(height * width); output.height = height; output.width = width; for (std::size_t i = 0; i < output.size(); ++i) { output[i].magnitude_x = (*magnitude_x)[i].intensity; output[i].magnitude_y = (*magnitude_y)[i].intensity; output[i].magnitude = std::sqrt((*magnitude_x)[i].intensity * (*magnitude_x)[i].intensity + (*magnitude_y)[i].intensity * (*magnitude_y)[i].intensity); output[i].direction = std::atan2((*magnitude_y)[i].intensity, (*magnitude_x)[i].intensity); } } template void Edge::cannyTraceEdge( int rowOffset, int colOffset, int row, int col, pcl::PointCloud& maxima) { int newRow = row + rowOffset; int newCol = col + colOffset; PointXYZI& pt = maxima(newCol, newRow); if (newRow > 0 && newRow < static_cast(maxima.height) && newCol > 0 && newCol < static_cast(maxima.width)) { if (pt.intensity == 0.0f || pt.intensity == std::numeric_limits::max()) return; pt.intensity = std::numeric_limits::max(); cannyTraceEdge(1, 0, newRow, newCol, maxima); cannyTraceEdge(-1, 0, newRow, newCol, maxima); cannyTraceEdge(1, 1, newRow, newCol, maxima); cannyTraceEdge(-1, -1, newRow, newCol, maxima); cannyTraceEdge(0, -1, newRow, newCol, maxima); cannyTraceEdge(0, 1, newRow, newCol, maxima); cannyTraceEdge(-1, 1, newRow, newCol, maxima); cannyTraceEdge(1, -1, newRow, newCol, maxima); } } template void Edge::discretizeAngles(pcl::PointCloud& thet) { const int height = thet.height; const int width = thet.width; float angle; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { angle = pcl::rad2deg(thet(j, i).direction); if (((angle <= 22.5) && (angle >= -22.5)) || (angle >= 157.5) || (angle <= -157.5)) thet(j, i).direction = 0; else if (((angle > 22.5) && (angle < 67.5)) || ((angle < -112.5) && (angle > -157.5))) thet(j, i).direction = 45; else if (((angle >= 67.5) && (angle <= 112.5)) || ((angle <= -67.5) && (angle >= -112.5))) thet(j, i).direction = 90; else if (((angle > 112.5) && (angle < 157.5)) || ((angle < -22.5) && (angle > -67.5))) thet(j, i).direction = 135; } } } template void Edge::suppressNonMaxima( const pcl::PointCloud& edges, pcl::PointCloud& maxima, float tLow) { const int height = edges.height; const int width = edges.width; maxima.height = height; maxima.width = width; maxima.resize(height * width); for (auto& point : maxima) point.intensity = 0.0f; // tHigh and non-maximal suppression for (int i = 1; i < height - 1; i++) { for (int j = 1; j < width - 1; j++) { const PointXYZIEdge& ptedge = edges(j, i); PointXYZI& ptmax = maxima(j, i); if (ptedge.magnitude < tLow) continue; // maxima (j, i).intensity = 0; switch (static_cast(ptedge.direction)) { case 0: { if (ptedge.magnitude >= edges(j - 1, i).magnitude && ptedge.magnitude >= edges(j + 1, i).magnitude) ptmax.intensity = ptedge.magnitude; break; } case 45: { if (ptedge.magnitude >= edges(j - 1, i - 1).magnitude && ptedge.magnitude >= edges(j + 1, i + 1).magnitude) ptmax.intensity = ptedge.magnitude; break; } case 90: { if (ptedge.magnitude >= edges(j, i - 1).magnitude && ptedge.magnitude >= edges(j, i + 1).magnitude) ptmax.intensity = ptedge.magnitude; break; } case 135: { if (ptedge.magnitude >= edges(j + 1, i - 1).magnitude && ptedge.magnitude >= edges(j - 1, i + 1).magnitude) ptmax.intensity = ptedge.magnitude; break; } } } } } template void Edge::detectEdgeCanny(pcl::PointCloud& output) { float tHigh = hysteresis_threshold_high_; float tLow = hysteresis_threshold_low_; const int height = input_->height; const int width = input_->width; output.resize(height * width); output.height = height; output.width = width; // Noise reduction using gaussian blurring pcl::PointCloud::Ptr gaussian_kernel(new pcl::PointCloud); PointCloudInPtr smoothed_cloud(new PointCloudIn); kernel_.setKernelSize(3); kernel_.setKernelSigma(1.0); kernel_.setKernelType(kernel::GAUSSIAN); kernel_.fetchKernel(*gaussian_kernel); convolution_.setKernel(*gaussian_kernel); convolution_.setInputCloud(input_); convolution_.filter(*smoothed_cloud); // Edge detection using Sobel pcl::PointCloud::Ptr edges(new pcl::PointCloud); setInputCloud(smoothed_cloud); detectEdgeSobel(*edges); // Edge discretization discretizeAngles(*edges); // tHigh and non-maximal suppression pcl::PointCloud::Ptr maxima(new pcl::PointCloud); suppressNonMaxima(*edges, *maxima, tLow); // Edge tracing for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if ((*maxima)(j, i).intensity < tHigh || (*maxima)(j, i).intensity == std::numeric_limits::max()) continue; (*maxima)(j, i).intensity = std::numeric_limits::max(); cannyTraceEdge(1, 0, i, j, *maxima); cannyTraceEdge(-1, 0, i, j, *maxima); cannyTraceEdge(1, 1, i, j, *maxima); cannyTraceEdge(-1, -1, i, j, *maxima); cannyTraceEdge(0, -1, i, j, *maxima); cannyTraceEdge(0, 1, i, j, *maxima); cannyTraceEdge(-1, 1, i, j, *maxima); cannyTraceEdge(1, -1, i, j, *maxima); } } // Final thresholding for (std::size_t i = 0; i < input_->size(); ++i) { if ((*maxima)[i].intensity == std::numeric_limits::max()) output[i].magnitude = 255; else output[i].magnitude = 0; } } template void Edge::canny(const pcl::PointCloud& input_x, const pcl::PointCloud& input_y, pcl::PointCloud& output) { float tHigh = hysteresis_threshold_high_; float tLow = hysteresis_threshold_low_; const int height = input_x.height; const int width = input_x.width; output.resize(height * width); output.height = height; output.width = width; // Noise reduction using gaussian blurring pcl::PointCloud::Ptr gaussian_kernel(new pcl::PointCloud); kernel_.setKernelSize(3); kernel_.setKernelSigma(1.0); kernel_.setKernelType(kernel::GAUSSIAN); kernel_.fetchKernel(*gaussian_kernel); convolution_.setKernel(*gaussian_kernel); PointCloudIn smoothed_cloud_x; convolution_.setInputCloud(input_x.makeShared()); convolution_.filter(smoothed_cloud_x); PointCloudIn smoothed_cloud_y; convolution_.setInputCloud(input_y.makeShared()); convolution_.filter(smoothed_cloud_y); // Edge detection using Sobel pcl::PointCloud::Ptr edges(new pcl::PointCloud); sobelMagnitudeDirection(smoothed_cloud_x, smoothed_cloud_y, *edges.get()); // Edge discretization discretizeAngles(*edges); pcl::PointCloud::Ptr maxima(new pcl::PointCloud); suppressNonMaxima(*edges, *maxima, tLow); // Edge tracing for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if ((*maxima)(j, i).intensity < tHigh || (*maxima)(j, i).intensity == std::numeric_limits::max()) continue; (*maxima)(j, i).intensity = std::numeric_limits::max(); // clang-format off cannyTraceEdge( 1, 0, i, j, *maxima); cannyTraceEdge(-1, 0, i, j, *maxima); cannyTraceEdge( 1, 1, i, j, *maxima); cannyTraceEdge(-1, -1, i, j, *maxima); cannyTraceEdge( 0, -1, i, j, *maxima); cannyTraceEdge( 0, 1, i, j, *maxima); cannyTraceEdge(-1, 1, i, j, *maxima); cannyTraceEdge( 1, -1, i, j, *maxima); // clang-format on } } // Final thresholding for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if ((*maxima)(j, i).intensity == std::numeric_limits::max()) output(j, i).magnitude = 255; else output(j, i).magnitude = 0; } } } template void Edge::detectEdgeLoG(const float kernel_sigma, const float kernel_size, pcl::PointCloud& output) { convolution_.setInputCloud(input_); pcl::PointCloud::Ptr log_kernel(new pcl::PointCloud); kernel_.setKernelType(kernel::LOG); kernel_.setKernelSigma(kernel_sigma); kernel_.setKernelSize(kernel_size); kernel_.fetchKernel(*log_kernel); convolution_.setKernel(*log_kernel); convolution_.filter(output); } } // namespace pcl