From: @tiancixiao Reviewed-by: Signed-off-by:tags/v1.2.0-rc1
| @@ -3,4 +3,6 @@ set_property(SOURCE ${_CURRENT_SRC_FILES} PROPERTY COMPILE_DEFINITIONS SUBMODULE | |||
| add_library(lite-cv OBJECT | |||
| image_process.cc | |||
| warp_affine.cc | |||
| gaussian_blur.cc | |||
| canny.cc | |||
| lite_mat.cc) | |||
| @@ -0,0 +1,257 @@ | |||
| /** | |||
| * Copyright 2021 Huawei Technologies Co., Ltd | |||
| * | |||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||
| * you may not use this file except in compliance with the License. | |||
| * You may obtain a copy of the License at | |||
| * | |||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, software | |||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| */ | |||
| #include <cmath> | |||
| #include "lite_cv/lite_mat.h" | |||
| #include "lite_cv/image_process.h" | |||
| #ifdef ENABLE_ANDROID | |||
| #if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) | |||
| #define USE_NEON | |||
| #include <arm_neon.h> | |||
| #endif | |||
| #endif | |||
| #define ANGLE_22_5 0.39269908169872414 | |||
| #define ANGLE_67_5 1.1780972450961724 | |||
| namespace mindspore { | |||
| namespace dataset { | |||
| static void GetSobelKernel(float *kernel, int flag, int ksize) { | |||
| std::vector<float> buffer(ksize + 1); | |||
| float *ptr = kernel; | |||
| if (ksize == 1) { | |||
| buffer[0] = 1; | |||
| } else if (ksize == 3) { | |||
| if (flag == 0) { | |||
| buffer[0] = 1, buffer[1] = 2, buffer[2] = 1; | |||
| } else if (flag == 1) { | |||
| buffer[0] = -1, buffer[1] = 0, buffer[2] = 1; | |||
| } else { | |||
| buffer[0] = 1, buffer[1] = -2, buffer[2] = 1; | |||
| } | |||
| } else { | |||
| int old, now; | |||
| buffer[0] = 1; | |||
| for (int i = 0; i < ksize; i++) { | |||
| buffer[i + 1] = 0; | |||
| } | |||
| for (int i = 0; i < ksize - flag - 1; i++) { | |||
| old = buffer[0]; | |||
| for (int j = 1; j <= ksize; j++) { | |||
| now = buffer[j] + buffer[j - 1]; | |||
| buffer[j - 1] = old; | |||
| old = now; | |||
| } | |||
| } | |||
| for (int i = 0; i < flag; i++) { | |||
| old = -buffer[0]; | |||
| for (int j = 1; j <= ksize; j++) { | |||
| now = buffer[j - 1] - buffer[j]; | |||
| buffer[j - 1] = old; | |||
| old = now; | |||
| } | |||
| } | |||
| } | |||
| for (int i = 0; i < ksize; i++) { | |||
| ptr[i] = buffer[i]; | |||
| } | |||
| } | |||
| bool Sobel(const LiteMat &src, LiteMat &dst, int flag_x, int flag_y, int ksize, PaddBorderType pad_type) { // NOLINT | |||
| if (src.IsEmpty() || src.data_type_ != LDataType::UINT8) { | |||
| return false; | |||
| } | |||
| if (flag_x < 0 || flag_y < 0 || flag_x + flag_y <= 0 || flag_x >= ksize || flag_y >= ksize) { | |||
| return false; | |||
| } | |||
| if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || | |||
| dst.data_type_ != LDataType::FLOAT32) { | |||
| dst.Init(src.width_, src.height_, src.channel_, LDataType::FLOAT32); | |||
| } | |||
| LiteMat kx, ky; | |||
| kx.Init(ksize, 1, 1, LDataType::FLOAT32); | |||
| ky.Init(1, ksize, 1, LDataType::FLOAT32); | |||
| GetSobelKernel(kx, flag_x, ksize); | |||
| GetSobelKernel(ky, flag_y, ksize); | |||
| return ConvRowCol(src, kx, ky, dst, LDataType::FLOAT32, pad_type); | |||
| } | |||
| static float GetEdge(const float *temp, int width, int height, int x, int y) { | |||
| if (x >= 0 && y >= 0 && x < width && y < height) { | |||
| return temp[y * width + x]; | |||
| } else { | |||
| return -1.0f; | |||
| } | |||
| } | |||
| static void NonMaximumSuppression(const LiteMat &gx, const LiteMat &gy, LiteMat &edges, bool L2gradient) { // NOLINT | |||
| edges.Init(gx.width_, gx.height_, gx.channel_, gx.data_type_); | |||
| const float *gx_ptr = gx; | |||
| const float *gy_ptr = gy; | |||
| float *edges_ptr = edges; | |||
| int size = gx.height_ * gx.width_; | |||
| float *temp = new float[size]; | |||
| for (int i = 0; i < size; i++) { | |||
| float gx_value = gx_ptr[i]; | |||
| float gy_value = gy_ptr[i]; | |||
| if (L2gradient) { | |||
| temp[i] = sqrt(gx_value * gx_value + gy_value * gy_value); | |||
| } else { | |||
| temp[i] = abs(gx_value) + abs(gy_value); | |||
| } | |||
| } | |||
| for (int y = 0; y < gx.height_; y++) { | |||
| for (int x = 0; x < gx.width_; x++) { | |||
| float gx_value = gx_ptr[y * gx.width_ + x]; | |||
| float gy_value = gy_ptr[y * gx.width_ + x]; | |||
| float gx_value_abs = std::abs(gx_value); | |||
| float gy_value_abs = std::abs(gy_value); | |||
| float angle_value = atan2(gy_value_abs, gx_value_abs); | |||
| float edge_value = temp[y * gx.width_ + x]; | |||
| float edge_pre, edge_nex; | |||
| if (angle_value < ANGLE_22_5) { | |||
| edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y); | |||
| edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y); | |||
| if (edge_value > edge_pre && edge_value >= edge_nex) { | |||
| edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; | |||
| } else { | |||
| edges_ptr[y * gx.width_ + x] = 0.f; | |||
| } | |||
| } else if (angle_value > ANGLE_67_5) { | |||
| edge_pre = GetEdge(temp, gx.width_, gx.height_, x, y - 1); | |||
| edge_nex = GetEdge(temp, gx.width_, gx.height_, x, y + 1); | |||
| if (edge_value > edge_pre && edge_value >= edge_nex) { | |||
| edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; | |||
| } else { | |||
| edges_ptr[y * gx.width_ + x] = 0.f; | |||
| } | |||
| } else if (gx_value * gy_value < 0) { | |||
| edge_pre = GetEdge(temp, gx.width_, gx.height_, x + 1, y - 1); | |||
| edge_nex = GetEdge(temp, gx.width_, gx.height_, x - 1, y + 1); | |||
| if (edge_value > edge_pre && edge_value > edge_nex) { | |||
| edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; | |||
| } else { | |||
| edges_ptr[y * gx.width_ + x] = 0.f; | |||
| } | |||
| } else { | |||
| edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y - 1); | |||
| edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y + 1); | |||
| if (edge_value > edge_pre && edge_value > edge_nex) { | |||
| edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; | |||
| } else { | |||
| edges_ptr[y * gx.width_ + x] = 0.f; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| static void Hysteresis(const LiteMat &edges, uint8_t *dst, double low_thresh, double high_thresh) { | |||
| const float *edges_ptr = edges; | |||
| uint8_t *dst_ptr = dst; | |||
| int size = edges.height_ * edges.width_; | |||
| int *buffer = new int[size]; | |||
| int buffer_step = edges.width_; | |||
| std::vector<int> stack; | |||
| for (int y = 0; y < edges.height_; y++) { | |||
| for (int x = 0; x < edges.width_; x++) { | |||
| int pos = y * edges.width_ + x; | |||
| float edge_value = edges_ptr[pos]; | |||
| if (edge_value > high_thresh) { | |||
| buffer[pos] = 2; | |||
| stack.push_back(pos); | |||
| } else if (edge_value <= low_thresh) { | |||
| buffer[pos] = 0; | |||
| } else { | |||
| buffer[pos] = 1; | |||
| } | |||
| } | |||
| } | |||
| while (!stack.empty()) { | |||
| int pos = stack.back(); | |||
| stack.pop_back(); | |||
| int y = static_cast<int>(pos / buffer_step); | |||
| int x = pos % buffer_step; | |||
| for (int i = -1; i < 2; i++) { | |||
| for (int j = -1; j < 2; j++) { | |||
| int next_y = y + i; | |||
| int next_x = x + j; | |||
| if (next_y < 0 || next_x < 0 || next_y >= edges.height_ || next_x >= edges.width_ || | |||
| (next_y == y && next_x == x)) { | |||
| continue; | |||
| } | |||
| int next = next_y * buffer_step + next_x; | |||
| if (buffer[next] == 1) { | |||
| buffer[next] = 2; | |||
| stack.push_back(next); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| for (int i = 0; i < size; i++) { | |||
| if (buffer[i] == 2) { | |||
| dst_ptr[i] = 255; | |||
| } else { | |||
| dst_ptr[i] = 0; | |||
| } | |||
| } | |||
| } | |||
| bool Canny(const LiteMat &src, LiteMat &dst, double low_thresh, double high_thresh, int ksize, // NOLINT | |||
| bool L2gradient) { | |||
| if (src.IsEmpty() || src.data_type_ != LDataType::UINT8 || src.channel_ != 1) { | |||
| return false; | |||
| } | |||
| if (low_thresh < 0 || high_thresh < 0 || low_thresh > high_thresh) { | |||
| return false; | |||
| } | |||
| if ((ksize & 1) == 0 || ksize < 3 || ksize > 7) { | |||
| return false; | |||
| } | |||
| if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || | |||
| dst.data_type_ != src.data_type_) { | |||
| dst.Init(src.width_, src.height_, src.channel_, src.data_type_); | |||
| } | |||
| LiteMat gx, gy; | |||
| Sobel(src, gx, 1, 0, ksize, PaddBorderType::PADD_BORDER_REPLICATE); | |||
| Sobel(src, gy, 0, 1, ksize, PaddBorderType::PADD_BORDER_REPLICATE); | |||
| LiteMat edges; | |||
| NonMaximumSuppression(gx, gy, edges, L2gradient); | |||
| Hysteresis(edges, dst, low_thresh, high_thresh); | |||
| return true; | |||
| } | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| @@ -0,0 +1,90 @@ | |||
| /** | |||
| * Copyright 2021 Huawei Technologies Co., Ltd | |||
| * | |||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||
| * you may not use this file except in compliance with the License. | |||
| * You may obtain a copy of the License at | |||
| * | |||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, software | |||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| */ | |||
| #include <math.h> | |||
| #include "lite_cv/lite_mat.h" | |||
| #include "lite_cv/image_process.h" | |||
| #ifdef ENABLE_ANDROID | |||
| #if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) | |||
| #define USE_NEON | |||
| #include <arm_neon.h> | |||
| #endif | |||
| #endif | |||
| namespace mindspore { | |||
| namespace dataset { | |||
| static void GetGaussianKernel(float *kernel, int size, double sigma) { | |||
| int n = (size - 1) / 2; | |||
| std::vector<float> buffer(n); | |||
| float sum = 0; | |||
| for (int i = 0; i < n; i++) { | |||
| int x = i - n; | |||
| float g = exp(-0.5 * x * x / (sigma * sigma)); | |||
| buffer[i] = g; | |||
| sum += g; | |||
| } | |||
| sum = sum * 2 + 1; | |||
| if ((size & 1) == 0) { | |||
| sum += 1; | |||
| } | |||
| float scale = 1. / sum; | |||
| float *ptr = kernel; | |||
| for (int i = 0; i < n; i++) { | |||
| float g = buffer[i] * scale; | |||
| ptr[i] = g; | |||
| ptr[size - 1 - i] = g; | |||
| } | |||
| ptr[n] = scale; | |||
| if ((size & 1) == 0) { | |||
| ptr[n + 1] = scale; | |||
| } | |||
| } | |||
| bool GaussianBlur(const LiteMat &src, LiteMat &dst, const std::vector<int> &ksize, double sigmaX, // NOLINT | |||
| double sigmaY, PaddBorderType pad_type) { | |||
| if (src.IsEmpty() || src.data_type_ != LDataType::UINT8) { | |||
| return false; | |||
| } | |||
| if (ksize.size() != 2 || ksize[0] <= 0 || ksize[1] <= 0 || ksize[0] % 2 != 1 || ksize[1] % 2 != 1) { | |||
| return false; | |||
| } | |||
| if (sigmaX <= 0) { | |||
| return false; | |||
| } | |||
| if (sigmaY <= 0) { | |||
| sigmaY = sigmaX; | |||
| } | |||
| if (ksize[0] == 1 && ksize[1] == 1) { | |||
| dst = src; | |||
| return true; | |||
| } | |||
| LiteMat kx, ky; | |||
| kx.Init(ksize[0], 1, 1, LDataType::FLOAT32); | |||
| ky.Init(1, ksize[1], 1, LDataType::FLOAT32); | |||
| GetGaussianKernel(kx, ksize[0], sigmaX); | |||
| GetGaussianKernel(ky, ksize[1], sigmaY); | |||
| return ConvRowCol(src, kx, ky, dst, src.data_type_, pad_type); | |||
| } | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| @@ -1,5 +1,5 @@ | |||
| /** | |||
| * Copyright 2020 Huawei Technologies Co., Ltd | |||
| * Copyright 2020-2021 Huawei Technologies Co., Ltd | |||
| * | |||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||
| * you may not use this file except in compliance with the License. | |||
| @@ -17,10 +17,10 @@ | |||
| #include "minddata/dataset/kernels/image/lite_cv/image_process.h" | |||
| #include <float.h> | |||
| #include <math.h> | |||
| #include <limits.h> | |||
| #include <string.h> | |||
| #include <cmath> | |||
| #include <limits> | |||
| #include <vector> | |||
| #include <utility> | |||
| #include <random> | |||
| @@ -29,19 +29,12 @@ | |||
| #include <arm_neon.h> | |||
| #endif | |||
| #ifdef ENABLE_NEON | |||
| #define R2GRAY 9798 | |||
| #define G2GRAY 19235 | |||
| #define B2GRAY 3735 | |||
| #define GRAYSHIFT 15 | |||
| #define GRAYSHIFT_DELTA (1 << (GRAYSHIFT - 1)) | |||
| #define U32TOU8CAST(value) ((uint8_t)std::min(value, (uint32_t)UCHAR_MAX)) | |||
| #else | |||
| #define R2GRAY 77 | |||
| #define G2GRAY 150 | |||
| #define B2GRAY 29 | |||
| #define GRAYSHIFT 8 | |||
| #endif | |||
| #define YSCALE 0x0101 | |||
| #define UTOB (-128) | |||
| @@ -247,6 +240,125 @@ static void ResizeBilinear1C(const unsigned char *src, int src_width, int src_he | |||
| delete[] data_buf; | |||
| } | |||
| static inline uint8_t clip(float value, int min = 0, int max = 255) { | |||
| int int_val = roundf(value); | |||
| return std::max<int32_t>(std::numeric_limits<uint8_t>::min(), | |||
| std::min<int32_t>(std::numeric_limits<uint8_t>::max(), int_val)); | |||
| } | |||
| template <typename T1, typename T2> | |||
| static bool Conv2DImplement(const LiteMat &src, const LiteMat &kernel, T2 *dst, LDataType dst_type, | |||
| PaddBorderType pad_type) { | |||
| int border_x = static_cast<int>(kernel.width_ / 2); | |||
| int border_y = static_cast<int>(kernel.height_ / 2); | |||
| LiteMat pad_mat; | |||
| pad_mat.Init(src.width_ + 2 * border_x, src.height_ + 2 * border_y, src.channel_, src.data_type_); | |||
| if (!Pad(src, pad_mat, border_y, border_y, border_x, border_x, pad_type)) { | |||
| return false; | |||
| } | |||
| const T1 *pad_ptr = pad_mat; | |||
| const float *kernel_ptr = kernel; | |||
| T2 *dst_ptr = dst; | |||
| int pad_step = pad_mat.width_ * pad_mat.channel_; | |||
| int dst_step = src.width_ * src.channel_; | |||
| if (src.channel_ == 1) { | |||
| for (int y = border_y; y < pad_mat.height_ - border_y; y++) { | |||
| for (int x = border_x; x < pad_mat.width_ - border_x; x++) { | |||
| float conv_sum = 0; | |||
| for (int i = -border_y; i < -border_y + kernel.height_; i++) { | |||
| for (int j = -border_x; j < -border_x + kernel.width_; j++) { | |||
| conv_sum += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_] * | |||
| kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; | |||
| } | |||
| } | |||
| if (dst_type == LDataType::UINT8) { | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_] = clip(conv_sum); | |||
| } else { | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_] = conv_sum; | |||
| } | |||
| } | |||
| } | |||
| } else if (src.channel_ == 3) { | |||
| for (int y = border_y; y < pad_mat.height_ - border_y; y++) { | |||
| for (int x = border_x; x < pad_mat.width_ - border_x; x++) { | |||
| float conv_sum_b = 0; | |||
| float conv_sum_g = 0; | |||
| float conv_sum_r = 0; | |||
| for (int i = -border_y; i < -border_y + kernel.height_; i++) { | |||
| for (int j = -border_x; j < -border_x + kernel.width_; j++) { | |||
| conv_sum_b += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_] * | |||
| kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; | |||
| conv_sum_g += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_ + 1] * | |||
| kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; | |||
| conv_sum_r += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_ + 2] * | |||
| kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; | |||
| } | |||
| } | |||
| if (dst_type == LDataType::UINT8) { | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_] = clip(conv_sum_b); | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 1] = clip(conv_sum_g); | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 2] = clip(conv_sum_r); | |||
| } else { | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_] = conv_sum_b; | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 1] = conv_sum_g; | |||
| dst_ptr[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 2] = conv_sum_r; | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| bool Conv2D(const LiteMat &src, const LiteMat &kernel, LiteMat &dst, LDataType dst_type, PaddBorderType pad_type) { | |||
| if (src.IsEmpty() || kernel.IsEmpty()) { | |||
| return false; | |||
| } | |||
| if ((dst_type != LDataType::UINT8 && dst_type != LDataType::FLOAT32) || kernel.data_type_ != LDataType::FLOAT32) { | |||
| return false; | |||
| } | |||
| if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || | |||
| dst.data_type_ != dst_type) { | |||
| dst.Init(src.width_, src.height_, src.channel_, dst_type); | |||
| } | |||
| if (src.data_type_ == LDataType::UINT8 && dst.data_type_ == LDataType::UINT8) { | |||
| return Conv2DImplement<uint8_t, uint8_t>(src, kernel, dst, dst_type, pad_type); | |||
| } else if (src.data_type_ == LDataType::UINT8 && dst.data_type_ == LDataType::FLOAT32) { | |||
| return Conv2DImplement<uint8_t, float>(src, kernel, dst, dst_type, pad_type); | |||
| } else if (src.data_type_ == LDataType::FLOAT32 && dst.data_type_ == LDataType::UINT8) { | |||
| return Conv2DImplement<float, uint8_t>(src, kernel, dst, dst_type, pad_type); | |||
| } else if (src.data_type_ == LDataType::FLOAT32 && dst.data_type_ == LDataType::FLOAT32) { | |||
| return Conv2DImplement<float, float>(src, kernel, dst, dst_type, pad_type); | |||
| } else { | |||
| return false; | |||
| } | |||
| } | |||
| bool ConvRowCol(const LiteMat &src, const LiteMat &kx, const LiteMat &ky, LiteMat &dst, LDataType dst_type, | |||
| PaddBorderType pad_type) { | |||
| if (src.IsEmpty() || kx.IsEmpty() || ky.IsEmpty()) { | |||
| return false; | |||
| } | |||
| if (dst_type != LDataType::UINT8 && dst_type != LDataType::FLOAT32) { | |||
| return false; | |||
| } | |||
| if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || | |||
| dst.data_type_ != dst_type) { | |||
| dst.Init(src.width_, src.height_, src.channel_, dst_type); | |||
| } | |||
| LiteMat mid; | |||
| bool ret = Conv2D(src, kx, mid, LDataType::FLOAT32, pad_type) && Conv2D(mid, ky, dst, dst_type, pad_type); | |||
| return ret; | |||
| } | |||
| bool ResizeBilinear(const LiteMat &src, LiteMat &dst, int dst_w, int dst_h) { | |||
| if (dst_h <= 0 || dst_w <= 0) { | |||
| return false; | |||
| @@ -485,7 +597,7 @@ static bool ConvertRGBAToGRAY(const unsigned char *data, LDataType data_type, in | |||
| #else | |||
| for (int y = 0; y < h; y++) { | |||
| for (int x = 0; x < w; x++) { | |||
| *ptr = (data_ptr[2] * B2GRAY + data_ptr[1] * G2GRAY + data_ptr[0] * R2GRAY) >> GRAYSHIFT; | |||
| *ptr = (data_ptr[2] * B2GRAY + data_ptr[1] * G2GRAY + data_ptr[0] * R2GRAY + GRAYSHIFT_DELTA) >> GRAYSHIFT; | |||
| ptr++; | |||
| data_ptr += 4; | |||
| } | |||
| @@ -763,6 +875,45 @@ static void PadWithConstant(const LiteMat &src, LiteMat &dst, const int top, con | |||
| } | |||
| } | |||
| static int PadFromPos(int p, int len, PaddBorderType pad_type) { | |||
| if (p >= 0 && p < len) { | |||
| return p; | |||
| } | |||
| if (pad_type == PaddBorderType::PADD_BORDER_REPLICATE) { | |||
| return p < 0 ? 0 : len - 1; | |||
| } else { | |||
| return p < 0 ? -p : 2 * len - p - 2; | |||
| } | |||
| } | |||
| template <typename T> | |||
| static void PadImplement(const LiteMat &src, LiteMat &dst, const int top, const int bottom, const int left, | |||
| const int right, const PaddBorderType pad_type) { | |||
| int src_step = src.width_ * src.channel_; | |||
| int dst_step = dst.width_ * dst.channel_; | |||
| uint8_t *src_data_ptr = reinterpret_cast<uint8_t *>(src.data_ptr_); | |||
| uint8_t *dst_data_ptr = reinterpret_cast<uint8_t *>(dst.data_ptr_); | |||
| for (int i = 0; i < src.height_; i++) { | |||
| memcpy(dst_data_ptr + (i + top) * dst.steps_[0] + left * dst.steps_[1], src_data_ptr + i * src.steps_[0], | |||
| src.steps_[0]); | |||
| } | |||
| const T *src_ptr = src; | |||
| T *dst_ptr = dst; | |||
| for (int y = 0; y < dst.height_; y++) { | |||
| for (int x = 0; x < dst.width_; x++) { | |||
| if (y < top || y >= dst.height_ - bottom || x < left || x >= dst.width_ - right) { | |||
| int src_y = PadFromPos(y - top, src.height_, pad_type); | |||
| int src_x = PadFromPos(x - left, src.width_, pad_type); | |||
| for (int cn = 0; cn < dst.channel_; cn++) { | |||
| dst_ptr[y * dst_step + x * dst.channel_ + cn] = src_ptr[src_y * src_step + src_x * src.channel_ + cn]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| template <typename T> | |||
| void ExtractChannelImpl(const T *src_ptr, T *dst_ptr, int height, int width, int channel, int col) { | |||
| int total = height * width; | |||
| @@ -909,6 +1060,10 @@ bool Pad(const LiteMat &src, LiteMat &dst, int top, int bottom, int left, int ri | |||
| PadWithConstant<float>(src, dst, top, bottom, left, right, pad_type, fill_b_or_gray, fill_g, fill_r); | |||
| } else if (pad_type == PADD_BORDER_CONSTANT && src.data_type_ == LDataType::UINT8) { | |||
| PadWithConstant<uint8_t>(src, dst, top, bottom, left, right, pad_type, fill_b_or_gray, fill_g, fill_r); | |||
| } else if (src.data_type_ == LDataType::FLOAT32) { | |||
| PadImplement<float>(src, dst, top, bottom, left, right, pad_type); | |||
| } else if (src.data_type_ == LDataType::UINT8) { | |||
| PadImplement<uint8_t>(src, dst, top, bottom, left, right, pad_type); | |||
| } else { | |||
| return false; | |||
| } | |||
| @@ -32,7 +32,12 @@ namespace dataset { | |||
| #define INT16_CAST(X) \ | |||
| static_cast<int16_t>(::std::min(::std::max(static_cast<int>(X + (X >= 0.f ? 0.5f : -0.5f)), -32768), 32767)); | |||
| enum PaddBorderType { PADD_BORDER_CONSTANT = 0, PADD_BORDER_REPLICATE = 1 }; | |||
| enum PaddBorderType { | |||
| PADD_BORDER_CONSTANT = 0, | |||
| PADD_BORDER_REPLICATE = 1, | |||
| PADD_BORDER_REFLECT_101 = 4, | |||
| PADD_BORDER_DEFAULT = PADD_BORDER_REFLECT_101 | |||
| }; | |||
| struct BoxesConfig { | |||
| public: | |||
| @@ -65,7 +70,7 @@ bool SubStractMeanNormalize(const LiteMat &src, LiteMat &dst, const std::vector< | |||
| /// \brief padd image, the channel supports is 3 and 1 | |||
| bool Pad(const LiteMat &src, LiteMat &dst, int top, int bottom, int left, int right, PaddBorderType pad_type, | |||
| uint8_t fill_b_or_gray, uint8_t fill_g, uint8_t fill_r); | |||
| uint8_t fill_b_or_gray = 0, uint8_t fill_g = 0, uint8_t fill_r = 0); | |||
| /// \brief Extract image channel by index | |||
| bool ExtractChannel(LiteMat &src, LiteMat &dst, int col); | |||
| @@ -113,6 +118,25 @@ bool GetAffineTransform(std::vector<Point> src_point, std::vector<Point> dst_poi | |||
| /// \brief Matrix transpose | |||
| bool Transpose(LiteMat &src, LiteMat &dst); | |||
| /// \brief Filter the image by a Gaussian kernel | |||
| bool GaussianBlur(const LiteMat &src, LiteMat &dst, const std::vector<int> &ksize, double sigmaX, double sigmaY = 0.f, | |||
| PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); | |||
| /// \brief Detect edges in an image | |||
| bool Canny(const LiteMat &src, LiteMat &dst, double low_thresh, double high_thresh, int ksize = 3, | |||
| bool L2gradient = false); | |||
| /// \brief Apply a 2D convolution over the image | |||
| bool Conv2D(const LiteMat &src, const LiteMat &kernel, LiteMat &dst, LDataType dst_type, | |||
| PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); | |||
| /// \brief Applies a separable linear convolution over the image | |||
| bool ConvRowCol(const LiteMat &src, const LiteMat &kx, const LiteMat &ky, LiteMat &dst, LDataType dst_type, | |||
| PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); | |||
| /// \brief Filter the image by a Sobel kernel | |||
| bool Sobel(const LiteMat &src, LiteMat &dst, int flag_x, int flag_y, int ksize, PaddBorderType pad_type); | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| #endif // IMAGE_PROCESS_H_ | |||
| @@ -13,7 +13,7 @@ | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| */ | |||
| #include "common/common.h" | |||
| #include "lite_cv/lite_mat.h" | |||
| #include "lite_cv/image_process.h" | |||
| @@ -207,6 +207,29 @@ bool ReadYUV(const char *filename, int w, int h, uint8_t **data) { | |||
| return true; | |||
| } | |||
| TEST_F(MindDataImageProcess, TestRGBA2GRAY) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat gray_image; | |||
| cv::cvtColor(src_image, gray_image, CV_BGR2GRAY); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| double distance = 0.f; | |||
| int total_size = gray_image.cols * gray_image.rows * gray_image.channels(); | |||
| for (int i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)gray_image.data[i] - ((uint8_t *)lite_mat_gray)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, testNV21ToBGR) { | |||
| // ffmpeg -i ./data/dataset/apple.jpg -s 1024*800 -pix_fmt nv21 ./data/dataset/yuv/test_nv21.yuv | |||
| const char *filename = "data/dataset/yuv/test_nv21.yuv"; | |||
| @@ -410,7 +433,6 @@ TEST_F(MindDataImageProcess, TestPadd) { | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat makeborder; | |||
| ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_CONSTANT, 255, 255, 255); | |||
| ASSERT_TRUE(ret == true); | |||
| @@ -441,7 +463,6 @@ TEST_F(MindDataImageProcess, TestPadZero) { | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat makeborder; | |||
| ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_CONSTANT, 255, 255, 255); | |||
| ASSERT_TRUE(ret == true); | |||
| @@ -454,6 +475,68 @@ TEST_F(MindDataImageProcess, TestPadZero) { | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestPadReplicate) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| int left = 20; | |||
| int right = 20; | |||
| int top = 20; | |||
| int bottom = 20; | |||
| cv::Mat b_image; | |||
| cv::copyMakeBorder(image, b_image, top, bottom, left, right, cv::BORDER_REPLICATE); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(image, rgba_mat, CV_BGR2RGBA); | |||
| LiteMat lite_mat_bgr; | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat makeborder; | |||
| ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_REPLICATE); | |||
| ASSERT_TRUE(ret == true); | |||
| size_t total_size = makeborder.height_ * makeborder.width_ * makeborder.channel_; | |||
| double distance = 0.0f; | |||
| for (size_t i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)b_image.data[i] - ((uint8_t *)makeborder)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestPadReflect101) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| int left = 20; | |||
| int right = 20; | |||
| int top = 20; | |||
| int bottom = 20; | |||
| cv::Mat b_image; | |||
| cv::copyMakeBorder(image, b_image, top, bottom, left, right, cv::BORDER_REFLECT_101); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(image, rgba_mat, CV_BGR2RGBA); | |||
| LiteMat lite_mat_bgr; | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat makeborder; | |||
| ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_REFLECT_101); | |||
| ASSERT_TRUE(ret == true); | |||
| size_t total_size = makeborder.height_ * makeborder.width_ * makeborder.channel_; | |||
| double distance = 0.0f; | |||
| for (size_t i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)b_image.data[i] - ((uint8_t *)makeborder)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestGetDefaultBoxes) { | |||
| std::string benchmark = "data/dataset/testLite/default_boxes.bin"; | |||
| BoxesConfig config; | |||
| @@ -1279,3 +1362,355 @@ TEST_F(MindDataImageProcess, testGetAffineTransform) { | |||
| EXPECT_TRUE(ret); | |||
| AccuracyComparison(expect_matrix, M); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestConv2D8U) { | |||
| LiteMat lite_mat_src; | |||
| lite_mat_src.Init(3, 3, 1, LDataType::UINT8); | |||
| uint8_t *src_ptr = lite_mat_src; | |||
| for (int i = 0; i < 9; i++) { | |||
| src_ptr[i] = i % 3; | |||
| } | |||
| LiteMat kernel; | |||
| kernel.Init(3, 3, 1, LDataType::FLOAT32); | |||
| float *kernel_ptr = kernel; | |||
| for (int i = 0; i < 9; i++) { | |||
| kernel_ptr[i] = i % 2; | |||
| } | |||
| LiteMat lite_mat_dst; | |||
| bool ret = Conv2D(lite_mat_src, kernel, lite_mat_dst, LDataType::UINT8); | |||
| ASSERT_TRUE(ret == true); | |||
| std::vector<uint8_t> expected_result = {2, 4, 6, 2, 4, 6, 2, 4, 6}; | |||
| size_t total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| float distance = 0.0f; | |||
| for (size_t i = 0; i < total_size; i++) { | |||
| distance += pow(((uint8_t *)lite_mat_dst)[i] - expected_result[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestConv2D32F) { | |||
| LiteMat lite_mat_src; | |||
| lite_mat_src.Init(2, 2, 1, LDataType::FLOAT32); | |||
| float *src_ptr = lite_mat_src; | |||
| for (int i = 0; i < 4; i++) { | |||
| src_ptr[i] = static_cast<float>(i) / 2; | |||
| } | |||
| LiteMat kernel; | |||
| kernel.Init(2, 2, 1, LDataType::FLOAT32); | |||
| float *kernel_ptr = kernel; | |||
| for (int i = 0; i < 4; i++) { | |||
| kernel_ptr[i] = static_cast<float>(i); | |||
| } | |||
| LiteMat lite_mat_dst; | |||
| bool ret = Conv2D(lite_mat_src, kernel, lite_mat_dst, LDataType::FLOAT32); | |||
| ASSERT_TRUE(ret == true); | |||
| std::vector<float> expected_result = {2.f, 3.f, 6.f, 7.f}; | |||
| size_t total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| float distance = 0.0f; | |||
| for (size_t i = 0; i < total_size; i++) { | |||
| distance += pow(((float *)lite_mat_dst)[i] - expected_result[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestGaussianBlurSize35) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat dst_image; | |||
| cv::GaussianBlur(src_image, dst_image, cv::Size(3, 5), 3, 3); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| LiteMat lite_mat_bgr; | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_dst; | |||
| ret = GaussianBlur(lite_mat_bgr, lite_mat_dst, {3, 5}, 3, 3); | |||
| ASSERT_TRUE(ret == true); | |||
| size_t total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| double distance = 0.0f; | |||
| for (size_t i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)dst_image.data[i] - ((uint8_t *)lite_mat_dst)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_LE(distance, 1.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestGaussianBlurSize13) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat dst_image; | |||
| cv::GaussianBlur(src_image, dst_image, cv::Size(1, 3), 3); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| LiteMat lite_mat_bgr; | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_dst; | |||
| ret = GaussianBlur(lite_mat_bgr, lite_mat_dst, {1, 3}, 3); | |||
| ASSERT_TRUE(ret == true); | |||
| size_t total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| double distance = 0.0f; | |||
| for (size_t i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)dst_image.data[i] - ((uint8_t *)lite_mat_dst)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_LE(distance, 1.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestGaussianBlurInvalidParams) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| LiteMat lite_mat_bgr; | |||
| bool ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_dst; | |||
| // even size | |||
| ret = GaussianBlur(lite_mat_bgr, lite_mat_dst, {3, 4}, 3); | |||
| ASSERT_TRUE(ret == false); | |||
| // ksize.size() != 2 | |||
| ret = GaussianBlur(lite_mat_bgr, lite_mat_dst, {3, 4, 5}, 3); | |||
| ASSERT_TRUE(ret == false); | |||
| // size less or equal to 0 | |||
| ret = GaussianBlur(lite_mat_bgr, lite_mat_dst, {0, 3}, 3); | |||
| ASSERT_TRUE(ret == false); | |||
| // sigmaX less or equal to 0 | |||
| ret = GaussianBlur(lite_mat_bgr, lite_mat_dst, {3, 3}, 0); | |||
| ASSERT_TRUE(ret == false); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestCannySize3) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat gray_image; | |||
| cv::cvtColor(src_image, gray_image, CV_BGR2GRAY); | |||
| cv::Mat dst_image; | |||
| cv::Canny(gray_image, dst_image, 100, 200, 3); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_dst; | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, 100, 200, 3); | |||
| ASSERT_TRUE(ret == true); | |||
| int total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| double distance = 0.0f; | |||
| for (int i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)dst_image.data[i] - ((uint8_t *)lite_mat_dst)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestCannySize5) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat gray_image; | |||
| cv::cvtColor(src_image, gray_image, CV_BGR2GRAY); | |||
| cv::Mat dst_image; | |||
| cv::Canny(gray_image, dst_image, 200, 300, 5); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_dst; | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, 200, 300, 5); | |||
| ASSERT_TRUE(ret == true); | |||
| int total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| double distance = 0.0f; | |||
| for (int i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)dst_image.data[i] - ((uint8_t *)lite_mat_dst)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestCannyL2) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat gray_image; | |||
| cv::cvtColor(src_image, gray_image, CV_BGR2GRAY); | |||
| cv::Mat dst_image; | |||
| cv::Canny(gray_image, dst_image, 50, 150, 3, true); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_dst; | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, 50, 150, 3, true); | |||
| ASSERT_TRUE(ret == true); | |||
| int total_size = lite_mat_dst.height_ * lite_mat_dst.width_ * lite_mat_dst.channel_; | |||
| double distance = 0.0f; | |||
| for (int i = 0; i < total_size; i++) { | |||
| distance += pow((uint8_t)dst_image.data[i] - ((uint8_t *)lite_mat_dst)[i], 2); | |||
| } | |||
| distance = sqrt(distance / total_size); | |||
| EXPECT_EQ(distance, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestCannyInvalidParams) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_bgr; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | |||
| ASSERT_TRUE(ret == true); | |||
| // channel is not 1 | |||
| LiteMat lite_mat_dst; | |||
| ret = Canny(lite_mat_bgr, lite_mat_dst, 70, 210, 3); | |||
| ASSERT_TRUE(ret == false); | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| // low_thresh less than 0 | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, -5, 230, 3); | |||
| ASSERT_TRUE(ret == false); | |||
| // high_thresh less than low_thresh | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, 250, 130, 3); | |||
| ASSERT_TRUE(ret == false); | |||
| // even size | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, 60, 180, 4); | |||
| ASSERT_TRUE(ret == false); | |||
| // size less than 3 or large than 7 | |||
| ret = Canny(lite_mat_gray, lite_mat_dst, 10, 190, 9); | |||
| ASSERT_TRUE(ret == false); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestSobel) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat gray_image; | |||
| cv::cvtColor(src_image, gray_image, CV_BGR2GRAY); | |||
| cv::Mat sobel_image_x; | |||
| cv::Mat sobel_image_y; | |||
| cv::Sobel(gray_image, sobel_image_x, CV_32F, 1, 0, 3, 1, 0, cv::BORDER_REPLICATE); | |||
| cv::Sobel(gray_image, sobel_image_y, CV_32F, 0, 1, 3, 1, 0, cv::BORDER_REPLICATE); | |||
| cv::Mat sobel_cv_x, sobel_cv_y; | |||
| sobel_image_x.convertTo(sobel_cv_x, CV_8UC1); | |||
| sobel_image_y.convertTo(sobel_cv_y, CV_8UC1); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_x; | |||
| LiteMat lite_mat_y; | |||
| Sobel(lite_mat_gray, lite_mat_x, 1, 0, 3, PaddBorderType::PADD_BORDER_REPLICATE); | |||
| Sobel(lite_mat_gray, lite_mat_y, 0, 1, 3, PaddBorderType::PADD_BORDER_REPLICATE); | |||
| ASSERT_TRUE(ret == true); | |||
| cv::Mat dst_imageX(lite_mat_x.height_, lite_mat_x.width_, CV_32FC1, lite_mat_x.data_ptr_); | |||
| cv::Mat dst_imageY(lite_mat_y.height_, lite_mat_y.width_, CV_32FC1, lite_mat_y.data_ptr_); | |||
| cv::Mat sobel_ms_x, sobel_ms_y; | |||
| dst_imageX.convertTo(sobel_ms_x, CV_8UC1); | |||
| dst_imageY.convertTo(sobel_ms_y, CV_8UC1); | |||
| size_t total_size = lite_mat_x.height_ * lite_mat_x.width_ * lite_mat_x.channel_; | |||
| float distance_x = 0.0f, distance_y = 0.0f; | |||
| for (int i = 0; i < total_size; i++) { | |||
| distance_x += pow((uint8_t)sobel_cv_x.data[i] - (uint8_t)sobel_ms_x.data[i], 2); | |||
| distance_y += pow((uint8_t)sobel_cv_y.data[i] - (uint8_t)sobel_ms_y.data[i], 2); | |||
| } | |||
| distance_x = sqrt(distance_x / total_size); | |||
| distance_y = sqrt(distance_y / total_size); | |||
| EXPECT_EQ(distance_x, 0.0f); | |||
| EXPECT_EQ(distance_y, 0.0f); | |||
| } | |||
| TEST_F(MindDataImageProcess, TestSobelFlag) { | |||
| std::string filename = "data/dataset/apple.jpg"; | |||
| cv::Mat src_image = cv::imread(filename, cv::ImreadModes::IMREAD_COLOR); | |||
| cv::Mat gray_image; | |||
| cv::cvtColor(src_image, gray_image, CV_BGR2GRAY); | |||
| cv::Mat sobel_image_x; | |||
| cv::Sobel(gray_image, sobel_image_x, CV_32F, 3, 1, 5, 1, 0, cv::BORDER_REPLICATE); | |||
| cv::Mat sobel_cv_x; | |||
| sobel_image_x.convertTo(sobel_cv_x, CV_8UC1); | |||
| cv::Mat rgba_mat; | |||
| cv::cvtColor(src_image, rgba_mat, CV_BGR2RGBA); | |||
| bool ret = false; | |||
| LiteMat lite_mat_gray; | |||
| ret = | |||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2GRAY, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_gray); | |||
| ASSERT_TRUE(ret == true); | |||
| LiteMat lite_mat_x; | |||
| Sobel(lite_mat_gray, lite_mat_x, 3, 1, 5, PaddBorderType::PADD_BORDER_REPLICATE); | |||
| ASSERT_TRUE(ret == true); | |||
| cv::Mat dst_imageX(lite_mat_x.height_, lite_mat_x.width_, CV_32FC1, lite_mat_x.data_ptr_); | |||
| cv::Mat sobel_ms_x; | |||
| dst_imageX.convertTo(sobel_ms_x, CV_8UC1); | |||
| size_t total_size = lite_mat_x.height_ * lite_mat_x.width_ * lite_mat_x.channel_; | |||
| float distance_x = 0.0f; | |||
| for (int i = 0; i < total_size; i++) { | |||
| distance_x += pow((uint8_t)sobel_cv_x.data[i] - (uint8_t)sobel_ms_x.data[i], 2); | |||
| } | |||
| distance_x = sqrt(distance_x / total_size); | |||
| EXPECT_EQ(distance_x, 0.0f); | |||
| } | |||