Browse Source

!12881 add GaussianBlur and Canny for lite_cv

From: @tiancixiao
Reviewed-by: 
Signed-off-by:
tags/v1.2.0-rc1
mindspore-ci-bot Gitee 4 years ago
parent
commit
9baec79337
6 changed files with 978 additions and 15 deletions
  1. +2
    -0
      mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/CMakeLists.txt
  2. +257
    -0
      mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/canny.cc
  3. +90
    -0
      mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc
  4. +165
    -10
      mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/image_process.cc
  5. +26
    -2
      mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/image_process.h
  6. +438
    -3
      tests/ut/cpp/dataset/image_process_test.cc

+ 2
- 0
mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/CMakeLists.txt View File

@@ -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)

+ 257
- 0
mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/canny.cc View File

@@ -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

+ 90
- 0
mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc View File

@@ -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

+ 165
- 10
mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/image_process.cc View File

@@ -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;
}


+ 26
- 2
mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/image_process.h View File

@@ -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_

+ 438
- 3
tests/ut/cpp/dataset/image_process_test.cc View File

@@ -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);
}

Loading…
Cancel
Save