47 const auto MARK_ZERO = 0u;
48 const auto MARK_MAYBE = 127u;
49 const auto MARK_EDGE = 255u;
54 std::stack<Coordinates> pixels_stack;
55 for(
auto i = 0; i < dst.num_elements(); ++i)
57 if(dst[i] == MARK_EDGE)
63 while(!pixels_stack.empty())
65 const Coordinates pixel_coord = pixels_stack.top();
68 std::array<Coordinates, 8> neighbours =
71 Coordinates(pixel_coord.x() - 1, pixel_coord.y() + 0),
72 Coordinates(pixel_coord.x() + 1, pixel_coord.y() + 0),
73 Coordinates(pixel_coord.x() - 1, pixel_coord.y() - 1),
74 Coordinates(pixel_coord.x() + 1, pixel_coord.y() + 1),
75 Coordinates(pixel_coord.x() + 0, pixel_coord.y() - 1),
76 Coordinates(pixel_coord.x() + 0, pixel_coord.y() + 1),
77 Coordinates(pixel_coord.x() + 1, pixel_coord.y() - 1),
78 Coordinates(pixel_coord.x() - 1, pixel_coord.y() + 1)
83 std::for_each(neighbours.begin(), neighbours.end(), [&](Coordinates & coord)
87 const size_t pixel_index =
coord2index(dst.shape(), coord);
88 const T pixel = dst[pixel_index];
89 if(pixel == MARK_MAYBE)
91 dst[pixel_index] = MARK_EDGE;
92 pixels_stack.push(coord);
99 for(
auto i = 0; i < dst.num_elements(); ++i)
101 if(dst[i] == MARK_MAYBE)
108 template <
typename U,
typename T>
109 SimpleTensor<T> canny_edge_detector_impl(
const SimpleTensor<T> &
src, int32_t upper, int32_t lower,
int gradient_size,
MagnitudeType norm_type,
110 BorderMode border_mode, T constant_border_value)
116 SimpleTensor<T> dst{ src.shape(), src.data_type() };
120 SimpleTensor<U> gx{};
121 SimpleTensor<U> gy{};
129 SimpleTensor<unsigned_U> grad_mag{ gx.shape(), mag_data_type };
130 SimpleTensor<uint8_t> grad_dir{ gy.shape(),
DataType::U8 };
132 for(
auto i = 0; i < grad_mag.num_elements(); ++i)
138 mag =
support::cpp11::round(std::sqrt(static_cast<promoted_U>(gx[i]) * gx[i] + static_cast<promoted_U>(gy[i]) * gy[i]));
142 mag =
static_cast<promoted_U
>(std::abs(gx[i])) + static_cast<promoted_U>(std::abs(gy[i]));
145 float angle = 180.f * std::atan2(static_cast<float>(gy[i]), static_cast<float>(gx[i])) /
M_PI;
157 for(
auto i = 0; i < grad_dir.num_elements(); ++i)
159 const auto direction = std::fabs(grad_dir[i]);
160 grad_dir[i] = (direction < 22.5 || direction >= 157.5) ? 0 : (direction < 67.5) ? 1 : (direction < 112.5) ? 2 : 3;
164 std::vector<int> strong_edges;
165 const auto upper_thresh =
static_cast<uint32_t
>(upper);
166 const auto lower_thresh =
static_cast<uint32_t
>(lower);
168 const auto pixel_at_offset = [&](
const SimpleTensor<unsigned_U> &tensor,
const Coordinates & coord,
int xoffset,
int yoffset)
170 return tensor_elem_at(tensor, Coordinates{ coord.x() + xoffset, coord.y() + yoffset }, border_mode,
static_cast<unsigned_U
>(constant_border_value));
173 for(
auto i = 0; i < dst.num_elements(); ++i)
187 mag_90 = pixel_at_offset(grad_mag, coord, -1, 0);
188 mag90 = pixel_at_offset(grad_mag, coord, 1, 0);
191 mag_90 = pixel_at_offset(grad_mag, coord, -1, -1);
192 mag90 = pixel_at_offset(grad_mag, coord, +1, +1);
195 mag_90 = pixel_at_offset(grad_mag, coord, 0, -1);
196 mag90 = pixel_at_offset(grad_mag, coord, 0, +1);
199 mag_90 = pixel_at_offset(grad_mag, coord, +1, -1);
200 mag90 = pixel_at_offset(grad_mag, coord, -1, +1);
208 if(grad_mag[i] > mag_90 && grad_mag[i] > mag90)
211 if(grad_mag[i] > upper_thresh)
214 strong_edges.emplace_back(i);
233 template <
typename T>
235 int32_t upper_thresh, int32_t lower_thresh,
int gradient_size,
MagnitudeType norm_type,
236 BorderMode border_mode, T constant_border_value)
238 if(gradient_size < 7)
240 return canny_edge_detector_impl<int16_t>(
src, upper_thresh, lower_thresh, gradient_size, norm_type, border_mode, constant_border_value);
244 return canny_edge_detector_impl<int32_t>(
src, upper_thresh, lower_thresh, gradient_size, norm_type, border_mode, constant_border_value);
249 int32_t upper_thresh, int32_t lower_thresh,
int gradient_size,
MagnitudeType norm_type,
250 BorderMode border_mode, uint8_t constant_border_value);
BorderMode
Methods available to handle borders.
SimpleTensor< T > canny_edge_detector(const SimpleTensor< T > &src, int32_t upper_thresh, int32_t lower_thresh, int gradient_size, MagnitudeType norm_type, BorderMode border_mode, T constant_border_value)
T tensor_elem_at(const SimpleTensor< T > &src, Coordinates coord, BorderMode border_mode, T constant_border_value)
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
1 channel, 1 U8 per channel
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
1 channel, 1 U16 per channel
const ValidRegion valid_region
decltype(strategy::transforms) typedef type
SimpleTensor< float > src
Copyright (c) 2017-2021 Arm Limited.
int coord2index(const TensorShape &shape, const Coordinates &coord)
Linearise the given coordinate.
bool is_in_valid_region(const ValidRegion &valid_region, Coordinates coord)
Check if a coordinate is within a valid region.
1 channel, 1 U32 per channel
Coordinates index2coord(const TensorShape &shape, int index)
Convert a linear index into n-dimensional coordinates.
1 channel, 1 S16 per channel
void for_each(F &&)
Base case of for_each.
Simple tensor object that stores elements in a consecutive chunk of memory.
x and y gradient dimension
Borders are left undefined.
T round(T value)
Round floating-point value with half value rounding away from zero.
T saturate_cast(T val)
Saturate a value of type T against the numeric limits of type U.
MagnitudeType
Magnitude calculation type.
DataType
Available data types.
ValidRegion shape_to_valid_region(const TensorShape &a_shape, bool border_undefined=false, BorderSize border_size=BorderSize(0))
Create a valid region based on tensor shape, border mode and border size.