From 3327def321af49040ae1fbd6026234a0980e3289 Mon Sep 17 00:00:00 2001 From: Sheri Zhang Date: Tue, 22 Sep 2020 15:51:37 +0100 Subject: COMPMID-2808: Add support for QASYMM8_SIGNED in NEROIAlignLayer Signed-off-by: Sheri Zhang Change-Id: Id4f4c96e1823a4b27886fee9baf70847172e619c Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/4335 Tested-by: Arm Jenkins Reviewed-by: Georgios Pinitas Comments-Addressed: Arm Jenkins --- .../runtime/NEON/functions/NEROIAlignLayer.h | 4 +- src/core/NEON/kernels/NEROIAlignLayerKernel.cpp | 122 ++++++++++++--------- src/core/NEON/kernels/NEROIAlignLayerKernel.h | 10 +- tests/validation/NEON/ROIAlignLayer.cpp | 15 ++- 4 files changed, 89 insertions(+), 62 deletions(-) diff --git a/arm_compute/runtime/NEON/functions/NEROIAlignLayer.h b/arm_compute/runtime/NEON/functions/NEROIAlignLayer.h index 1d992f53df..ea3be18932 100644 --- a/arm_compute/runtime/NEON/functions/NEROIAlignLayer.h +++ b/arm_compute/runtime/NEON/functions/NEROIAlignLayer.h @@ -43,7 +43,7 @@ class NEROIAlignLayer : public INESimpleFunctionNoBorder public: /** Set the input and output tensors. * - * @param[in] input Source tensor. Data types supported: QASYMM8/F16/F32. + * @param[in] input Source tensor. Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. * @param[in] rois ROIs tensor, it is a 2D tensor of size [5, N] (where N is the number of ROIs) containing top left and bottom right corner * as coordinate of an image and batch_id of ROI [ batch_id, x1, y1, x2, y2 ]. * Data types supported: QASYMM16 with scale of 0.125 and 0 offset if @p input is QASYMM8, otherwise same as @p input @@ -58,7 +58,7 @@ public: void configure(const ITensor *input, const ITensor *rois, ITensor *output, const ROIPoolingLayerInfo &pool_info); /** Static function to check if given info will lead to a valid configuration of @ref NEROIAlignLayerKernel * - * @param[in] input Source tensor info. Data types supported: QASYMM8/F16/F32. + * @param[in] input Source tensor info. Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. * @param[in] rois ROIs tensor info. Data types supported: QASYMM16 with scale of 0.125 and 0 offset if @p input is QASYMM8, * otherwise same as @p input * @param[in] output Destination tensor info. Data types supported: Same as @p input. diff --git a/src/core/NEON/kernels/NEROIAlignLayerKernel.cpp b/src/core/NEON/kernels/NEROIAlignLayerKernel.cpp index c48cda8b8e..e937dadba7 100644 --- a/src/core/NEON/kernels/NEROIAlignLayerKernel.cpp +++ b/src/core/NEON/kernels/NEROIAlignLayerKernel.cpp @@ -47,7 +47,7 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *rois, ITe ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, rois, output); ARM_COMPUTE_RETURN_ERROR_ON(rois->dimension(0) != 5); ARM_COMPUTE_RETURN_ERROR_ON(rois->num_dimensions() > 2); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::QASYMM8, DataType::F32, DataType::F16); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::QASYMM8, DataType::QASYMM8_SIGNED, DataType::F32, DataType::F16); ARM_COMPUTE_RETURN_ERROR_ON_DATA_LAYOUT_NOT_IN(input, DataLayout::NHWC, DataLayout::NCHW); ARM_COMPUTE_RETURN_ERROR_ON((pool_info.pooled_width() == 0) || (pool_info.pooled_height() == 0)); ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input); @@ -59,7 +59,7 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *rois, ITe ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(compute_roi_align_shape(*input, *rois, pool_info), output->tensor_shape()); } - if(input->data_type() == DataType::QASYMM8) + if(input->data_type() == DataType::QASYMM8 || input->data_type() == DataType::QASYMM8_SIGNED) { ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(rois, 1, DataType::QASYMM16); @@ -116,7 +116,7 @@ Status NEROIAlignLayerKernel::validate(const ITensorInfo *input, const ITensorIn } /** Average pooling over an aligned window */ -template +template inline input_data_type roi_align_1x1(const ITensor *input, unsigned int roi_batch, float region_start_x, @@ -135,7 +135,8 @@ inline input_data_type roi_align_1x1(const ITensor *input, } else { - float avg = 0; + const DataLayout data_layout = input->info()->data_layout(); + float avg = 0; // Iterate through the aligned pooling region for(int iy = 0; iy < grid_size_y; ++iy) { @@ -185,7 +186,7 @@ inline input_data_type roi_align_1x1(const ITensor *input, } /** Average pooling over an aligned window */ -template +template inline input_data_type roi_align_1x1_qasymm8(const ITensor *input, unsigned int roi_batch, float region_start_x, @@ -205,8 +206,11 @@ inline input_data_type roi_align_1x1_qasymm8(const ITensor *input, } else { - float avg = 0; - const UniformQuantizationInfo input_qinfo = input->info()->quantization_info().uniform(); + float avg = 0; + const UniformQuantizationInfo input_qinfo = input->info()->quantization_info().uniform(); + const bool is_qasymm_signed = is_data_type_quantized_asymmetric_signed(input->info()->data_type()); + const DataLayout data_layout = input->info()->data_layout(); + // Iterate through the aligned pooling region for(int iy = 0; iy < grid_size_y; ++iy) { @@ -234,26 +238,57 @@ inline input_data_type roi_align_1x1_qasymm8(const ITensor *input, if(data_layout == DataLayout::NCHW) { - float data1 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_low, y_low, pz, roi_batch))), input_qinfo); - float data2 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_high, y_low, pz, roi_batch))), input_qinfo); - float data3 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_low, y_high, pz, roi_batch))), input_qinfo); - float data4 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_high, y_high, pz, roi_batch))), input_qinfo); - avg += w1 * data1 + w2 * data2 + w3 * data3 + w4 * data4; + if(is_qasymm_signed) + { + float data1 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(x_low, y_low, pz, roi_batch))), input_qinfo); + float data2 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(x_high, y_low, pz, roi_batch))), input_qinfo); + float data3 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(x_low, y_high, pz, roi_batch))), input_qinfo); + float data4 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(x_high, y_high, pz, roi_batch))), input_qinfo); + avg += w1 * data1 + w2 * data2 + w3 * data3 + w4 * data4; + } + else + { + float data1 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_low, y_low, pz, roi_batch))), input_qinfo); + float data2 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_high, y_low, pz, roi_batch))), input_qinfo); + float data3 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_low, y_high, pz, roi_batch))), input_qinfo); + float data4 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(x_high, y_high, pz, roi_batch))), input_qinfo); + avg += w1 * data1 + w2 * data2 + w3 * data3 + w4 * data4; + } } else { - const auto data1 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_low, y_low, roi_batch))), input_qinfo); - const auto data2 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_high, y_low, roi_batch))), input_qinfo); - const auto data3 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_low, y_high, roi_batch))), input_qinfo); - const auto data4 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_high, y_high, roi_batch))), input_qinfo); - avg += w1 * data1 + w2 * data2 + w3 * data3 + w4 * data4; + if(is_qasymm_signed) + { + const auto data1 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_low, y_low, roi_batch))), input_qinfo); + const auto data2 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_high, y_low, roi_batch))), input_qinfo); + const auto data3 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_low, y_high, roi_batch))), input_qinfo); + const auto data4 = dequantize_qasymm8_signed(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_high, y_high, roi_batch))), input_qinfo); + avg += w1 * data1 + w2 * data2 + w3 * data3 + w4 * data4; + } + else + { + const auto data1 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_low, y_low, roi_batch))), input_qinfo); + const auto data2 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_high, y_low, roi_batch))), input_qinfo); + const auto data3 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_low, y_high, roi_batch))), input_qinfo); + const auto data4 = dequantize_qasymm8(*reinterpret_cast(input->ptr_to_element(Coordinates(pz, x_high, y_high, roi_batch))), input_qinfo); + avg += w1 * data1 + w2 * data2 + w3 * data3 + w4 * data4; + } } } } avg /= grid_size_x * grid_size_y; - return quantize_qasymm8(avg, out_qinfo); + input_data_type res = 0; + if(is_qasymm_signed) + { + res = quantize_qasymm8_signed(avg, out_qinfo); + } + else + { + res = quantize_qasymm8(avg, out_qinfo); + } + return res; } } @@ -265,52 +300,30 @@ inline float compute_region_coordinate(int p, float bin_size, float roi_anchor, void NEROIAlignLayerKernel::run(const Window &window, const ThreadInfo &info) { - if(_input->info()->data_layout() == DataLayout::NCHW) + const DataLayout data_layout = _input->info()->data_layout(); + if(data_layout == DataLayout::NCHW || data_layout == DataLayout::NHWC) { switch(_input->info()->data_type()) { case DataType::QASYMM8: { - NEROIAlignLayerKernel::internal_run(window, info); - break; - } - case DataType::F32: - { - NEROIAlignLayerKernel::internal_run(window, info); - break; - } -#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC - case DataType::F16: - { - NEROIAlignLayerKernel::internal_run(window, info); + NEROIAlignLayerKernel::internal_run(window, info); break; } -#endif // __ARM_FEATURE_FP16_VECTOR_ARITHMETIC - default: - { - ARM_COMPUTE_ERROR("DataType not supported"); - break; - } - } - } - else if(_input->info()->data_layout() == DataLayout::NHWC) - { - switch(_input->info()->data_type()) - { - case DataType::QASYMM8: + case DataType::QASYMM8_SIGNED: { - NEROIAlignLayerKernel::internal_run(window, info); + NEROIAlignLayerKernel::internal_run(window, info); break; } case DataType::F32: { - NEROIAlignLayerKernel::internal_run(window, info); + NEROIAlignLayerKernel::internal_run(window, info); break; } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC case DataType::F16: { - NEROIAlignLayerKernel::internal_run(window, info); + NEROIAlignLayerKernel::internal_run(window, info); break; } #endif // __ARM_FEATURE_FP16_VECTOR_ARITHMETIC @@ -327,21 +340,22 @@ void NEROIAlignLayerKernel::run(const Window &window, const ThreadInfo &info) } } -template +template void NEROIAlignLayerKernel::internal_run(const Window &window, const ThreadInfo &info) { ARM_COMPUTE_UNUSED(info); ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window); - const size_t values_per_roi = _rois->info()->dimension(0); + const DataLayout data_layout = _input->info()->data_layout(); + const size_t values_per_roi = _rois->info()->dimension(0); const int roi_list_start = window.x().start(); const int roi_list_end = window.x().end(); - const unsigned int idx_width = get_data_layout_dimension_index(_input->info()->data_layout(), DataLayoutDimension::WIDTH); - const unsigned int idx_height = get_data_layout_dimension_index(_input->info()->data_layout(), DataLayoutDimension::HEIGHT); - const unsigned int idx_depth = get_data_layout_dimension_index(_input->info()->data_layout(), DataLayoutDimension::CHANNEL); + const unsigned int idx_width = get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH); + const unsigned int idx_height = get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT); + const unsigned int idx_depth = get_data_layout_dimension_index(data_layout, DataLayoutDimension::CHANNEL); const int input_width = _input->info()->dimension(idx_width); const int input_height = _input->info()->dimension(idx_height); @@ -397,14 +411,14 @@ void NEROIAlignLayerKernel::internal_run(const Window &window, const ThreadInfo input_data_type out_val(0); if(is_qasymm) { - out_val = roi_align_1x1_qasymm8( + out_val = roi_align_1x1_qasymm8( _input, roi_batch, region_start_x, bin_size_x, roi_bin_grid_x, region_end_x, region_start_y, bin_size_y, roi_bin_grid_y, region_end_y, ch, _output->info()->quantization_info()); } else { - out_val = roi_align_1x1( + out_val = roi_align_1x1( _input, roi_batch, region_start_x, bin_size_x, roi_bin_grid_x, region_end_x, region_start_y, bin_size_y, roi_bin_grid_y, region_end_y, ch); diff --git a/src/core/NEON/kernels/NEROIAlignLayerKernel.h b/src/core/NEON/kernels/NEROIAlignLayerKernel.h index d909fb1758..fa31a879b7 100644 --- a/src/core/NEON/kernels/NEROIAlignLayerKernel.h +++ b/src/core/NEON/kernels/NEROIAlignLayerKernel.h @@ -55,10 +55,10 @@ public: /** Set the input and output tensors. * - * @param[in] input Source tensor. Data types supported: QASYMM8/F16/F32. + * @param[in] input Source tensor. Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. * @param[in] rois ROIs tensor, it is a 2D tensor of size [5, N] (where N is the number of ROIs) containing top left and bottom right corner * as coordinate of an image and batch_id of ROI [ batch_id, x1, y1, x2, y2 ]. - * Data types supported: QASYMM16 with scale of 0.125 and 0 offset if @p input is QASYMM8, otherwise same as @p input + * Data types supported: QASYMM16 with scale of 0.125 and 0 offset if @p input is QASYMM8/QASYMM8_SIGNED, otherwise same as @p input * @param[out] output Destination tensor. Data types supported: Same as @p input. * @param[in] pool_info Contains pooling operation information described in @ref ROIPoolingLayerInfo. * @@ -70,8 +70,8 @@ public: void configure(const ITensor *input, const ITensor *rois, ITensor *output, const ROIPoolingLayerInfo &pool_info); /** Static function to check if given info will lead to a valid configuration of @ref NEROIAlignLayerKernel * - * @param[in] input Source tensor info. Data types supported: QASYMM8/F16/F32. - * @param[in] rois ROIs tensor info. Data types supported: QASYMM16 with scale of 0.125 and 0 offset if @p input is QASYMM8, + * @param[in] input Source tensor info. Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. + * @param[in] rois ROIs tensor info. Data types supported: QASYMM16 with scale of 0.125 and 0 offset if @p input is QASYMM8/QASYMM8_SIGNED, * otherwise same as @p input * @param[in] output Destination tensor info. Data types supported: Same as @p input. * @param[in] pool_info Contains pooling operation information described in @ref ROIPoolingLayerInfo. @@ -89,7 +89,7 @@ public: void run(const Window &window, const ThreadInfo &info) override; private: - template + template void internal_run(const Window &window, const ThreadInfo &info); const ITensor *_input; diff --git a/tests/validation/NEON/ROIAlignLayer.cpp b/tests/validation/NEON/ROIAlignLayer.cpp index 3f6c9d2082..e475c46c7d 100644 --- a/tests/validation/NEON/ROIAlignLayer.cpp +++ b/tests/validation/NEON/ROIAlignLayer.cpp @@ -129,10 +129,10 @@ FIXTURE_DATA_TEST_CASE(SmallROIAlignLayerHalf, NEROIAlignLayerHalfFixture, frame TEST_SUITE_END() // Float TEST_SUITE(Quantized) -TEST_SUITE(QASYMM8) template using NEROIAlignLayerQuantizedFixture = ROIAlignLayerQuantizedFixture; +TEST_SUITE(QASYMM8) FIXTURE_DATA_TEST_CASE(Small, NEROIAlignLayerQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(combine(datasets::SmallROIDataset(), framework::dataset::make("DataType", { DataType::QASYMM8 })), @@ -144,6 +144,19 @@ FIXTURE_DATA_TEST_CASE(Small, NEROIAlignLayerQuantizedFixture, framewor validate(Accessor(_target), _reference, tolerance_qasymm8); } TEST_SUITE_END() // QASYMM8 + +TEST_SUITE(QASYMM8_SIGNED) +FIXTURE_DATA_TEST_CASE(Small, NEROIAlignLayerQuantizedFixture, framework::DatasetMode::ALL, + combine(combine(combine(combine(datasets::SmallROIDataset(), + framework::dataset::make("DataType", { DataType::QASYMM8_SIGNED })), + framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), + framework::dataset::make("InputQuantizationInfo", { QuantizationInfo(1.f / 255.f, 127) })), + framework::dataset::make("OutputQuantizationInfo", { QuantizationInfo(2.f / 255.f, 120) }))) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_qasymm8); +} +TEST_SUITE_END() // QASYMM8_SIGNED TEST_SUITE_END() // Quantized TEST_SUITE_END() // RoiAlign -- cgit v1.2.1