Redo:Adds support for half_pixel_centers & align_corners in TFLite's resizeNN kernel.

(Fix MacOS error)

PiperOrigin-RevId: 308716383
Change-Id: I577a87b9527a006650f3173dbfd0f0499bc72fb5
This commit is contained in:
Sachin Joglekar 2020-04-27 16:00:25 -07:00 committed by TensorFlower Gardener
parent 85e3642cfe
commit 2658a4a20c
6 changed files with 257 additions and 16 deletions

View File

@ -30,6 +30,7 @@ limitations under the License.
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/reference/add.h"
#include "tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h"
#if defined(TF_LITE_USE_CBLAS) && defined(__APPLE__)
#include <Accelerate/Accelerate.h>
@ -5915,13 +5916,21 @@ inline void TransposeConvV2(
// Integer-only version of ResizeNearestNeighbor. Since scales are represented
// in fixed-point and thus approximated, |in_x| or |in_y| may differ from the
// reference version. Debug checks are in place to test if this occurs.
// NOTE: If align_corners or half_pixel_centers is true, we use the reference
// version.
inline void ResizeNearestNeighbor(
const tflite::ResizeNearestNeighborParams& op_params,
const RuntimeShape& unextended_input_shape, const uint8* input_data,
const RuntimeShape& output_size_shape, const int32* output_size_data,
const RuntimeShape& unextended_output_shape, uint8* output_data) {
// Align corners = true is not supported.
TFLITE_DCHECK(!op_params.align_corners);
if (op_params.align_corners || op_params.half_pixel_centers) {
// TODO(b/149823713): Add support for align_corners & half_pixel_centers in
// this kernel.
reference_ops::ResizeNearestNeighbor(
op_params, unextended_input_shape, input_data, output_size_shape,
output_size_data, unextended_output_shape, output_data);
return;
}
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);

View File

@ -23,14 +23,32 @@ namespace tflite {
namespace reference_ops {
inline int32 GetNearestNeighbor(const int input_value, const int32 input_size,
const int32 output_size,
const bool align_corners,
const bool half_pixel_centers) {
const float scale =
(align_corners && output_size > 1)
? (input_size - 1) / static_cast<float>(output_size - 1)
: input_size / static_cast<float>(output_size);
const float offset = half_pixel_centers ? 0.5f : 0.0f;
int32 output_value = std::min(
align_corners
? static_cast<int32>(std::round((input_value + offset) * scale))
: static_cast<int32>(std::floor((input_value + offset) * scale)),
input_size - 1);
if (half_pixel_centers) {
output_value = std::max(static_cast<int32>(0), output_value);
}
return output_value;
}
template <typename T>
inline void ResizeNearestNeighbor(
const tflite::ResizeNearestNeighborParams& op_params,
const RuntimeShape& unextended_input_shape, const T* input_data,
const RuntimeShape& output_size_shape, const int32* output_size_data,
const RuntimeShape& unextended_output_shape, T* output_data) {
// Align corners = true is not supported.
TFLITE_DCHECK(!op_params.align_corners);
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
@ -50,10 +68,6 @@ inline void ResizeNearestNeighbor(
int32 output_height = output_size_data[0];
int32 output_width = output_size_data[1];
// We use float to ensure agreement with the Tensorflow implementation.
const float height_scale = static_cast<float>(input_height) / output_height;
const float width_scale = static_cast<float>(input_width) / output_width;
const int col_offset = input_shape.Dims(3);
const int row_offset = input_shape.Dims(2) * col_offset;
const int batch_offset = input_shape.Dims(1) * row_offset;
@ -62,12 +76,14 @@ inline void ResizeNearestNeighbor(
T* output_ptr = output_data;
for (int b = 0; b < batches; ++b) {
for (int y = 0; y < output_height; ++y) {
int32 in_y = std::min(static_cast<int32>(std::floor(y * height_scale)),
input_height - 1);
int32 in_y = GetNearestNeighbor(y, input_height, output_height,
op_params.align_corners,
op_params.half_pixel_centers);
const T* y_input_ptr = input_ptr + in_y * row_offset;
for (int x = 0; x < output_width; ++x) {
int32 in_x = std::min(static_cast<int32>(std::floor(x * width_scale)),
input_width - 1);
int32 in_x = GetNearestNeighbor(x, input_width, output_width,
op_params.align_corners,
op_params.half_pixel_centers);
const T* x_input_ptr = y_input_ptr + in_x * col_offset;
memcpy(output_ptr, x_input_ptr, depth * sizeof(T));
output_ptr += depth;

View File

@ -30,8 +30,9 @@ void TestReferenceResizeNearestNeighbor(
const RuntimeShape& input_shape, const std::vector<T>& input_data,
const std::vector<int32>& output_size_data,
const RuntimeShape& output_shape,
const std::vector<T>& expected_output_data) {
ResizeNearestNeighborParams op_params{/*align_corners=*/false};
const std::vector<T>& expected_output_data, bool align_corners = false,
bool half_pixel_centers = false) {
ResizeNearestNeighborParams op_params{align_corners, half_pixel_centers};
RuntimeShape output_size_shape({1, 1, 1, 2});
std::vector<T> output_data(expected_output_data.size());
@ -55,6 +56,30 @@ TEST(ResizeNearestNeighborReference, Test2x2To1x1) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test2x2To1x1_AlignCorners) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<float> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {1, 1};
RuntimeShape output_shape = {1, 1, 1, 1};
std::vector<float> output_data = {1};
TestReferenceResizeNearestNeighbor(input_shape, input_data, output_size_data,
output_shape, output_data,
/*align_corners=*/true);
}
TEST(ResizeNearestNeighborReference, Test2x2To1x1_HalfPixelCenters) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<float> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {1, 1};
RuntimeShape output_shape = {1, 1, 1, 1};
std::vector<float> output_data = {4};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference, Test2x2To3x3) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<uint8> input_data = {1, 2, 3, 4};
@ -66,6 +91,30 @@ TEST(ResizeNearestNeighborReference, Test2x2To3x3) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test2x2To3x3_AlignCorners) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<uint8> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {1, 3, 3, 1};
std::vector<uint8> output_data = {1, 2, 2, 3, 4, 4, 3, 4, 4};
TestReferenceResizeNearestNeighbor(input_shape, input_data, output_size_data,
output_shape, output_data,
/*align_corners=*/true);
}
TEST(ResizeNearestNeighborReference, Test2x2To3x3_HalfPixelCenters) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<uint8> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {1, 3, 3, 1};
std::vector<uint8> output_data = {1, 2, 2, 3, 4, 4, 3, 4, 4};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference, Test3x3To2x2) {
RuntimeShape input_shape = {1, 3, 3, 1};
std::vector<float> input_data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
@ -77,6 +126,30 @@ TEST(ResizeNearestNeighborReference, Test3x3To2x2) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test3x3To2x2_AlignCorners) {
RuntimeShape input_shape = {1, 3, 3, 1};
std::vector<float> input_data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int32> output_size_data = {2, 2};
RuntimeShape output_shape = {1, 2, 2, 1};
std::vector<float> output_data = {1, 3, 7, 9};
TestReferenceResizeNearestNeighbor(input_shape, input_data, output_size_data,
output_shape, output_data,
/*align_corners=*/true);
}
TEST(ResizeNearestNeighborReference, Test3x3To2x2_HalfPixelCenters) {
RuntimeShape input_shape = {1, 3, 3, 1};
std::vector<float> input_data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int32> output_size_data = {2, 2};
RuntimeShape output_shape = {1, 2, 2, 1};
std::vector<float> output_data = {1, 3, 7, 9};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference, Test2x2To2x5) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<uint8> input_data = {1, 2, 3, 4};
@ -88,6 +161,18 @@ TEST(ResizeNearestNeighborReference, Test2x2To2x5) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test2x2To2x5_HalfPixelCenters) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<uint8> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {2, 5};
RuntimeShape output_shape = {1, 2, 5, 1};
std::vector<uint8> output_data = {1, 1, 2, 2, 2, 3, 3, 4, 4, 4};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference, Test4x4To3x3) {
RuntimeShape input_shape = {1, 4, 4, 1};
std::vector<uint8> input_data = {1, 2, 3, 4, 5, 6, 7, 8,
@ -100,6 +185,32 @@ TEST(ResizeNearestNeighborReference, Test4x4To3x3) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test4x4To3x3_AlignCorners) {
RuntimeShape input_shape = {1, 4, 4, 1};
std::vector<uint8> input_data = {1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {1, 3, 3, 1};
std::vector<uint8> output_data = {1, 3, 4, 9, 11, 12, 13, 15, 16};
TestReferenceResizeNearestNeighbor(input_shape, input_data, output_size_data,
output_shape, output_data,
/*align_corners=*/true);
}
TEST(ResizeNearestNeighborReference, Test4x4To3x3_HalfPixelCenters) {
RuntimeShape input_shape = {1, 4, 4, 1};
std::vector<uint8> input_data = {1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {1, 3, 3, 1};
std::vector<uint8> output_data = {1, 3, 4, 9, 11, 12, 13, 15, 16};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference, Test2x2To5x2) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<float> input_data = {1, 2, 3, 4};
@ -111,6 +222,31 @@ TEST(ResizeNearestNeighborReference, Test2x2To5x2) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test2x2To5x2_HalfPixelCenters) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<float> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {5, 2};
RuntimeShape output_shape = {1, 5, 2, 1};
std::vector<float> output_data = {1, 2, 1, 2, 3, 4, 3, 4, 3, 4};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference,
Test2x2To5x2_HalfPixelCenters_AlignCorners) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<float> input_data = {1, 2, 3, 4};
std::vector<int32> output_size_data = {5, 2};
RuntimeShape output_shape = {1, 5, 2, 1};
std::vector<float> output_data = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/true, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference, Test2x2To4x4) {
RuntimeShape input_shape = {1, 2, 2, 1};
std::vector<uint8> input_data = {1, 2, 3, 4};
@ -149,10 +285,56 @@ TEST(ResizeNearestNeighborReference, Test2x2x2x2To2x3x3x2) {
output_shape, output_data);
}
TEST(ResizeNearestNeighborReference, Test2x2x2x2To2x3x3x2_AlignCorners) {
RuntimeShape input_shape = {2, 2, 2, 2};
std::vector<float> input_data = {1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {2, 3, 3, 2};
std::vector<float> output_data = {
1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 5, 6, 7, 8, 7, 8,
1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 5, 6, 7, 8, 7, 8,
};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/true, /*half_pixel_centers=*/false);
}
TEST(ResizeNearestNeighborReference, Test2x2x2x2To2x3x3x2_HalfPixelCenters) {
RuntimeShape input_shape = {2, 2, 2, 2};
std::vector<float> input_data = {1, 1, 2, 2, 3, 3, 4, 4,
5, 5, 6, 6, 7, 7, 8, 8};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {2, 3, 3, 2};
std::vector<float> output_data = {1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 4,
3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6,
7, 7, 8, 8, 8, 8, 7, 7, 8, 8, 8, 8};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/false, /*half_pixel_centers=*/true);
}
TEST(ResizeNearestNeighborReference,
Test2x2x2x2To2x3x3x2_HalfPixelCenters_AlignCorners) {
RuntimeShape input_shape = {2, 2, 2, 2};
std::vector<float> input_data = {1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int32> output_size_data = {3, 3};
RuntimeShape output_shape = {2, 3, 3, 2};
std::vector<float> output_data = {1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8,
5, 6, 7, 8, 7, 8, 1, 2, 3, 4, 3, 4,
5, 6, 7, 8, 7, 8, 5, 6, 7, 8, 7, 8};
TestReferenceResizeNearestNeighbor(
input_shape, input_data, output_size_data, output_shape, output_data,
/*align_corners=*/true, /*half_pixel_centers=*/true);
}
void TestOptimizedResizeNearestNeighbor(int batch, int depth, int input_width,
int input_height, int output_width,
int output_height) {
ResizeNearestNeighborParams op_params{/*align_corners=*/false};
RuntimeShape output_size_shape({1, 1, 1, 2});
RuntimeShape input_shape({batch, input_height, input_width, depth});
@ -167,6 +349,9 @@ void TestOptimizedResizeNearestNeighbor(int batch, int depth, int input_width,
std::vector<uint8> output_data(output_shape.FlatSize(), 3);
std::vector<int32> output_size_data = {output_height, output_width};
ResizeNearestNeighborParams op_params{/*align_corners=*/false,
/*half_pixel_centers=*/false};
// Test the optimized version against the reference version.
reference_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
@ -174,7 +359,35 @@ void TestOptimizedResizeNearestNeighbor(int batch, int depth, int input_width,
optimized_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, output_data.data());
ASSERT_EQ(reference_output_data, output_data);
op_params.align_corners = true;
reference_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, reference_output_data.data());
optimized_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, output_data.data());
ASSERT_EQ(reference_output_data, output_data);
op_params.align_corners = false;
op_params.half_pixel_centers = true;
reference_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, reference_output_data.data());
optimized_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, output_data.data());
ASSERT_EQ(reference_output_data, output_data);
op_params.align_corners = true;
op_params.half_pixel_centers = true;
reference_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, reference_output_data.data());
optimized_ops::ResizeNearestNeighbor(
op_params, input_shape, input_data.data(), output_size_shape,
output_size_data.data(), output_shape, output_data.data());
ASSERT_EQ(reference_output_data, output_data);
}
@ -214,7 +427,7 @@ bool is_valid_scale(int input_width, int input_height, int output_width,
TEST(ResizeNearestNeighborOptimized, TestReferenceParity) {
int invalid_count = 0;
const int kTestsToRun = 100 * 1000;
const int kTestsToRun = 10000;
for (int i = 0; i < kTestsToRun; i++) {
const int batch = ExponentialRandomPositiveInt(0.9f, 3, 20);
const int depth = ExponentialRandomPositiveInt(0.9f, 6, 50);

View File

@ -1007,6 +1007,7 @@ struct ResizeBilinearParams {
struct ResizeNearestNeighborParams {
bool align_corners;
bool half_pixel_centers;
};
struct SliceParams {

View File

@ -89,6 +89,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
tflite::ResizeNearestNeighborParams op_params;
op_params.align_corners = params->align_corners;
op_params.half_pixel_centers = false;
if (output->type == kTfLiteFloat32) {
reference_ops::ResizeNearestNeighbor(

View File

@ -67,6 +67,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
tflite::ResizeNearestNeighborParams op_params;
op_params.align_corners = params->align_corners;
op_params.half_pixel_centers = false;
if (output->type == kTfLiteFloat32) {
reference_ops::ResizeNearestNeighbor(