| /* |
| |
| * 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 <math.h> |
| #include <float.h> |
| #include <string.h> |
| #include <VX/vx.h> |
| #include <VX/vxu.h> |
| |
| #include "test_engine/test.h" |
| |
| static CT_Image own_read_image_8u(const char* fileName, int width, int height) |
| { |
| CT_Image image = NULL; |
| |
| image = ct_read_image(fileName, 1); |
| ASSERT_(return 0, image); |
| ASSERT_(return 0, image->format == VX_DF_IMAGE_U8); |
| |
| return image; |
| } |
| |
| static CT_Image own_generate_random(const char* fileName, int width, int height) |
| { |
| CT_Image image; |
| |
| ASSERT_NO_FAILURE_(return 0, |
| image = ct_allocate_ct_image_random(width, height, VX_DF_IMAGE_U8, &CT()->seed_, 0, 256)); |
| |
| return image; |
| } |
| |
| |
| TESTCASE(WarpPerspective, CT_VXContext, ct_setup_vx_context, 0) |
| |
| TEST(WarpPerspective, testNodeCreation) |
| { |
| vx_context context = context_->vx_context_; |
| vx_image input = 0, output = 0; |
| vx_matrix matrix = 0; |
| vx_graph graph = 0; |
| vx_node node = 0; |
| vx_enum type = VX_INTERPOLATION_NEAREST_NEIGHBOR; |
| const vx_enum matrix_type = VX_TYPE_FLOAT32; |
| const vx_size matrix_rows = 3; |
| const vx_size matrix_cols = 3; |
| const vx_size matrix_data_size = 4 * matrix_rows * matrix_cols; |
| |
| ASSERT_VX_OBJECT(input = vxCreateImage(context, 128, 128, VX_DF_IMAGE_U8), VX_TYPE_IMAGE); |
| ASSERT_VX_OBJECT(output = vxCreateImage(context, 128, 128, VX_DF_IMAGE_U8), VX_TYPE_IMAGE); |
| ASSERT_VX_OBJECT(matrix = vxCreateMatrix(context, matrix_type, matrix_cols, matrix_rows), VX_TYPE_MATRIX); |
| |
| { |
| vx_enum ch_matrix_type; |
| vx_size ch_matrix_rows, ch_matrix_cols, ch_data_size; |
| |
| VX_CALL(vxQueryMatrix(matrix, VX_MATRIX_TYPE, &ch_matrix_type, sizeof(ch_matrix_type))); |
| if (matrix_type != ch_matrix_type) |
| { |
| CT_FAIL("check for Matrix attribute VX_MATRIX_TYPE failed\n"); |
| } |
| VX_CALL(vxQueryMatrix(matrix, VX_MATRIX_ROWS, &ch_matrix_rows, sizeof(ch_matrix_rows))); |
| if (matrix_rows != ch_matrix_rows) |
| { |
| CT_FAIL("check for Matrix attribute VX_MATRIX_ROWS failed\n"); |
| } |
| VX_CALL(vxQueryMatrix(matrix, VX_MATRIX_COLUMNS, &ch_matrix_cols, sizeof(ch_matrix_cols))); |
| if (matrix_cols != ch_matrix_cols) |
| { |
| CT_FAIL("check for Matrix attribute VX_MATRIX_COLUMNS failed\n"); |
| } |
| VX_CALL(vxQueryMatrix(matrix, VX_MATRIX_SIZE, &ch_data_size, sizeof(ch_data_size))); |
| if (matrix_data_size > ch_data_size) |
| { |
| CT_FAIL("check for Matrix attribute VX_MATRIX_SIZE failed\n"); |
| } |
| } |
| |
| graph = vxCreateGraph(context); |
| ASSERT_VX_OBJECT(graph, VX_TYPE_GRAPH); |
| |
| ASSERT_VX_OBJECT(node = vxWarpPerspectiveNode(graph, input, matrix, type, output), VX_TYPE_NODE); |
| |
| VX_CALL(vxVerifyGraph(graph)); |
| |
| VX_CALL(vxReleaseNode(&node)); |
| VX_CALL(vxReleaseGraph(&graph)); |
| VX_CALL(vxReleaseMatrix(&matrix)); |
| VX_CALL(vxReleaseImage(&output)); |
| VX_CALL(vxReleaseImage(&input)); |
| |
| ASSERT(node == 0); |
| ASSERT(graph == 0); |
| ASSERT(matrix == 0); |
| ASSERT(output == 0); |
| ASSERT(input == 0); |
| } |
| |
| |
| enum CT_PerspectiveMatrixType { |
| VX_MATRIX_IDENT = 0, |
| VX_MATRIX_SCALE, |
| VX_MATRIX_SCALE_ROTATE, |
| VX_MATRIX_RANDOM |
| }; |
| |
| #define VX_NN_AREA_SIZE 1.5 |
| #define VX_BILINEAR_TOLERANCE 1 |
| |
| static CT_Image warp_perspective_read_image_8u(const char* fileName, int width, int height) |
| { |
| CT_Image image = NULL; |
| |
| image = ct_read_image(fileName, 1); |
| ASSERT_(return 0, image); |
| ASSERT_(return 0, image->format == VX_DF_IMAGE_U8); |
| |
| return image; |
| } |
| |
| static CT_Image warp_perspective_generate_random(const char* fileName, int width, int height) |
| { |
| CT_Image image; |
| |
| ASSERT_NO_FAILURE_(return 0, |
| image = ct_allocate_ct_image_random(width, height, VX_DF_IMAGE_U8, &CT()->seed_, 0, 256)); |
| |
| return image; |
| } |
| |
| #define RND_FLT(low, high) (vx_float32)CT_RNG_NEXT_REAL(CT()->seed_, low, high); |
| static void warp_perspective_generate_matrix(vx_float32 *m, int src_width, int src_height, int dst_width, int dst_height, int type) |
| { |
| vx_float32 mat[3][3]; |
| vx_float32 angle, scale_x, scale_y, cos_a, sin_a; |
| if (VX_MATRIX_IDENT == type) |
| { |
| mat[0][0] = 1.f; |
| mat[0][1] = 0.f; |
| mat[0][2] = 0.f; |
| |
| mat[1][0] = 0.f; |
| mat[1][1] = 1.f; |
| mat[1][2] = 0.f; |
| |
| mat[2][0] = 0.f; |
| mat[2][1] = 0.f; |
| mat[2][2] = 1.f; |
| } |
| else if (VX_MATRIX_SCALE == type) |
| { |
| scale_x = src_width / (vx_float32)dst_width; |
| scale_y = src_height / (vx_float32)dst_height; |
| |
| mat[0][0] = scale_x; |
| mat[0][1] = 0.f; |
| mat[0][2] = 0.f; |
| |
| mat[1][0] = 0.f; |
| mat[1][1] = scale_y; |
| mat[1][2] = 0.f; |
| |
| mat[2][0] = 0.f; |
| mat[2][1] = 0.f; |
| mat[2][2] = 1.f; |
| } |
| else if (VX_MATRIX_SCALE_ROTATE == type) |
| { |
| angle = M_PIF / RND_FLT(3.f, 6.f); |
| scale_x = src_width / (vx_float32)dst_width; |
| scale_y = src_height / (vx_float32)dst_height; |
| cos_a = cosf(angle); |
| sin_a = sinf(angle); |
| |
| mat[0][0] = cos_a * scale_x; |
| mat[0][1] = sin_a * scale_y; |
| mat[0][2] = 0.f; |
| |
| mat[1][0] = -sin_a * scale_x; |
| mat[1][1] = cos_a * scale_y; |
| mat[1][2] = 0.f; |
| |
| mat[2][0] = 0.f; |
| mat[2][1] = 0.f; |
| mat[2][2] = 1.f; |
| } |
| else// if (VX_MATRIX_RANDOM == type) |
| { |
| angle = M_PIF / RND_FLT(3.f, 6.f); |
| scale_x = src_width / (vx_float32)dst_width; |
| scale_y = src_height / (vx_float32)dst_height; |
| cos_a = cosf(angle); |
| sin_a = sinf(angle); |
| |
| mat[0][0] = cos_a * RND_FLT(scale_x / 2.f, scale_x); |
| mat[0][1] = sin_a * RND_FLT(scale_y / 2.f, scale_y); |
| mat[0][2] = RND_FLT(0.f, 0.1f); |
| |
| mat[1][0] = -sin_a * RND_FLT(scale_y / 2.f, scale_y); |
| mat[1][1] = cos_a * RND_FLT(scale_x / 2.f, scale_x); |
| mat[1][2] = RND_FLT(0.f, 0.1f); |
| |
| mat[2][0] = src_width / 5.f * RND_FLT(-1.f, 1.f); |
| mat[2][1] = src_height / 5.f * RND_FLT(-1.f, 1.f); |
| mat[2][2] = 1.f; |
| } |
| memcpy(m, mat, sizeof(mat)); |
| } |
| |
| static vx_matrix warp_perspective_create_matrix(vx_context context, vx_float32 *m) |
| { |
| vx_matrix matrix; |
| matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 3, 3); |
| if (vxGetStatus((vx_reference)matrix) == VX_SUCCESS) |
| { |
| if (VX_SUCCESS != vxCopyMatrix(matrix, m, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST)) |
| { |
| VX_CALL_(return 0, vxReleaseMatrix(&matrix)); |
| } |
| } |
| return matrix; |
| } |
| |
| static int warp_perspective_check_pixel(CT_Image input, CT_Image output, int x, int y, vx_enum interp_type, vx_border_t border, vx_float32 *m) |
| { |
| vx_float64 x0, y0, z0, xlower, ylower, s, t; |
| vx_int32 xi, yi; |
| int candidate; |
| vx_uint8 res = *CT_IMAGE_DATA_PTR_8U(output, x, y); |
| |
| x0 = (vx_float64)m[3 * 0 + 0] * (vx_float64)x + (vx_float64)m[3 * 1 + 0] * (vx_float64)y + (vx_float64)m[3 * 2 + 0]; |
| y0 = (vx_float64)m[3 * 0 + 1] * (vx_float64)x + (vx_float64)m[3 * 1 + 1] * (vx_float64)y + (vx_float64)m[3 * 2 + 1]; |
| z0 = (vx_float64)m[3 * 0 + 2] * (vx_float64)x + (vx_float64)m[3 * 1 + 2] * (vx_float64)y + (vx_float64)m[3 * 2 + 2]; |
| if (fabs(z0) < DBL_MIN) |
| return 0; |
| |
| x0 = x0 / z0; |
| y0 = y0 / z0; |
| if (VX_INTERPOLATION_NEAREST_NEIGHBOR == interp_type) |
| { |
| for (yi = (vx_int32)ceil(y0 - VX_NN_AREA_SIZE); (vx_float64)yi <= y0 + VX_NN_AREA_SIZE; yi++) |
| { |
| for (xi = (vx_int32)ceil(x0 - VX_NN_AREA_SIZE); (vx_float64)xi <= x0 + VX_NN_AREA_SIZE; xi++) |
| { |
| if (0 <= xi && 0 <= yi && xi < (vx_int32)input->width && yi < (vx_int32)input->height) |
| { |
| candidate = *CT_IMAGE_DATA_PTR_8U(input, xi, yi); |
| } |
| else if (VX_BORDER_CONSTANT == border.mode) |
| { |
| candidate = border.constant_value.U8; |
| } |
| else |
| { |
| candidate = -1; |
| } |
| if (candidate == -1 || candidate == res) |
| return 0; |
| } |
| } |
| CT_FAIL_(return 1, "Check failed for pixel (%d, %d): %d", x, y, (int)res); |
| } |
| else if (VX_INTERPOLATION_BILINEAR == interp_type) |
| { |
| xlower = floor(x0); |
| ylower = floor(y0); |
| |
| s = x0 - xlower; |
| t = y0 - ylower; |
| |
| xi = (vx_int32)xlower; |
| yi = (vx_int32)ylower; |
| |
| candidate = -1; |
| if (VX_BORDER_UNDEFINED == border.mode) |
| { |
| if (xi >= 0 && yi >= 0 && xi < (vx_int32)input->width - 1 && yi < (vx_int32)input->height - 1) |
| { |
| candidate = (int)((1. - s) * (1. - t) * (vx_float64) *CT_IMAGE_DATA_PTR_8U(input, xi , yi ) + |
| s * (1. - t) * (vx_float64) *CT_IMAGE_DATA_PTR_8U(input, xi + 1, yi ) + |
| (1. - s) * t * (vx_float64) *CT_IMAGE_DATA_PTR_8U(input, xi , yi + 1) + |
| s * t * (vx_float64) *CT_IMAGE_DATA_PTR_8U(input, xi + 1, yi + 1)); |
| } |
| } |
| else if (VX_BORDER_CONSTANT == border.mode) |
| { |
| candidate = (int)((1. - s) * (1. - t) * (vx_float32)CT_IMAGE_DATA_CONSTANT_8U(input, xi , yi , border.constant_value.U8) + |
| s * (1. - t) * (vx_float32)CT_IMAGE_DATA_CONSTANT_8U(input, xi + 1, yi , border.constant_value.U8) + |
| (1. - s) * t * (vx_float32)CT_IMAGE_DATA_CONSTANT_8U(input, xi , yi + 1, border.constant_value.U8) + |
| s * t * (vx_float32)CT_IMAGE_DATA_CONSTANT_8U(input, xi + 1, yi + 1, border.constant_value.U8)); |
| } |
| if (candidate == -1 || (abs(candidate - res) <= VX_BILINEAR_TOLERANCE)) |
| return 0; |
| return 1; |
| } |
| CT_FAIL_(return 1, "Interpolation type undefined"); |
| } |
| |
| static void warp_perspective_validate(CT_Image input, CT_Image output, vx_enum interp_type, vx_border_t border, vx_float32 *m) |
| { |
| vx_uint32 err_count = 0; |
| |
| CT_FILL_IMAGE_8U(, output, |
| { |
| ASSERT_NO_FAILURE(err_count += warp_perspective_check_pixel(input, output, x, y, interp_type, border, m)); |
| }); |
| if (10 * err_count > output->width * output->height) |
| CT_FAIL_(return, "Check failed for %d pixels", err_count); |
| } |
| |
| static void warp_perspective_check(CT_Image input, CT_Image output, vx_enum interp_type, vx_border_t border, vx_float32* m) |
| { |
| ASSERT(input && output); |
| ASSERT( (interp_type == VX_INTERPOLATION_NEAREST_NEIGHBOR) || |
| (interp_type == VX_INTERPOLATION_BILINEAR)); |
| |
| ASSERT( (border.mode == VX_BORDER_UNDEFINED) || |
| (border.mode == VX_BORDER_CONSTANT)); |
| |
| warp_perspective_validate(input, output, interp_type, border, m); |
| if (CT_HasFailure()) |
| { |
| printf("=== INPUT ===\n"); |
| ct_dump_image_info(input); |
| printf("=== OUTPUT ===\n"); |
| ct_dump_image_info(output); |
| printf("Matrix:\n%g %g %g\n%g %g %g\n%g %g %g\n", |
| m[0], m[3], m[6], |
| m[1], m[4], m[7], |
| m[2], m[5], m[8]); |
| } |
| } |
| |
| typedef struct { |
| const char* testName; |
| CT_Image(*generator)(const char* fileName, int width, int height); |
| const char* fileName; |
| int src_width, src_height; |
| int width, height; |
| vx_border_t border; |
| vx_enum interp_type; |
| int matrix_type; |
| } Arg; |
| |
| #define ADD_VX_BORDERS_WARP_PERSPECTIVE(testArgName, nextmacro, ...) \ |
| CT_EXPAND(nextmacro(testArgName "/VX_BORDER_UNDEFINED", __VA_ARGS__, { VX_BORDER_UNDEFINED, {{ 0 }} })), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_BORDER_CONSTANT=0", __VA_ARGS__, { VX_BORDER_CONSTANT, {{ 0 }} })), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_BORDER_CONSTANT=1", __VA_ARGS__, { VX_BORDER_CONSTANT, {{ 1 }} })), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_BORDER_CONSTANT=127", __VA_ARGS__, { VX_BORDER_CONSTANT, {{ 127 }} })), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_BORDER_CONSTANT=255", __VA_ARGS__, { VX_BORDER_CONSTANT, {{ 255 }} })) |
| |
| #define ADD_VX_INTERP_TYPE_WARP_PERSPECTIVE(testArgName, nextmacro, ...) \ |
| CT_EXPAND(nextmacro(testArgName "/VX_INTERPOLATION_NEAREST_NEIGHBOR", __VA_ARGS__, VX_INTERPOLATION_NEAREST_NEIGHBOR)), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_INTERPOLATION_BILINEAR", __VA_ARGS__, VX_INTERPOLATION_BILINEAR )) |
| |
| #define ADD_VX_INTERPOLATION_TYPE_NEAREST_NEIGHBOR(testArgName, nextmacro, ...) \ |
| CT_EXPAND(nextmacro(testArgName "/VX_INTERPOLATION_NEAREST_NEIGHBOR", __VA_ARGS__, VX_INTERPOLATION_NEAREST_NEIGHBOR)) |
| |
| #define ADD_VX_MATRIX_PARAM_WARP_PERSPECTIVE(testArgName, nextmacro, ...) \ |
| CT_EXPAND(nextmacro(testArgName "/VX_MATRIX_IDENT", __VA_ARGS__, VX_MATRIX_IDENT)), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_MATRIX_SCALE", __VA_ARGS__, VX_MATRIX_SCALE)), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_MATRIX_SCALE_ROTATE", __VA_ARGS__, VX_MATRIX_SCALE_ROTATE)), \ |
| CT_EXPAND(nextmacro(testArgName "/VX_MATRIX_RANDOM", __VA_ARGS__, VX_MATRIX_RANDOM)) |
| |
| |
| #define PARAMETERS \ |
| CT_GENERATE_PARAMETERS("random", ADD_SIZE_SMALL_SET, ADD_VX_BORDERS_WARP_PERSPECTIVE, ADD_VX_INTERPOLATION_TYPE_NEAREST_NEIGHBOR, ADD_VX_MATRIX_PARAM_WARP_PERSPECTIVE, ARG, own_generate_random, NULL, 128, 128), \ |
| CT_GENERATE_PARAMETERS("lena", ADD_SIZE_SMALL_SET, ADD_VX_BORDERS_WARP_PERSPECTIVE, ADD_VX_INTERP_TYPE_WARP_PERSPECTIVE, ADD_VX_MATRIX_PARAM_WARP_PERSPECTIVE, ARG, own_read_image_8u, "lena.bmp", 0, 0) |
| |
| |
| TEST_WITH_ARG(WarpPerspective, testGraphProcessing, Arg, |
| PARAMETERS |
| ) |
| { |
| vx_context context = context_->vx_context_; |
| vx_graph graph = 0; |
| vx_node node = 0; |
| vx_image input_image = 0, output_image = 0; |
| vx_matrix matrix = 0; |
| vx_float32 m[9]; |
| |
| CT_Image input = NULL, output = NULL; |
| |
| vx_border_t border = arg_->border; |
| |
| ASSERT_NO_FAILURE(input = arg_->generator(arg_->fileName, arg_->src_width, arg_->src_height)); |
| ASSERT_NO_FAILURE(output = ct_allocate_image(arg_->width, arg_->height, VX_DF_IMAGE_U8)); |
| |
| ASSERT_VX_OBJECT(input_image = ct_image_to_vx_image(input, context), VX_TYPE_IMAGE); |
| ASSERT_VX_OBJECT(output_image = ct_image_to_vx_image(output, context), VX_TYPE_IMAGE); |
| ASSERT_NO_FAILURE(warp_perspective_generate_matrix(m, input->width, input->height, arg_->width, arg_->height, arg_->matrix_type)); |
| ASSERT_VX_OBJECT(matrix = warp_perspective_create_matrix(context, m), VX_TYPE_MATRIX); |
| |
| ASSERT_VX_OBJECT(graph = vxCreateGraph(context), VX_TYPE_GRAPH); |
| |
| ASSERT_VX_OBJECT(node = vxWarpPerspectiveNode(graph, input_image, matrix, arg_->interp_type, output_image), VX_TYPE_NODE); |
| |
| VX_CALL(vxSetNodeAttribute(node, VX_NODE_BORDER, &border, sizeof(border))); |
| |
| VX_CALL(vxVerifyGraph(graph)); |
| VX_CALL(vxProcessGraph(graph)); |
| |
| ASSERT_NO_FAILURE(output = ct_image_from_vx_image(output_image)); |
| ASSERT_NO_FAILURE(warp_perspective_check(input, output, arg_->interp_type, arg_->border, m)); |
| |
| VX_CALL(vxReleaseNode(&node)); |
| VX_CALL(vxReleaseGraph(&graph)); |
| VX_CALL(vxReleaseMatrix(&matrix)); |
| VX_CALL(vxReleaseImage(&output_image)); |
| VX_CALL(vxReleaseImage(&input_image)); |
| |
| ASSERT(node == 0); |
| ASSERT(graph == 0); |
| ASSERT(matrix == 0); |
| ASSERT(output_image == 0); |
| ASSERT(input_image == 0); |
| } |
| |
| TEST_WITH_ARG(WarpPerspective, testImmediateProcessing, Arg, |
| PARAMETERS |
| ) |
| { |
| vx_context context = context_->vx_context_; |
| vx_image input_image = 0, output_image = 0; |
| vx_matrix matrix = 0; |
| vx_float32 m[9]; |
| |
| CT_Image input = NULL, output = NULL; |
| |
| vx_border_t border = arg_->border; |
| |
| ASSERT_NO_FAILURE(input = arg_->generator(arg_->fileName, arg_->src_width, arg_->src_height)); |
| ASSERT_NO_FAILURE(output = ct_allocate_image(arg_->width, arg_->height, VX_DF_IMAGE_U8)); |
| |
| ASSERT_VX_OBJECT(input_image = ct_image_to_vx_image(input, context), VX_TYPE_IMAGE); |
| ASSERT_VX_OBJECT(output_image = ct_image_to_vx_image(output, context), VX_TYPE_IMAGE); |
| ASSERT_NO_FAILURE(warp_perspective_generate_matrix(m, input->width, input->height, arg_->width, arg_->height, arg_->matrix_type)); |
| ASSERT_VX_OBJECT(matrix = warp_perspective_create_matrix(context, m), VX_TYPE_MATRIX); |
| |
| VX_CALL(vxSetContextAttribute(context, VX_CONTEXT_IMMEDIATE_BORDER, &border, sizeof(border))); |
| |
| VX_CALL(vxuWarpPerspective(context, input_image, matrix, arg_->interp_type, output_image)); |
| |
| ASSERT_NO_FAILURE(output = ct_image_from_vx_image(output_image)); |
| |
| ASSERT_NO_FAILURE(warp_perspective_check(input, output, arg_->interp_type, arg_->border, m)); |
| |
| VX_CALL(vxReleaseMatrix(&matrix)); |
| VX_CALL(vxReleaseImage(&output_image)); |
| VX_CALL(vxReleaseImage(&input_image)); |
| |
| ASSERT(matrix == 0); |
| ASSERT(output_image == 0); |
| ASSERT(input_image == 0); |
| } |
| |
| TESTCASE_TESTS(WarpPerspective, |
| testNodeCreation, |
| testGraphProcessing, |
| testImmediateProcessing |
| ) |