diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp index 37a781c6c0b0a6cd5ec4553889d739b5d20cac2d..a4586edfd697472d7b6206fb3b36267f52f9e556 100644 --- a/include/aidge/backend/cpu.hpp +++ b/include/aidge/backend/cpu.hpp @@ -40,6 +40,7 @@ #include "aidge/backend/cpu/operator/PowImpl.hpp" #include "aidge/backend/cpu/operator/ReduceMeanImpl.hpp" #include "aidge/backend/cpu/operator/ReduceSumImpl.hpp" +#include "aidge/backend/cpu/operator/ResizeImpl.hpp" #include "aidge/backend/cpu/operator/ReLUImpl.hpp" #include "aidge/backend/cpu/operator/RoundImpl.hpp" #include "aidge/backend/cpu/operator/ScalingImpl.hpp" diff --git a/include/aidge/backend/cpu/operator/ResizeImpl.hpp b/include/aidge/backend/cpu/operator/ResizeImpl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2bf5c1e807c0b0a64ac0dd2d3ac87219ba6349df --- /dev/null +++ b/include/aidge/backend/cpu/operator/ResizeImpl.hpp @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#ifndef AIDGE_CPU_OPERATOR_RESIZEIMPL_H_ +#define AIDGE_CPU_OPERATOR_RESIZEIMPL_H_ + +#include "aidge/backend/cpu/operator/OperatorImpl.hpp" +#include "aidge/operator/Resize.hpp" +#include "aidge/utils/Registrar.hpp" +#include <aidge/data/Interpolation.hpp> +#include <aidge/operator/Pad.hpp> +#include <cstdint> + +namespace Aidge { +// Operator implementation entry point for the backend +using ResizeImpl_cpu = OperatorImpl_cpu< + Resize_Op, + void(const void *, // input + const std::vector<DimSize_t> &, // INput dims + const std::vector<DimSize_t> &, // OUTput dims + const Interpolation::CoordinateTransformation, // coord transfo + const Interpolation::Mode, // interpolation mode + const PadBorderType, // padding mode + void *)>; // output +// Implementation entry point registration to Operator +REGISTRAR(Resize_Op, "cpu", Aidge::ResizeImpl_cpu::create); +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_RESIZEIMPL_H_ */ diff --git a/include/aidge/backend/cpu/operator/ResizeImpl_kernels.hpp b/include/aidge/backend/cpu/operator/ResizeImpl_kernels.hpp new file mode 100644 index 0000000000000000000000000000000000000000..aac3dc64618c852423b350b90a4632345c258f72 --- /dev/null +++ b/include/aidge/backend/cpu/operator/ResizeImpl_kernels.hpp @@ -0,0 +1,130 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#ifndef AIDGE_CPU_OPERATOR_RESIZEIMPL_FORWARD_KERNEL_H_ +#define AIDGE_CPU_OPERATOR_RESIZEIMPL_FORWARD_KERNEL_H_ + +#include "aidge/backend/cpu/operator/ResizeImpl.hpp" + +#include <aidge/data/Data.hpp> +#include <aidge/data/half.hpp> +#include <aidge/operator/Pad.hpp> +#include <cmath> +#include <cstdint> +#include <numeric> + +#include "aidge/backend/cpu/data/Interpolation.hpp" +#include "aidge/data/Interpolation.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +template <typename IO> +void ResizeImpl_cpu_forward_kernel( + const void *input_, + const std::vector<DimSize_t> &inputDims, + const std::vector<DimSize_t> &outputDims, + const Interpolation::CoordinateTransformation coordTransfoMode, + const Interpolation::Mode interpMode, + const PadBorderType paddingMode, + // const double * /*roi*/, + // const float * /*scales*/, + // const int64_t * /*sizes*/, + void *output_) { + + // Seting a data + const IO *input = static_cast<const IO *>(input_); + IO *output = static_cast<IO *>(output_); + + DimSize_t outputLen = std::accumulate(outputDims.begin(), + outputDims.end(), + 1, + std::multiplies<DimSize_t>()); + std::vector<float> coordInApprox; + std::vector<DimSize_t> coordOut; + for (DimSize_t idxFlatOut = 0; idxFlatOut < outputLen; ++idxFlatOut) { + coordOut = Tensor::getCoord(outputDims, idxFlatOut); + coordInApprox = + Interpolation::untransformCoordinates(coordOut, + inputDims, + outputDims, + coordTransfoMode); + std::set<Interpolation::Point<IO>> neighbours = + InterpolationCPU::retrieveNeighbours(input, + inputDims, + coordInApprox, + paddingMode); + output[idxFlatOut] = InterpolationCPU::interpolate(coordInApprox, + neighbours, + interpMode); + } + return; +} +// Kernels registration to implementation entry point +REGISTRAR(ResizeImpl_cpu, + {{{DataType::Int16}, + {DataType::Float32}, + {DataType::Float32}, + {DataType::Int64}}, + {DataType::Int16}}, + {ProdConso::inPlaceModel, + ResizeImpl_cpu_forward_kernel<int16_t>, + nullptr}); +REGISTRAR(ResizeImpl_cpu, + {{{DataType::Int32}, + {DataType::Float32}, + {DataType::Float32}, + {DataType::Int64}}, + {DataType::Int32}}, + {ProdConso::inPlaceModel, + ResizeImpl_cpu_forward_kernel<int32_t>, + nullptr}); +REGISTRAR(ResizeImpl_cpu, + {{{DataType::Int64}, + {DataType::Float32}, + {DataType::Float32}, + {DataType::Int64}}, + {DataType::Int64}}, + {ProdConso::inPlaceModel, + ResizeImpl_cpu_forward_kernel<int64_t>, + nullptr}); + +REGISTRAR(ResizeImpl_cpu, + {{{DataType::Float16}, + {DataType::Float32}, + {DataType::Float32}, + {DataType::Int64}}, + {DataType::Float16}}, + {ProdConso::inPlaceModel, + ResizeImpl_cpu_forward_kernel<half_float::half>, + nullptr}); +REGISTRAR(ResizeImpl_cpu, + {{{DataType::Float32}, + {DataType::Float32}, + {DataType::Float32}, + {DataType::Int64}}, + {DataType::Float32}}, + {ProdConso::inPlaceModel, + ResizeImpl_cpu_forward_kernel<float>, + nullptr}); +REGISTRAR(ResizeImpl_cpu, + {{{DataType::Float64}, + {DataType::Float32}, + {DataType::Float32}, + {DataType::Int64}}, + {DataType::Float64}}, + {ProdConso::inPlaceModel, + ResizeImpl_cpu_forward_kernel<double>, + nullptr}); +} // namespace Aidge + +#endif /* AIDGE_CPU_OPERATOR_RESIZEIMPL_FORWARD_KERNEL_H_ */ diff --git a/src/operator/ResizeImpl.cpp b/src/operator/ResizeImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38e3639312879ed75dac13fd5ed1226620e0cbd9 --- /dev/null +++ b/src/operator/ResizeImpl.cpp @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ +#include "aidge/backend/cpu/operator/ResizeImpl.hpp" +#include "aidge/backend/cpu/operator/ResizeImpl_kernels.hpp" +#include "aidge/operator/Resize.hpp" + +#include <cassert> +#include <cstdint> +#include <sys/stat.h> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/utils/ErrorHandling.hpp" + +namespace Aidge { + +template <> void ResizeImpl_cpu::forward() { + auto &op = dynamic_cast<const Resize_Op &>(mOp); + + /** @brief input #0 */ + int8_t idxData = 0; + + const bool input0DataPresent = + op.getInput(idxData) && !op.getInput(idxData)->undefined(); + + /////////////////////////////////////// + // CHECKING NODE CONNECTIONS + AIDGE_ASSERT(input0DataPresent, "{}: missing data input #0", op.type()); + + /////////////////////////////////////// + // CALL TO FORWARD + const auto impl = + Registrar<ResizeImpl_cpu>::create(getBestMatch(getRequiredSpec())); + + impl.forward(op.getInput(idxData)->getImpl()->rawPtr(), + op.getInput(idxData)->dims(), + op.getOutput(0)->dims(), + + op.coordinateTransformationMode(), + op.interpolationMode(), + op.paddingMode(), + + op.getOutput(0)->getImpl()->rawPtr() // output pointer + ); +} + +template <> void Aidge::ResizeImpl_cpu::backward() { + AIDGE_THROW_OR_ABORT( + std::runtime_error, + "Backward not yet implemented for Slice_Op on backend cpu"); +} +} // namespace Aidge diff --git a/unit_tests/operator/Test_ResizeImpl.cpp b/unit_tests/operator/Test_ResizeImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b007316274684108c52b3c2fa7927018a6cdd1e7 --- /dev/null +++ b/unit_tests/operator/Test_ResizeImpl.cpp @@ -0,0 +1,261 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <aidge/data/Data.hpp> +#include <aidge/data/Interpolation.hpp> +#include <aidge/data/half.hpp> +#include <aidge/operator/Pad.hpp> +#include <aidge/utils/ArrayHelpers.hpp> +#include <catch2/catch_test_macros.hpp> +#include <cstdint> +#include <memory> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/OperatorTensor.hpp" + +#include "aidge/operator/Resize.hpp" + +#include "aidge/utils/TensorUtils.hpp" + +namespace Aidge { + +void setupTestResize(std::shared_ptr<Resize_Op> op, + Interpolation::Mode interpMode, + Interpolation::CoordinateTransformation coordTransfoMode, + PadBorderType paddingMode, + std::shared_ptr<Tensor> input, + std::shared_ptr<Tensor> roi, + std::shared_ptr<Tensor> scales, + std::shared_ptr<Tensor> sizes) { + + //////////////////////////////////// + // attributes + op->interpolationMode() = interpMode; + op->coordinateTransformationMode() = coordTransfoMode; + op->paddingMode() = paddingMode; + + //////////////////////////////////// + // op backend & inputs + op->setBackend("cpu"); + + input->setBackend("cpu"); + op->associateInput(0, input); + + if (scales != nullptr) { + scales->setBackend("cpu"); + op->associateInput(2, scales); + if (roi != nullptr) { + roi->setBackend("cpu"); + op->associateInput(1, roi); + } + } else if (sizes != nullptr) { + sizes->setBackend("cpu"); + op->associateInput(3, sizes); + } else { + Log::warn("Test Resize Impl : Neither scales nor sizes provided. test " + "is excpected to crash."); + } +} + +TEST_CASE("[cpu/operator] Resize(forward)", "[Resize][CPU]") { + + Log::setConsoleLevel(Log::Level::Debug); + + std::shared_ptr<Node> node = Resize(); + auto op = std::static_pointer_cast<Resize_Op>(node->getOperator()); + + std::shared_ptr<Tensor> input; + std::shared_ptr<Tensor> roi; + std::shared_ptr<Tensor> scales; + std::shared_ptr<Tensor> sizes; + std::shared_ptr<Tensor> expectedOutput; + + SECTION("1-sized input tensor (upscaling)") { + setupTestResize( + op, + Interpolation::Linear, + Interpolation::CoordinateTransformation::HalfPixel, + PadBorderType::Nearest, + std::make_shared<Tensor>( + Array4D<float, 1, 1, 1, 1>{{{{{0.417022}}}}}), + nullptr, + nullptr, + std::make_shared<Tensor>(Array1D<int64_t, 4>{1, 1, 2, 2})); + op->setDataType(DataType::Float32); + op->forwardDims(true); + op->forward(); + expectedOutput = std::make_shared<Tensor>(Array4D<float, 1, 1, 2, 2>{ + {{{{0.417022, 0.417022}, {0.417022, 0.417022}}}}}); + op->getOutput(0)->print(); + CHECK(approxEq<float>(*op->getOutput(0), *expectedOutput) == true); + } + SECTION("Upscaling from 5x5 to 10x10 (linear)") { + setupTestResize( + op, + Interpolation::Linear, + Interpolation::Asymmetric, + PadBorderType::Nearest, + std::make_shared<Tensor>( + Array4D<float, 1, 1, 5, 5>{{{{{7.20324516e-01, + 1.14374816e-04, + 3.02332580e-01, + 1.46755889e-01, + 9.23385918e-02}, + {1.86260208e-01, + 3.45560730e-01, + 3.96767467e-01, + 5.38816750e-01, + 4.19194520e-01}, + {6.85219526e-01, + 2.04452246e-01, + 8.78117442e-01, + 2.73875929e-02, + 6.70467496e-01}, + {4.17304814e-01, + 5.58689833e-01, + 1.40386939e-01, + 1.98101491e-01, + 8.00744593e-01}, + {9.68261600e-01, + 3.13424170e-01, + 6.92322612e-01, + 8.76389146e-01, + 8.94606650e-01}}}}} + + ), + nullptr, + nullptr, + std::make_shared<Tensor>(Array1D<int64_t, 4>{1, 1, 10, 10})); + op->setDataType(DataType::Float32); + op->forwardDims(true); + op->forward(); + expectedOutput = std::make_shared<Tensor>( + Array4D<float, 1, 1, 10, 10>{{{{{7.20324516e-01, + 3.60219449e-01, + 1.14374816e-04, + 1.51223481e-01, + 3.02332580e-01, + 2.24544227e-01, + 1.46755889e-01, + 1.19547240e-01, + 9.23385918e-02, + 9.23385918e-02}, + + {4.53292370e-01, + 3.13064963e-01, + 1.72837555e-01, + 2.61193782e-01, + 3.49550009e-01, + 3.46168160e-01, + 3.42786312e-01, + 2.99276441e-01, + 2.55766571e-01, + 2.55766571e-01}, + + {1.86260208e-01, + 2.65910476e-01, + 3.45560730e-01, + 3.71164083e-01, + 3.96767467e-01, + 4.67792094e-01, + 5.38816750e-01, + 4.79005635e-01, + 4.19194520e-01, + 4.19194520e-01}, + + {4.35739875e-01, + 3.55373204e-01, + 2.75006473e-01, + 4.56224471e-01, + 6.37442470e-01, + 4.60272312e-01, + 2.83102185e-01, + 4.13966596e-01, + 5.44831038e-01, + 5.44831038e-01}, + + {6.85219526e-01, + 4.44835901e-01, + 2.04452246e-01, + 5.41284859e-01, + 8.78117442e-01, + 4.52752531e-01, + 2.73875929e-02, + 3.48927557e-01, + 6.70467496e-01, + 6.70467496e-01}, + + {5.51262140e-01, + 4.66416597e-01, + 3.81571054e-01, + 4.45411623e-01, + 5.09252191e-01, + 3.10998380e-01, + 1.12744540e-01, + 4.24175322e-01, + 7.35606015e-01, + 7.35606015e-01}, + + {4.17304814e-01, + 4.87997323e-01, + 5.58689833e-01, + 3.49538386e-01, + 1.40386939e-01, + 1.69244215e-01, + 1.98101491e-01, + 4.99423027e-01, + 8.00744593e-01, + 8.00744593e-01}, + + {6.92783237e-01, + 5.64420104e-01, + 4.36057001e-01, + 4.26205903e-01, + 4.16354775e-01, + 4.76800054e-01, + 5.37245333e-01, + 6.92460477e-01, + 8.47675622e-01, + 8.47675622e-01}, + + {9.68261600e-01, + 6.40842915e-01, + 3.13424170e-01, + 5.02873421e-01, + 6.92322612e-01, + 7.84355879e-01, + 8.76389146e-01, + 8.85497928e-01, + 8.94606650e-01, + 8.94606650e-01}, + + {9.68261600e-01, + 6.40842915e-01, + 3.13424170e-01, + 5.02873421e-01, + 6.92322612e-01, + 7.84355879e-01, + 8.76389146e-01, + 8.85497928e-01, + 8.94606650e-01, + 8.94606650e-01}}}}}); + Log::notice("Expected result : dims = {}", expectedOutput->dims()); + expectedOutput->print(); + Log::notice("\nActual result: dims = {}", op->getOutput(0)->dims()); + op->getOutput(0)->print(); + CHECK(approxEq<float>(*op->getOutput(0), + *expectedOutput, + 1e-5f, + 1e-5f) == true); + } +} + +} // namespace Aidge