blob: e2b0f6831eb55642ff927b73594151b973b0f13f [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.h>
#include <VX/vxu.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
static void reference_histogram(CT_Image src, int32_t* hist, int nbins, int offset, int range)
{
int i, hist0[256];
uint32_t x, y, width = src->width, height = src->height, stride;
ASSERT(src);
ASSERT(src->format == VX_DF_IMAGE_U8);
ASSERT(src->width > 0 && src->height > 0);
stride = ct_stride_bytes(src);
for( i = 0; i < 256; i++ )
hist0[i] = 0;
for( y = 0; y < height; y++ )
{
const uint8_t* ptr = src->data.y + y*stride;
for( x = 0; x < width; x++ )
hist0[ptr[x]]++;
}
for( i = 0; i < nbins; i++ )
hist[i] = 0;
for( i = offset; i < offset + range; i++ )
{
int j = (i - offset)*nbins/range;
hist[j] = (int32_t)(hist[j] + hist0[i]);
}
}
static vx_uint32 reference_window(vx_uint32 range, vx_size nbins)
{
vx_uint32 test_window = (vx_uint32)(range / nbins);
if (test_window*nbins == range)
return test_window;
else
return 0;
}
static void reverse_histogram(int nbins, int32_t hist[])
{
int i, j;
for (i = 0, j = nbins-1; i < j; ++i, --j)
{
int32_t a = hist[i];
int32_t b = hist[j];
hist[i] = b;
hist[j] = a;
}
}
TESTCASE(Histogram, CT_VXContext, ct_setup_vx_context, 0)
typedef struct {
const char* name;
int mode;
vx_df_image format;
} format_arg;
#define MAX_BINS 256
#define HIST_TEST_CASE(imm, tp) \
{#imm "/" #tp, CT_##imm##_MODE, VX_DF_IMAGE_##tp}
#define COMPARE_HISTOGRAMS(_equal, _hist0, _hist1) \
do { \
_equal = 1; \
int _i; \
for( _i = 0; _i < nbins; _i++ ) \
{ \
if( _hist0[_i] != _hist1[_i] ) \
{ \
_equal = 0; \
CT_RecordFailureAtFormat("Test case %d. width=%d, height=%d, nbins=%d, offset=%d, range=%d\n" \
"\tExpected: %s[%d]=%d\n" \
"\tActual: %s[%d]=%d\n", \
__FUNCTION__, __FILE__, __LINE__, \
iter, width, height, nbins, offset, range, \
#_hist1, _i, _hist0[_i], #_hist1, _i, _hist1[_i]); \
break; \
} \
} \
} while(0)
TEST_WITH_ARG(Histogram, testOnRandom, format_arg,
HIST_TEST_CASE(Immediate, U8),
HIST_TEST_CASE(Graph, U8),
)
{
int format = arg_->format;
int mode = arg_->mode;
vx_image src;
CT_Image src0;
vx_context context = context_->vx_context_;
vx_graph graph = 0;
vx_node node = 0;
vx_distribution dist1;
int iter, niters = 100;
uint64_t rng;
int a = 0, b = 256;
int32_t hist0[MAX_BINS];
int32_t hist1[MAX_BINS];
const vx_enum mem_type = VX_MEMORY_TYPE_HOST;
const vx_bitfield flags = 0;
rng = CT()->seed_;
for( iter = 0; iter < niters; iter++ )
{
int width, height;
int val0 = CT_RNG_NEXT_INT(rng, 0, (MAX_BINS-1)), val1 = CT_RNG_NEXT_INT(rng, 0, (MAX_BINS-1));
int offset = CT_MIN(val0, val1), range = CT_MAX(val0, val1) - offset + 1;
int nbins = CT_RNG_NEXT_INT(rng, 1, range+1);
if( ct_check_any_size() )
{
width = ct_roundf(ct_log_rng(&rng, 0, 10));
height = ct_roundf(ct_log_rng(&rng, 0, 10));
width = CT_MAX(width, 1);
height = CT_MAX(height, 1);
}
else
{
width = 640;
height = 480;
}
ct_update_progress(iter, niters);
if( iter % 30 == 0 )
{
offset = 0;
range = 256;
nbins = 1 << CT_RNG_NEXT_INT(rng, 0, 9);
}
else
{
// new definition of histogram formula doesn't require
// that the range will be divisible by the number of bins
/*
// make sure the range is divisible by the number of bins,
// otherwise the histogram will be statistically unbalanced
range = (range/nbins)*nbins;
*/
}
ASSERT_NO_FAILURE(src0 = ct_allocate_ct_image_random(width, height, format, &rng, a, b));
ASSERT_NO_FAILURE(reference_histogram(src0, hist0, nbins, offset, range));
src = ct_image_to_vx_image(src0, context);
ASSERT_VX_OBJECT(src, VX_TYPE_IMAGE);
dist1 = vxCreateDistribution(context, nbins, offset, range);
ASSERT_VX_OBJECT(dist1, VX_TYPE_DISTRIBUTION);
if( mode == CT_Immediate_MODE )
{
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxuHistogram(context, src, dist1));
}
else
{
graph = vxCreateGraph(context);
ASSERT_VX_OBJECT(graph, VX_TYPE_GRAPH);
node = vxHistogramNode(graph, src, dist1);
ASSERT_VX_OBJECT(node, VX_TYPE_NODE);
VX_CALL(vxVerifyGraph(graph));
VX_CALL(vxProcessGraph(graph));
}
/* test for copy - read distribution */
{
int equal = 0;
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxCopyDistribution(dist1, hist1, VX_READ_ONLY, mem_type));
COMPARE_HISTOGRAMS(equal, hist0, hist1);
if( !equal )
CT_FAIL("check for vxCopyDistribution(read dist1) failed\n");
}
{
/* smoke tests for query distribution attributes */
vx_size attr_dims = 0;
vx_int32 attr_offset = 0;
vx_uint32 attr_range = 0;
vx_size attr_bins = 0;
vx_uint32 attr_window = 0;
vx_size attr_size = 0;
VX_CALL(vxQueryDistribution(dist1, VX_DISTRIBUTION_DIMENSIONS, &attr_dims, sizeof(attr_dims)));
if (1 != attr_dims)
CT_FAIL("check for query distribution attribute VX_DISTRIBUTION_DIMENSIONS failed\n");
VX_CALL(vxQueryDistribution(dist1, VX_DISTRIBUTION_OFFSET, &attr_offset, sizeof(attr_offset)));
if (attr_offset != offset)
CT_FAIL("check for query distribution attribute VX_DISTRIBUTION_OFFSET failed\n");
VX_CALL(vxQueryDistribution(dist1, VX_DISTRIBUTION_RANGE, &attr_range, sizeof(attr_range)));
if (attr_range != range)
CT_FAIL("check for query distribution attribute VX_DISTRIBUTION_RANGE failed\n");
VX_CALL(vxQueryDistribution(dist1, VX_DISTRIBUTION_BINS, &attr_bins, sizeof(attr_bins)));
if (attr_bins != nbins)
CT_FAIL("check for query distribution attribute VX_DISTRIBUTION_BINS failed\n");
VX_CALL(vxQueryDistribution(dist1, VX_DISTRIBUTION_WINDOW, &attr_window, sizeof(attr_window)));
/*Tthe attribute is specified as valid only when the range is a multiple of nbins,
* in other cases, its value shouldn't be checked */
if (((range % nbins) == 0) && (attr_window != reference_window(range, nbins)))
CT_FAIL("check for query distribution attribute VX_DISTRIBUTION_WINDOW failed\n");
VX_CALL(vxQueryDistribution(dist1, VX_DISTRIBUTION_SIZE, &attr_size, sizeof(attr_size)));
if (attr_size < nbins*sizeof(hist1[0]))
CT_FAIL("check for query distribution attribute VX_DISTRIBUTION_SIZE failed\n");
}
/* test for copy - write distribution */
{
vx_distribution dist2;
int32_t hist2[MAX_BINS];
int equal = 0;
dist2 = vxCreateDistribution(context, nbins, offset, range);
ASSERT_VX_OBJECT(dist2, VX_TYPE_DISTRIBUTION);
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxCopyDistribution(dist2, hist1, VX_WRITE_ONLY, mem_type));
COMPARE_HISTOGRAMS(equal, hist0, hist1);
if( !equal )
CT_FAIL("check for vxCopyDistribution(write dist2) failed\n");
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxCopyDistribution(dist2, hist2, VX_READ_ONLY, mem_type));
COMPARE_HISTOGRAMS(equal, hist0, hist2);
if( !equal )
CT_FAIL("check for vxCopyDistribution(read dist2) failed\n");
VX_CALL(vxReleaseDistribution(&dist2));
}
/* test for map/unmap - read/write distribution */
{
vx_map_id map1;
int32_t* hptr1 = NULL;
int32_t hist1r[MAX_BINS];
int equal = 0;
reverse_histogram(nbins, hist1);
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxMapDistribution(dist1, &map1, (void*)&hptr1, VX_WRITE_ONLY, mem_type, flags));
memcpy(hptr1, hist1, nbins*sizeof(hist1[0]));
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxUnmapDistribution(dist1, map1));
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxCopyDistribution(dist1, hist1r, VX_READ_ONLY, mem_type));
COMPARE_HISTOGRAMS(equal, hist1, hist1r);
if( !equal )
CT_FAIL("check for vxMapDistribution/vxUnmapDistribution(write dist1) failed\n");
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxMapDistribution(dist1, &map1, (void*)&hptr1, VX_READ_ONLY, mem_type, flags));
COMPARE_HISTOGRAMS(equal, hist1, hptr1);
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxUnmapDistribution(dist1, map1));
if( !equal )
CT_FAIL("check for vxMapDistribution/vxUnmapDistribution(read dist1) failed\n");
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxMapDistribution(dist1, &map1, (void*)&hptr1, VX_READ_AND_WRITE, mem_type, flags));
COMPARE_HISTOGRAMS(equal, hist1, hptr1);
reverse_histogram(nbins, hist1);
memcpy(hist1, hptr1, nbins*sizeof(hist1[0]));
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxUnmapDistribution(dist1, map1));
ASSERT_EQ_VX_STATUS(VX_SUCCESS, vxCopyDistribution(dist1, hist1r, VX_READ_ONLY, mem_type));
COMPARE_HISTOGRAMS(equal, hist1, hist1r);
if( !equal )
CT_FAIL("check for vxMapDistribution/vxUnmapDistribution(read-write dist1) failed\n");
}
VX_CALL(vxReleaseImage(&src));
VX_CALL(vxReleaseDistribution(&dist1));
if(node)
VX_CALL(vxReleaseNode(&node));
if(graph)
VX_CALL(vxReleaseGraph(&graph));
ASSERT(node == 0 && graph == 0);
CT_CollectGarbage(CT_GC_IMAGE);
}
}
TESTCASE_TESTS(Histogram, testOnRandom)