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 | add_library(lite-cv OBJECT | ||||
| image_process.cc | image_process.cc | ||||
| warp_affine.cc | warp_affine.cc | ||||
| gaussian_blur.cc | |||||
| canny.cc | |||||
| lite_mat.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"); | * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| * you may not use this file except in compliance with 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 "minddata/dataset/kernels/image/lite_cv/image_process.h" | ||||
| #include <float.h> | #include <float.h> | ||||
| #include <math.h> | |||||
| #include <limits.h> | #include <limits.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <cmath> | #include <cmath> | ||||
| #include <limits> | |||||
| #include <vector> | #include <vector> | ||||
| #include <utility> | #include <utility> | ||||
| #include <random> | #include <random> | ||||
| @@ -29,19 +29,12 @@ | |||||
| #include <arm_neon.h> | #include <arm_neon.h> | ||||
| #endif | #endif | ||||
| #ifdef ENABLE_NEON | |||||
| #define R2GRAY 9798 | #define R2GRAY 9798 | ||||
| #define G2GRAY 19235 | #define G2GRAY 19235 | ||||
| #define B2GRAY 3735 | #define B2GRAY 3735 | ||||
| #define GRAYSHIFT 15 | #define GRAYSHIFT 15 | ||||
| #define GRAYSHIFT_DELTA (1 << (GRAYSHIFT - 1)) | #define GRAYSHIFT_DELTA (1 << (GRAYSHIFT - 1)) | ||||
| #define U32TOU8CAST(value) ((uint8_t)std::min(value, (uint32_t)UCHAR_MAX)) | #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 YSCALE 0x0101 | ||||
| #define UTOB (-128) | #define UTOB (-128) | ||||
| @@ -247,6 +240,125 @@ static void ResizeBilinear1C(const unsigned char *src, int src_width, int src_he | |||||
| delete[] data_buf; | 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) { | bool ResizeBilinear(const LiteMat &src, LiteMat &dst, int dst_w, int dst_h) { | ||||
| if (dst_h <= 0 || dst_w <= 0) { | if (dst_h <= 0 || dst_w <= 0) { | ||||
| return false; | return false; | ||||
| @@ -485,7 +597,7 @@ static bool ConvertRGBAToGRAY(const unsigned char *data, LDataType data_type, in | |||||
| #else | #else | ||||
| for (int y = 0; y < h; y++) { | for (int y = 0; y < h; y++) { | ||||
| for (int x = 0; x < w; x++) { | 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++; | ptr++; | ||||
| data_ptr += 4; | 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> | template <typename T> | ||||
| void ExtractChannelImpl(const T *src_ptr, T *dst_ptr, int height, int width, int channel, int col) { | void ExtractChannelImpl(const T *src_ptr, T *dst_ptr, int height, int width, int channel, int col) { | ||||
| int total = height * width; | 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); | 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) { | } 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); | 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 { | } else { | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -32,7 +32,12 @@ namespace dataset { | |||||
| #define INT16_CAST(X) \ | #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)); | 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 { | struct BoxesConfig { | ||||
| public: | 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 | /// \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, | 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 | /// \brief Extract image channel by index | ||||
| bool ExtractChannel(LiteMat &src, LiteMat &dst, int col); | 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 | /// \brief Matrix transpose | ||||
| bool Transpose(LiteMat &src, LiteMat &dst); | 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 dataset | ||||
| } // namespace mindspore | } // namespace mindspore | ||||
| #endif // IMAGE_PROCESS_H_ | #endif // IMAGE_PROCESS_H_ | ||||
| @@ -13,7 +13,7 @@ | |||||
| * See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
| * limitations under the License. | * limitations under the License. | ||||
| */ | */ | ||||
| #include "common/common.h" | #include "common/common.h" | ||||
| #include "lite_cv/lite_mat.h" | #include "lite_cv/lite_mat.h" | ||||
| #include "lite_cv/image_process.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; | 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) { | TEST_F(MindDataImageProcess, testNV21ToBGR) { | ||||
| // ffmpeg -i ./data/dataset/apple.jpg -s 1024*800 -pix_fmt nv21 ./data/dataset/yuv/test_nv21.yuv | // 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"; | const char *filename = "data/dataset/yuv/test_nv21.yuv"; | ||||
| @@ -410,7 +433,6 @@ TEST_F(MindDataImageProcess, TestPadd) { | |||||
| bool ret = | bool ret = | ||||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | 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); | ||||
| ASSERT_TRUE(ret == true); | |||||
| LiteMat makeborder; | LiteMat makeborder; | ||||
| ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_CONSTANT, 255, 255, 255); | ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_CONSTANT, 255, 255, 255); | ||||
| ASSERT_TRUE(ret == true); | ASSERT_TRUE(ret == true); | ||||
| @@ -441,7 +463,6 @@ TEST_F(MindDataImageProcess, TestPadZero) { | |||||
| bool ret = | bool ret = | ||||
| InitFromPixel(rgba_mat.data, LPixelType::RGBA2BGR, LDataType::UINT8, rgba_mat.cols, rgba_mat.rows, lite_mat_bgr); | 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); | ||||
| ASSERT_TRUE(ret == true); | |||||
| LiteMat makeborder; | LiteMat makeborder; | ||||
| ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_CONSTANT, 255, 255, 255); | ret = Pad(lite_mat_bgr, makeborder, top, bottom, left, right, PaddBorderType::PADD_BORDER_CONSTANT, 255, 255, 255); | ||||
| ASSERT_TRUE(ret == true); | ASSERT_TRUE(ret == true); | ||||
| @@ -454,6 +475,68 @@ TEST_F(MindDataImageProcess, TestPadZero) { | |||||
| EXPECT_EQ(distance, 0.0f); | 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) { | TEST_F(MindDataImageProcess, TestGetDefaultBoxes) { | ||||
| std::string benchmark = "data/dataset/testLite/default_boxes.bin"; | std::string benchmark = "data/dataset/testLite/default_boxes.bin"; | ||||
| BoxesConfig config; | BoxesConfig config; | ||||
| @@ -1279,3 +1362,355 @@ TEST_F(MindDataImageProcess, testGetAffineTransform) { | |||||
| EXPECT_TRUE(ret); | EXPECT_TRUE(ret); | ||||
| AccuracyComparison(expect_matrix, M); | 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); | |||||
| } | |||||