Skip to content
Snippets Groups Projects
Commit e110db9e authored by Maxence Naud's avatar Maxence Naud Committed by Maxence Naud
Browse files

add GridSample operator with python binding and test

parent 4c7931a7
No related branches found
No related tags found
2 merge requests!212Version 0.3.0,!179Add GridSample Operator
Pipeline #52611 canceled
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include "aidge/operator/Gather.hpp" #include "aidge/operator/Gather.hpp"
#include "aidge/operator/GenericOperator.hpp" #include "aidge/operator/GenericOperator.hpp"
#include "aidge/operator/GlobalAveragePooling.hpp" #include "aidge/operator/GlobalAveragePooling.hpp"
#include "aidge/operator/GridSample.hpp"
#include "aidge/operator/MatMul.hpp" #include "aidge/operator/MatMul.hpp"
#include "aidge/operator/MaxPooling.hpp" #include "aidge/operator/MaxPooling.hpp"
#include "aidge/operator/MetaOperator.hpp" #include "aidge/operator/MetaOperator.hpp"
......
/********************************************************************************
* 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_CORE_OPERATOR_GRIDSAMPLE_H_
#define AIDGE_CORE_OPERATOR_GRIDSAMPLE_H_
#include <cstddef> // std::size_t
#include <memory>
#include <string>
#include <vector>
#include "aidge/graph/Node.hpp"
#include "aidge/operator/OperatorTensor.hpp"
#include "aidge/utils/Attributes.hpp"
#include "aidge/utils/Registrar.hpp"
#include "aidge/utils/StaticAttributes.hpp"
namespace Aidge {
enum class GridSampleAttr { Mode, PaddingMode, AlignCorners };
template <DimIdx_t DIM>
class GridSample_Op : public OperatorTensor,
public Registrable<GridSample_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const GridSample_Op<DIM>&)> {
public:
static const std::string Type;
enum class Mode { Linear, Nearest, Cubic };
enum class PaddingMode { Zeros, Border, Reflexion };
private:
using Attributes_ = StaticAttributes<GridSampleAttr, Mode, PaddingMode, bool>;
template <GridSampleAttr e>
using attr = typename Attributes_::template attr<e>;
const std::shared_ptr<Attributes_> mAttributes;
public:
GridSample_Op(Mode mode = Mode::Linear,
PaddingMode paddingMode = PaddingMode::Zeros,
bool alignCorners = false);
GridSample_Op(const GridSample_Op<DIM>& other);
~GridSample_Op() noexcept;
public:
std::shared_ptr<Operator> clone() const override;
bool forwardDims(bool /*allowDataDependencies*/ = false) override final;
void setBackend(const std::string& name, DeviceIdx_t device = 0) override final;
inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; }
inline Mode mode() const { return mAttributes->template getAttr<GridSampleAttr::Mode>(); }
inline PaddingMode paddingMode() const { return mAttributes->template getAttr<GridSampleAttr::PaddingMode>(); }
inline bool alignBorders() const { return mAttributes->template getAttr<GridSampleAttr::AlignCorners>(); }
static const std::vector<std::string> getInputsName() {
return {"data_input", "grid_field"};
}
static const std::vector<std::string> getOutputsName() {
return {"data_output"};
}
};
extern template class GridSample_Op<1>;
extern template class GridSample_Op<2>;
template <DimIdx_t DIM>
std::shared_ptr<Node> GridSample(
typename GridSample_Op<DIM>::Mode mode = GridSample_Op<DIM>::Mode::Linear,
typename GridSample_Op<DIM>::PaddingMode paddingMode = GridSample_Op<DIM>::PaddingMode::Zeros,
bool alignCorners = false,
const std::string& name = "");
} // namespace Aidge
namespace {
template <>
const char* const EnumStrings<Aidge::GridSampleAttr>::data[] = {
"mode",
"padding_mode",
"align_corners"
};
}
#endif /* AIDGE_CORE_OPERATOR_GRIDSAMPLE_H_ */
/********************************************************************************
* 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 <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include <vector>
#include <array>
#include "aidge/backend/OperatorImpl.hpp"
#include "aidge/data/Tensor.hpp"
#include "aidge/operator/GridSample.hpp"
#include "aidge/operator/OperatorTensor.hpp"
#include "aidge/utils/Types.h"
#include "aidge/utils/Registrar.hpp" // declare_registrable
static Aidge::GridSample_Op::Mode stringToInterpolationMode(const std::string& mode) {
static std::unordered_map<std::string, Aidge::GridSample_Op::Mode> map = {
{"linear", Aidge::GridSample_Op::Mode::Linear},
{"nearest", Aidge::GridSample_Op::Mode::Nearest},
{"cubic", Aidge::GridSample_Op::Mode::Cubic}
}
return map[mode];
}
static Aidge::GridSample_Op::PaddingMode stringToPaddingMode(const std::string& mode) {
static std::unordered_map<std::string, Aidge::GridSample_Op::PaddingMode> map = {
{"zeros", Aidge::GridSample_Op::PaddingMode::Zeros},
{"border", Aidge::GridSample_Op::PaddingMode::Border},
{"reflexion", Aidge::GridSample_Op::PaddingMode::Reflexion}
}
return map[mode];
}
namespace py = pybind11;
namespace Aidge {
template <DimIdx_t DIM> void declare_GridSampleOp(py::module &m) {
const std::string pyClassName("GridSampleOp" + std::to_string(DIM) + "D");
py::class_<GridSample_Op<DIM>, std::shared_ptr<GridSample_Op<DIM>>, OperatorTensor>(
m, pyClassName.c_str(),
py::multiple_inheritance())
.def(py::init([](const std::string& mode,
const std::string& padding_mode,
bool align_corners) {
return new GridSample_Op<DIM>(stringToInterpolationMode(mode), stringToPaddingMode(padding_mode), align_corners);
}), py::arg("mode") = "linear",
py::arg("padding_mode") = "zeros",
py::arg("alogn_corners") = false)
.def_static("get_inputs_name", &GridSample_Op<DIM>::getInputsName)
.def_static("get_outputs_name", &GridSample_Op<DIM>::getOutputsName)
;
declare_registrable<GridSample_Op<DIM>>(m, pyClassName);
m.def(("GridSample" + std::to_string(DIM) + "D").c_str(), [](const std::string& mode,
const std::string& padding_mode,
bool align_corners
const std::string& name) {
return GridSample<DIM>(stringToInterpolationMode(mode), stringToPaddingMode(padding_mode), align_corners, name);
}, py::arg("mode"),
py::arg("padding_mode"),
py::arg("align_corners"),
py::arg("name") = "");
}
void init_GridSample(py::module &m) {
declare_GridSampleOp<1>(m);
declare_GridSampleOp<2>(m);
// declare_GridSampleOp<3>(m);
}
} // namespace Aidge
...@@ -39,6 +39,7 @@ void init_FC(py::module&); ...@@ -39,6 +39,7 @@ void init_FC(py::module&);
void init_Gather(py::module&); void init_Gather(py::module&);
void init_GenericOperator(py::module&); void init_GenericOperator(py::module&);
void init_GlobalAveragePooling(py::module&); void init_GlobalAveragePooling(py::module&);
void init_GridSample(py::module&);
void init_LeakyReLU(py::module&); void init_LeakyReLU(py::module&);
void init_MatMul(py::module&); void init_MatMul(py::module&);
void init_MaxPooling(py::module&); void init_MaxPooling(py::module&);
...@@ -110,6 +111,7 @@ void init_Aidge(py::module& m) { ...@@ -110,6 +111,7 @@ void init_Aidge(py::module& m) {
init_Gather(m); init_Gather(m);
init_GenericOperator(m); init_GenericOperator(m);
init_GlobalAveragePooling(m); init_GlobalAveragePooling(m);
init_GridSample(m);
init_LeakyReLU(m); init_LeakyReLU(m);
init_MatMul(m); init_MatMul(m);
init_MaxPooling(m); init_MaxPooling(m);
......
/********************************************************************************
* 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/operator/GridSample.hpp"
#include <cstddef> // std::size_t
#include <memory>
#include <string>
#include <vector>
#include "aidge/data/Tensor.hpp"
#include "aidge/utils/ErrorHandling.hpp"
#include "aidge/utils/Registrar.hpp"
#include "aidge/utils/Types.h"
template <Aidge::DimIdx_t DIM>
const std::string Aidge::GridSample_Op<DIM>::Type = "GridSample";
template <Aidge::DimIdx_t DIM>
Aidge::GridSample_Op<DIM>::GridSample_Op(
typename Aidge::GridSample_Op<DIM>::Mode mode,
typename Aidge::GridSample_Op<DIM>::PaddingMode paddingMode,
bool alignCorners)
: OperatorTensor(Type, {InputCategory::Data, InputCategory::Param}, 1),
mAttributes(std::make_shared<Attributes_>(
attr<GridSampleAttr::Mode>(mode),
attr<GridSampleAttr::PaddingMode>(paddingMode),
attr<GridSampleAttr::AlignCorners>(alignCorners)))
{
// ctor
}
template <Aidge::DimIdx_t DIM>
Aidge::GridSample_Op<DIM>::GridSample_Op(const Aidge::GridSample_Op<DIM>& other)
: OperatorTensor(other),
mAttributes(other.mAttributes)
{
if (other.mImpl) {
SET_IMPL_MACRO(GridSample_Op<DIM>, *this, other.backend());
} else {
mImpl = nullptr;
}
}
template <Aidge::DimIdx_t DIM>
Aidge::GridSample_Op<DIM>::~GridSample_Op() noexcept = default;
template <Aidge::DimIdx_t DIM>
std::shared_ptr<Aidge::Operator> Aidge::GridSample_Op<DIM>::clone() const {
return std::make_shared<GridSample_Op<DIM>>(*this);
}
template <Aidge::DimIdx_t DIM>
bool Aidge::GridSample_Op<DIM>::forwardDims(bool /*allowDataDependency*/) {
// TODO: adapt for other formats than NCHW
if (inputsAssociated()) {
// check data has batch and channel dimensions: (N, C, D0, D1, ..., DN)
AIDGE_ASSERT((getInput(0)->nbDims() == (DIM+2)),
"Wrong input size for {} operator.", type());
// check grid field
// should be (N, D0_out, D1_out, ..., DN_out, N+1)
AIDGE_ASSERT(((getInput(1)->nbDims() == (DIM+2)) &&
(getInput(1)->dims<DIM+2>()[DIM+1] == DIM)),
"Wrong grid size {} for {} operator.", getInput(1)->dims(), type());
std::array<DimSize_t, DIM + 2> outputDims{};
const std::vector<DimSize_t>& inputDims(getInput(1)->dims());
outputDims[1] = getInput(0)->dims<DIM+2>()[1];
outputDims[0] = inputDims[0];
for (std::size_t i = 2; i < DIM+2; ++i) {
outputDims[i] = inputDims[i-1];
}
mOutputs[0]->resize(outputDims);
return true;
}
return false;
}
template <Aidge::DimIdx_t DIM>
void Aidge::GridSample_Op<DIM>::setBackend(const std::string &name, Aidge::DeviceIdx_t device) {
SET_IMPL_MACRO(GridSample_Op<DIM>, *this, name);
mOutputs[0]->setBackend(name, device);
}
template class Aidge::GridSample_Op<1>;
template class Aidge::GridSample_Op<2>;
template <Aidge::DimIdx_t DIM>
std::shared_ptr<Aidge::Node> Aidge::GridSample(
typename Aidge::GridSample_Op<DIM>::Mode mode,
typename Aidge::GridSample_Op<DIM>::PaddingMode paddingMode,
bool alignCorners,
const std::string& name)
{
return std::make_shared<Node>(
GridSample_Op<DIM>(
mode,
paddingMode,
alignCorners),
name);
}
\ No newline at end of file
/********************************************************************************
* 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 <cstddef> // std::size_t
#include <memory>
#include <random> // std::mt19937, std::uniform_int_distribution
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators_random.hpp>
#include "aidge/data/Tensor.hpp"
#include "aidge/operator/GridSample.hpp"
#include "aidge/operator/OperatorTensor.hpp"
namespace Aidge {
TEST_CASE("[core/operator] GridSample_Op(forwardDims)", "[GridSample][forwardDims]") {
constexpr std::uint16_t NBTRIALS = 10;
// Create a random number generator
auto rd = Catch::Generators::Detail::getSeed;
std::mt19937 gen(rd());
std::uniform_int_distribution<std::size_t> dimsDist(1, 10);
std::uniform_int_distribution<std::size_t> nbDimsDist(1, 5);
// Create GridSample Operator
std::shared_ptr<Node> myGridSample = GridSample<2>(GridSample_Op<2>::Mode::Cubic, GridSample_Op<2>::PaddingMode::Border, false);
auto op = std::static_pointer_cast<OperatorTensor>(myGridSample -> getOperator());
// input_0
std::shared_ptr<Tensor> data_in0 = std::make_shared<Tensor>();
op -> associateInput(0,data_in0);
// input_1
std::shared_ptr<Tensor> grid_in1 = std::make_shared<Tensor>();
op -> associateInput(1,grid_in1);
SECTION("Valid shape provided") {
for (std::uint16_t trial = 0; trial < NBTRIALS; ++trial) {
std::size_t N = dimsDist(gen);
std::size_t C = dimsDist(gen);
std::size_t H_data_in0 = dimsDist(gen);
std::size_t W_data_in0 = dimsDist(gen);
std::size_t H_grid_in1 = dimsDist(gen);
std::size_t W_grid_in1 = dimsDist(gen);
data_in0->resize({N, C, H_data_in0, W_data_in0});
grid_in1->resize({N, H_grid_in1, W_grid_in1, 2});
REQUIRE_NOTHROW(op->forwardDims());
REQUIRE((op->getOutput(0)->dims()) == std::vector<std::size_t>({N, C, H_grid_in1, W_grid_in1}));
}
}
SECTION("Invalid shape provided") {
std::size_t N_in = dimsDist(gen);
std::size_t C = dimsDist(gen);
std::size_t H_data_in0 = dimsDist(gen);
std::size_t W_data_in0 = dimsDist(gen);
std::size_t H_grid_in1 = dimsDist(gen);
std::size_t W_grid_in1 = dimsDist(gen);
// different batch number
std::size_t N_out = N_in+1;
data_in0->resize({N_in, C, H_data_in0, W_data_in0});
grid_in1->resize({N_out, H_grid_in1, W_grid_in1, 2});
REQUIRE_THROWS(op->forwardDims());
// different number of dimensions
data_in0->resize({N_in, C, H_data_in0, W_data_in0});
grid_in1->resize({N_in, H_grid_in1, W_grid_in1, 2, 2});
REQUIRE_THROWS(op->forwardDims());
// wrong pixel coordinates number
data_in0->resize({N_in, C, H_data_in0, W_data_in0});
grid_in1->resize({N_in, H_grid_in1, W_grid_in1, 2 + dimsDist(gen)});
REQUIRE_THROWS(op->forwardDims());
}
}
} // namespace Aidge
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment