/*******************************************************
* Copyright (c) 2014, ArrayFire
* All rights reserved.
*
* This file is distributed under 3-clause BSD license.
* The complete license agreement can be obtained at:
* http://arrayfire.com/licenses/BSD-3-Clause
********************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
using af::dim4;
using af::dtype_traits;
using std::abs;
using std::endl;
using std::string;
using std::vector;
template
class Morph : public ::testing::Test {
public:
virtual void SetUp() {}
};
// create a list of types to be tested
typedef ::testing::Types
TestTypes;
// register the type list
TYPED_TEST_SUITE(Morph, TestTypes);
template
void morphTest(string pTestFile) {
SUPPORTED_TYPE_CHECK(inType);
vector numDims;
vector in;
vector tests;
readTests(pTestFile, numDims, in, tests);
dim4 dims = numDims[0];
dim4 maskDims = numDims[1];
af_array outArray = 0;
af_array inArray = 0;
af_array maskArray = 0;
ASSERT_SUCCESS(af_create_array(&inArray, &(in[0].front()), dims.ndims(),
dims.get(),
(af_dtype)dtype_traits::af_type));
ASSERT_SUCCESS(af_create_array(&maskArray, &(in[1].front()),
maskDims.ndims(), maskDims.get(),
(af_dtype)dtype_traits::af_type));
af_err af_stat;
if (isDilation) {
if (isVolume) {
ASSERT_SUCCESS(af_dilate3(&outArray, inArray, maskArray));
} else {
ASSERT_SUCCESS(af_dilate(&outArray, inArray, maskArray));
}
} else {
if (isVolume) {
ASSERT_SUCCESS(af_erode3(&outArray, inArray, maskArray));
} else {
ASSERT_SUCCESS(af_erode(&outArray, inArray, maskArray));
}
}
for (size_t testIter = 0; testIter < tests.size(); ++testIter) {
vector currGoldBar = tests[testIter];
ASSERT_VEC_ARRAY_EQ(currGoldBar, dims, outArray);
}
// cleanup
ASSERT_SUCCESS(af_release_array(inArray));
ASSERT_SUCCESS(af_release_array(maskArray));
ASSERT_SUCCESS(af_release_array(outArray));
}
TYPED_TEST(Morph, Dilate3x3) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(string(TEST_DIR "/morph/dilate3x3.test"));
}
TYPED_TEST(Morph, Erode3x3) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(string(TEST_DIR "/morph/erode3x3.test"));
}
TYPED_TEST(Morph, Dilate4x4) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(string(TEST_DIR "/morph/dilate4x4.test"));
}
TYPED_TEST(Morph, Dilate12x12) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/dilate12x12.test"));
}
TYPED_TEST(Morph, Erode4x4) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(string(TEST_DIR "/morph/erode4x4.test"));
}
TYPED_TEST(Morph, Dilate3x3_Batch) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/dilate3x3_batch.test"));
}
TYPED_TEST(Morph, Erode3x3_Batch) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/erode3x3_batch.test"));
}
TYPED_TEST(Morph, Dilate3x3x3) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/dilate3x3x3.test"));
}
TYPED_TEST(Morph, Erode3x3x3) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/erode3x3x3.test"));
}
TYPED_TEST(Morph, Dilate4x4x4) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/dilate4x4x4.test"));
}
TYPED_TEST(Morph, Erode4x4x4) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphTest(
string(TEST_DIR "/morph/erode4x4x4.test"));
}
template
void morphImageTest(string pTestFile, dim_t seLen) {
SUPPORTED_TYPE_CHECK(T);
IMAGEIO_ENABLED_CHECK();
vector inDims;
vector inFiles;
vector outSizes;
vector outFiles;
readImageTests(pTestFile, inDims, inFiles, outSizes, outFiles);
size_t testCount = inDims.size();
for (size_t testId = 0; testId < testCount; ++testId) {
af_array _inArray = 0;
af_array inArray = 0;
af_array maskArray = 0;
af_array outArray = 0;
af_array _goldArray = 0;
af_array goldArray = 0;
dim_t nElems = 0;
inFiles[testId].insert(0, string(TEST_DIR "/morph/"));
outFiles[testId].insert(0, string(TEST_DIR "/morph/"));
af_dtype targetType = static_cast(dtype_traits::af_type);
dim4 mdims(seLen, seLen, 1, 1);
ASSERT_SUCCESS(af_constant(&maskArray, 1.0, mdims.ndims(), mdims.get(),
targetType));
ASSERT_SUCCESS(
af_load_image(&_inArray, inFiles[testId].c_str(), isColor));
ASSERT_SUCCESS(af_cast(&inArray, _inArray, targetType));
ASSERT_SUCCESS(
af_load_image(&_goldArray, outFiles[testId].c_str(), isColor));
ASSERT_SUCCESS(af_cast(&goldArray, _goldArray, targetType));
ASSERT_SUCCESS(af_get_elements(&nElems, goldArray));
af_err error_code = AF_SUCCESS;
if (isDilation) {
error_code = af_dilate(&outArray, inArray, maskArray);
} else {
error_code = af_erode(&outArray, inArray, maskArray);
}
#if defined(AF_CPU)
ASSERT_SUCCESS(error_code);
ASSERT_IMAGES_NEAR(goldArray, outArray, 0.018f);
#else
if (targetType != b8 && seLen > 19) {
ASSERT_EQ(error_code, AF_ERR_NOT_SUPPORTED);
} else {
ASSERT_SUCCESS(error_code);
ASSERT_IMAGES_NEAR(goldArray, outArray, 0.018f);
}
#endif
ASSERT_SUCCESS(af_release_array(_inArray));
ASSERT_SUCCESS(af_release_array(inArray));
ASSERT_SUCCESS(af_release_array(maskArray));
ASSERT_SUCCESS(af_release_array(outArray));
ASSERT_SUCCESS(af_release_array(_goldArray));
ASSERT_SUCCESS(af_release_array(goldArray));
}
}
TEST(Morph, GrayscaleDilation3x3StructuringElement) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphImageTest(string(TEST_DIR "/morph/gray.test"), 3);
}
TEST(Morph, ColorImageErosion3x3StructuringElement) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
morphImageTest(string(TEST_DIR "/morph/color.test"), 3);
}
TEST(Morph, BinaryImageDilationBy33x33Kernel) {
morphImageTest(
string(TEST_DIR "/morph/zag_dilation.test"), 33);
}
TEST(Morph, BinaryImageErosionBy33x33Kernel) {
morphImageTest(
string(TEST_DIR "/morph/zag_erosion.test"), 33);
}
TEST(Morph, DilationBy33x33Kernel) {
morphImageTest(
string(TEST_DIR "/morph/baboon_dilation.test"), 33);
}
TEST(Morph, ErosionBy33x33Kernel) {
morphImageTest(
string(TEST_DIR "/morph/baboon_erosion.test"), 33);
}
template
void morphInputTest(void) {
SUPPORTED_TYPE_CHECK(T);
af_array inArray = 0;
af_array maskArray = 0;
af_array outArray = 0;
vector in(100, 1);
vector mask(9, 1);
// Check for 1D inputs
dim4 dims = dim4(100, 1, 1, 1);
dim4 mdims(3, 3, 1, 1);
ASSERT_SUCCESS(af_create_array(&maskArray, &mask.front(), mdims.ndims(),
mdims.get(),
(af_dtype)dtype_traits::af_type));
ASSERT_SUCCESS(af_create_array(&inArray, &in.front(), dims.ndims(),
dims.get(),
(af_dtype)dtype_traits::af_type));
if (isDilation)
ASSERT_EQ(AF_ERR_SIZE, af_dilate(&outArray, inArray, maskArray));
else
ASSERT_EQ(AF_ERR_SIZE, af_erode(&outArray, inArray, maskArray));
ASSERT_SUCCESS(af_release_array(inArray));
ASSERT_SUCCESS(af_release_array(maskArray));
}
TYPED_TEST(Morph, DilateInvalidInput) { morphInputTest(); }
TYPED_TEST(Morph, ErodeInvalidInput) { morphInputTest(); }
template
void morphMaskTest(void) {
SUPPORTED_TYPE_CHECK(T);
af_array inArray = 0;
af_array maskArray = 0;
af_array outArray = 0;
vector in(100, 1);
vector mask(16, 1);
// Check for 4D mask
dim4 dims(10, 10, 1, 1);
dim4 mdims(2, 2, 2, 2);
ASSERT_SUCCESS(af_create_array(&inArray, &in.front(), dims.ndims(),
dims.get(),
(af_dtype)dtype_traits::af_type));
ASSERT_SUCCESS(af_create_array(&maskArray, &mask.front(), mdims.ndims(),
mdims.get(),
(af_dtype)dtype_traits::af_type));
if (isDilation)
ASSERT_EQ(AF_ERR_SIZE, af_dilate(&outArray, inArray, maskArray));
else
ASSERT_EQ(AF_ERR_SIZE, af_erode(&outArray, inArray, maskArray));
ASSERT_SUCCESS(af_release_array(maskArray));
// Check for 1D mask
mdims = dim4(16, 1, 1, 1);
ASSERT_SUCCESS(af_create_array(&maskArray, &mask.front(), mdims.ndims(),
mdims.get(),
(af_dtype)dtype_traits::af_type));
if (isDilation)
ASSERT_EQ(AF_ERR_SIZE, af_dilate(&outArray, inArray, maskArray));
else
ASSERT_EQ(AF_ERR_SIZE, af_erode(&outArray, inArray, maskArray));
ASSERT_SUCCESS(af_release_array(maskArray));
ASSERT_SUCCESS(af_release_array(inArray));
}
TYPED_TEST(Morph, DilateInvalidMask) { morphMaskTest(); }
TYPED_TEST(Morph, ErodeInvalidMask) { morphMaskTest(); }
template
void morph3DMaskTest(void) {
SUPPORTED_TYPE_CHECK(T);
af_array inArray = 0;
af_array maskArray = 0;
af_array outArray = 0;
vector in(1000, 1);
vector mask(81, 1);
// Check for 2D mask
dim4 dims(10, 10, 10, 1);
dim4 mdims(9, 9, 1, 1);
ASSERT_SUCCESS(af_create_array(&inArray, &in.front(), dims.ndims(),
dims.get(),
(af_dtype)dtype_traits::af_type));
ASSERT_SUCCESS(af_create_array(&maskArray, &mask.front(), mdims.ndims(),
mdims.get(),
(af_dtype)dtype_traits::af_type));
if (isDilation)
ASSERT_EQ(AF_ERR_SIZE, af_dilate3(&outArray, inArray, maskArray));
else
ASSERT_EQ(AF_ERR_SIZE, af_erode3(&outArray, inArray, maskArray));
ASSERT_SUCCESS(af_release_array(maskArray));
// Check for 4D mask
mdims = dim4(3, 3, 3, 3);
ASSERT_SUCCESS(af_create_array(&maskArray, &mask.front(), mdims.ndims(),
mdims.get(),
(af_dtype)dtype_traits::af_type));
if (isDilation)
ASSERT_EQ(AF_ERR_SIZE, af_dilate3(&outArray, inArray, maskArray));
else
ASSERT_EQ(AF_ERR_SIZE, af_erode3(&outArray, inArray, maskArray));
ASSERT_SUCCESS(af_release_array(maskArray));
ASSERT_SUCCESS(af_release_array(inArray));
}
TYPED_TEST(Morph, DilateVolumeInvalidMask) {
morph3DMaskTest();
}
TYPED_TEST(Morph, ErodeVolumeInvalidMask) {
morph3DMaskTest();
}
////////////////////////////////////// CPP //////////////////////////////////
//
using af::array;
using af::constant;
using af::erode;
using af::iota;
using af::loadImage;
using af::max;
using af::randu;
using af::seq;
using af::span;
template
void cppMorphImageTest(string pTestFile) {
SUPPORTED_TYPE_CHECK(T);
IMAGEIO_ENABLED_CHECK();
vector inDims;
vector inFiles;
vector outSizes;
vector outFiles;
readImageTests(pTestFile, inDims, inFiles, outSizes, outFiles);
size_t testCount = inDims.size();
for (size_t testId = 0; testId < testCount; ++testId) {
inFiles[testId].insert(0, string(TEST_DIR "/morph/"));
outFiles[testId].insert(0, string(TEST_DIR "/morph/"));
array mask = constant(1.0, 3, 3);
array img = loadImage(inFiles[testId].c_str(), isColor);
array gold = loadImage(outFiles[testId].c_str(), isColor);
dim_t nElems = gold.elements();
array output;
if (isDilation)
output = dilate(img, mask);
else
output = erode(img, mask);
vector outData(nElems);
output.host((void*)outData.data());
vector goldData(nElems);
gold.host((void*)goldData.data());
ASSERT_EQ(true, compareArraysRMSD(nElems, goldData.data(),
outData.data(), 0.018f));
}
}
TEST(Morph, Grayscale_CPP) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
cppMorphImageTest(string(TEST_DIR "/morph/gray.test"));
}
TEST(Morph, ColorImage_CPP) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
cppMorphImageTest(string(TEST_DIR "/morph/color.test"));
}
TEST(Morph, GFOR) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
dim4 dims = dim4(10, 10, 3);
array A = iota(dims);
array B = constant(0, dims);
array mask = randu(3, 3) > 0.3;
gfor(seq ii, 3) { B(span, span, ii) = erode(A(span, span, ii), mask); }
for (int ii = 0; ii < 3; ii++) {
array c_ii = erode(A(span, span, ii), mask);
array b_ii = B(span, span, ii);
ASSERT_EQ(max(abs(c_ii - b_ii)) < 1E-5, true);
}
}
TEST(Morph, EdgeIssue1564) {
UNSUPPORTED_BACKEND(AF_BACKEND_ONEAPI);
int inputData[10 * 10] = {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
int goldData[10 * 10] = {0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1,
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1};
array input(10, 10, inputData);
int maskData[3 * 3] = {1, 1, 1, 1, 0, 1, 1, 1, 1};
array mask(3, 3, maskData);
array dilated = dilate(input.as(b8), mask.as(b8));
size_t nElems = dilated.elements();
vector outData(nElems);
dilated.host((void*)outData.data());
for (size_t i = 0; i < nElems; ++i) {
ASSERT_EQ((int)outData[i], goldData[i]);
}
}
TEST(Morph, UnsupportedKernel2D) {
const unsigned ndims = 2;
const dim_t dims[2] = {10, 10};
const dim_t kdims[2] = {32, 32};
af_array in, mask, out;
ASSERT_SUCCESS(af_constant(&mask, 1.0, ndims, kdims, f32));
ASSERT_SUCCESS(af_randu(&in, ndims, dims, f32));
#if defined(AF_CPU)
ASSERT_SUCCESS(af_dilate(&out, in, mask));
ASSERT_SUCCESS(af_release_array(out));
#else
ASSERT_EQ(AF_ERR_NOT_SUPPORTED, af_dilate(&out, in, mask));
#endif
ASSERT_SUCCESS(af_release_array(in));
ASSERT_SUCCESS(af_release_array(mask));
}