blob: 28f059773699d014da1f957455ac0b79363bc0ef [file] [log] [blame]
/*
* Copyright (c) 2012-2017 The Khronos Group Inc.
*
* 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 "test_engine/test.h"
#include <VX/vx_types.h>
#include <VX/vx_khr_nn.h>
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define DEBUG_TEST_TENSOR_ENABLE_PRINTF 0
#define DEBUG_TEST_TENSOR_CONTINUE_AFTER_ERROR 0
#define DEBUG_TEST_TENSOR_BEYOND_FOUR_DIMS 1
#define TEST_TENSOR_NUM_ITERATIONS 10
#define TEST_TENSOR_MIN_DIM_SZ 1
#define TEST_TENSOR_MAX_DIM_SZ 20
#define Q78_FIXED_POINT_POSITION 8
#define MAX_TENSOR_DIMS 3
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define CLAMP(v, lower, upper) MAX((lower), MIN((v), (upper)))
#define MIN_TOLERANCE (0.95)
enum TestTensorDF
{
TT_Q78,
TT_U8
};
static size_t ownGetFlatByteOffset(
size_t index,
vx_size dim_num,
const vx_size * in_dims,
const vx_size * in_strides)
{
size_t res = 0;
for (vx_size d = 0; d < dim_num; ++d)
{
res += in_strides[d] * (index % in_dims[d]);
index /= in_dims[d];
}
return res;
}
static void ownUnpackFormat(
enum TestTensorDF fmt,
/*OUT*/ vx_enum * data_type,
/*OUT*/ vx_uint8 * fixed_point_position,
/*out*/ vx_size * sizeof_data_type)
{
switch(fmt)
{
case TT_Q78:
*data_type = VX_TYPE_INT16;
*fixed_point_position = Q78_FIXED_POINT_POSITION;
*sizeof_data_type = sizeof(vx_int16);
break;
case TT_U8:
*data_type = VX_TYPE_UINT8;
*fixed_point_position = 0;
*sizeof_data_type = sizeof(vx_uint8);
break;
default:
assert(0);
}
}
static void ownFillRandData(
enum TestTensorDF fmt,
uint64_t * rng,
size_t count,
/*OUT*/ void * data)
{
switch(fmt)
{
case TT_Q78:
for(size_t i = 0; i < count; ++i)
((vx_int16*)data)[i] = (vx_int16)CT_RNG_NEXT_INT(*rng, INT16_MIN, INT16_MAX+1);
break;
case TT_U8:
for(size_t i = 0; i < count; ++i)
((vx_uint8*)data)[i] = (vx_uint8)CT_RNG_NEXT_INT(*rng, 0, UINT8_MAX+1);
break;
default:
assert(0);
}
}
static void ownCheckBilateralFilterResult(
const void * in_ptr, const vx_size * in_dims, const vx_size * in_strides,
enum TestTensorDF fmt,
vx_size dim_num,
vx_size out_count,
int diameter,
float sigmaSpace,
float sigmaValues,
void * out_ptr, const vx_size * out_dims, const vx_size * out_strides)
{
vx_float32 tolerance = 0.0;
vx_float32 total_num = 0.0;
vx_float32 equal_num = 0.0;
vx_int32 radius = diameter/2;
vx_float32 color_weight_8[256];
vx_float32 color_weight_16[256*256];
vx_float32 sum = 0, wsum = 0, w = 0;
vx_float32 gauss_color_coeff = -0.5/(sigmaValues*sigmaValues);
vx_float32 gauss_space_coeff = -0.5/(sigmaSpace*sigmaSpace);
vx_float32 *space_weight = (vx_float32*)malloc((radius * 2 + 1) * sizeof(vx_float32));
for(vx_int32 i = 0; i < 256; i++)
{
color_weight_8[i] = (vx_float32)exp(i*i*gauss_color_coeff);
}
for(vx_int32 i = 0; i < 256*256; i++)
{
color_weight_16[i] = (vx_float32)exp(i*i*gauss_color_coeff);
}
for(vx_int32 i = -radius; i <= radius; i++ )
{
space_weight[i+radius] = (vx_float32)exp(i*i*gauss_space_coeff);
}
for (size_t index = radius; index < out_count-radius; ++index)
{
const size_t in_byte_offset = ownGetFlatByteOffset(index, dim_num, in_dims, in_strides);
const size_t out_byte_offset = ownGetFlatByteOffset(index, dim_num, out_dims, out_strides);
const char * in_b_ptr = (char*)in_ptr + in_byte_offset;
const char * out_b_ptr = (char*)out_ptr + out_byte_offset;
switch (fmt)
{
case TT_Q78:
{
const vx_int16 in = *(vx_int16*)in_b_ptr;
const vx_int16 out = *(vx_int16*)out_b_ptr;
int16_t ref;
sum = 0, wsum = 0;
for(vx_int32 j = -radius; j <= radius; j++)
{
vx_size nei_byte_offset = ownGetFlatByteOffset(index + j, dim_num, in_dims, in_strides);
const char *nei_b_ptr = (char*)in_ptr + nei_byte_offset;
const vx_int16 nei = *(vx_int16*)nei_b_ptr;
w = space_weight[j+radius]*color_weight_16[abs(nei - in)];
sum += nei*w;
wsum += w;
}
ref = (vx_int16)round(sum/wsum);
total_num += 1;
if (ref == out)
{
equal_num += 1;
}
}
break;
case TT_U8:
{
const vx_uint8 in = *(vx_uint8*)in_b_ptr;
const vx_uint8 out = *(vx_uint8*)out_b_ptr;
uint8_t ref;
sum = 0, wsum = 0;
for(vx_int32 j = -radius; j <= radius; j++)
{
vx_size nei_byte_offset = ownGetFlatByteOffset(index + j, dim_num, in_dims, in_strides);
const char *nei_b_ptr = (char*)in_ptr + nei_byte_offset;
const vx_uint8 nei = *(vx_uint8*)nei_b_ptr;
w = space_weight[j+radius]*color_weight_8[abs(nei - in)];
sum += nei*w;
wsum += w;
}
ref = (vx_uint8)round(sum/wsum);
total_num += 1;
if (ref == out)
{
equal_num += 1;
}
}
break;
default: assert(0);
}
}
tolerance = (equal_num / total_num);
free(space_weight);
ASSERT(tolerance >= MIN_TOLERANCE);
}
/****************************************************************************
* *
* Test vxBilateralFilterNode *
* *
***************************************************************************/
TESTCASE(BilateralFilter, CT_VXContext, ct_setup_vx_context, 0)
typedef struct
{
const char * name;
enum TestTensorDF src_fmt;
enum TestTensorDF dst_fmt;
int diameter;
float sigmaSpace;
float sigmaValues;
} test_bilateral_filter_op_arg;
TEST_WITH_ARG(BilateralFilter, testBilateralFilterOp, test_bilateral_filter_op_arg,
ARG("BILATERAL_FILTER_Q78", TT_Q78, TT_Q78, 5, 1, 1),
ARG("BILATERAL_FILTER_U8", TT_U8, TT_U8, 5, 1, 1),
)
{
const vx_context context = context_->vx_context_;
const enum TestTensorDF src_fmt = arg_->src_fmt;
const enum TestTensorDF dst_fmt = arg_->dst_fmt;
assert(src_fmt == TT_Q78 || src_fmt == TT_U8);
assert(dst_fmt == TT_Q78 || dst_fmt == TT_U8);
const int diameter = arg_->diameter;
const float sigmaSpace = arg_->sigmaSpace;
const float sigmaValues = arg_->sigmaValues;
vx_size max_dims = 0;
{
VX_CALL(vxQueryContext(context, VX_CONTEXT_MAX_TENSOR_DIMS, &max_dims, sizeof(max_dims)));
ASSERT(max_dims > 3);
if(!DEBUG_TEST_TENSOR_BEYOND_FOUR_DIMS) max_dims = 4; else max_dims = MIN(max_dims, MAX_TENSOR_DIMS);
}
uint64_t rng;
{
uint64_t * seed = &CT()->seed_;
ASSERT(!!seed);
CT_RNG_INIT(rng, *seed);
}
vx_enum src_data_type;
vx_enum dst_data_type;
vx_uint8 src_fixed_point_position;
vx_uint8 dst_fixed_point_position;
vx_size src_sizeof_data_type;
vx_size dst_sizeof_data_type;
ownUnpackFormat(src_fmt, &src_data_type, &src_fixed_point_position, &src_sizeof_data_type);
ownUnpackFormat(dst_fmt, &dst_data_type, &dst_fixed_point_position, &dst_sizeof_data_type);
size_t * const tensor_dims = malloc(sizeof(*tensor_dims) * max_dims);
size_t * const src_tensor_strides = malloc(sizeof(*src_tensor_strides) * max_dims);
size_t * const dst_tensor_strides = malloc(sizeof(*dst_tensor_strides) * max_dims);
ASSERT(tensor_dims && src_tensor_strides && dst_tensor_strides);
// The input data a vx_tensor. maximum 3 dimension and minimum 2.
for (vx_size dims = 2; dims <= max_dims; ++dims)
for (int iter = 0; iter < TEST_TENSOR_NUM_ITERATIONS; ++iter)
{
if (DEBUG_TEST_TENSOR_ENABLE_PRINTF)
{
printf("dims #: %zu,\titer #: %d\n", dims, iter);
fflush(stdout);
}
for (vx_size i = 0; i < dims; ++i)
{
tensor_dims[i] = (size_t)CT_RNG_NEXT_INT(rng, TEST_TENSOR_MIN_DIM_SZ, TEST_TENSOR_MAX_DIM_SZ+1);
src_tensor_strides[i] = i ? src_tensor_strides[i-1] * tensor_dims[i-1] : src_sizeof_data_type;
dst_tensor_strides[i] = i ? dst_tensor_strides[i-1] * tensor_dims[i-1] : dst_sizeof_data_type;
}
vx_tensor src_tensor = vxCreateTensor(context, dims, tensor_dims, src_data_type, src_fixed_point_position);
vx_tensor dst_tensor = vxCreateTensor(context, dims, tensor_dims, dst_data_type, dst_fixed_point_position);
ASSERT_VX_OBJECT(src_tensor, VX_TYPE_TENSOR);
ASSERT_VX_OBJECT(dst_tensor, VX_TYPE_TENSOR);
const size_t src_tensor_bytes = tensor_dims[dims-1] * src_tensor_strides[dims-1];
const size_t dst_tensor_bytes = tensor_dims[dims-1] * dst_tensor_strides[dims-1];
const size_t count = src_tensor_bytes / src_sizeof_data_type;
if (DEBUG_TEST_TENSOR_ENABLE_PRINTF)
{
printf("\tconfig: {\n");
printf("\t tensor_dims: { "); for (size_t i = 0; i < dims; ++i) { printf("%zu, ", tensor_dims[i]); } printf(" }, \n");
printf("\t }\n");
}
void * const src_data = malloc(src_tensor_bytes);
void * const dst_data = malloc(dst_tensor_bytes);
ASSERT(src_data && dst_data);
{
ownFillRandData(src_fmt, &rng, count, src_data);
vx_size view_start[MAX_TENSOR_DIMS] = { 0 };
VX_CALL(vxCopyTensorPatch(src_tensor, dims, view_start, tensor_dims, src_tensor_strides, src_data, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST));
}
{
vx_graph graph = vxCreateGraph(context);
ASSERT_VX_OBJECT(graph, VX_TYPE_GRAPH);
vx_node node = vxBilateralFilterNode(graph, src_tensor, diameter, sigmaSpace, sigmaValues, dst_tensor);
ASSERT_VX_OBJECT(node, VX_TYPE_NODE);
VX_CALL(vxReleaseNode(&node));
EXPECT_EQ_PTR(NULL, node);
VX_CALL(vxVerifyGraph(graph));
VX_CALL(vxProcessGraph(graph));
VX_CALL(vxReleaseGraph(&graph));
EXPECT_EQ_PTR(NULL, graph);
}
// Verify the reuslts
{
const size_t view_start[MAX_TENSOR_DIMS] = { 0 };
VX_CALL(vxCopyTensorPatch(dst_tensor, dims, view_start, tensor_dims, dst_tensor_strides, dst_data, VX_READ_ONLY, VX_MEMORY_TYPE_HOST));
ownCheckBilateralFilterResult(
src_data, tensor_dims, src_tensor_strides,
dst_fmt,
dims,
count,
diameter,
sigmaSpace,
sigmaValues,
dst_data, tensor_dims, dst_tensor_strides);
}
VX_CALL(vxReleaseTensor(&src_tensor));
VX_CALL(vxReleaseTensor(&dst_tensor));
EXPECT_EQ_PTR(NULL, src_tensor);
EXPECT_EQ_PTR(NULL, dst_tensor);
free(src_data);
free(dst_data);
}
free(tensor_dims);
free(src_tensor_strides);
free(dst_tensor_strides);
}
TEST(BilateralFilter, testNodeCreation)
{
const vx_context context = context_->vx_context_;
const enum TestTensorDF src_fmt = TT_Q78;
const enum TestTensorDF dst_fmt = TT_Q78;
const int diameter = 5;
const float sigmaSpace = 1;
const float sigmaValues = 1;
vx_size max_dims = 0;
{
VX_CALL(vxQueryContext(context, VX_CONTEXT_MAX_TENSOR_DIMS, &max_dims, sizeof(max_dims)));
ASSERT(max_dims > 3);
if(!DEBUG_TEST_TENSOR_BEYOND_FOUR_DIMS) max_dims = 4; else max_dims = MIN(max_dims, MAX_TENSOR_DIMS);
}
uint64_t rng;
{
uint64_t * seed = &CT()->seed_;
ASSERT(!!seed);
CT_RNG_INIT(rng, *seed);
}
vx_enum src_data_type;
vx_enum dst_data_type;
vx_uint8 src_fixed_point_position;
vx_uint8 dst_fixed_point_position;
vx_size src_sizeof_data_type;
vx_size dst_sizeof_data_type;
ownUnpackFormat(src_fmt, &src_data_type, &src_fixed_point_position, &src_sizeof_data_type);
ownUnpackFormat(dst_fmt, &dst_data_type, &dst_fixed_point_position, &dst_sizeof_data_type);
size_t * const tensor_dims = malloc(sizeof(*tensor_dims) * max_dims);
size_t * const src_tensor_strides = malloc(sizeof(*src_tensor_strides) * max_dims);
size_t * const dst_tensor_strides = malloc(sizeof(*dst_tensor_strides) * max_dims);
ASSERT(tensor_dims && src_tensor_strides && dst_tensor_strides);
// The input data a vx_tensor. maximum 3 dimension and minimum 2.
for (vx_size dims = 2; dims <= max_dims; ++dims)
for (int iter = 0; iter < TEST_TENSOR_NUM_ITERATIONS; ++iter)
{
if (DEBUG_TEST_TENSOR_ENABLE_PRINTF)
{
printf("dims #: %zu,\titer #: %d\n", dims, iter);
fflush(stdout);
}
for (vx_size i = 0; i < dims; ++i)
{
tensor_dims[i] = (size_t)CT_RNG_NEXT_INT(rng, TEST_TENSOR_MIN_DIM_SZ, TEST_TENSOR_MAX_DIM_SZ+1);
src_tensor_strides[i] = i ? src_tensor_strides[i-1] * tensor_dims[i-1] : src_sizeof_data_type;
dst_tensor_strides[i] = i ? dst_tensor_strides[i-1] * tensor_dims[i-1] : dst_sizeof_data_type;
}
vx_tensor src_tensor = vxCreateTensor(context, dims, tensor_dims, src_data_type, src_fixed_point_position);
vx_tensor dst_tensor = vxCreateTensor(context, dims, tensor_dims, dst_data_type, dst_fixed_point_position);
ASSERT_VX_OBJECT(src_tensor, VX_TYPE_TENSOR);
ASSERT_VX_OBJECT(dst_tensor, VX_TYPE_TENSOR);
const size_t src_tensor_bytes = tensor_dims[dims-1] * src_tensor_strides[dims-1];
const size_t dst_tensor_bytes = tensor_dims[dims-1] * dst_tensor_strides[dims-1];
const size_t count = src_tensor_bytes / src_sizeof_data_type;
if (DEBUG_TEST_TENSOR_ENABLE_PRINTF)
{
printf("\tconfig: {\n");
printf("\t tensor_dims: { "); for (size_t i = 0; i < dims; ++i) { printf("%zu, ", tensor_dims[i]); } printf(" }, \n");
printf("\t }\n");
}
void * const src_data = malloc(src_tensor_bytes);
void * const dst_data = malloc(dst_tensor_bytes);
ASSERT(src_data && dst_data);
{
ownFillRandData(src_fmt, &rng, count, src_data);
vx_size view_start[MAX_TENSOR_DIMS] = { 0 };
VX_CALL(vxCopyTensorPatch(src_tensor, dims, view_start, tensor_dims, src_tensor_strides, src_data, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST));
}
{
vx_graph graph = vxCreateGraph(context);
ASSERT_VX_OBJECT(graph, VX_TYPE_GRAPH);
vx_node node = vxBilateralFilterNode(graph, src_tensor, diameter, sigmaSpace, sigmaValues, dst_tensor);
ASSERT_VX_OBJECT(node, VX_TYPE_NODE);
VX_CALL(vxReleaseNode(&node));
EXPECT_EQ_PTR(NULL, node);
VX_CALL(vxVerifyGraph(graph));
VX_CALL(vxReleaseGraph(&graph));
EXPECT_EQ_PTR(NULL, graph);
}
VX_CALL(vxReleaseTensor(&src_tensor));
VX_CALL(vxReleaseTensor(&dst_tensor));
EXPECT_EQ_PTR(NULL, src_tensor);
EXPECT_EQ_PTR(NULL, dst_tensor);
free(src_data);
free(dst_data);
}
free(tensor_dims);
free(src_tensor_strides);
free(dst_tensor_strides);
}
TESTCASE_TESTS(BilateralFilter,
testNodeCreation,
testBilateralFilterOp
);