diff --git a/libnd4j/include/ops/declarable/generic/images/image_resize.cpp b/libnd4j/include/ops/declarable/generic/images/image_resize.cpp index 3ceba93d8..4e680b337 100644 --- a/libnd4j/include/ops/declarable/generic/images/image_resize.cpp +++ b/libnd4j/include/ops/declarable/generic/images/image_resize.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Konduit K.K. + * Copyright (c) 2020 Konduit K.K. * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -32,57 +32,65 @@ namespace sd { auto size = INPUT_VARIABLE(1); auto output = OUTPUT_VARIABLE(0); + int width; int height; - bool preserveAspectRatio = false; // - default value bool antialias = false; - REQUIRE_TRUE(size->lengthOf() == 2, 0, "resize_bilinear: Resize params is a pair of values, not %lld.", size->lengthOf()); - width = size->e(0); - height = size->e(1); - if (block.getBArguments()->size()) { - preserveAspectRatio = B_ARG(0); - if (block.getBArguments()->size() > 1) - antialias = B_ARG(1); + REQUIRE_TRUE(size->lengthOf() == 2, 0, "image_resize: Resize params is a pair of values, not %lld.", size->lengthOf()); + width = size->e(1); + height = size->e(0); + if (block.numB() == 2) { + antialias = B_ARG(1); } auto method = helpers::ImageResizeMethods::kResizeBilinear; if (block.numI() == 1) { method = (helpers::ImageResizeMethods)INT_ARG(0); } + REQUIRE_TRUE(method == helpers::ImageResizeMethods::kResizeNearest || output->dataType() == DataType::FLOAT32, 0, "image_resize: Output data type should be FLOAT32 for this method %i", (int)method ); + REQUIRE_TRUE(method >= helpers::ImageResizeMethods::kResizeFirst && method <= helpers::ImageResizeMethods::kResizeLast, 0, "image_resize: Resize method should be between %i and %i, but %i was given.", (int)helpers::ImageResizeMethods::kResizeFirst, (int)helpers::ImageResizeMethods::kResizeLast, (int)method); + auto inRank = image->rankOf(); + REQUIRE_TRUE(inRank >=3 && inRank <=4, 0, "image_resize: Input rank should be 4 or 3, but %i given.", image->rankOf()); + auto source = inRank == 4?image->reshape(image->ordering(), {image->sizeAt(0), image->sizeAt(1), image->sizeAt(2), image->sizeAt(3)}):image->reshape(image->ordering(), {1, image->sizeAt(0), image->sizeAt(1), image->sizeAt(2)}); + auto target = inRank == 4?output->reshape(output->ordering(), {output->sizeAt(0), output->sizeAt(1), output->sizeAt(2), output->sizeAt(3)}, false) : output->reshape(output->ordering(), {1, output->sizeAt(0), output->sizeAt(1), output->sizeAt(2)}, false); - return helpers::resizeFunctor(block.launchContext(), image, width, height, method, preserveAspectRatio, antialias, output); + return helpers::resizeFunctor(block.launchContext(), image, width, height, method, antialias, output); } DECLARE_SHAPE_FN(image_resize) { - auto shapeList = SHAPELIST(); auto in = inputShape->at(0); Nd4jLong* outputShape; + auto method = helpers::ImageResizeMethods::kResizeBilinear; + if (block.numI() == 1) { + method = (helpers::ImageResizeMethods)INT_ARG(0); + } int width; int height; + double ratio = shape::sizeAt(in, 1) / (0.0 + shape::sizeAt(in, 2)); auto newImageSize = INPUT_VARIABLE(1); REQUIRE_TRUE(newImageSize->lengthOf() == 2, 0, "resize_bilinear: Resize params is a pair of values, not %i.", newImageSize->lengthOf()); REQUIRE_TRUE(block.numI() <= 1, 0, "resize_bilinear: Resize params already given by the second param. Int params are expensive."); - width = newImageSize->e(0); - height = newImageSize->e(1); - - ALLOCATE(outputShape, block.getWorkspace(), shape::shapeInfoLength(4), Nd4jLong); - outputShape[0] = 4; - outputShape[1] = in[1]; - outputShape[2] = width; - outputShape[3] = height; - outputShape[4] = in[4]; - ShapeUtils::updateStridesAndType(outputShape, in, shape::order(in)); + width = newImageSize->e(1); + height = newImageSize->e(0); + if (block.numB() > 0) { + if (B_ARG(0)) { + width = math::nd4j_ceil(height / ratio); + } + } + auto dtype = DataType::FLOAT32; + if (method == helpers::ImageResizeMethods::kResizeNearest) + dtype = ArrayOptions::dataType(in); + auto shape = ConstantShapeHelper::getInstance()->createShapeInfo(dtype, 'c', shape::rank(in) == 4?std::vector{in[1], height, width, in[4]}:std::vector{ height, width, in[4]}); - shapeList->push_back(CONSTANT(outputShape)); - return shapeList; + return SHAPELIST(shape); } DECLARE_TYPES(image_resize) { getOpDescriptor() - ->setAllowedInputTypes(0, {ALL_FLOATS}) + ->setAllowedInputTypes(0, {ALL_INTS, ALL_FLOATS}) ->setAllowedInputTypes(1, {ALL_INTS}) - ->setAllowedOutputTypes({ALL_FLOATS}); + ->setAllowedOutputTypes({ALL_FLOATS, ALL_INTS}); } } diff --git a/libnd4j/include/ops/declarable/generic/images/resize_images.cpp b/libnd4j/include/ops/declarable/generic/images/resize_images.cpp new file mode 100644 index 000000000..c3f9ae8f1 --- /dev/null +++ b/libnd4j/include/ops/declarable/generic/images/resize_images.cpp @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2020 Konduit K.K. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +// +// @author sgazeos@gmail.com +// + +#include +#if NOT_EXCLUDED(OP_resize_images) + +#include +#include + +namespace sd { + namespace ops { + CUSTOM_OP_IMPL(resize_images, 1, 1, false, 0, 0) { + + auto image = INPUT_VARIABLE(0); + + auto output = OUTPUT_VARIABLE(0); + int width = output->sizeAt(2); + int height = output->sizeAt(1); + int method = helpers::ImageResizeMethods::kResizeBilinear; + if (block.width() > 1) { + auto size = INPUT_VARIABLE(1); + REQUIRE_TRUE(size->lengthOf() == 2, 0, "resize_images: Resize params is a pair of values, not %lld.", size->lengthOf()); +// width = size->e(1); +// height = size->e(0); + if (block.width() > 2) { + auto methodT = INPUT_VARIABLE(2); + + REQUIRE_TRUE(methodT->isZ() && methodT->isScalar(), 0, "resize_images: Method tensor should be integer scalar, but rank of %i tensor given.", methodT->rankOf()); + method = methodT->e(0); + } + else if (block.numI() == 1) { + method = I_ARG(0); + } + } + else { + REQUIRE_TRUE(block.numI() > 1 && block.numI() < 4, 0, "resize_images: Method and size should be given properly."); + if(block.numI() == 3) { // full stack of args +// height = I_ARG(0); +// width = I_ARG(1); + method = I_ARG(2); + } + else if (block.numI() == 2) { +// height = I_ARG(0); +// width = I_ARG(1); + } + } + bool preserveAspectRatio = false; // - default value + bool alignCorners = false; + if (block.numB()) { + alignCorners = B_ARG(0); + if (block.numB() > 1) + preserveAspectRatio = B_ARG(1); + } + REQUIRE_TRUE(method >= helpers::ImageResizeMethods::kResizeFirst && method <= helpers::ImageResizeMethods::kResizeOldLast, 0, "resize_images: Resize method should be between %i and %i, but %i was given.", (int)helpers::ImageResizeMethods::kResizeFirst, (int)helpers::ImageResizeMethods::kResizeOldLast, (int)method); + REQUIRE_TRUE(method == helpers::ImageResizeMethods::kResizeNearest || output->dataType() == DataType::FLOAT32, 0, "image_resize: Output data type should be FLOAT32 for this method %i", (int)method ); + + auto inRank = image->rankOf(); + REQUIRE_TRUE(inRank >=3 && inRank <=4, 0, "image_resize: Input rank should be 4 or 3, but %i given.", inRank); + + auto source = inRank == 4?image->reshape(image->ordering(), {image->sizeAt(0), image->sizeAt(1), image->sizeAt(2), image->sizeAt(3)}):image->reshape(image->ordering(), {1, image->sizeAt(0), image->sizeAt(1), image->sizeAt(2)}); + auto target = inRank == 4?output->reshape(output->ordering(), {output->sizeAt(0), output->sizeAt(1), output->sizeAt(2), output->sizeAt(3)}, false) : output->reshape(output->ordering(), {1, output->sizeAt(0), output->sizeAt(1), output->sizeAt(2)}, false); + + return helpers::resizeImagesFunctor(block.launchContext(), &source, width, height, (helpers::ImageResizeMethods)method, alignCorners, &target); + } + + DECLARE_SHAPE_FN(resize_images) { + auto shapeList = SHAPELIST(); + auto in = inputShape->at(0); + + Nd4jLong* outputShape; + + int width; + int height; + if (block.width() > 1) { + auto size = INPUT_VARIABLE(1); + REQUIRE_TRUE(size->lengthOf() == 2, 0, "resize_images: Resize params is a pair of values, not %lld.", size->lengthOf()); + width = size->e(1); + height = size->e(0); + } + else { + REQUIRE_TRUE(block.numI() > 1 && block.numI() < 4, 0, "resize_images: Method and size should be given properly."); + if(block.numI() == 3) { // full stack of args + height = I_ARG(0); + width = I_ARG(1); + } + else if (block.numI() == 2) { + height = I_ARG(0); + width = I_ARG(1); + } + } + + double ratio = shape::sizeAt(in, 1) / (0.0 + shape::sizeAt(in, 2)); + if (block.numB() > 1) { + if (B_ARG(1)) { + width = math::nd4j_ceil(height / ratio); + } + } + + std::vector shape; + if (shape::rank(in) == 4) + shape = {in[1], height, width, in[4]}; + else if (shape::rank(in) == 3) + shape = {height, width, in[3]}; + + auto outShape = ConstantShapeHelper::getInstance()->createShapeInfo(DataType::FLOAT32, shape::order(in), shape); + return SHAPELIST(outShape); + } + DECLARE_TYPES(resize_images) { + getOpDescriptor() + ->setAllowedInputTypes(0, {ALL_FLOATS, ALL_INTS}) + ->setAllowedInputTypes(1, {ALL_INTS}) + ->setAllowedOutputTypes({DataType::FLOAT32}); + } + + } +} + +#endif \ No newline at end of file diff --git a/libnd4j/include/ops/declarable/generic/linalg/matrix_band_part.cpp b/libnd4j/include/ops/declarable/generic/linalg/matrix_band_part.cpp index 08e059c37..51beff4c8 100644 --- a/libnd4j/include/ops/declarable/generic/linalg/matrix_band_part.cpp +++ b/libnd4j/include/ops/declarable/generic/linalg/matrix_band_part.cpp @@ -25,14 +25,27 @@ namespace sd { namespace ops { - CONFIGURABLE_OP_IMPL(matrix_band_part, 1, 1, true, 0, 2) { + CONFIGURABLE_OP_IMPL(matrix_band_part, 1, 1, true, 0, 0) { auto input = INPUT_VARIABLE(0); auto output = OUTPUT_VARIABLE(0); - Nd4jLong minLower = INT_ARG(0); - Nd4jLong maxUpper = INT_ARG(1); + Nd4jLong minLower(0LL); + Nd4jLong maxUpper(0LL); + if (block.width() == 1) { + REQUIRE_TRUE(block.numI() == 2, 0, "matrix_band_part: min and max band numbers should be given before."); + minLower = INT_ARG(0); + maxUpper = INT_ARG(1); + } + else { + REQUIRE_TRUE(block.width() == 3, 0, "matrix_band_part: min and max band numbers should be given as scalars before."); + auto minLowerT = INPUT_VARIABLE(1); + auto maxUpperT = INPUT_VARIABLE(2); + REQUIRE_TRUE(minLowerT->isScalar() && maxUpperT->isScalar(), 0, "matrix_band_part: min and max should be scalars, but %i and %i ranks given", minLowerT->rankOf(), maxUpperT->rankOf()); + minLower = minLowerT->e(0); + maxUpper = maxUpperT->e(0); + } REQUIRE_TRUE(input->rankOf() >= 2, 0, "matrix_band_part: Input rank should be 2 or greater."); Nd4jLong N = input->sizeAt(-2); Nd4jLong M = input->sizeAt(-1); @@ -49,9 +62,10 @@ namespace sd { DECLARE_TYPES(matrix_band_part) { getOpDescriptor() - ->setAllowedInputTypes({ALL_INTS, ALL_FLOATS}) - ->setAllowedInputTypes({ALL_INTS, ALL_FLOATS}) - ->setSameMode(true); + ->setAllowedInputTypes(0, {ALL_INTS, ALL_FLOATS}) + ->setAllowedInputTypes(1, {ALL_INTS}) + ->setAllowedInputTypes(2, {ALL_INTS}) + ->setAllowedInputTypes({ALL_INTS, ALL_FLOATS}); } } diff --git a/libnd4j/include/ops/declarable/headers/images.h b/libnd4j/include/ops/declarable/headers/images.h index 41974901a..aa2114540 100644 --- a/libnd4j/include/ops/declarable/headers/images.h +++ b/libnd4j/include/ops/declarable/headers/images.h @@ -85,6 +85,7 @@ namespace ops { */ #if NOT_EXCLUDED(OP_rgb_to_yuv) DECLARE_CONFIGURABLE_OP(yuv_to_rgb, 1, 1, true, 0, 0); +#endif /** * Rgb To Yiq @@ -108,8 +109,156 @@ namespace ops { DECLARE_CONFIGURABLE_OP(yiq_to_rgb, 1, 1, true, 0, 0); #endif -} -} +/** + * resize_images - resize image with given size and method + * there are 4 methods allowed: RESIZE_BILINEAR(0), RESIZE_NEIGHBOR(1), RESIZE_AREA(2) and RESIZE_BICUBIC(3) + * inputs: + * 0 - 4D tensor with shape {batch, height, width, channels} + * 1 - 1D integer tensor with {new_height, new_width} (optional) + * 2 - 0D integer tensor with method (0 to 3) (optional) + * + * int args: + * 0 - new_height + * 1 - new_width + * 2 - method + * + * bool args: + * 0 - align corners (default false) - optional + * 1 - preserve_aspect_ratio (default false) - optional + * + * CAUTION: one of methods can be used to give size and method - as tensors or as int args only + * + * output: + * 0 - 4D float32 tensor with shape {batch, new_height, new_width, channels} + * + */ +#if NOT_EXCLUDED(OP_resize_images) + DECLARE_CUSTOM_OP(resize_images, 1,1,false, 0, 0); +#endif -#endif + /** + * This op make bilinear or nearest neighbor interpolated resize for given tensor + * + * input array: + * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) numeric type + * 1 - 2D-Tensor with shape (num_boxes, 4) float type + * 2 - 1D-Tensor with shape (num_boxes) int type + * 3 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) int type + * + * float arguments (optional) + * 0 - exprapolation_value (optional) default 0.f + * + * int arguments: (optional) + * 0 - mode (default 0 - bilinear interpolation) + * + * output array: + * the 4D-Tensor with resized to crop_size images given - float type + */ + #if NOT_EXCLUDED(OP_crop_and_resize) + DECLARE_CUSTOM_OP(crop_and_resize, 4, 1, false, -1, -1); + #endif + + /** + * This op make bilinear interpolated resize for given tensor + * + * input array: + * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) + * 1 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) + * + * int arguments: (optional) + * 0 - new width + * 1 - new height + * + * output array: + * the 4D-Tensor with calculated backproped dots + * + * CAUTION: either size tensor or a pair of int params should be provided. + */ + + #if NOT_EXCLUDED(OP_resize_bilinear) + DECLARE_CUSTOM_OP(resize_bilinear, 1, 1, false, 0, -2); + #endif + + /** + * This op make nearest neighbor interpolated resize for given tensor + * + * input array: + * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) + * 1 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) + * + * int arguments: (optional) + * 0 - new width + * 1 - new height + * + * output array: + * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) + * + * CAUTION: either size tensor or a pair of int params should be provided. + */ + + #if NOT_EXCLUDED(OP_resize_nearest_neighbor) + DECLARE_CUSTOM_OP(resize_nearest_neighbor, 1, 1, false, 0, -2); + #endif + + /** + * This op make bicubic interpolated resize for given tensor + * + * input array: + * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) + * 1 - 1D-Tensor with 2 values (newWidth, newHeight) + * + * output array: + * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) + * + */ + #if NOT_EXCLUDED(OP_resize_bicubic) + DECLARE_CUSTOM_OP(resize_bicubic, 1, 1, false, 0, -2); + #endif + + /** + * This op make area interpolated resize (as OpenCV INTER_AREA algorithm) for given tensor + * + * input array: + * 0 - images - 4D-Tensor with shape (batch, sizeX, sizeY, channels) + * 1 - size - 1D-Tensor with 2 values (newWidth, newHeight) (if missing a pair of integer args should be provided). + * + * int args: - proveded only when size tensor is missing + * 0 - new height + * 1 - new width + * boolean args: + * 0 - align_corners - optional (default is false) + * + * output array: + * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) + * + */ + #if NOT_EXCLUDED(OP_resize_area) + DECLARE_CUSTOM_OP(resize_area, 1, 1, false, 0, -2); + #endif + + /** + * This op make interpolated resize for given tensor with given algorithm. + * Supported algorithms are bilinear, bicubic, nearest_neighbor, lanczos5, gaussian, area and mitchellcubic. + * + * input array: + * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) + * 1 - 1D-Tensor with 2 values (newWidth, newHeight) + * + * optional int args: + * 0 - algorithm - bilinear by default + * optional bool args: + * 0 - preserve_aspect_ratio - default False + * 1 - antialias - default False + * + * output array: + * the 4D-Tensor with resized by given algorithm image (shape is {batch, newWidth, newHeight, channels}) + * + */ + + #if NOT_EXCLUDED(OP_image_resize) + DECLARE_CUSTOM_OP(image_resize, 2, 1, false, 0, 0); + #endif + +} +} #endif diff --git a/libnd4j/include/ops/declarable/headers/parity_ops.h b/libnd4j/include/ops/declarable/headers/parity_ops.h index 74221133c..27c012214 100644 --- a/libnd4j/include/ops/declarable/headers/parity_ops.h +++ b/libnd4j/include/ops/declarable/headers/parity_ops.h @@ -1771,130 +1771,6 @@ namespace sd { DECLARE_CUSTOM_OP(reduce_logsumexp, 1, 1, false, 0, 0); #endif - /** - * This op make bilinear or nearest neighbor interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) numeric type - * 1 - 2D-Tensor with shape (num_boxes, 4) float type - * 2 - 1D-Tensor with shape (num_boxes) int type - * 3 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) int type - * - * float arguments (optional) - * 0 - exprapolation_value (optional) default 0.f - * - * int arguments: (optional) - * 0 - mode (default 0 - bilinear interpolation) - * - * output array: - * the 4D-Tensor with resized to crop_size images given - float type - */ - #if NOT_EXCLUDED(OP_crop_and_resize) - DECLARE_CUSTOM_OP(crop_and_resize, 4, 1, false, -1, -1); - #endif - - /** - * This op make bilinear interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) - * - * int arguments: (optional) - * 0 - new width - * 1 - new height - * - * output array: - * the 4D-Tensor with calculated backproped dots - * - * CAUTION: either size tensor or a pair of int params should be provided. - */ - - #if NOT_EXCLUDED(OP_resize_bilinear) - DECLARE_CUSTOM_OP(resize_bilinear, 1, 1, false, 0, -2); - #endif - - /** - * This op make nearest neighbor interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) - * - * int arguments: (optional) - * 0 - new width - * 1 - new height - * - * output array: - * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) - * - * CAUTION: either size tensor or a pair of int params should be provided. - */ - - #if NOT_EXCLUDED(OP_resize_nearest_neighbor) - DECLARE_CUSTOM_OP(resize_nearest_neighbor, 1, 1, false, 0, -2); - #endif - - /** - * This op make bicubic interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) - * - * output array: - * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) - * - */ - #if NOT_EXCLUDED(OP_resize_bicubic) - DECLARE_CUSTOM_OP(resize_bicubic, 1, 1, false, 0, -2); - #endif - - /** - * This op make area interpolated resize (as OpenCV INTER_AREA algorithm) for given tensor - * - * input array: - * 0 - images - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - size - 1D-Tensor with 2 values (newWidth, newHeight) (if missing a pair of integer args should be provided). - * - * int args: - proveded only when size tensor is missing - * 0 - new height - * 1 - new width - * boolean args: - * 0 - align_corners - optional (default is false) - * - * output array: - * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) - * - */ - #if NOT_EXCLUDED(OP_resize_area) - DECLARE_CUSTOM_OP(resize_area, 1, 1, false, 0, -2); - #endif - - /** - * This op make interpolated resize for given tensor with given algorithm. - * Supported algorithms are bilinear, bicubic, nearest_neighbor. - * Need to implement to full compatibility with TF: lanczos5, gaussian, area and mitchellcubic - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) - * - * optional int args: - * 0 - algorithm - bilinear by default - * optional bool args: - * 0 - preserve_aspect_ratio - default False - * 1 - antialias - default False - * - * output array: - * the 4D-Tensor with resized by given algorithm image (shape is {batch, newWidth, newHeight, channels}) - * - */ - - #if NOT_EXCLUDED(OP_image_resize) - DECLARE_CUSTOM_OP(image_resize, 2, 1, false, 0, 0); - #endif - /** * Copy a tensor setting everything outside a central band in each innermost matrix * diff --git a/libnd4j/include/ops/declarable/helpers/cpu/image_resize.cpp b/libnd4j/include/ops/declarable/helpers/cpu/image_resize.cpp index 68b2130ac..7206b03e5 100644 --- a/libnd4j/include/ops/declarable/helpers/cpu/image_resize.cpp +++ b/libnd4j/include/ops/declarable/helpers/cpu/image_resize.cpp @@ -418,17 +418,17 @@ namespace helpers { // Allocate and initialize coefficients table using Bicubic // convolution algorithm. // https://en.wikipedia.org/wiki/Bicubic_interpolation - float* coeffs_table = new float[(kTableSize + 1) * 2]; + float* coeffsTable = new float[(kTableSize + 1) * 2]; auto func = PRAGMA_THREADS_FOR { for (auto i = start; i <= stop; ++i) { float x = i * 1.0 / kTableSize; - coeffs_table[i * 2] = ((a + 2) * x - (a + 3)) * x * x + 1; + coeffsTable[i * 2] = ((a + 2) * x - (a + 3)) * x * x + 1; x += 1.0; - coeffs_table[i * 2 + 1] = ((a * x - 5 * a) * x + 8 * a) * x - 4 * a; + coeffsTable[i * 2 + 1] = ((a * x - 5 * a) * x + 8 * a) * x - 4 * a; } }; samediff::Threads::parallel_for(func, 0, kTableSize); - return coeffs_table; + return coeffsTable; } const float* getCoeffsTable(const bool use_keys_cubic) { @@ -988,25 +988,392 @@ namespace helpers { return res; } - int resizeAreaFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, - bool const alignCorners, NDArray* output) { + int resizeAreaFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const alignCorners, NDArray* output) { BUILD_SINGLE_SELECTOR(image->dataType(), return resizeAreaFunctor_, (context, image, width, height, alignCorners, output), NUMERIC_TYPES); } + /** + * resize as TF v.2.x implemented (with preserve aspect ratio and antialias flags routines + * */ + // An interface for integrated scale functors. + struct IKernelFunc { + virtual float operator()(float x) const = 0; + virtual float radius() const = 0; + }; + + struct LanczosKernelFunc : public IKernelFunc { + // Pass 1 for Lanczos1 kernel, 3 for Lanczos3 etc. + explicit LanczosKernelFunc(float const radius) : _radius(radius) {} + float operator()(float x) const { + float const kPI = 3.141592653589793f; + x = math::nd4j_abs(x); + if (x > _radius) return 0.f; + // Need to special case the limit case of sin(x) / x when x is zero. + if (x <= 1.e-3f) { + return 1.f; + } + return _radius * std::sin(kPI * x) * std::sin(kPI * x / _radius) / (kPI * kPI * x * x); + } + float radius() const { return _radius; } + const float _radius; + }; + + struct GaussianKernelFunc : public IKernelFunc { + static constexpr float kRadiusMultiplier = 3.0f; + // https://en.wikipedia.org/wiki/Gaussian_function + // We use sigma = 0.5, as suggested on p. 4 of Ken Turkowski's "Filters + // for Common Resampling Tasks" for kernels with a support of 3 pixels: + // www.realitypixels.com/turk/computergraphics/ResamplingFilters.pdf + // This implies a radius of 1.5, + explicit GaussianKernelFunc(float radius = 1.5f) + : _radius(radius), _sigma(radius / kRadiusMultiplier) {} + float operator()(float x) const { + x = math::nd4j_abs(x); + if (x >= _radius) return 0.0f; + return std::exp(-x * x / (2.0 * _sigma * _sigma)); + } + float radius() const { return _radius; } + const float _radius; + const float _sigma; // Gaussian standard deviation + }; + + struct BoxKernelFunc : public IKernelFunc { + float operator()(float x) const { + x = math::nd4j_abs(x); + return x < 0.5f ? 1.f : x == 0.5f ? 0.5f : 0.f; + } + float radius() const { return 1.f; } + }; + + struct TriangleKernelFunc : public IKernelFunc { + // https://en.wikipedia.org/wiki/Triangle_function + float operator()(float x) const { + x = math::nd4j_abs(x); + return x < 1.f ? 1.f - x : 0.f; + } + float radius() const { return 1.f; } + }; + + struct KeysCubicKernelFunc : public IKernelFunc { + // http://ieeexplore.ieee.org/document/1163711/ + // R. G. Keys. Cubic convolution interpolation for digital image + // processing. IEEE Transactions on Acoustics, Speech, and Signal + // Processing, 29(6):1153–1160, 1981. + float operator()(float x) const { + x = math::nd4j_abs(x); + if (x >= 2.0f) { + return 0.0f; + } else if (x >= 1.0f) { + return ((-0.5f * x + 2.5f) * x - 4.0f) * x + 2.0f; + } else { + return ((1.5f * x - 2.5f) * x) * x + 1.0f; + } + } + float radius() const { return 2.f; } + }; + + struct MitchellCubicKernelFunc : public IKernelFunc { + // https://doi.org/10.1145/378456.378514 + // D. P. Mitchell and A. N. Netravali. Reconstruction filters in computer + // graphics. Computer Graphics (Proceedings of ACM SIGGRAPH 1988), + // 22(4):221–228, 1988. + float operator()(float x) const { + x = math::nd4j_abs(x); + if (x >= 2.f) { + return 0.f; + } else if (x >= 1.f) { + return (((-7.f / 18.f) * x + 2.f) * x - 10.f / 3.f) * x + 16.f / 9.f; + } else { + return (((7.f / 6.f) * x - 2.f) * x) * x + 8.f / 9.f; + } + } + float radius() const { return 2.f; } + }; + + // A pre-computed span of pixels along a single dimension. + // The output pixel will be the weighted sum of pixels starting from start. + struct Spans { + // The maximum span size of any output pixel. + int _spanSize; + // int32 tensor with shape {outputSize}. + NDArray _starts; + + // float32 tensor of size {outputSize, spanSize}. + // The output pixel at x is computed as: + // dot_product(input[starts[x]:starts[x]+span_size], weights[x]). + NDArray _weights; + }; + + static int + computeSpans(IKernelFunc* kernel, Nd4jLong const outSize, Nd4jLong const inSize, float const scale, float const translate, bool const antialias, Spans& spans) { + // When sampling, we need the inverse scale and translation, to map from an + // output to an input pixel. + float const invScale = 1.f / scale; + float const invTranslate = -invScale * translate; + // When downsampling the kernel should be scaled since we want to low pass + // filter and interpolate, but when upsampling it should not be since we only + // want to interpolate. + float const kernelScale = antialias ? math::nd4j_max(invScale, 1.f) : 1.f; + spans._spanSize = math::nd4j_min(2 * static_cast(std::ceil(kernel->radius() * kernelScale)) + 1, static_cast(inSize)); + spans._starts = NDArrayFactory::create('c', {outSize}); + spans._weights = NDArrayFactory::create('c', {outSize, spans._spanSize}); + + auto startsVec = spans._starts.bufferAsT(); + auto weightsVector = spans._weights.bufferAsT(); + spans._weights.nullify(); + + const float invKernelScale = 1.f / kernelScale; + int maxSpanSize = 0; + std::vector tempWeights; + + // return value if within bounds or bounds otherwise + auto boundsAmp = [](Nd4jLong const low, Nd4jLong const high, Nd4jLong const value) { + if (high < value) return high; + if (value < low) return low; + return value; + }; + + for (auto x = 0LL; x < outSize; ++x) { + const float columnFloat = x + 0.5f; + const float sampleFloat = columnFloat * invScale + invTranslate; + + // Don't sample when the sampling location is outside the source image. + if (sampleFloat < 0 || sampleFloat > inSize) { + // Add an empty span. + startsVec[x] = 0; + continue; + } + Nd4jLong spanStart = math::nd4j_ceil(sampleFloat - kernel->radius() * kernelScale - 0.5f); + Nd4jLong spanEnd = math::nd4j_floor(sampleFloat + kernel->radius() * kernelScale - 0.5f); + spanStart = boundsAmp(0LL, inSize - 1, spanStart); + spanEnd = boundsAmp(0LL, inSize - 1, spanEnd) + 1; + int const spanSize = spanEnd - spanStart; + if (spanSize > spans._spanSize) { + return Status::CODE(ND4J_STATUS_BAD_INPUT, "Span is too large: "); // + spanSize + " vs " + spans._spanSize);//, spanSize, spans._spanSize)); + } + float totalWeightSum = 0.f; + tempWeights.clear(); + for (int source = spanStart; source < spanEnd; ++source) { + float kernelPos = static_cast(source) + 0.5f - sampleFloat; + float weight = (*kernel)(kernelPos * invKernelScale); + totalWeightSum += weight; + tempWeights.push_back(weight); + } + maxSpanSize = std::max(maxSpanSize, spanSize); + if (math::nd4j_abs(totalWeightSum) >= 1000.f * DataTypeUtils::min()) { // + auto totalWeightSumInverted = 1.0f / totalWeightSum; + auto outIndex = spans._spanSize * x; + for (auto weight : tempWeights) { + weightsVector[outIndex] = weight * totalWeightSumInverted; + ++outIndex; + } + } + startsVec[x] = spanStart; + } + return Status::OK(); + } + + template + static void gatherRows(int const spanSize, int const* starts, Z const* weights, X const* imagePtr, Nd4jLong const inputHeight, Nd4jLong const inputWidth, Nd4jLong const outputHeight, + Nd4jLong const outputWidth, Nd4jLong const channels, Z* outputPtr) { + auto inRowSize = inputWidth * channels; + auto outRowSize = outputWidth * channels; + + auto addScaledVector = [](const X* inVector, int vectorLen, Z weight, Z* outVector) { + Z* outVecEnd = outVector + vectorLen; + for (; outVector != outVecEnd; ++outVector, ++inVector) { + *outVector += weight * static_cast(*inVector); + } + }; + + for (int y = 0; y < outputHeight; ++y) { + Z* outRowData = outputPtr + outRowSize * y; + memset(outRowData, '\0', outRowSize * sizeof(Z));// std::fill(outRowData, outRowData + outRowSize, 0.f); + int inRow = starts[y]; + auto inRowData = imagePtr + inRowSize * inRow; + auto weightsStart = weights + y * spanSize; + auto realSpanSize = math::nd4j_min(starts[y] + spanSize, static_cast(inputHeight)) - starts[y]; + auto weightsEnd = weightsStart + realSpanSize; + for (auto weightPtr = weightsStart; weightPtr != weightsEnd; ++weightPtr) { + addScaledVector(inRowData, inRowSize, *weightPtr, outRowData); + inRowData += inRowSize; + } + } + } + + template + static void gatherColumns(int const spanSize, int const* starts, Z const* weights, Z const* imagesPtr, Nd4jLong const inputHeight, Nd4jLong const inputWidth, Nd4jLong const outputHeight, Nd4jLong const outputWidth, Nd4jLong channels, Z* outputPtr) { + auto inRowSize = inputWidth * channels; + auto outRowSize = outputWidth * channels; + + for (auto y = 0LL; y < outputHeight; ++y) { + auto inputRowStart = imagesPtr + inRowSize * y; + auto outPixels = outputPtr + outRowSize * y; + for (auto x = 0LL; x < outputWidth; ++x, outPixels += channels) { + auto inPixels = inputRowStart + starts[x] * channels; + auto weightsStart = weights + x * spanSize; + auto realSpanSize = math::nd4j_min(starts[x] + spanSize, static_cast(inputWidth)) - starts[x]; + auto weightsEnd = weightsStart + realSpanSize; + for (int c = 0; c < channels; ++c) { + outPixels[c] = 0.0f; + } + for (auto weightPtr = weightsStart; weightPtr != weightsEnd; ++weightPtr) { + Z w = *weightPtr; + for (int c = 0; c < channels; ++c) { + outPixels[c] += w * static_cast(inPixels[c]); + } + inPixels += channels; + } + } + } + } + + template + static void gatherSpans(int const rowSpanSize, NDArray const& rowStarts, NDArray const& rowWeights, int const colSpanSize, NDArray const& columnStarts, NDArray const& columnWeights, NDArray const* images, NDArray& intermediate, NDArray* output) { + auto batchSize = images->sizeAt(0); + auto inputHeight = images->sizeAt(1); + auto inputWidth = images->sizeAt(2); + auto channels = images->sizeAt(3); + + auto outputHeight = output->sizeAt(1); + auto outputWidth = output->sizeAt(2); + + auto inputPixPerBatch = inputWidth * inputHeight * channels; + auto intermediatePixPerBatch = inputWidth * outputHeight * channels; + auto outputPixPerBatch = outputWidth * outputHeight * channels; + Z* intermediatePtr = intermediate.bufferAsT(); + + const X* imagePtr = images->bufferAsT(); + Z* outPtr = output->bufferAsT(); + for (int b = 0; b < batchSize; ++b, imagePtr += inputPixPerBatch, + intermediatePtr += intermediatePixPerBatch, + outPtr += outputPixPerBatch) { + gatherRows(rowSpanSize, rowStarts.bufferAsT(), rowWeights.bufferAsT(), + imagePtr, inputHeight, inputWidth, outputHeight, + inputWidth, channels, intermediatePtr); + gatherColumns(colSpanSize, columnStarts.bufferAsT(), columnWeights.bufferAsT(), + intermediatePtr, outputHeight, inputWidth, outputHeight, outputWidth, channels, outPtr); + } + } + + template + static int resizeKernel(IKernelFunc* transformationKernel, NDArray const* input, Nd4jLong outWidth, Nd4jLong outHeight, bool antialias, NDArray* output) { + Nd4jLong const batchSize = input->sizeAt(0); + Nd4jLong const inputHeight = input->sizeAt(1); + Nd4jLong const inputWidth = input->sizeAt(2); + Nd4jLong const channels = input->sizeAt(3); + + Z rowScale = Z(outHeight) / Z(inputHeight); + Z columnScale = Z(outWidth) / Z(inputWidth); + + // Return if the output is empty. + if (output->lengthOf() == 0) return Status::OK(); + + Spans colSpans; + + auto res = computeSpans(transformationKernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans); + if (res != Status::OK()) return res; + Spans rowSpans; + res = computeSpans(transformationKernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + + NDArray intermediate = NDArrayFactory::create('c', {batchSize, outHeight, inputWidth, channels}); + + //const functor::Spans& const_row_spans = row_spans; + //typename TTypes::ConstTensor row_starts( + //const_row_spans.starts.tensor()); + auto& rowStarts = rowSpans._starts; // shape {outWidth} + auto& rowWeights = rowSpans._weights; // shape {outWidth, numSpans} + auto& columnStarts = colSpans._starts; // shape {outHeights} + auto& columnWeights = colSpans._weights; // shape {outHeights, numSpans} + + gatherSpans(rowSpans._spanSize, rowStarts, rowWeights, colSpans._spanSize, columnStarts, columnWeights, input, intermediate, output); + return res; + } + + static int resizeBilinear(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + auto kernel = std::unique_ptr(new TriangleKernelFunc()); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel, + (kernel.get(), image, (Nd4jLong) width, (Nd4jLong) height, antialias, output), + NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeBilinear: Unknown error occured."); + } + + static int resizeBicubic(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + if (antialias) { + auto kernel = std::unique_ptr(new KeysCubicKernelFunc()); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel, + (kernel.get(), image, (Nd4jLong) width, (Nd4jLong) height, antialias, output), + NUMERIC_TYPES, FLOAT_TYPES_1); + } + else { + return resizeBicubicFunctorA(context, image, width, height, false, true, output); + } + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeBicubic: Unknown error occured."); + } + + static int resizeNeighbor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + return resizeNeighborFunctor(context, image, width, height, false, true, output); + } + + static int resizeArea(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + return resizeAreaFunctor(context, image, width, height, false, output); + } + + static int resizeLanczos3(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + auto kernel = std::unique_ptr(new LanczosKernelFunc(3.f)); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel, (kernel.get(), image, (Nd4jLong)width, (Nd4jLong)height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeLanczos3: Unknown error occured."); + } + + static int resizeLanczos5(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + auto kernel = std::unique_ptr(new LanczosKernelFunc(5.f)); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel, (kernel.get(), image, (Nd4jLong)width, (Nd4jLong)height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeLanczos5: Unknown error occured."); + } + + static int resizeGaussian(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + auto kernel = std::unique_ptr(new GaussianKernelFunc()); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel, (kernel.get(), image, (Nd4jLong)width, (Nd4jLong)height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeGaussian: Unknown error occured."); + } + + static int resizeMitchellcubic(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + auto kernel = std::unique_ptr(new MitchellCubicKernelFunc()); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel, (kernel.get(), image, (Nd4jLong)width, (Nd4jLong)height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeMitchelcubic: Unknown error occured."); + } + +// ------------------------------------------------------------------------------------------------------------------ // + int resizeImagesFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, + ImageResizeMethods method, bool alignCorners, NDArray* output) { + switch (method) { + case kResizeBilinear: + return resizeBilinearFunctor(context, image, width, height, alignCorners, false, output); + case kResizeNearest: + return resizeNeighborFunctor(context, image, width, height, alignCorners, false, output); + case kResizeBicubic: + return resizeBicubicFunctor(context, image, width, height, alignCorners, false, output); + case kResizeArea: + return resizeAreaFunctor(context, image, width, height, alignCorners, output); + } + nd4j_printf("helper::resizeImagesFunctor: Wrong resize method %i\n", (int)method); + return Status::CODE(ND4J_STATUS_BAD_INPUT, "helper::resizeImagesFunctor: Wrong resize method"); + } // ------------------------------------------------------------------------------------------------------------------ // int resizeFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, - ImageResizeMethods method, bool preserveAspectRatio, bool antialias, NDArray* output) { + ImageResizeMethods method, bool antialias, NDArray* output) { switch (method) { - case kResizeBilinear: return resizeBilinearFunctor(context, image, width, height, false, false, output); break; - case kResizeNearest: return resizeNeighborFunctor(context, image, width, height, false, false, output); break; - case kResizeBicubic: return resizeBicubicFunctor(context, image, width, height, preserveAspectRatio, antialias, output); break; - case kResizeArea: return resizeAreaFunctor(context, image, width, height, preserveAspectRatio, output); - case kResizeLanczos5: - case kResizeGaussian: - case kResizeMitchelcubic: - throw std::runtime_error("helper::resizeFunctor: Non implemented yet."); + case kResizeBilinear: return resizeBilinear(context, image, width, height, antialias, output); + case kResizeNearest: return resizeNeighbor(context, image, width, height, antialias, output); + case kResizeBicubic: return resizeBicubic(context, image, width, height, antialias, output); + case kResizeArea: return resizeArea(context, image, width, height, antialias, output); + case kResizeLanczos3: return resizeLanczos3(context, image, width, height, antialias, output); + case kResizeLanczos5: return resizeLanczos5(context, image, width, height, antialias, output); + case kResizeGaussian: return resizeGaussian(context, image, width, height, antialias, output); + case kResizeMitchellcubic: return resizeMitchellcubic(context, image, width, height, antialias, output); } - return ND4J_STATUS_OK; + nd4j_printf("helper::resizeFunctor: Wrong resize method %i\n", (int)method); + return Status::CODE(ND4J_STATUS_BAD_INPUT, "helper::resizeFunctor: Wrong resize method"); } diff --git a/libnd4j/include/ops/declarable/helpers/cuda/image_resize.cu b/libnd4j/include/ops/declarable/helpers/cuda/image_resize.cu index 180c8ad0e..3365d5d62 100644 --- a/libnd4j/include/ops/declarable/helpers/cuda/image_resize.cu +++ b/libnd4j/include/ops/declarable/helpers/cuda/image_resize.cu @@ -35,6 +35,7 @@ limitations under the License. #include #include +#include namespace sd { namespace ops { @@ -1203,20 +1204,22 @@ namespace helpers { BUILD_SINGLE_TEMPLATE(template int resizeBicubicFunctorA_, (sd::LaunchContext * context, NDArray const* image, int width, int height, bool const alignCorners, bool const halfPixelCenters, NDArray* output), NUMERIC_TYPES); -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - int resizeFunctor(sd::LaunchContext * context, NDArray const* image, int width, int height, - ImageResizeMethods method, bool preserveAspectRatio, bool antialias, NDArray* output) { + +// ------------------------------------------------------------------------------------------------------------------ // + int resizeImagesFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, + ImageResizeMethods method, bool alignCorners, NDArray* output) { switch (method) { - case kResizeBilinear: return resizeBilinearFunctor(context, image, width, height, false, false, output); break; - case kResizeNearest: return resizeNeighborFunctor(context, image, width, height, false, false, output); break; - case kResizeBicubic: return resizeBicubicFunctor(context, image, width, height, preserveAspectRatio, antialias, output); break; - case kResizeLanczos5: - case kResizeGaussian: + case kResizeBilinear: + return resizeBilinearFunctor(context, image, width, height, alignCorners, false, output); + case kResizeNearest: + return resizeNeighborFunctor(context, image, width, height, alignCorners, false, output); + case kResizeBicubic: + return resizeBicubicFunctor(context, image, width, height, alignCorners, false, output); case kResizeArea: - case kResizeMitchelcubic: - throw std::runtime_error("helper::resizeFunctor: Non implemented yet."); + return resizeAreaFunctor(context, image, width, height, alignCorners, output); + default: + throw std::runtime_error("helper::resizeImagesFunctor: Wrong resize method."); } - return ND4J_STATUS_OK; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libnd4j/include/ops/declarable/helpers/cuda/image_resize_v2.cu b/libnd4j/include/ops/declarable/helpers/cuda/image_resize_v2.cu new file mode 100644 index 000000000..b727822c9 --- /dev/null +++ b/libnd4j/include/ops/declarable/helpers/cuda/image_resize_v2.cu @@ -0,0 +1,497 @@ +#include +#include +#include +#include + +namespace sd { + namespace ops { + namespace helpers { +// -------------------------------------------------------------------------------------------------------------- // +// resize v2 implementation // +// -------------------------------------------------------------------------------------------------------------- // +// A functional interface for a scale kernels. +//struct IKernelFunc { +// _CUDA_HD virtual float operator()(float x) const = 0; +// _CUDA_HD virtual float radius() const = 0; +// _CUDA_HD virtual size_t size() const = 0; +//}; + +struct LanczosKernelFunc /*: public IKernelFunc*/ { + // Pass 1 for Lanczos1 kernel, 3 for Lanczos3 etc. + explicit LanczosKernelFunc(float const radius) : _radius(radius) {} + _CUDA_HD float operator()(float x) const { + float const kPI = 3.141592653589793f; + x = math::nd4j_abs(x); + if (x > _radius) return 0.f; + // Need to special case the limit case of sin(x) / x when x is zero. + if (x <= 1.e-3f) { + return 1.f; + } + return _radius * std::sin(kPI * x) * std::sin(kPI * x / _radius) / (kPI * kPI * x * x); + } + _CUDA_HD float radius() const { return _radius; } + const float _radius; +}; + +struct GaussianKernelFunc /*: public IKernelFunc*/ { + static constexpr float kRadiusMultiplier = 3.0f; + // https://en.wikipedia.org/wiki/Gaussian_function + // We use sigma = 0.5, as suggested on p. 4 of Ken Turkowski's "Filters + // for Common Resampling Tasks" for kernels with a support of 3 pixels: + // www.realitypixels.com/turk/computergraphics/ResamplingFilters.pdf + // This implies a radius of 1.5, + explicit GaussianKernelFunc(float radius = 1.5f) + : _radius(radius), _sigma(radius / kRadiusMultiplier) {} + _CUDA_HD float operator()(float x) const { + x = math::nd4j_abs(x); + if (x >= _radius) return 0.0f; + return std::exp(-x * x / (2.0 * _sigma * _sigma)); + } + _CUDA_HD float radius() const { return _radius; } + const float _radius; + const float _sigma; // Gaussian standard deviation +}; + +struct BoxKernelFunc /*: public IKernelFunc*/ { + _CUDA_HD float operator()(float x) const { + x = math::nd4j_abs(x); + return x < 0.5f ? 1.f : x == 0.5f ? 0.5f : 0.f; + } + _CUDA_HD float radius() const { return 1.f; } + _CUDA_HD size_t size() const { return sizeof(BoxKernelFunc); } +}; + +struct TriangleKernelFunc /*: public IKernelFunc*/ { + // https://en.wikipedia.org/wiki/Triangle_function + _CUDA_HD float operator()(float x) const { + x = math::nd4j_abs(x); + return x < 1.f ? 1.f - x : 0.f; + } + _CUDA_HD float radius() const { return 1.f; } +}; + +struct KeysCubicKernelFunc /*: public IKernelFunc*/ { + // http://ieeexplore.ieee.org/document/1163711/ + // R. G. Keys. Cubic convolution interpolation for digital image + // processing. IEEE Transactions on Acoustics, Speech, and Signal + // Processing, 29(6):1153–1160, 1981. + _CUDA_HD float operator()(float x) const { + x = math::nd4j_abs(x); + if (x >= 2.0f) { + return 0.0f; + } else if (x >= 1.0f) { + return ((-0.5f * x + 2.5f) * x - 4.0f) * x + 2.0f; + } else { + return ((1.5f * x - 2.5f) * x) * x + 1.0f; + } + } + _CUDA_HD float radius() const { return 2.f; } +}; + +struct MitchellCubicKernelFunc/* : public IKernelFunc*/ { + // https://doi.org/10.1145/378456.378514 + // D. P. Mitchell and A. N. Netravali. Reconstruction filters in computer + // graphics. Computer Graphics (Proceedings of ACM SIGGRAPH 1988), + // 22(4):221–228, 1988. + _CUDA_HD float operator()(float x) const { + x = math::nd4j_abs(x); + if (x >= 2.f) { + return 0.f; + } else if (x >= 1.f) { + return (((-7.f / 18.f) * x + 2.f) * x - 10.f / 3.f) * x + 16.f / 9.f; + } else { + return (((7.f / 6.f) * x - 2.f) * x) * x + 8.f / 9.f; + } + } + _CUDA_HD float radius() const { return 2.f; } +}; + +// A pre-computed span of pixels along a single dimension. +// The output pixel will be the weighted sum of pixels starting from start. +struct Spans { + // The maximum span size of any output pixel. + int _spanSize; + // int32 tensor with shape {outputSize}. + NDArray _starts; + + // float32 tensor of size {outputSize, spanSize}. + // The output pixel at x is computed as: + // dot_product(input[starts[x]:starts[x]+span_size], weights[x]). + NDArray _weights; +}; + +static inline _CUDA_HD Nd4jLong boundsAmp(Nd4jLong const low, Nd4jLong const high, Nd4jLong const value) { + if (high < value) return high; + if (value < low) return low; + return value; +} + +template +static __global__ void computeSpansKernel(TKernelFunc* kernel, int* startsVec, float* weightsVector, Nd4jLong outSize, Nd4jLong inSize, float kernelScale, int spanSize, float const invScale, float const invTranslate, float invKernelScale, float* tempWeightsBuf) { + + // return value if within bounds or bounds otherwise + auto tid = threadIdx.x + blockIdx.x * blockDim.x; + auto step = blockDim.x * gridDim.x; + __shared__ int maxSpanSize; + + if (threadIdx.x == 0 && blockIdx.x == 0) { + maxSpanSize = 0; + } + __syncthreads(); + + for (auto x = tid; x < outSize; x += step) { + const float columnFloat = x + 0.5f; + const float sampleFloat = columnFloat * invScale + invTranslate; + + // Don't sample when the sampling location is outside the source image. + if (sampleFloat < 0 || sampleFloat > inSize) { + // Add an empty span. + startsVec[x] = 0; + continue; + } + Nd4jLong spanStart = math::nd4j_ceil(sampleFloat - kernel->radius() * kernelScale - 0.5f); + Nd4jLong spanEnd = math::nd4j_floor(sampleFloat + kernel->radius() * kernelScale - 0.5f); + spanStart = boundsAmp(0LL, inSize - 1, spanStart); + spanEnd = boundsAmp(0LL, inSize - 1, spanEnd) + 1; + int const spanSize = spanEnd - spanStart; + if (spanSize > spanSize) { + return ; //throw "Exception"; ////return Status::CODE(ND4J_STATUS_BAD_INPUT, "Span is too large: "); // + spanSize + " vs " + spans._spanSize);//, spanSize, spans._spanSize)); + } + float totalWeightSum = 0.f; + auto tempWeights = &tempWeightsBuf[x]; + auto actualWeights = 0; + for (int source = spanStart; source < spanEnd; ++source) { + float kernelPos = static_cast(source) + 0.5f - sampleFloat; + float weight = (*kernel)(kernelPos * invKernelScale); + totalWeightSum += weight; + tempWeights[actualWeights++] = weight; + } + maxSpanSize = math::nd4j_max(maxSpanSize, spanSize); + if (math::nd4j_abs(totalWeightSum) >= 1000.f * DataTypeUtils::min()) { // + auto totalWeightSumInverted = 1.0f / totalWeightSum; + auto outIndex = spanSize * x; + for (auto weightIndex = 0; weightIndex < actualWeights; ++weightIndex) { + weightsVector[outIndex] = tempWeights[weightIndex] * totalWeightSumInverted; + ++outIndex; + } + } + startsVec[x] = spanStart; + } + +} + +template +static int computeSpans(LaunchContext* context, TKernelFunc& kernel, Nd4jLong const outSize, Nd4jLong const inSize, float const scale, float const translate, bool const antialias, Spans& spans) { + // When sampling, we need the inverse scale and translation, to map from an + // output to an input pixel. + float const invScale = 1.f / scale; + float const invTranslate = -invScale * translate; + // When downsampling the kernel should be scaled since we want to low pass + // filter and interpolate, but when upsampling it should not be since we only + // want to interpolate. + float const kernelScale = antialias ? math::nd4j_max(invScale, 1.f) : 1.f; + spans._spanSize = math::nd4j_min(2 * static_cast(std::ceil(kernel.radius() * kernelScale)) + 1, static_cast(inSize)); + spans._starts = NDArrayFactory::create('c', {outSize}); spans._starts.syncToHost(); + spans._weights = NDArrayFactory::create('c', {outSize, spans._spanSize}); spans._weights.syncToHost(); + + auto startsVec = reinterpret_cast(spans._starts.buffer()); + auto weightsVector = reinterpret_cast(spans._weights.buffer()); + spans._weights.nullify(); + + const float invKernelScale = 1.f / kernelScale; +// NDArray tempWeights = NDArrayFactory::create('c', {outSize, spans._spanSize}); +// auto tempWeightsBuf = reinterpret_cast(tempWeights.specialBuffer()); +// PointersManager mg(context, "ops::helpers::computeSpans"); +// auto specialKernel = reinterpret_cast(mg.replicatePointer(&kernel, sizeof(TKernelFunc))); + auto stream = context->getCudaStream(); + //computeSpansKernel<<<1, 1, 128, *stream>>>(specialKernel, startsVec, weightsVector, outSize, inSize, kernelScale, spans._spanSize, invScale, invTranslate, invKernelScale, tempWeightsBuf); + auto maxSpanSize = 0; + std::vector tempWeights; + for (auto x = 0; x < outSize; x ++) { + const float columnFloat = x + 0.5f; + const float sampleFloat = columnFloat * invScale + invTranslate; + + // Don't sample when the sampling location is outside the source image. + if (sampleFloat < 0 || sampleFloat > inSize) { + // Add an empty span. + startsVec[x] = 0; + continue; + } + Nd4jLong spanStart = math::nd4j_ceil(sampleFloat - kernel.radius() * kernelScale - 0.5f); + Nd4jLong spanEnd = math::nd4j_floor(sampleFloat + kernel.radius() * kernelScale - 0.5f); + spanStart = boundsAmp(0LL, inSize - 1, spanStart); + spanEnd = boundsAmp(0LL, inSize - 1, spanEnd) + 1; + int const spanSize = spanEnd - spanStart; + if (spanSize > spans._spanSize) { + return Status::CODE(ND4J_STATUS_BAD_INPUT, "Span is too large: "); // + spanSize + " vs " + spans._spanSize);//, spanSize, spans._spanSize)); + } + float totalWeightSum = 0.f; + tempWeights.clear(); + + for (int source = spanStart; source < spanEnd; ++source) { + float kernelPos = static_cast(source) + 0.5f - sampleFloat; + float weight = kernel(kernelPos * invKernelScale); + totalWeightSum += weight; + tempWeights.push_back(weight); + } + maxSpanSize = math::nd4j_max(maxSpanSize, spanSize); + if (math::nd4j_abs(totalWeightSum) >= 1000.f * DataTypeUtils::min()) { // + auto totalWeightSumInverted = 1.0f / totalWeightSum; + auto outIndex = spans._spanSize * x; + for (auto weightIndex = 0; weightIndex < tempWeights.size(); ++weightIndex) { + weightsVector[outIndex++] = tempWeights[weightIndex] * totalWeightSumInverted; +// ++outIndex; + } + } + startsVec[x] = spanStart; + } + spans._starts.tickWriteHost(); spans._weights.tickWriteHost(); + spans._starts.syncToDevice(); + spans._weights.syncToDevice(); +// cudaStreamSynchronize(*stream); + return Status::OK(); +} + +//template int computeSpans(LaunchContext* context, TriangleKernelFunc& kernel, Nd4jLong const outSize, Nd4jLong const inSize, float const scale, float const translate, bool const antialias, Spans& spans); + + +template +static __device__ void gatherRows(int const spanSize, int const* starts, Z const* weights, X const* imagePtr, Nd4jLong const inputHeight, Nd4jLong const inputWidth, Nd4jLong const outputHeight, + Nd4jLong const outputWidth, Nd4jLong const channels, Z* outputPtr) { + auto inRowSize = inputWidth * channels; + auto outRowSize = outputWidth * channels; + + auto addScaledVector = [](const X* inVector, int vectorLen, Z weight, Z* outVector) { + Z* outVecEnd = outVector + vectorLen; + for (; outVector != outVecEnd; ++outVector, ++inVector) { + *outVector += weight * static_cast(*inVector); + } + }; + + for (int y = 0; y < outputHeight; ++y) { + Z* outRowData = outputPtr + outRowSize * y; + memset(outRowData, '\0', outRowSize * sizeof(Z));// std::fill(outRowData, outRowData + outRowSize, 0.f); + int inRow = starts[y]; + auto inRowData = imagePtr + inRowSize * inRow; + auto weightsStart = weights + y * spanSize; + auto realSpanSize = math::nd4j_min(starts[y] + spanSize, static_cast(inputHeight)) - starts[y]; + auto weightsEnd = weightsStart + realSpanSize; + for (auto weightPtr = weightsStart; weightPtr != weightsEnd; ++weightPtr) { + addScaledVector(inRowData, inRowSize, *weightPtr, outRowData); + inRowData += inRowSize; + } + } +} + +template +static __device__ void gatherColumns(int const spanSize, int const* starts, Z const* weights, Z const* imagesPtr, Nd4jLong const inputHeight, Nd4jLong const inputWidth, Nd4jLong const outputHeight, Nd4jLong const outputWidth, Nd4jLong channels, Z* outputPtr) { + auto inRowSize = inputWidth * channels; + auto outRowSize = outputWidth * channels; + + for (auto y = 0LL; y < outputHeight; ++y) { + auto inputRowStart = imagesPtr + inRowSize * y; + auto outPixels = outputPtr + outRowSize * y; + for (auto x = 0LL; x < outputWidth; ++x, outPixels += channels) { + auto inPixels = inputRowStart + starts[x] * channels; + auto weightsStart = weights + x * spanSize; + auto realSpanSize = math::nd4j_min(starts[x] + spanSize, static_cast(inputWidth)) - starts[x]; + auto weightsEnd = weightsStart + realSpanSize; + for (int c = 0; c < channels; ++c) { + outPixels[c] = 0.0f; + } + for (auto weightPtr = weightsStart; weightPtr != weightsEnd; ++weightPtr) { + Z w = *weightPtr; + for (int c = 0; c < channels; ++c) { + outPixels[c] += w * static_cast(inPixels[c]); + } + inPixels += channels; + } + } + } +} + +template +static __global__ void batchedGatherSpan(Nd4jLong batchSize, Nd4jLong inputWidth, Nd4jLong inputHeight, Nd4jLong outputWidth, Nd4jLong outputHeight, Nd4jLong channels, int rowSpanSize, int const* rowStartsBuf, Z const* rowWeightBuf, int columnSpanSize, int const* columnStartsBuf, Z const* columnWeightBuf, X const* pImages, Z* pIntermediate, Z* pOutput, + Nd4jLong inputPixPerBatch, Nd4jLong intermediatePixPerBatch, Nd4jLong outputPixPerBatch) { + + auto tid = threadIdx.x + blockIdx.x * blockDim.x; + auto step = blockDim.x * gridDim.x; + + for (int b = tid; b < batchSize; b += step) { + auto imagePtr = pImages + b * inputPixPerBatch; + auto intermediatePtr = pIntermediate + b * intermediatePixPerBatch; + auto outputPtr = pOutput + b * outputPixPerBatch; + gatherRows(rowSpanSize, rowStartsBuf, rowWeightBuf, + imagePtr, inputHeight, inputWidth, outputHeight, + inputWidth, channels, intermediatePtr); + gatherColumns(columnSpanSize, columnStartsBuf, columnWeightBuf, + intermediatePtr, outputHeight, inputWidth, outputHeight, outputWidth, channels, outputPtr); + } +} + +template +static void gatherSpans(LaunchContext* context, int const rowSpanSize, NDArray const& rowStarts, NDArray const& rowWeights, int const colSpanSize, NDArray const& columnStarts, NDArray const& columnWeights, NDArray const* images, NDArray& intermediate, NDArray* output) { + auto batchSize = images->sizeAt(0); + auto inputHeight = images->sizeAt(1); + auto inputWidth = images->sizeAt(2); + auto channels = images->sizeAt(3); + + auto outputHeight = output->sizeAt(1); + auto outputWidth = output->sizeAt(2); + + auto inputPixPerBatch = inputWidth * inputHeight * channels; + auto intermediatePixPerBatch = inputWidth * outputHeight * channels; + auto outputPixPerBatch = outputWidth * outputHeight * channels; + auto intermediatePtr = reinterpret_cast(intermediate.specialBuffer()); + + auto imagePtr = reinterpret_cast(images->specialBuffer()); + auto outputPtr = reinterpret_cast(output->specialBuffer()); + auto stream = context->getCudaStream(); + auto rowStartsBuf = reinterpret_cast(rowStarts.specialBuffer()); + auto rowWeightBuf = reinterpret_cast(rowWeights.specialBuffer()); + auto columnStartsBuf = reinterpret_cast(columnStarts.specialBuffer()); + auto columnWeightBuf = reinterpret_cast(columnWeights.specialBuffer()); + batchedGatherSpan<<<128, 128, 256, *stream>>>(batchSize, inputWidth, inputHeight, outputWidth, outputHeight, channels, rowSpanSize, rowStartsBuf, rowWeightBuf, colSpanSize, columnStartsBuf, columnWeightBuf, imagePtr, intermediatePtr, outputPtr, inputPixPerBatch, intermediatePixPerBatch, outputPixPerBatch); +} + +template +static int resizeKernel(LaunchContext* context, ImageResizeMethods method, NDArray const* input, Nd4jLong outWidth, Nd4jLong outHeight, bool antialias, NDArray* output) { + Nd4jLong const batchSize = input->sizeAt(0); + Nd4jLong const inputHeight = input->sizeAt(1); + Nd4jLong const inputWidth = input->sizeAt(2); + Nd4jLong const channels = input->sizeAt(3); + NDArray::prepareSpecialUse({output}, {input}); + Z rowScale = Z(outHeight) / Z(inputHeight); + Z columnScale = Z(outWidth) / Z(inputWidth); + + // Return if the output is empty. + if (output->lengthOf() == 0) return Status::OK(); + + Spans colSpans; + Spans rowSpans; + auto res = Status::OK(); + switch(method) { + case kResizeBilinear: { + TriangleKernelFunc kernel; + res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, + colSpans); + if (res != Status::OK()) return res; + res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + + } + break; + case kResizeBicubic: { + KeysCubicKernelFunc kernel; + res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, + colSpans); + if (res != Status::OK()) return res; + res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + } break; + case kResizeLanczos3:{ + LanczosKernelFunc kernel(3.f); + res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, + colSpans); + if (res != Status::OK()) return res; + res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + + } break; + + case kResizeLanczos5: { + LanczosKernelFunc kernel(5.f); + res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, + colSpans); + if (res != Status::OK()) return res; + res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + + } break; + case kResizeGaussian: { + GaussianKernelFunc kernel; + res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, + colSpans); + if (res != Status::OK()) return res; + res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + + } break; + case kResizeMitchellcubic:{ + MitchellCubicKernelFunc kernel; + res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, + colSpans); + if (res != Status::OK()) return res; + res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans); + + } break; + }; + + NDArray intermediate = NDArrayFactory::create('c', {batchSize, outHeight, inputWidth, channels}); + + //const functor::Spans& const_row_spans = row_spans; + //typename TTypes::ConstTensor row_starts( + //const_row_spans.starts.tensor()); + auto& rowStarts = rowSpans._starts; // shape {outWidth} + auto& rowWeights = rowSpans._weights; // shape {outWidth, numSpans} + auto& columnStarts = colSpans._starts; // shape {outHeights} + auto& columnWeights = colSpans._weights; // shape {outHeights, numSpans} + + gatherSpans(context, rowSpans._spanSize, rowStarts, rowWeights, colSpans._spanSize, columnStarts, columnWeights, input, intermediate, output); + + NDArray::registerSpecialUse({output}, {input}); + return res; +} + + +static int resizeTriangle(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { +// std::unique_ptr kernel(new TriangleKernelFunc); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,(context, kResizeBilinear, image, width, height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeTriangle: This resize method is avaliable in future versions"); +} + +static int resizeLanczos3(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { +// std::unique_ptr kernel(new LanczosKernelFunc(3.f)); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,(context, kResizeLanczos3, image, width, height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeLanczos3: This resize method is avaliable in future versions"); +} + +static int resizeLanczos5(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { +// std::unique_ptr kernel(new LanczosKernelFunc(5.f)); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,(context, kResizeLanczos5, image, width, height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeLanczos5: This resize method is avaliable in future versions"); +} + +static int resizeGaussian(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,(context, kResizeGaussian, image, width, height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeGaussian: This resize method is avaliable in future versions"); +} +static int resizeMitchellcubic(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,(context, kResizeMitchellcubic, image, width, height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeMitchelcubic: This resize method is avaliable in future versions"); +} +static int resizeKeycubic(sd::LaunchContext * context, NDArray const* image, int const width, int const height, bool const antialias, NDArray* output) { + if (!antialias) + return resizeBicubicFunctorA(context, image, width, height, false, true, output); + BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,(context, kResizeBicubic, image, width, height, antialias, output), NUMERIC_TYPES, FLOAT_TYPES_1); + return Status::CODE(ND4J_STATUS_VALIDATION, "helpers::resizeKeycubic: This resize method is avaliable in future versions"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int resizeFunctor(sd::LaunchContext * context, NDArray const* image, int width, int height, + ImageResizeMethods method, bool antialias, NDArray* output) { + switch (method) { + case kResizeBilinear: return resizeTriangle(context, image, width, height, antialias, output); + case kResizeNearest: return resizeNeighborFunctor(context, image, width, height, false, true, output); + case kResizeBicubic: return resizeKeycubic(context, image, width, height, antialias, output); + case kResizeLanczos3: return resizeLanczos3(context, image, width, height, antialias, output); + case kResizeLanczos5: return resizeLanczos5(context, image, width, height, antialias, output); + case kResizeGaussian: return resizeGaussian(context, image, width, height, antialias, output); + case kResizeArea: return resizeAreaFunctor(context, image, width, height, false, output); + case kResizeMitchellcubic: return resizeMitchellcubic(context, image, width, height, antialias, output); + default: + nd4j_printf("helper::resizeFunctor: Wrong resize method %i\n", (int)method); + throw std::runtime_error("helper::resizeFunctor: Wrong resize method."); + } + return ND4J_STATUS_OK; +} + + + } + } +} \ No newline at end of file diff --git a/libnd4j/include/ops/declarable/helpers/image_resize.h b/libnd4j/include/ops/declarable/helpers/image_resize.h index c11e94ed4..bd9e10b58 100644 --- a/libnd4j/include/ops/declarable/helpers/image_resize.h +++ b/libnd4j/include/ops/declarable/helpers/image_resize.h @@ -28,13 +28,17 @@ namespace ops { namespace helpers { enum ImageResizeMethods { - kResizeBilinear = 1, - kResizeBicubic, + kResizeBilinear = 0, // as java require kResizeNearest, + kResizeBicubic, + kResizeArea, kResizeGaussian, + kResizeLanczos3, kResizeLanczos5, - kResizeMitchelcubic, - kResizeArea + kResizeMitchellcubic, + kResizeFirst = kResizeBilinear, + kResizeLast = kResizeMitchellcubic, + kResizeOldLast = kResizeArea }; int resizeBilinearFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, @@ -49,7 +53,10 @@ namespace helpers { bool const alignCorners, NDArray* output); int resizeFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, - ImageResizeMethods method, bool preserveAspectRatio, bool antialias, NDArray* output); + ImageResizeMethods method, bool antialias, NDArray* output); + + int resizeImagesFunctor(sd::LaunchContext * context, NDArray const* image, int const width, int const height, + ImageResizeMethods method, bool alignCorners, NDArray* output); } } } diff --git a/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests10.cpp b/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests10.cpp index 6d89bd182..2ffc2c22d 100644 --- a/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests10.cpp +++ b/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests10.cpp @@ -396,6 +396,29 @@ TEST_F(DeclarableOpsTests10, TestMarixBandPart_Test_1) { ASSERT_TRUE(exp.equalsTo(results.at(0))); } +/////////////////////////////////////////////////////////////////// +TEST_F(DeclarableOpsTests10, TestMarixBandPart_Test_2) { + + auto x = NDArrayFactory::create('c', {2, 3, 3}); + auto minD = NDArrayFactory::create(1); + auto maxD = NDArrayFactory::create(1); + auto exp = NDArrayFactory::create('c', {2, 3, 3}); + x.linspace(1); + exp.linspace(1); + exp.p(0, 0, 2, 0.); + exp.p(1, 0, 2, 0.); + exp.p(0, 2, 0, 0.); + exp.p(1, 2, 0, 0.); + + sd::ops::matrix_band_part op; + auto results = op.evaluate({&x, &minD, &maxD}, {}, {}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + //results.at(0)->printIndexedBuffer("MBP Test1"); + //exp.printIndexedBuffer("MBP Expec"); + ASSERT_TRUE(exp.equalsTo(results.at(0))); +} + ////////////////////////////////////////////////////////////////////////////// TEST_F(DeclarableOpsTests10, atan2_test1) { @@ -1528,6 +1551,71 @@ TEST_F(DeclarableOpsTests10, ImageResizeBilinear_Test01) { } +TEST_F(DeclarableOpsTests10, ResizeImages_Test1) { + + NDArray input = NDArrayFactory::create('c', {2, 4, 5, 3}); + input.linspace(1.); + + auto expected = NDArrayFactory::create('c', {2, 7, 9, 3}, { + 1.f, 2.f, 3.f, 2.6666667f, 3.6666667f, 4.666667f, 4.3333335f, 5.3333335f, 6.3333335f, 6.f, + 7.f, 8.f, 7.666667f, 8.666667f, 9.666667f, 9.333334f, 10.333334f, 11.333334f, 11.f, 12.f, + 13.f, 12.666667f, 13.666667f, 14.666667f, 13.f, 14.f, 15.f, 9.571429f, 10.571429f, 11.571429f, + 11.238095f, 12.238095f, 13.238095f, 12.904762f, 13.904762f, 14.904762f, 14.571429f, 15.571429f, 16.57143f, + 16.238096f, 17.238096f, 18.238096f, 17.904762f, 18.904762f, 19.904762f, 19.57143f, 20.57143f, 21.57143f, + 21.238096f, 22.238096f, 23.238096f, 21.57143f, 22.57143f, 23.57143f, 18.142859f, 19.142859f, 20.142859f, + 19.809525f, 20.809525f, 21.809525f, 21.476192f, 22.476192f, 23.476192f, 23.142859f, 24.142859f, 25.142859f, + 24.809526f, 25.809526f, 26.809526f, 26.476192f, 27.476192f, 28.476192f, 28.142859f, 29.142859f, 30.142859f, + 29.809526f, 30.809526f, 31.809526f, 30.142859f, 31.142859f, 32.142857f, 26.714287f, 27.714287f, 28.714287f, + 28.380955f, 29.380955f, 30.380955f, 30.04762f, 31.04762f, 32.047623f, 31.714287f, 32.714287f, 33.714287f, + 33.380955f, 34.380955f, 35.380955f, 35.047623f, 36.047623f, 37.047623f, 36.714287f, 37.714287f, 38.714287f, + 38.380955f, 39.380955f, 40.380955f, 38.714287f, 39.714287f, 40.714287f, 35.285717f, 36.285717f, 37.285717f, + 36.952385f, 37.952385f, 38.952385f, 38.61905f, 39.61905f, 40.61905f, 40.285717f, 41.285717f, 42.285717f, + 41.952385f, 42.952385f, 43.952385f, 43.61905f, 44.61905f, 45.61905f, 45.285717f, 46.285717f, 47.285717f, + 46.952385f, 47.952385f, 48.952385f, 47.285717f, 48.285717f, 49.285717f, 43.857143f, 44.857143f, 45.857143f, + 45.52381f, 46.52381f, 47.52381f, 47.190475f, 48.190475f, 49.190475f, 48.857143f, 49.857143f, 50.857143f, + 50.52381f, 51.52381f, 52.52381f, 52.190475f, 53.190475f, 54.190475f, 53.857143f, 54.857143f, 55.857143f, + 55.52381f, 56.52381f, 57.52381f, 55.857143f, 56.857143f, 57.857143f, 46.f, 47.f, 48.f, + 47.666668f, 48.666668f, 49.666668f, 49.333332f, 50.333332f, 51.333332f, 51.f, 52.f, 53.f, + 52.666668f, 53.666668f, 54.666668f, 54.333332f, 55.333332f, 56.333332f, 56.f, 57.f, 58.f, + 57.666668f, 58.666668f, 59.666668f, 58.f, 59.f, 60.f, 61.f, 62.f, 63.f, + 62.666668f, 63.666668f, 64.666664f, 64.333336f, 65.333336f, 66.333336f, 66.f, 67.f, 68.f, + 67.666664f, 68.666664f, 69.666664f, 69.333336f, 70.333336f, 71.333336f, 71.f, 72.f, 73.f, + 72.666664f, 73.666664f, 74.666664f, 73.f, 74.f, 75.f, 69.57143f, 70.57143f, 71.57143f, + 71.2381f, 72.2381f, 73.23809f, 72.90476f, 73.90476f, 74.90476f, 74.57143f, 75.57143f, 76.57143f, + 76.23809f, 77.23809f, 78.23809f, 77.90476f, 78.90476f, 79.90476f, 79.57143f, 80.57143f, 81.57143f, + 81.23809f, 82.23809f, 83.23809f, 81.57143f, 82.57143f, 83.57143f, 78.14286f, 79.14286f, 80.14286f, + 79.809525f, 80.809525f, 81.809525f, 81.4762f, 82.4762f, 83.4762f, 83.14286f, 84.14286f, 85.14286f, + 84.809525f, 85.809525f, 86.809525f, 86.4762f, 87.4762f, 88.4762f, 88.14286f, 89.14286f, 90.14286f, + 89.809525f, 90.809525f, 91.809525f, 90.14286f, 91.14286f, 92.14286f, 86.71429f, 87.71429f, 88.71429f, + 88.38095f, 89.38095f, 90.38095f, 90.04762f, 91.04762f, 92.04762f, 91.71429f, 92.71429f, 93.71429f, + 93.38095f, 94.38095f, 95.38095f, 95.04762f, 96.04762f, 97.04762f, 96.71429f, 97.71429f, 98.71429f, + 98.38095f, 99.38095f, 100.38095f, 98.71429f, 99.71429f, 100.71429f, 95.28571f, 96.28571f, 97.28571f, + 96.95238f, 97.95238f, 98.95238f, 98.61905f, 99.61905f, 100.61905f, 100.28571f, 101.28571f, 102.28571f, + 101.95238f, 102.95238f, 103.95238f, 103.61905f, 104.61905f, 105.61905f, 105.28571f, 106.28571f, 107.28571f, + 106.95238f, 107.95238f, 108.95238f, 107.28571f, 108.28571f, 109.28571f, 103.85715f, 104.85715f, 105.85715f, + 105.5238f, 106.5238f, 107.5238f,107.190475f,108.190475f,109.190475f, 108.85715f, 109.85715f, 110.85715f, + 110.5238f, 111.5238f, 112.5238f,112.190475f,113.190475f,114.190475f, 113.85715f, 114.85715f, 115.85715f, + 115.5238f, 116.5238f, 117.5238f, 115.85715f, 116.85715f, 117.85715f, 106.f, 107.f, 108.f, + 107.666664f,108.666664f,109.666664f,109.333336f,110.333336f,111.333336f, 111.f, 112.f, 113.f, + 112.666664f,113.666664f,114.666664f,114.333336f,115.333336f,116.333336f, 116.f, 117.f, 118.f, + 117.666664f,118.666664f,119.666664f, 118.f, 119.f, 120.f + }); + + auto size = NDArrayFactory::create({7, 11}); + sd::ops::resize_images op; + auto results = op.evaluate({&input, &size}, {}, {0}, {false, true}); // resize with bilinear method + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + NDArray *result = results.at(0); + +// result->printBuffer("Resized to 7x9"); +// expected.printBuffer("Expect for 7x9"); +// result.printShapeInfo("Output shape"); +// expected.printShapeInfo("Expect shape"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} TEST_F(DeclarableOpsTests10, ImageResizeBilinear_Test02) { NDArray input = NDArrayFactory::create('c', {2, 5,5,3}, { diff --git a/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests11.cpp b/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests11.cpp index 23c40ebae..97dcf7574 100644 --- a/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests11.cpp +++ b/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests11.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using namespace sd; @@ -1346,6 +1347,34 @@ TEST_F(DeclarableOpsTests11, ImageResizeArea_Test8) { ASSERT_TRUE(expected.equalsTo(result)); } +TEST_F(DeclarableOpsTests11, ResizeImages_Test8) { + + NDArray input = NDArrayFactory::create('c', {1, 3, 3, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9 + }); + + NDArray expected = NDArrayFactory::create('c', {1, 6, 6, 1}, { +// 1.f, 1.f, 2.f, 2.f, 3.f, 3.f, 1.f, 1.f, 2.f, 2.f, 3.f, 3.f, 4.f, 4.f, 5.f, 5.f, 6.f, 6.f, 4.f, 4.f, 5.f, 5.f, +// 6.f, 6.f, 7.f, 7.f, 8.f, 8.f, 9.f, 9.f, 7.f, 7.f, 8.f, 8.f, 9.f, 9.f + 1.f , 1.f , 1.5f, 2.f , 2.f, 3.f, 1.f , 1.f , 1.5f, 2.f , 2.f, 3.f, + 2.5f, 2.5f, 3.f, 3.5f, 3.5f, 4.5f, 4.f , 4.f , 4.5f , 5.f, 5.f, 6.f , + 4.f, 4.f, 4.5f , 5.f, 5.f, 6.f, 7.f , 7.f , 7.5f , 8.f , 8.f , 9.f + }); + //input.linspace(1); +// auto size = NDArrayFactory::create({6, 6}); + sd::ops::resize_images op; + auto results = op.evaluate({&input}, {}, {6, 8, ops::helpers::kResizeArea}, {true, true}); // resize_area to 6x8 with align corners and preserve aspect ratio of input image + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + NDArray* result = results.at(0); + +// result->printBuffer("Area Resized to 6x6"); +// expected.printBuffer("Area Expect for 6x6"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + /////////////////////////////////////////////////////////////////// TEST_F(DeclarableOpsTests11, ImageResizeArea_Test9) { @@ -1354,7 +1383,10 @@ TEST_F(DeclarableOpsTests11, ImageResizeArea_Test9) { }); NDArray expected = NDArrayFactory::create('c', {1, 10, 10, 4}, { - 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333336f, 8.999999f, 9.999999f, 11.000000f, 11.999999f, 8.999999f, 9.999999f, 11.000000f, 11.999999f, 8.999998f, 9.999997f, 10.999997f, 11.999997f, 13.000003f, 14.000004f, 15.000003f, 16.000004f, 13.000003f, 14.000004f, 15.000003f, 16.000004f, 13.000003f, 14.000004f, 15.000003f, 16.000004f, 15.666671f, 16.666672f, 17.666672f, 18.666672f, 17.000006f, 18.000004f, 19.000006f, 20.000004f, 17.000006f, 18.000004f, 19.000006f, 20.000004f, 18.333344f, 19.333344f, 20.333345f, 21.333344f, 21.000006f, 22.000006f, 23.000006f, 24.000006f, 21.000006f, 22.000006f, 23.000006f, 24.000006f, 21.000002f, 22.000000f, 23.000002f, 24.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 15.666667f, 16.666668f, 17.666668f, 18.666668f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 18.333340f, 19.333340f, 20.333342f, 21.333340f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 20.999996f, 21.999996f, 22.999994f, 23.999996f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 15.666667f, 16.666668f, 17.666668f, 18.666668f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 18.333340f, 19.333340f, 20.333342f, 21.333340f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 20.999996f, 21.999996f, 22.999994f, 23.999996f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 15.666667f, 16.666668f, 17.666668f, 18.666668f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 18.333340f, 19.333340f, 20.333342f, 21.333340f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 20.999996f, 21.999996f, 22.999994f, 23.999996f, 12.999995f, 13.999995f, 14.999994f, 15.999994f, 12.999995f, 13.999995f, 14.999994f, 15.999994f, 12.999995f, 13.999995f, 14.999994f, 15.999994f, 15.666661f, 16.666662f, 17.666660f, 18.666660f, 16.999994f, 17.999994f, 18.999992f, 19.999992f, 16.999994f, 17.999994f, 18.999992f, 19.999992f, 18.333334f, 19.333332f, 20.333334f, 21.333332f, 20.999992f, 21.999992f, 22.999990f, 23.999992f, 20.999992f, 21.999992f, 22.999990f, 23.999992f, 20.999989f, 21.999989f, 22.999987f, 23.999987f + 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, + 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, + 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, + 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333337f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 9.000000f, 10.000000f, 11.000000f, 12.000000f, 8.999998f, 9.999998f, 10.999998f, 11.999998f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 1.000000f, 2.000000f, 3.000000f, 4.000000f, 3.666667f, 4.666667f, 5.666667f, 6.666667f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 5.000000f, 6.000000f, 7.000000f, 8.000000f, 6.333336f, 7.333336f, 8.333336f, 9.333336f, 8.999999f, 9.999999f, 11.000000f, 11.999999f, 8.999999f, 9.999999f, 11.000000f, 11.999999f, 8.999998f, 9.999997f, 10.999997f, 11.999997f, 13.000003f, 14.000004f, 15.000003f, 16.000004f, 13.000003f, 14.000004f, 15.000003f, 16.000004f, 13.000003f, 14.000004f, 15.000003f, 16.000004f, 15.666671f, 16.666672f, 17.666672f, 18.666672f, 17.000006f, 18.000004f, 19.000006f, 20.000004f, 17.000006f, 18.000004f, 19.000006f, 20.000004f, 18.333344f, 19.333344f, 20.333345f, 21.333344f, 21.000006f, 22.000006f, 23.000006f, 24.000006f, 21.000006f, 22.000006f, 23.000006f, 24.000006f, 21.000002f, 22.000000f, 23.000002f, 24.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 15.666667f, 16.666668f, 17.666668f, 18.666668f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 18.333340f, 19.333340f, 20.333342f, 21.333340f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 20.999996f, 21.999996f, 22.999994f, 23.999996f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 15.666667f, 16.666668f, 17.666668f, 18.666668f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 18.333340f, 19.333340f, 20.333342f, 21.333340f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 20.999996f, 21.999996f, 22.999994f, 23.999996f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 13.000000f, 14.000001f, 15.000000f, 16.000000f, 15.666667f, 16.666668f, 17.666668f, 18.666668f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 17.000002f, 18.000000f, 19.000002f, 20.000000f, 18.333340f, 19.333340f, 20.333342f, 21.333340f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 21.000002f, 22.000000f, 22.999998f, 24.000000f, 20.999996f, 21.999996f, 22.999994f, 23.999996f, 12.999995f, 13.999995f, 14.999994f, 15.999994f, 12.999995f, 13.999995f, 14.999994f, 15.999994f, 12.999995f, 13.999995f, 14.999994f, 15.999994f, 15.666661f, 16.666662f, 17.666660f, 18.666660f, 16.999994f, 17.999994f, 18.999992f, 19.999992f, 16.999994f, 17.999994f, 18.999992f, 19.999992f, 18.333334f, 19.333332f, 20.333334f, 21.333332f, 20.999992f, 21.999992f, 22.999990f, 23.999992f, 20.999992f, 21.999992f, 22.999990f, 23.999992f, 20.999989f, 21.999989f, 22.999987f, 23.999987f }); //input.linspace(1); diff --git a/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests12.cpp b/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests12.cpp index 2bca43ae9..66762f79d 100644 --- a/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests12.cpp +++ b/libnd4j/tests_cpu/layers_tests/DeclarableOpsTests12.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace sd; @@ -2821,6 +2822,330 @@ TEST_F(DeclarableOpsTests12, QR_Test_2) { } +TEST_F(DeclarableOpsTests12, ImageResize_Test1) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 0.628328f, 0.97913796f, 1.8058043f, 2.563919f, 2.844548f, + 3.6026628f, 4.4293294f, 4.7801394f, 2.9474494f, 3.2982588f, + 4.1249247f, 4.8830395f, 5.1636696f, 5.9217834f, 6.7484493f, + 7.09926f, 8.165832f, 8.516642f, 9.3433075f, 10.101422f, + 10.382052f, 11.140167f, 11.966835f, 12.317646f, 10.924093f, + 11.274903f, 12.10157f, 12.859686f, 13.140315f, 13.898429f, + 14.725095f, 15.075906f, 13.682358f, 14.033167f, 14.859833f, + 15.617949f, 15.898578f, 16.656693f, 17.48336f, 17.834171f, + 18.900742f, 19.251549f, 20.078213f, 20.83633f, 21.11696f, + 21.875074f, 22.701742f, 23.052553f, 21.219858f, 21.57067f, + 22.397337f, 23.155449f, 23.436079f, 24.194195f, 25.020863f, + 25.371672f + }); + + sd::ops::image_resize op; + // resize with lancos5 without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeLanczos5}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Lancos5 Resized to 7x8"); +// expected.printBuffer("Lancos5 Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test2) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 0.628328f, 0.97913796f, 1.8058043f, 2.563919f, 2.844548f, + 3.6026628f, 4.4293294f, 4.7801394f, 2.9474494f, 3.2982588f, + 4.1249247f, 4.8830395f, 5.1636696f, 5.9217834f, 6.7484493f, + 7.09926f, 8.165832f, 8.516642f, 9.3433075f, 10.101422f, + 10.382052f, 11.140167f, 11.966835f, 12.317646f, 10.924093f, + 11.274903f, 12.10157f, 12.859686f, 13.140315f, 13.898429f, + 14.725095f, 15.075906f, 13.682358f, 14.033167f, 14.859833f, + 15.617949f, 15.898578f, 16.656693f, 17.48336f, 17.834171f, + 18.900742f, 19.251549f, 20.078213f, 20.83633f, 21.11696f, + 21.875074f, 22.701742f, 23.052553f, 21.219858f, 21.57067f, + 22.397337f, 23.155449f, 23.436079f, 24.194195f, 25.020863f, + 25.371672f + }); + + sd::ops::image_resize op; + // resize with lanczos5 without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeLanczos5}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result.printBuffer("Lanczos5 Resized to 8x7"); +// expected.printBuffer("Lanczos5 Expect for 8x7"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test3) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 0.6537938f, 1.0309073f, 1.8018917f, 2.4606667f, 2.9888396f, 3.6476145f, 4.418599f, + 4.7957115f, 3.1913466f, 3.5684595f, 4.3394437f, 4.998219f, 5.526393f, 6.185168f, + 6.956152f, 7.3332644f, 7.626866f, 8.00398f, 8.774965f, 9.433739f, 9.961912f, + 10.620688f, 11.391673f, 11.7687845f, 10.929041f, 11.306154f, 12.077138f, 12.735914f, + 13.264087f, 13.922862f, 14.693848f, 15.07096f, 14.231217f, 14.60833f, 15.379314f, + 16.038086f, 16.56626f, 17.225037f, 17.996023f, 18.373135f, 18.666735f, 19.043848f, + 19.814833f, 20.473606f, 21.00178f, 21.660557f, 22.431541f, 22.808653f, 21.204287f, + 21.581398f, 22.352386f, 23.01116f, 23.539333f, 24.19811f, 24.969095f, 25.346205f + }); + + sd::ops::image_resize op; + // resize with lanczos3 without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeLanczos3}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result.printBuffer("Lanczos3 Resized to 8x7"); +// expected.printBuffer("Lanczos3 Expect for 8x7"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test4) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 1.4150869f, 1.7928237f, 2.4084527f, 3.0680697f, 3.6419308f, 4.301548f, 4.9171767f, + 5.294914f, 4.012885f, 4.390622f, 5.0062513f, 5.6658688f, 6.23973f, 6.899347f, + 7.514975f, 7.8927126f, 7.358912f, 7.736648f, 8.352278f, 9.011895f, 9.585756f, + 10.245375f, 10.861001f, 11.238739f, 11.060086f, 11.437822f, 12.0534525f, 12.713069f, + 13.28693f, 13.946548f, 14.562176f, 14.939912f, 14.761261f, 15.138998f, 15.754629f, + 16.414246f, 16.988108f, 17.647724f, 18.263351f, 18.641088f, 18.107288f, 18.485023f, + 19.100655f, 19.760273f, 20.334133f, 20.993752f, 21.609377f, 21.987114f, 20.705086f, + 21.082823f, 21.698452f, 22.35807f, 22.93193f, 23.591549f, 24.207174f, 24.584913f + }); + + sd::ops::image_resize op; + // resize with gaussian without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeGaussian}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result.printBuffer("Lanczos3 Resized to 8x7"); +// expected.printBuffer("Lanczos3 Expect for 8x7"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test5) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 0.6372399f, 1.0536414f, 1.7716959f, 2.3966959f, 3.0216959f, 3.6466963f, 4.3647504f, 4.781152f, + 3.3926036f, 3.8090053f, 4.5270596f, 5.1520596f, 5.7770596f, 6.4020596f, 7.1201134f, 7.5365143f, + 7.358708f, 7.7751093f, 8.493164f, 9.118163f, 9.743165f, 10.368165f, 11.086218f, 11.502619f, + 10.928043f, 11.344445f, 12.0625f, 12.6875f, 13.3125f, 13.9375f, 14.655554f, 15.071955f, + 14.49738f, 14.913782f, 15.631836f, 16.256836f, 16.881836f, 17.506836f, 18.22489f, 18.64129f, + 18.463486f, 18.879889f, 19.597942f, 20.222942f, 20.847942f, 21.472942f, 22.190996f, 22.607397f, + 21.218851f, 21.635252f, 22.353308f, 22.978308f, 23.603308f, 24.228308f, 24.946362f, 25.362762f + }); + + sd::ops::image_resize op; + // resize with bicubic without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeBicubic}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Bicubic Resized to 7x8"); +// expected.printBuffer("Bicubic Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test6) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 0.63678247f, 1.0531839f, 1.7712381f, 2.396238f, 3.021238f , 3.646238f, 4.364292f, 4.780694f, + 3.3934183f, 3.8098197f, 4.5278745f, 5.1528745f, 5.7778745f, 6.402874f, 7.1209283f, 7.5373297f, + 7.3566165f, 7.7730184f, 8.491073f, 9.116073f, 9.741073f, 10.366074f , 11.084127f , 11.500528f, + 10.928043f, 11.344445f, 12.0625f , 12.6875f , 13.3125f , 13.9375f , 14.655554f, 15.071955f , 14.499474f , 14.915876f , 15.633932f, 16.25893f, 16.883932f, 17.508932f, 18.226984f , 18.643385f, + 18.46267f, 18.87907f, 19.597128f, 20.222126f , 20.847128f, 21.472126f, 22.190182f , 22.606583f , 21.219305f, 21.635706f , + 22.353762f, 22.978762f , 23.603762f , 24.228764f, 24.946815f , 25.363216f + }); + + sd::ops::image_resize op; + // resize with bicubic with antialising and without aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeBicubic}, {false, true}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Bicubic Resized to 7x8"); +// expected.printBuffer("Bicubic Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test7) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 0.98593485f, 1.3872082f, 2.0625007f, 2.6875007f, 3.3125012f, 3.937501f, 4.612794f, 5.014066f, + 3.6096964f, 4.01097f, 4.6862626f, 5.311262f, 5.936263f, 6.561262f, 7.2365556f, 7.637828f, + 7.4145045f, 7.8157787f, 8.491071f, 9.116072f, 9.741073f, 10.366072f, 11.041365f, 11.4426365f, + 10.985933f, 11.387209f, 12.062499f, 12.687501f, 13.312502f, 13.9375f, 14.612794f, 15.014066f, + 14.557361f, 14.958637f, 15.633926f, 16.25893f, 16.88393f, 17.508926f, 18.18422f, 18.585491f, + 18.36217f, 18.763443f, 19.438736f, 20.063736f, 20.688738f, 21.313736f, 21.98903f, 22.3903f, + 20.985931f, 21.387209f, 22.0625f, 22.6875f, 23.3125f, 23.937498f, 24.612793f, 25.014061f + }); + + sd::ops::image_resize op; + // resize with Mitchell cubic with antialising and without aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeMitchellcubic}, {false, true}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Mitchell cubic Resized to 7x8"); +// expected.printBuffer("Mitchell cubic Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test8) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 1.f , 1.4375f , 2.0625f , 2.6875f , 3.3125f , 3.9375f , 4.5625f , 5.f , + 3.8571427f, 4.2946424f, 4.9196424f, 5.5446424f, 6.1696424f, 6.7946424f, 7.4196424f, 7.8571424f, + 7.4285717f, 7.8660717f, 8.491072f , 9.116072f , 9.741072f , 10.366072f , 10.991072f , 11.428572f , + 11.f , 11.4375f , 12.0625f , 12.6875f , 13.3125f , 13.9375f , 14.5625f , 15.f , + 14.571429f , 15.008929f, 15.633929f, 16.25893f , 16.88393f , 17.50893f , 18.13393f , 18.57143f , + 18.142857f , 18.580357f, 19.205357f, 19.830357f , 20.455357f , 21.080357f , 21.705357f , 22.142857f , + 21.f , 21.4375f , 22.0625f , 22.6875f , 23.3125f , 23.9375f , 24.5625f , 25.f + }); + + sd::ops::image_resize op; + // resize with bilinear without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeBilinear}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Bilinear Resized to 7x8"); +// expected.printBuffer("Bilinear Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test9) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 1.f , 1.4f , 2.f , 2.8f , 3.2f , 4.f , 4.6f , 5.f , + 4.f , 4.4f , 5.f , 5.8f , 6.2f , 7.f , 7.6f , 8.f , + 6.999998f, 7.399998f, 7.999998f, 8.799997f, 9.199997f, 9.999997f, 10.599997f, 10.999996f, + 11.f, 11.399999f, 12.f, 12.799999f, 13.199999f, 13.999998f, 14.599998f, 14.999999f, + 15.f, 15.4f, 16.f, 16.8f, 17.2f, 18.f, 18.6f, 19.f, 17.999989f, + 18.399990f, 18.999989f, 19.799988f, 20.199987f, 20.999989f, 21.599989f, 21.999989f, 21.f, + 21.4f, 22.f, 22.8f, 23.2f, 24.f, 24.6f, 25.f + }); + + sd::ops::image_resize op; + // resize with area without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeArea}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Area Resized to 7x8"); +// expected.printBuffer("Area Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test10) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, 6, + 6, 7, 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 16, + 17, 18, 18, 19, 20, 20, 16, 16, 17, 18, 18, 19, 20, 20, 21, 21, 22, + 23, 23, 24, 25, 25 + }); + + sd::ops::image_resize op; + // resize with nearest neigbors without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeNearest}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Nearest neighbor Resized to 7x8"); +// expected.printBuffer("Nearest neighbor Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + +TEST_F(DeclarableOpsTests12, ImageResize_Test11) { + + NDArray input = NDArrayFactory::create('c', {1, 5, 5, 1}, { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 + }); + auto size = NDArrayFactory::create({7, 8}); + NDArray expected = NDArrayFactory::create('c', {1, 7, 8, 1}, { + 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, 6, + 6, 7, 8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 16, + 17, 18, 18, 19, 20, 20, 16, 16, 17, 18, 18, 19, 20, 20, 21, 21, 22, + 23, 23, 24, 25, 25 + }); + + sd::ops::image_resize op; + // resize with nearest neigbors without antialising and aspect ratio preserving + auto results = op.evaluate({&input, &size}, {}, {ops::helpers::kResizeNearest}, {false, false}); + + ASSERT_EQ(ND4J_STATUS_OK, results.status()); + + auto result = results[0];///.at(0); +// result->printBuffer("Nearest neighbor Resized to 7x8"); +// expected.printBuffer("Nearest neighbor Expect for 7x8"); + ASSERT_TRUE(expected.isSameShape(result)); + ASSERT_TRUE(expected.equalsTo(result)); +} + //////////////////////////////////////////////////////////////////////////////// TEST_F(DeclarableOpsTests12, TriangularSolve_Test_1) { diff --git a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/enums/ImageResizeMethod.java b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/enums/ImageResizeMethod.java index 42043dad7..951e87fdc 100644 --- a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/enums/ImageResizeMethod.java +++ b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/enums/ImageResizeMethod.java @@ -27,17 +27,12 @@ package org.nd4j.enums; * ResizeArea: Anti-aliased resampling with area interpolation. 'antialias' has no effect when used with area interpolation; it always anti-aliases. * ResizeMitchelcubic: Mitchell-Netravali Cubic non-interpolating filter. For synthetic images (especially those lacking proper prefiltering), less ringing than Keys cubic kernel but less sharp. */ public enum ImageResizeMethod { - ResizeBilinear, - - ResizeBicubic, - + ResizeBilinear, // as java require ResizeNearest, - + ResizeBicubic, + ResizeArea, ResizeGaussian, - + ResizeLanczos3, ResizeLanczos5, - - ResizeMitchelcubic, - - ResizeArea + ResizeMitchellcubic; } diff --git a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-cuda/src/main/java/org/nd4j/nativeblas/Nd4jCuda.java b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-cuda/src/main/java/org/nd4j/nativeblas/Nd4jCuda.java index 1307ab0ae..59496d780 100644 --- a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-cuda/src/main/java/org/nd4j/nativeblas/Nd4jCuda.java +++ b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-cuda/src/main/java/org/nd4j/nativeblas/Nd4jCuda.java @@ -4417,7 +4417,7 @@ public native @Cast("bool") boolean isOptimalRequirementsMet(); /** * fill target matrix with given value in one or two directions from main diagonal: - * - down from main diagonal starting at subdiagonal number "lower" if direction = 'd' (down) or 'b' (both) + * - down from main diagonal starting at subdiagonal number "lower" if direction = 'l' (down) or 'b' (both) * - up from main diagonal starting at superdiagonal number "upper"if direction = 'u' (up) or 'b' (both) * direction - in what direction to fill matrix. There are 3 possible directions: * 'u' - fill up, mathematically this corresponds to lower triangular matrix, subdiagonal "lower" unaffected @@ -4830,9 +4830,11 @@ public native @Cast("bool") boolean isOptimalRequirementsMet(); //////////////////////////////////////////////////////////////////////// - +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// - // #ifndef __JAVACPP_HACK__ // #endif @@ -7349,9 +7351,9 @@ public static final int PREALLOC_SIZE = 33554432; * Returns the element wise stride for this information * buffer */ - @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongPointer buffer); - @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongBuffer buffer); - @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") long[] buffer); + @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongPointer shapeInfo); + @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongBuffer shapeInfo); + @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") long[] shapeInfo); /** diff --git a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native/src/main/java/org/nd4j/nativeblas/Nd4jCpu.java b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native/src/main/java/org/nd4j/nativeblas/Nd4jCpu.java index b4ef3cb05..b9e4adb5a 100644 --- a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native/src/main/java/org/nd4j/nativeblas/Nd4jCpu.java +++ b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native/src/main/java/org/nd4j/nativeblas/Nd4jCpu.java @@ -4421,7 +4421,7 @@ public native @Cast("bool") boolean isOptimalRequirementsMet(); /** * fill target matrix with given value in one or two directions from main diagonal: - * - down from main diagonal starting at subdiagonal number "lower" if direction = 'd' (down) or 'b' (both) + * - down from main diagonal starting at subdiagonal number "lower" if direction = 'l' (down) or 'b' (both) * - up from main diagonal starting at superdiagonal number "upper"if direction = 'u' (up) or 'b' (both) * direction - in what direction to fill matrix. There are 3 possible directions: * 'u' - fill up, mathematically this corresponds to lower triangular matrix, subdiagonal "lower" unaffected @@ -4834,9 +4834,11 @@ public native @Cast("bool") boolean isOptimalRequirementsMet(); //////////////////////////////////////////////////////////////////////// - +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// - // #ifndef __JAVACPP_HACK__ // #endif @@ -7353,9 +7355,9 @@ public static final int PREALLOC_SIZE = 33554432; * Returns the element wise stride for this information * buffer */ - @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongPointer buffer); - @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongBuffer buffer); - @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") long[] buffer); + @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongPointer shapeInfo); + @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") LongBuffer shapeInfo); + @Namespace("shape") public static native @Cast("Nd4jLong") long elementWiseStride(@Cast("const Nd4jLong*") long[] shapeInfo); /** @@ -21173,214 +21175,6 @@ public static final int TAD_THRESHOLD = TAD_THRESHOLD(); } // #endif - /** - * This op make bilinear or nearest neighbor interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) numeric type - * 1 - 2D-Tensor with shape (num_boxes, 4) float type - * 2 - 1D-Tensor with shape (num_boxes) int type - * 3 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) int type - * - * float arguments (optional) - * 0 - exprapolation_value (optional) default 0.f - * - * int arguments: (optional) - * 0 - mode (default 0 - bilinear interpolation) - * - * output array: - * the 4D-Tensor with resized to crop_size images given - float type - */ -// #if NOT_EXCLUDED(OP_crop_and_resize) - @Namespace("sd::ops") public static class crop_and_resize extends DeclarableCustomOp { - static { Loader.load(); } - /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ - public crop_and_resize(Pointer p) { super(p); } - /** Native array allocator. Access with {@link Pointer#position(long)}. */ - public crop_and_resize(long size) { super((Pointer)null); allocateArray(size); } - private native void allocateArray(long size); - @Override public crop_and_resize position(long position) { - return (crop_and_resize)super.position(position); - } - - public crop_and_resize() { super((Pointer)null); allocate(); } - private native void allocate(); - public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } -// #endif - - /** - * This op make bilinear interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) - * - * int arguments: (optional) - * 0 - new width - * 1 - new height - * - * output array: - * the 4D-Tensor with calculated backproped dots - * - * CAUTION: either size tensor or a pair of int params should be provided. - */ - -// #if NOT_EXCLUDED(OP_resize_bilinear) - @Namespace("sd::ops") public static class resize_bilinear extends DeclarableCustomOp { - static { Loader.load(); } - /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ - public resize_bilinear(Pointer p) { super(p); } - /** Native array allocator. Access with {@link Pointer#position(long)}. */ - public resize_bilinear(long size) { super((Pointer)null); allocateArray(size); } - private native void allocateArray(long size); - @Override public resize_bilinear position(long position) { - return (resize_bilinear)super.position(position); - } - - public resize_bilinear() { super((Pointer)null); allocate(); } - private native void allocate(); - public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } -// #endif - - /** - * This op make nearest neighbor interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) (optional) - * - * int arguments: (optional) - * 0 - new width - * 1 - new height - * - * output array: - * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) - * - * CAUTION: either size tensor or a pair of int params should be provided. - */ - -// #if NOT_EXCLUDED(OP_resize_nearest_neighbor) - @Namespace("sd::ops") public static class resize_nearest_neighbor extends DeclarableCustomOp { - static { Loader.load(); } - /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ - public resize_nearest_neighbor(Pointer p) { super(p); } - /** Native array allocator. Access with {@link Pointer#position(long)}. */ - public resize_nearest_neighbor(long size) { super((Pointer)null); allocateArray(size); } - private native void allocateArray(long size); - @Override public resize_nearest_neighbor position(long position) { - return (resize_nearest_neighbor)super.position(position); - } - - public resize_nearest_neighbor() { super((Pointer)null); allocate(); } - private native void allocate(); - public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } -// #endif - - /** - * This op make bicubic interpolated resize for given tensor - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) - * - * output array: - * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) - * - */ -// #if NOT_EXCLUDED(OP_resize_bicubic) - @Namespace("sd::ops") public static class resize_bicubic extends DeclarableCustomOp { - static { Loader.load(); } - /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ - public resize_bicubic(Pointer p) { super(p); } - /** Native array allocator. Access with {@link Pointer#position(long)}. */ - public resize_bicubic(long size) { super((Pointer)null); allocateArray(size); } - private native void allocateArray(long size); - @Override public resize_bicubic position(long position) { - return (resize_bicubic)super.position(position); - } - - public resize_bicubic() { super((Pointer)null); allocate(); } - private native void allocate(); - public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } -// #endif - - /** - * This op make area interpolated resize (as OpenCV INTER_AREA algorithm) for given tensor - * - * input array: - * 0 - images - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - size - 1D-Tensor with 2 values (newWidth, newHeight) (if missing a pair of integer args should be provided). - * - * int args: - proveded only when size tensor is missing - * 0 - new height - * 1 - new width - * boolean args: - * 0 - align_corners - optional (default is false) - * - * output array: - * the 4D-Tensor with resized image (shape is {batch, newWidth, newHeight, channels}) - * - */ -// #if NOT_EXCLUDED(OP_resize_area) - @Namespace("sd::ops") public static class resize_area extends DeclarableCustomOp { - static { Loader.load(); } - /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ - public resize_area(Pointer p) { super(p); } - /** Native array allocator. Access with {@link Pointer#position(long)}. */ - public resize_area(long size) { super((Pointer)null); allocateArray(size); } - private native void allocateArray(long size); - @Override public resize_area position(long position) { - return (resize_area)super.position(position); - } - - public resize_area() { super((Pointer)null); allocate(); } - private native void allocate(); - public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } -// #endif - - /** - * This op make interpolated resize for given tensor with given algorithm. - * Supported algorithms are bilinear, bicubic, nearest_neighbor. - * Need to implement to full compatibility with TF: lanczos5, gaussian, area and mitchellcubic - * - * input array: - * 0 - 4D-Tensor with shape (batch, sizeX, sizeY, channels) - * 1 - 1D-Tensor with 2 values (newWidth, newHeight) - * - * optional int args: - * 0 - algorithm - bilinear by default - * optional bool args: - * 0 - preserve_aspect_ratio - default False - * 1 - antialias - default False - * - * output array: - * the 4D-Tensor with resized by given algorithm image (shape is {batch, newWidth, newHeight, channels}) - * - */ - -// #if NOT_EXCLUDED(OP_image_resize) - @Namespace("sd::ops") public static class image_resize extends DeclarableCustomOp { - static { Loader.load(); } - /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ - public image_resize(Pointer p) { super(p); } - /** Native array allocator. Access with {@link Pointer#position(long)}. */ - public image_resize(long size) { super((Pointer)null); allocateArray(size); } - private native void allocateArray(long size); - @Override public image_resize position(long position) { - return (image_resize)super.position(position); - } - - public image_resize() { super((Pointer)null); allocate(); } - private native void allocate(); - public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } -// #endif - /** * Copy a tensor setting everything outside a central band in each innermost matrix * @@ -22783,7 +22577,7 @@ public static final int TAD_THRESHOLD = TAD_THRESHOLD(); // #define LIBND4J_HEADERS_BLAS_H // #include - + /** * This op is general matmum implementation. Depending on inputs dimensionality output result might be different. * matrix x matrix = BLAS gemm @@ -22904,11 +22698,11 @@ public static final int TAD_THRESHOLD = TAD_THRESHOLD(); * alpha: vector of T * beta: vector of T * ...: A, B matrices sequentially. i.e: AAAAABBBBB - * + * * Integer arguments: * transA, transB, M, N, K, ldA, ldB, ldC - usual BLAS gemm arguments * batchCount - number of operations in this batch - * + * * PLEASE NOTE: M, N, K, ldA, ldB, ldC should be equal for all matrices within batch. */ // #if NOT_EXCLUDED(OP_batched_gemm) @@ -22931,22 +22725,22 @@ public static final int TAD_THRESHOLD = TAD_THRESHOLD(); /** * performs singular value decomposition (SVD) of one or more matrices, evaluates the SVD of each inner-most 2D matrix in input array: - * x[..., :, :] = u[..., :, :] * s[...,:] * transpose(v[..., :, :]) + * x[..., :, :] = u[..., :, :] * s[...,:] * transpose(v[..., :, :]) * * Input array: * x[..., Rows, Cols], the necessary condition is: rank of x >= 2 - * + * * Outputs arrays: * s[..., diagSize] - array with singular values which are stored in decreasing order, diagSize is smaller among Rows and Cols * u[..., Rows, Rows] if IArgs[1] is true, else u[..., Rows, diagSize] - array with right singular vectors * v[..., Cols, Cols] if IArgs[1] is true, else v[..., Cols, diagSize] - array with left singular vectors - * + * * Integer arguments: * IArgs[0] - bool, whether to calculate u and v, s is calculated in any case * IArgs[1] - bool, whether to calculate full-sized u and v * IArgs[2] - the number of cols or rows which determines what algorithm to use. More precisely: * if diagSize < IArgs[2] then Jacobi algorithm is used, in opposite case the Divide-And-Conquer is applied - * Recommended value is 16. + * Recommended value is 16. */ // #if NOT_EXCLUDED(OP_svd) @Namespace("sd::ops") public static class svd extends DeclarableCustomOp { @@ -22963,7 +22757,35 @@ public static final int TAD_THRESHOLD = TAD_THRESHOLD(); public svd() { super((Pointer)null); allocate(); } private native void allocate(); public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); - } + } +// #endif + + /** + * calculates square root of matrix such that + * x[..., M, M] = z[..., M, M] x z[..., M, M] + * + * Input array: + * x[..., M, M], the necessary condition is: rank of x >= 2 and equality of last two dimensions + * + * Outputs arrays: + * z - same shape as x + */ +// #if NOT_EXCLUDED(OP_sqrtm) + @Namespace("sd::ops") public static class sqrtm extends DeclarableOp { + static { Loader.load(); } + /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ + public sqrtm(Pointer p) { super(p); } + /** Native array allocator. Access with {@link Pointer#position(long)}. */ + public sqrtm(long size) { super((Pointer)null); allocateArray(size); } + private native void allocateArray(long size); + @Override public sqrtm position(long position) { + return (sqrtm)super.position(position); + } + + public sqrtm() { super((Pointer)null); allocate(); } + private native void allocate(); + public native ShapeList calculateOutputShape(ShapeList inputShape, @ByRef Context block); + } // #endif diff --git a/nd4j/nd4j-backends/nd4j-tests/src/test/java/org/nd4j/autodiff/opvalidation/TransformOpValidation.java b/nd4j/nd4j-backends/nd4j-tests/src/test/java/org/nd4j/autodiff/opvalidation/TransformOpValidation.java index c42b7f7a5..54f78dbf9 100644 --- a/nd4j/nd4j-backends/nd4j-tests/src/test/java/org/nd4j/autodiff/opvalidation/TransformOpValidation.java +++ b/nd4j/nd4j-backends/nd4j-tests/src/test/java/org/nd4j/autodiff/opvalidation/TransformOpValidation.java @@ -2107,14 +2107,16 @@ public class TransformOpValidation extends BaseOpValidation { //TODO: Methods failed ResizeLanczos5, ResizeMitchelcubic, ResizeArea for (ImageResizeMethod method : ImageResizeMethod.values()) { - if (method==ImageResizeMethod.ResizeLanczos5 || method==ImageResizeMethod.ResizeArea || method==ImageResizeMethod.ResizeMitchelcubic) + if (method==ImageResizeMethod.ResizeLanczos5 || method==ImageResizeMethod.ResizeArea || method==ImageResizeMethod.ResizeMitchellcubic) {continue;} + log.info("Trying {}", method); + Nd4j.getRandom().setSeed(12345); SameDiff sd = SameDiff.create(); boolean preserveAspectRatio = true; boolean antialias = true; - SDVariable inputImage = sd.var(Nd4j.rand(1, 5, 5, 3)); + SDVariable inputImage = sd.var(Nd4j.rand(DataType.FLOAT, 1, 5, 5, 3)); // NHWC format long[] expectedShape = new long[]{1, 3, 3, 3}; SDVariable requestedSize = sd.constant(Nd4j.createFromArray( new long[]{3, 3}));