From 913a2fbade9ac6100261a1b2806726dcf3e3a344 Mon Sep 17 00:00:00 2001 From: SundarRajan98 Date: Sun, 29 Oct 2023 18:14:18 +0000 Subject: [PATCH 1/3] Adding fmadd kernel integration in rocAL --- rocAL/include/api/rocal_api_augmentation.h | 3 +++ rocAL/source/api/rocal_api_augmentation.cpp | 23 +++++++++++++++++++ rocAL_pybind/amd/rocal/fn.py | 15 ++++++++++++ .../examples/rocAL_api_numpy_reader.py | 20 ++++++++++++++-- rocAL_pybind/rocal_pybind.cpp | 2 ++ 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/rocAL/include/api/rocal_api_augmentation.h b/rocAL/include/api/rocal_api_augmentation.h index e9c9a68..e40cda6 100644 --- a/rocAL/include/api/rocal_api_augmentation.h +++ b/rocAL/include/api/rocal_api_augmentation.h @@ -1098,4 +1098,7 @@ extern "C" RocalTensor ROCAL_API_CALL rocalSSDRandomCrop(RocalContext context, R RocalTensorLayout output_layout = ROCAL_NONE, RocalTensorOutputType output_datatype = ROCAL_UINT8); +extern "C" RocalTensor ROCAL_API_CALL rocalSetLayout(RocalContext context, RocalTensor input, + RocalTensorLayout output_layout = ROCAL_NONE); + #endif // MIVISIONX_ROCAL_API_AUGMENTATION_H diff --git a/rocAL/source/api/rocal_api_augmentation.cpp b/rocAL/source/api/rocal_api_augmentation.cpp index b4ca8b4..7f31077 100644 --- a/rocAL/source/api/rocal_api_augmentation.cpp +++ b/rocAL/source/api/rocal_api_augmentation.cpp @@ -2145,3 +2145,26 @@ rocalNop( } return output; } + +RocalTensor ROCAL_API_CALL +rocalSetLayout( + RocalContext p_context, + RocalTensor p_input, + RocalTensorLayout output_layout) { + Tensor* output = nullptr; + if ((p_context == nullptr) || (p_input == nullptr)) { + ERR("Invalid ROCAL context or invalid input tensor") + return output; + } + + auto context = static_cast(p_context); + auto input = static_cast(p_input); + try { + RocalTensorlayout op_tensor_layout = static_cast(output_layout); + input->set_layout(op_tensor_layout); + } catch (const std::exception& e) { + context->capture_error(e.what()); + ERR(e.what()) + } + return input; +} diff --git a/rocAL_pybind/amd/rocal/fn.py b/rocAL_pybind/amd/rocal/fn.py index a5b60b6..99dc05e 100644 --- a/rocAL_pybind/amd/rocal/fn.py +++ b/rocAL_pybind/amd/rocal/fn.py @@ -1055,3 +1055,18 @@ def box_iou_matcher(*inputs, anchors, criteria=0.5, high_threshold=0.5, Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) Pipeline._current_pipeline._box_iou_matcher = True return (box_iou_matcher, []) + + +def set_layout(*inputs, output_layout=types.NHWC): + """!Adjusts brightness of the image. + + @param inputs the input image passed to the augmentation + @param output_layout (int, optional, default = types.NHWC) tensor layout for the augmentation output + + @return Tensor with required output layout + """ + # pybind call arguments + kwargs_pybind = {"input_image": inputs[0], "output_layout": output_layout} + new_output = b.setLayout( + Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) + return (new_output) diff --git a/rocAL_pybind/examples/rocAL_api_numpy_reader.py b/rocAL_pybind/examples/rocAL_api_numpy_reader.py index bdcc885..6061dff 100644 --- a/rocAL_pybind/examples/rocAL_api_numpy_reader.py +++ b/rocAL_pybind/examples/rocAL_api_numpy_reader.py @@ -9,6 +9,7 @@ import amd.rocal.types as types import sys import os +import numpy as np def main(): if len(sys.argv) < 3: @@ -33,23 +34,38 @@ def main(): world_size = 1 random_seed = random.SystemRandom().randint(0, 2**32 - 1) + files_list = [] + for file in os.listdir(data_path): + files_list.append(os.path.join(data_path, file)) + + import time + start = time.time() pipeline = Pipeline(batch_size=batch_size, num_threads=num_threads, device_id=device_id, seed=random_seed, rocal_cpu=rocal_cpu) with pipeline: numpy_reader_output = fn.readers.numpy(file_root=data_path, shard_id=local_rank, num_shards=world_size) - pipeline.set_outputs(numpy_reader_output) + new_output = fn.set_layout(numpy_reader_output, output_layout=types.NCDHW) + brightness_output = fn.brightness(new_output, brightness=1.25, brightness_shift=0.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) + pipeline.set_outputs(brightness_output) pipeline.build() numpyIteratorPipeline = ROCALNumpyIterator(pipeline, tensor_dtype=types.UINT8) print(len(numpyIteratorPipeline)) + cnt = 0 for epoch in range(1): print("+++++++++++++++++++++++++++++EPOCH+++++++++++++++++++++++++++++++++++++",epoch) for i , [it] in enumerate(numpyIteratorPipeline): - print(it.shape) + print(i, it.shape) + for j in range(batch_size): + arr = np.load(files_list[cnt]) + shape = arr.shape + print(np.array_equal(arr * 1.25, it[j].cpu().numpy()[:, :shape[1], :shape[2], :shape[3]])) + cnt += 1 print("************************************** i *************************************",i) numpyIteratorPipeline.reset() print("*********************************************************************") + print(f'Took {time.time() - start} seconds') if __name__ == '__main__': main() diff --git a/rocAL_pybind/rocal_pybind.cpp b/rocAL_pybind/rocal_pybind.cpp index f16360f..cc1b550 100644 --- a/rocAL_pybind/rocal_pybind.cpp +++ b/rocAL_pybind/rocal_pybind.cpp @@ -522,6 +522,8 @@ PYBIND11_MODULE(rocal_pybind, m) { m.def("rocalResetLoaders", &rocalResetLoaders); m.def("videoMetaDataReader", &rocalCreateVideoLabelReader, py::return_value_policy::reference); // rocal_api_augmentation.h + m.def("setLayout", &rocalSetLayout, + py::return_value_policy::reference); m.def("ssdRandomCrop", &rocalSSDRandomCrop, py::return_value_policy::reference); m.def("resize", &rocalResize, From eedad89c54fbca17ddc157e5b2bb70ee7aeacdd9 Mon Sep 17 00:00:00 2001 From: SundarRajan98 Date: Sun, 29 Oct 2023 18:22:47 +0000 Subject: [PATCH 2/3] Adding flip and gaussian noise 3D kernel support in rocAL --- rocAL/include/api/rocal_api_augmentation.h | 41 +++++++++- .../augmentations/augmentations_nodes.h | 1 + .../node_gaussian_noise.h | 46 ++++++++++++ .../geometry_augmentations/node_flip.h | 8 +- rocAL/source/api/rocal_api_augmentation.cpp | 75 ++++++++++++++++++- .../node_gaussian_noise.cpp | 67 +++++++++++++++++ .../geometry_augmentations/node_flip.cpp | 13 +++- rocAL_pybind/amd/rocal/fn.py | 33 +++++++- .../examples/rocAL_api_numpy_reader.py | 8 +- rocAL_pybind/rocal_pybind.cpp | 2 + 10 files changed, 278 insertions(+), 16 deletions(-) create mode 100644 rocAL/include/augmentations/effects_augmentations/node_gaussian_noise.h create mode 100644 rocAL/source/augmentations/effects_augmentations/node_gaussian_noise.cpp diff --git a/rocAL/include/api/rocal_api_augmentation.h b/rocAL/include/api/rocal_api_augmentation.h index e40cda6..438d523 100644 --- a/rocAL/include/api/rocal_api_augmentation.h +++ b/rocAL/include/api/rocal_api_augmentation.h @@ -307,7 +307,7 @@ extern "C" RocalTensor ROCAL_API_CALL rocalContrastFixed(RocalContext context, R * \return RocalTensor */ extern "C" RocalTensor ROCAL_API_CALL rocalFlip(RocalContext context, RocalTensor input, bool is_output, - RocalIntParam horizonal_flag = NULL, RocalIntParam vertical_flag = NULL, + RocalIntParam horizonal_flag = NULL, RocalIntParam vertical_flag = NULL, RocalIntParam depth_flag = NULL, RocalTensorLayout output_layout = ROCAL_NONE, RocalTensorOutputType output_datatype = ROCAL_UINT8); @@ -323,7 +323,7 @@ extern "C" RocalTensor ROCAL_API_CALL rocalFlip(RocalContext context, RocalTenso * \return RocalTensor */ extern "C" RocalTensor ROCAL_API_CALL rocalFlipFixed(RocalContext context, RocalTensor input, - int horizonal_flag, int vertical_flag, bool is_output, + int horizonal_flag, int vertical_flag, int depth_flag, bool is_output, RocalTensorLayout output_layout = ROCAL_NONE, RocalTensorOutputType output_datatype = ROCAL_UINT8); @@ -565,6 +565,43 @@ extern "C" RocalTensor ROCAL_API_CALL rocalSnPNoiseFixed(RocalContext context, R RocalTensorLayout output_layout = ROCAL_NONE, RocalTensorOutputType output_datatype = ROCAL_UINT8); +/*! \brief Applies gaussian noise effect on images. + * \ingroup group_rocal_augmentations + * \param [in] context Rocal context + * \param [in] input Input Rocal tensor + * \param [in] is_output is the output tensor part of the graph output + * \param [in] mean Mean of the distribution + * \param [in] std_dev Standard deviation of the distribution + * \param [in] seed seed value for the random number generator + * \param [in] output_layout the layout of the output tensor + * \param [in] output_datatype the data type of the output tensor + * \return RocalTensor + */ +extern "C" RocalTensor ROCAL_API_CALL rocalGaussianNoise(RocalContext context, RocalTensor input, + bool is_output, + RocalFloatParam mean = NULL, RocalFloatParam std_dev = NULL, + int seed = 0, + RocalTensorLayout output_layout = ROCAL_NONE, + RocalTensorOutputType output_datatype = ROCAL_UINT8); + +/*! \brief Applies gaussian noise effect on images with fixed parameters. + * \ingroup group_rocal_augmentations + * \param [in] context Rocal context + * \param [in] input Input Rocal tensor + * \param [in] is_output is the output tensor part of the graph output + * \param [in] mean Mean of the distribution + * \param [in] std_dev Standard deviation of the distribution + * \param [in] seed seed value for the random number generator + * \param [in] output_layout the layout of the output tensor + * \param [in] output_datatype the data type of the output tensor + * \return RocalTensor + */ +extern "C" RocalTensor ROCAL_API_CALL rocalGaussianNoiseFixed(RocalContext context, RocalTensor input, + float mean, float std_dev, + bool is_output, int seed = 0, + RocalTensorLayout output_layout = ROCAL_NONE, + RocalTensorOutputType output_datatype = ROCAL_UINT8); + /*! \brief Applies snow effect on images. * \ingroup group_rocal_augmentations * \param [in] context Rocal context diff --git a/rocAL/include/augmentations/augmentations_nodes.h b/rocAL/include/augmentations/augmentations_nodes.h index 34bc1d6..08b3f55 100644 --- a/rocAL/include/augmentations/augmentations_nodes.h +++ b/rocAL/include/augmentations/augmentations_nodes.h @@ -55,3 +55,4 @@ THE SOFTWARE. #include "node_copy.h" #include "node_nop.h" #include "node_sequence_rearrange.h" +#include "node_gaussian_noise.h" diff --git a/rocAL/include/augmentations/effects_augmentations/node_gaussian_noise.h b/rocAL/include/augmentations/effects_augmentations/node_gaussian_noise.h new file mode 100644 index 0000000..38bf4d1 --- /dev/null +++ b/rocAL/include/augmentations/effects_augmentations/node_gaussian_noise.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2019 - 2023 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +#include "graph.h" +#include "node.h" +#include "parameter_factory.h" +#include "parameter_vx.h" + +class GaussianNoiseNode : public Node { + public: + GaussianNoiseNode(const std::vector &inputs, const std::vector &outputs); + GaussianNoiseNode() = delete; + void init(float mean, float std_dev, int seed); + void init(FloatParam *mean_param, FloatParam *stddev_param, int seed); + + protected: + void create_node() override; + void update_node() override; + + private: + ParameterVX _mean, _stddev; + constexpr static float MEAN_RANGE[2] = {0, 5}; + constexpr static float STDDEV_RANGE[2] = {1, 5}; + int _seed; +}; diff --git a/rocAL/include/augmentations/geometry_augmentations/node_flip.h b/rocAL/include/augmentations/geometry_augmentations/node_flip.h index c2168ad..7873cd8 100644 --- a/rocAL/include/augmentations/geometry_augmentations/node_flip.h +++ b/rocAL/include/augmentations/geometry_augmentations/node_flip.h @@ -29,17 +29,19 @@ class FlipNode : public Node { public: FlipNode(const std::vector &inputs, const std::vector &outputs); FlipNode() = delete; - void init(int h_flag, int v_flag); - void init(IntParam *h_flag_param, IntParam *v_flag_param); + void init(int h_flag, int v_flag, int d_flag); + void init(IntParam *h_flag_param, IntParam *v_flag_param, IntParam *d_flag_param); vx_array get_horizontal_flip() { return _horizontal.default_array(); } vx_array get_vertical_flip() { return _vertical.default_array(); } + vx_array get_depth_flip() { return _depth.default_array(); } protected: void create_node() override; void update_node() override; private: - ParameterVX _horizontal, _vertical; + ParameterVX _horizontal, _vertical, _depth; constexpr static int HORIZONTAL_RANGE[2] = {0, 1}; constexpr static int VERTICAL_RANGE[2] = {0, 1}; + constexpr static int DEPTH_RANGE[2] = {0, 1}; }; diff --git a/rocAL/source/api/rocal_api_augmentation.cpp b/rocAL/source/api/rocal_api_augmentation.cpp index 7f31077..0e8c149 100644 --- a/rocAL/source/api/rocal_api_augmentation.cpp +++ b/rocAL/source/api/rocal_api_augmentation.cpp @@ -1145,6 +1145,74 @@ rocalSnPNoiseFixed( return output; } +RocalTensor ROCAL_API_CALL +rocalGaussianNoise( + RocalContext p_context, + RocalTensor p_input, + bool is_output, + RocalFloatParam mean, + RocalFloatParam std_dev, + int seed, + RocalTensorLayout output_layout, + RocalTensorOutputType output_datatype) { + Tensor* output = nullptr; + if ((p_context == nullptr) || (p_input == nullptr)) { + ERR("Invalid ROCAL context or invalid input tensor") + return output; + } + + auto context = static_cast(p_context); + auto input = static_cast(p_input); + auto mean_value = static_cast(mean); + auto stddev_value = static_cast(std_dev); + try { + RocalTensorlayout op_tensor_layout = static_cast(output_layout); + RocalTensorDataType op_tensor_datatype = static_cast(output_datatype); + TensorInfo output_info = input->info(); + output_info.set_tensor_layout(op_tensor_layout); + output_info.set_data_type(op_tensor_datatype); + output = context->master_graph->create_tensor(output_info, is_output); + context->master_graph->add_node({input}, {output})->init(mean_value, stddev_value, seed); + } catch (const std::exception& e) { + context->capture_error(e.what()); + ERR(e.what()) + } + return output; +} + +RocalTensor ROCAL_API_CALL +rocalGaussianNoiseFixed( + RocalContext p_context, + RocalTensor p_input, + float mean, + float std_dev, + bool is_output, + int seed, + RocalTensorLayout output_layout, + RocalTensorOutputType output_datatype) { + Tensor* output = nullptr; + if ((p_context == nullptr) || (p_input == nullptr)) { + ERR("Invalid ROCAL context or invalid input tensor") + return output; + } + + auto context = static_cast(p_context); + auto input = static_cast(p_input); + try { + RocalTensorlayout op_tensor_layout = static_cast(output_layout); + RocalTensorDataType op_tensor_datatype = static_cast(output_datatype); + TensorInfo output_info = input->info(); + output_info.set_tensor_layout(op_tensor_layout); + output_info.set_data_type(op_tensor_datatype); + output = context->master_graph->create_tensor(output_info, is_output); + context->master_graph->add_node({input}, {output})->init(mean, std_dev, seed); + } catch (const std::exception& e) { + context->capture_error(e.what()); + ERR(e.what()) + } + return output; +} + RocalTensor ROCAL_API_CALL rocalFlip( RocalContext p_context, @@ -1152,6 +1220,7 @@ rocalFlip( bool is_output, RocalIntParam p_horizontal_flag, RocalIntParam p_vertical_flag, + RocalIntParam p_depth_flag, RocalTensorLayout output_layout, RocalTensorOutputType output_datatype) { Tensor* output = nullptr; @@ -1163,6 +1232,7 @@ rocalFlip( auto input = static_cast(p_input); auto horizontal_flag = static_cast(p_horizontal_flag); auto vertical_flag = static_cast(p_vertical_flag); + auto depth_flag = static_cast(p_depth_flag); try { RocalTensorlayout op_tensor_layout = static_cast(output_layout); RocalTensorDataType op_tensor_datatype = static_cast(output_datatype); @@ -1171,7 +1241,7 @@ rocalFlip( output_info.set_data_type(op_tensor_datatype); output = context->master_graph->create_tensor(output_info, is_output); std::shared_ptr flip_node = context->master_graph->add_node({input}, {output}); - flip_node->init(horizontal_flag, vertical_flag); + flip_node->init(horizontal_flag, vertical_flag, depth_flag); if (context->master_graph->meta_data_graph()) context->master_graph->meta_add_node(flip_node); } catch (const std::exception& e) { @@ -1187,6 +1257,7 @@ rocalFlipFixed( RocalTensor p_input, int horizontal_flag, int vertical_flag, + int depth_flag, bool is_output, RocalTensorLayout output_layout, RocalTensorOutputType output_datatype) { @@ -1205,7 +1276,7 @@ rocalFlipFixed( output_info.set_data_type(op_tensor_datatype); output = context->master_graph->create_tensor(output_info, is_output); std::shared_ptr flip_node = context->master_graph->add_node({input}, {output}); - flip_node->init(horizontal_flag, vertical_flag); + flip_node->init(horizontal_flag, vertical_flag, depth_flag); if (context->master_graph->meta_data_graph()) context->master_graph->meta_add_node(flip_node); } catch (const std::exception& e) { diff --git a/rocAL/source/augmentations/effects_augmentations/node_gaussian_noise.cpp b/rocAL/source/augmentations/effects_augmentations/node_gaussian_noise.cpp new file mode 100644 index 0000000..3d022be --- /dev/null +++ b/rocAL/source/augmentations/effects_augmentations/node_gaussian_noise.cpp @@ -0,0 +1,67 @@ +/* +Copyright (c) 2019 - 2023 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include "node_gaussian_noise.h" +#include "exception.h" + +GaussianNoiseNode::GaussianNoiseNode(const std::vector& inputs, const std::vector& outputs) : Node(inputs, outputs), + _mean(MEAN_RANGE[0], MEAN_RANGE[1]), + _stddev(STDDEV_RANGE[0], STDDEV_RANGE[1]) {} + +void GaussianNoiseNode::create_node() { + if (_node) + return; + + _mean.create_array(_graph, VX_TYPE_FLOAT32, _batch_size); + _stddev.create_array(_graph, VX_TYPE_FLOAT32, _batch_size); + vx_scalar seed = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_UINT32, &_seed); + int input_layout = static_cast(_inputs[0]->info().layout()); + int output_layout = static_cast(_outputs[0]->info().layout()); + int roi_type = static_cast(_inputs[0]->info().roi_type()); + vx_scalar input_layout_vx = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_INT32, &input_layout); + vx_scalar output_layout_vx = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_INT32, &output_layout); + vx_scalar roi_type_vx = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_INT32, &roi_type); + + _node = vxExtRppGaussianNoise(_graph->get(), _inputs[0]->handle(), _inputs[0]->get_roi_tensor(), _outputs[0]->handle(), _mean.default_array(), + _stddev.default_array(), seed, input_layout_vx, output_layout_vx, roi_type_vx); + vx_status status; + if ((status = vxGetStatus((vx_reference)_node)) != VX_SUCCESS) + THROW("Adding the Noise (vxExtRppGaussianNoise) node failed: " + TOSTR(status)) +} + +void GaussianNoiseNode::init(float mean, float stddev, int seed) { + _mean.set_param(mean); + _stddev.set_param(stddev); + _seed = seed; +} + +void GaussianNoiseNode::init(FloatParam* mean_param, FloatParam* stddev_param, int seed) { + _mean.set_param(core(mean_param)); + _stddev.set_param(core(stddev_param)); + _seed = seed; +} + +void GaussianNoiseNode::update_node() { + _mean.update_array(); + _stddev.update_array(); +} diff --git a/rocAL/source/augmentations/geometry_augmentations/node_flip.cpp b/rocAL/source/augmentations/geometry_augmentations/node_flip.cpp index 86be1dc..0e22a46 100644 --- a/rocAL/source/augmentations/geometry_augmentations/node_flip.cpp +++ b/rocAL/source/augmentations/geometry_augmentations/node_flip.cpp @@ -26,7 +26,8 @@ THE SOFTWARE. FlipNode::FlipNode(const std::vector &inputs, const std::vector &outputs) : Node(inputs, outputs), _horizontal(HORIZONTAL_RANGE[0], HORIZONTAL_RANGE[1]), - _vertical(VERTICAL_RANGE[0], VERTICAL_RANGE[1]) {} + _vertical(VERTICAL_RANGE[0], VERTICAL_RANGE[1]), + _depth(DEPTH_RANGE[0], DEPTH_RANGE[1]) {} void FlipNode::create_node() { if (_node) @@ -34,6 +35,7 @@ void FlipNode::create_node() { _horizontal.create_array(_graph, VX_TYPE_UINT32, _batch_size); _vertical.create_array(_graph, VX_TYPE_UINT32, _batch_size); + _depth.create_array(_graph, VX_TYPE_UINT32, _batch_size); int input_layout = static_cast(_inputs[0]->info().layout()); int output_layout = static_cast(_outputs[0]->info().layout()); int roi_type = static_cast(_inputs[0]->info().roi_type()); @@ -42,23 +44,26 @@ void FlipNode::create_node() { vx_scalar roi_type_vx = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_INT32, &roi_type); _node = vxExtRppFlip(_graph->get(), _inputs[0]->handle(), _inputs[0]->get_roi_tensor(), _outputs[0]->handle(), - _horizontal.default_array(), _vertical.default_array(), input_layout_vx, output_layout_vx,roi_type_vx); + _horizontal.default_array(), _vertical.default_array(), _depth.default_array(), input_layout_vx, output_layout_vx,roi_type_vx); vx_status status; if ((status = vxGetStatus((vx_reference)_node)) != VX_SUCCESS) THROW("Adding the flip (vxExtRppFlip) node failed: " + TOSTR(status)) } -void FlipNode::init(int h_flag, int v_flag) { +void FlipNode::init(int h_flag, int v_flag, int d_flag) { _horizontal.set_param(h_flag); _vertical.set_param(v_flag); + _depth.set_param(d_flag); } -void FlipNode::init(IntParam *h_flag, IntParam *v_flag) { +void FlipNode::init(IntParam *h_flag, IntParam *v_flag, IntParam *d_flag) { _horizontal.set_param(core(h_flag)); _vertical.set_param(core(v_flag)); + _depth.set_param(core(d_flag)); } void FlipNode::update_node() { _horizontal.update_array(); _vertical.update_array(); + _depth.update_array(); } diff --git a/rocAL_pybind/amd/rocal/fn.py b/rocAL_pybind/amd/rocal/fn.py index 99dc05e..3397796 100644 --- a/rocAL_pybind/amd/rocal/fn.py +++ b/rocAL_pybind/amd/rocal/fn.py @@ -252,12 +252,13 @@ def contrast(*inputs, contrast=None, contrast_center=None, device=None, output_l return (contrast_image) -def flip(*inputs, horizontal=0, vertical=0, device=None, output_layout=types.NHWC, output_dtype=types.UINT8): +def flip(*inputs, horizontal=0, vertical=0, depth=0, device=None, output_layout=types.NHWC, output_dtype=types.UINT8): """!Flip images horizontally and/or vertically based on inputs. @param inputs the input image passed to the augmentation @param horizontal (int, optional, default = 0) flip the horizontal dimension @param vertical (int, optional, default = 0) flip the vertical dimension + @param depth (int, optional, default = 0) flip the depth dimension @param device (string, optional, default = None) Parameter unused for augmentation @param output_layout (int, optional, default = types.NHWC) tensor layout for the augmentation output @param output_dtype (int, optional, default = types.UINT8) tensor dtype for the augmentation output @@ -268,10 +269,12 @@ def flip(*inputs, horizontal=0, vertical=0, device=None, output_layout=types.NHW horizontal, int) else horizontal vertical = b.createIntParameter( vertical) if isinstance(vertical, int) else vertical + depth = b.createIntParameter( + depth) if isinstance(depth, int) else depth # pybind call arguments kwargs_pybind = {"input_image": inputs[0], - "is_output": False, "horizontal": horizontal, "vertical": vertical, "output_layout": output_layout, "output_dtype": output_dtype} + "is_output": False, "horizontal": horizontal, "vertical": vertical, "depth": depth, "output_layout": output_layout, "output_dtype": output_dtype} flip_image = b.flip(Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) return (flip_image) @@ -1070,3 +1073,29 @@ def set_layout(*inputs, output_layout=types.NHWC): new_output = b.setLayout( Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) return (new_output) + + +def gaussian_noise(*inputs, mean=0.0, std_dev=1.0, seed=0, device=None, output_layout=types.NHWC, output_dtype=types.UINT8): + """!Applies Gaussian noise to the input image. + + @param inputs (list) The input image to which salt-and-pepper noise is applied. + @param mean (float, optional, default = 0.0) Mean used for noise generation. Default is 0.0. + @param std_dev (float, optional, default = 1.0) Standard deviation used for noise generation. Default is 1.0. + @param seed (int, optional, default = 0) Random seed. Default is 0. + @param device (string, optional, default = None) Parameter unused for augmentation + @param output_layout (int, optional, default = types.NHWC) Tensor layout for the augmentation output. Default is types.NHWC. + @param output_dtype (int, optional, default = types.UINT*) Tensor dtype for the augmentation output. Default is types.UINT8. + + @return images with Gaussian noise added. + """ + mean = b.createFloatParameter( + mean) if isinstance(mean, float) else mean + std_dev = b.createFloatParameter( + std_dev) if isinstance(std_dev, float) else std_dev + + # pybind call arguments + kwargs_pybind = {"input_image": inputs[0], "is_output": False, "mean": mean, "std_dev": std_dev, + "seed": seed, "output_layout": output_layout, "output_dtype": output_dtype} + noise_added_image = b.gaussianNoise( + Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) + return (noise_added_image) diff --git a/rocAL_pybind/examples/rocAL_api_numpy_reader.py b/rocAL_pybind/examples/rocAL_api_numpy_reader.py index 6061dff..e62c063 100644 --- a/rocAL_pybind/examples/rocAL_api_numpy_reader.py +++ b/rocAL_pybind/examples/rocAL_api_numpy_reader.py @@ -46,11 +46,13 @@ def main(): numpy_reader_output = fn.readers.numpy(file_root=data_path, shard_id=local_rank, num_shards=world_size) new_output = fn.set_layout(numpy_reader_output, output_layout=types.NCDHW) brightness_output = fn.brightness(new_output, brightness=1.25, brightness_shift=0.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) - pipeline.set_outputs(brightness_output) + flip_output = fn.flip(brightness_output, horizontal=0, vertical=1, depth=1, output_layout=types.NCDHW, output_dtype=types.FLOAT) + # noise_output = fn.gaussian_noise(flip_output, mean=0.0, std_dev=1.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) + pipeline.set_outputs(flip_output) pipeline.build() - numpyIteratorPipeline = ROCALNumpyIterator(pipeline, tensor_dtype=types.UINT8) + numpyIteratorPipeline = ROCALNumpyIterator(pipeline, device='cpu' if rocal_cpu else 'gpu') print(len(numpyIteratorPipeline)) cnt = 0 for epoch in range(1): @@ -60,7 +62,7 @@ def main(): for j in range(batch_size): arr = np.load(files_list[cnt]) shape = arr.shape - print(np.array_equal(arr * 1.25, it[j].cpu().numpy()[:, :shape[1], :shape[2], :shape[3]])) + print(np.array_equal(np.flip(arr * 1.25, axis=[1,2]), it[j].cpu().numpy()[:, :shape[1], :shape[2], :shape[3]])) cnt += 1 print("************************************** i *************************************",i) numpyIteratorPipeline.reset() diff --git a/rocAL_pybind/rocal_pybind.cpp b/rocAL_pybind/rocal_pybind.cpp index cc1b550..917d017 100644 --- a/rocAL_pybind/rocal_pybind.cpp +++ b/rocAL_pybind/rocal_pybind.cpp @@ -596,5 +596,7 @@ PYBIND11_MODULE(rocal_pybind, m) { py::return_value_policy::reference); m.def("lensCorrection", &rocalLensCorrection, py::return_value_policy::reference); + m.def("gaussianNoise", &rocalGaussianNoise, + py::return_value_policy::reference); } } // namespace rocal From 5f095fb9fae232a523ec474aa2658437bd9692f3 Mon Sep 17 00:00:00 2001 From: SundarRajan98 Date: Fri, 17 Nov 2023 15:04:57 +0000 Subject: [PATCH 3/3] Adding roi_random_crop and slice kernel integration --- rocAL/include/api/rocal_api_augmentation.h | 41 +++-- rocAL/include/api/rocal_api_meta_data.h | 11 ++ rocAL/include/api/rocal_api_types.h | 12 ++ .../augmentations/augmentations_nodes.h | 1 + .../geometry_augmentations/node_slice.h | 51 ++++++ rocAL/include/pipeline/master_graph.h | 8 + rocAL/include/pipeline/tensor.h | 34 +++- rocAL/source/api/rocal_api_augmentation.cpp | 33 ++++ rocAL/source/api/rocal_api_meta_data.cpp | 11 ++ .../geometry_augmentations/node_slice.cpp | 114 +++++++++++++ rocAL/source/pipeline/master_graph.cpp | 152 ++++++++++++++++++ rocAL/source/pipeline/tensor.cpp | 55 ++++--- rocAL_pybind/amd/rocal/fn.py | 32 ++++ rocAL_pybind/amd/rocal/types.py | 6 + .../examples/rocAL_api_numpy_reader.py | 12 +- rocAL_pybind/rocal_pybind.cpp | 7 + 16 files changed, 541 insertions(+), 39 deletions(-) create mode 100644 rocAL/include/augmentations/geometry_augmentations/node_slice.h create mode 100644 rocAL/source/augmentations/geometry_augmentations/node_slice.cpp diff --git a/rocAL/include/api/rocal_api_augmentation.h b/rocAL/include/api/rocal_api_augmentation.h index 438d523..1466f5b 100644 --- a/rocAL/include/api/rocal_api_augmentation.h +++ b/rocAL/include/api/rocal_api_augmentation.h @@ -578,11 +578,11 @@ extern "C" RocalTensor ROCAL_API_CALL rocalSnPNoiseFixed(RocalContext context, R * \return RocalTensor */ extern "C" RocalTensor ROCAL_API_CALL rocalGaussianNoise(RocalContext context, RocalTensor input, - bool is_output, - RocalFloatParam mean = NULL, RocalFloatParam std_dev = NULL, - int seed = 0, - RocalTensorLayout output_layout = ROCAL_NONE, - RocalTensorOutputType output_datatype = ROCAL_UINT8); + bool is_output, + RocalFloatParam mean = NULL, RocalFloatParam std_dev = NULL, + int seed = 0, + RocalTensorLayout output_layout = ROCAL_NONE, + RocalTensorOutputType output_datatype = ROCAL_UINT8); /*! \brief Applies gaussian noise effect on images with fixed parameters. * \ingroup group_rocal_augmentations @@ -597,10 +597,33 @@ extern "C" RocalTensor ROCAL_API_CALL rocalGaussianNoise(RocalContext context, R * \return RocalTensor */ extern "C" RocalTensor ROCAL_API_CALL rocalGaussianNoiseFixed(RocalContext context, RocalTensor input, - float mean, float std_dev, - bool is_output, int seed = 0, - RocalTensorLayout output_layout = ROCAL_NONE, - RocalTensorOutputType output_datatype = ROCAL_UINT8); + float mean, float std_dev, + bool is_output, int seed = 0, + RocalTensorLayout output_layout = ROCAL_NONE, + RocalTensorOutputType output_datatype = ROCAL_UINT8); + +/*! \brief Applies slice augmentation on images. + * \ingroup group_rocal_augmentations + * \param [in] context Rocal context + * \param [in] input Input Rocal tensor + * \param [in] is_output is the output tensor part of the graph output + * \param [in] anchor_tensor Anchor used for slice + * \param [in] shape_tensor Shape of the output slice + * \param [in] fill_values Fill value for the slice padding + * \param [in] policy Padding policy used for slice augmentation + * \param [in] output_layout the layout of the output tensor + * \param [in] output_datatype the data type of the output tensor + * \return RocalTensor + */ +extern "C" RocalTensor ROCAL_API_CALL rocalSlice(RocalContext context, + RocalTensor input, + bool is_output, + RocalTensor anchor_tensor, + std::vector shape_tensor, + std::vector fill_values, + RocalOutOfBoundsPolicy policy, + RocalTensorLayout output_layout = ROCAL_NONE, + RocalTensorOutputType output_datatype = ROCAL_UINT8); /*! \brief Applies snow effect on images. * \ingroup group_rocal_augmentations diff --git a/rocAL/include/api/rocal_api_meta_data.h b/rocAL/include/api/rocal_api_meta_data.h index dfe961a..b022b7a 100644 --- a/rocAL/include/api/rocal_api_meta_data.h +++ b/rocAL/include/api/rocal_api_meta_data.h @@ -284,4 +284,15 @@ extern "C" void ROCAL_API_CALL rocalGetImageId(RocalContext p_context, int* buf) */ extern "C" void ROCAL_API_CALL rocalGetJointsDataPtr(RocalContext p_context, RocalJointsData** joints_data); +/*! \brief initialize the values required for ROI Random crop + * \ingroup group_rocal_meta_data + * \param [in] rocal_context rocal context + * \param [in] crop_shape_batch + * \param [in] roi_begin_batch + * \param [in] input_shape_batch + * \param [in] roi_end_batch + * \param [out] anchor The generated anchor tensor + */ +extern "C" RocalTensor ROCAL_API_CALL rocalROIRandomCrop(RocalContext p_context, RocalTensor p_input, std::vector crop_shape, int remove_dim=-1); + #endif // MIVISIONX_ROCAL_API_META_DATA_H diff --git a/rocAL/include/api/rocal_api_types.h b/rocAL/include/api/rocal_api_types.h index 4e5bc33..2a35533 100644 --- a/rocAL/include/api/rocal_api_types.h +++ b/rocAL/include/api/rocal_api_types.h @@ -356,4 +356,16 @@ enum class RocalROICordsType { ROCAL_XYWH = 1 }; +/*! \brief Tensor padding types + * \ingroup group_rocal_types + */ +enum RocalOutOfBoundsPolicy { + /*! \brief TRIM_TO_SHAPE + */ + TRIMTOSHAPE = 0, + /*! \brief PAD + */ + PAD, +}; + #endif // MIVISIONX_ROCAL_API_TYPES_H diff --git a/rocAL/include/augmentations/augmentations_nodes.h b/rocAL/include/augmentations/augmentations_nodes.h index 08b3f55..ef6beff 100644 --- a/rocAL/include/augmentations/augmentations_nodes.h +++ b/rocAL/include/augmentations/augmentations_nodes.h @@ -56,3 +56,4 @@ THE SOFTWARE. #include "node_nop.h" #include "node_sequence_rearrange.h" #include "node_gaussian_noise.h" +#include "node_slice.h" diff --git a/rocAL/include/augmentations/geometry_augmentations/node_slice.h b/rocAL/include/augmentations/geometry_augmentations/node_slice.h new file mode 100644 index 0000000..08f5cd0 --- /dev/null +++ b/rocAL/include/augmentations/geometry_augmentations/node_slice.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once +#include "graph.h" +#include "node.h" +#include "parameter_factory.h" +#include "parameter_vx.h" +#include "rocal_api_types.h" + +class SliceNode : public Node { + public: + SliceNode(const std::vector &inputs, const std::vector &outputs); + SliceNode() = delete; + ~SliceNode(); + void init(Tensor *anchor_param, std::vector shape_param, std::vector &fill_values_param, RocalOutOfBoundsPolicy policy); + + protected: + void create_node() override; + void update_node() override; + void create_shape_tensor(); + + private: + vx_array _fill_values_array; + void *_shape_array; + Tensor *_anchor; + vx_tensor _shape = nullptr; + std::vector _fill_values, _fill_values_vec; + std::vector _anchor_vec, _shape_vec; + std::vector> _slice_roi; + RocalOutOfBoundsPolicy _policy = RocalOutOfBoundsPolicy::PAD; +}; \ No newline at end of file diff --git a/rocAL/include/pipeline/master_graph.h b/rocAL/include/pipeline/master_graph.h index d514bf8..06aee78 100644 --- a/rocAL/include/pipeline/master_graph.h +++ b/rocAL/include/pipeline/master_graph.h @@ -133,6 +133,8 @@ class MasterGraph { void set_sequence_batch_size(size_t sequence_length) { _sequence_batch_size = _user_batch_size * sequence_length; } std::vector get_bbox_encoded_buffers(size_t num_encoded_boxes); size_t bounding_box_batch_count(pMetaDataBatch meta_data_batch); + Tensor* roi_random_crop(Tensor *input, int *crop_shape, int remove_dim=-1); + void update_roi_random_crop(int *crop_shape_batch, int *roi_begin_batch, int *roi_end_batch); #if ENABLE_OPENCL cl_command_queue get_ocl_cmd_q() { return _device.resources()->cmd_queue; } #endif @@ -205,6 +207,12 @@ class MasterGraph { bool _offset; // Returns normalized offsets ((encoded_bboxes*scale - anchors*scale) - mean) / stds in EncodedBBoxes that use std and the mean and scale arguments if offset="True" std::vector _means, _stds; //_means: [x y w h] mean values for normalization _stds: [x y w h] standard deviations for offset normalization. bool _augmentation_metanode = false; + bool _is_roi_random_crop = false; + uint _input_dims, _output_dims, _roi_dim_to_remove; + int *_crop_shape_batch = nullptr; + int *_roi_batch = nullptr; + Tensor *_roi_random_crop_tensor = nullptr; + void *_roi_random_crop_buf = nullptr; #if ENABLE_HIP BoxEncoderGpu *_box_encoder_gpu = nullptr; #endif diff --git a/rocAL/include/pipeline/tensor.h b/rocAL/include/pipeline/tensor.h index 428dfe3..2f49b8f 100644 --- a/rocAL/include/pipeline/tensor.h +++ b/rocAL/include/pipeline/tensor.h @@ -183,17 +183,17 @@ class TensorInfo { _channels = _dims.at(2); } else if (_layout == RocalTensorlayout::NDHWC) { _is_image = false; - _max_shape.resize(3); - _max_shape = {_dims.at(1), _dims.at(2), _dims.at(3)}; + _max_shape.resize(4); + _max_shape.assign(_dims.begin() + 1, _dims.end()); _channels = _dims.at(4); } else if (_layout == RocalTensorlayout::NCDHW) { _is_image = false; - _max_shape.resize(3); - _max_shape = {_dims.at(2), _dims.at(3), _dims.at(4)}; + _max_shape.resize(4); + _max_shape.assign(_dims.begin() + 1, _dims.end()); _channels = _dims.at(1); } } else { - if (!_max_shape.size()) _max_shape.resize(_num_of_dims - 1, 0); // Since 2 values will be stored in the vector + if (!_max_shape.size()) _max_shape.resize(_num_of_dims - 1, 0); _max_shape.assign(_dims.begin() + 1, _dims.end()); } reset_tensor_roi_buffers(); @@ -247,6 +247,29 @@ class TensorInfo { set_tensor_layout(layout); // Modify the layout and dims based on the layout input reset_tensor_roi_buffers(); // Reset ROI buffers to reflect the modified width and height } + void modify_dims(RocalTensorlayout layout, std::vector new_dims) { + switch (_layout) { + case RocalTensorlayout::NDHWC: { + _max_shape[0] = _dims[1] = new_dims[0]; + _max_shape[1] = _dims[2] = new_dims[1]; + _max_shape[2] = _dims[3] = new_dims[2]; + break; + } + case RocalTensorlayout::NCDHW: { + _max_shape[1] = _dims[2] = new_dims[0]; + _max_shape[2] = _dims[3] = new_dims[1]; + _max_shape[3] = _dims[4] = new_dims[2]; + break; + } + default: { + THROW("Invalid layout type specified") + } + } + modify_strides(); + _data_size = _strides[0] * _dims[0]; // Modify data size wrt latest width and height + set_tensor_layout(layout); // Modify the layout and dims based on the layout input + reset_tensor_roi_buffers(); // Reset ROI buffers to reflect the modified width and height + } void modify_strides() { _strides[_num_of_dims - 1] = _data_type_size; for (int i = _num_of_dims - 2; i >= 0; i--) { @@ -345,6 +368,7 @@ class Tensor : public rocalTensor { // create_from_handle() no internal memory allocation is done here since // tensor's handle should be swapped with external buffers before usage int create_from_handle(vx_context context); + int create_from_ptr(vx_context context, void *ptr); int create_virtual(vx_context context, vx_graph graph); bool is_handle_set() { return (_vx_handle != 0); } void set_dims(std::vector dims) { _info.set_dims(dims); } diff --git a/rocAL/source/api/rocal_api_augmentation.cpp b/rocAL/source/api/rocal_api_augmentation.cpp index 0e8c149..c233a7e 100644 --- a/rocAL/source/api/rocal_api_augmentation.cpp +++ b/rocAL/source/api/rocal_api_augmentation.cpp @@ -1213,6 +1213,39 @@ rocalGaussianNoiseFixed( return output; } +RocalTensor ROCAL_API_CALL +rocalSlice( + RocalContext p_context, + RocalTensor p_input, + bool is_output, + RocalTensor anchor_tensor, + std::vector shape, + std::vector fill_values, + RocalOutOfBoundsPolicy policy, + RocalTensorLayout output_layout, + RocalTensorOutputType output_datatype) { + Tensor* output = nullptr; + if ((p_context == nullptr) || (p_input == nullptr)) + ERR("Invalid ROCAL context or invalid input tensor") + auto context = static_cast(p_context); + auto input = static_cast(p_input); + auto anchor = static_cast(anchor_tensor); + try { + RocalTensorlayout op_tensor_layout = static_cast(output_layout); + RocalTensorDataType op_tensor_datatype = static_cast(output_datatype); + TensorInfo output_info = input->info(); + output_info.set_tensor_layout(op_tensor_layout); + output_info.set_data_type(op_tensor_datatype); + output_info.modify_dims(op_tensor_layout, shape); + output = context->master_graph->create_tensor(output_info, is_output); + context->master_graph->add_node({input}, {output})->init(anchor, shape, fill_values, policy); + } catch (const std::exception& e) { + context->capture_error(e.what()); + ERR(e.what()) + } + return output; +} + RocalTensor ROCAL_API_CALL rocalFlip( RocalContext p_context, diff --git a/rocAL/source/api/rocal_api_meta_data.cpp b/rocAL/source/api/rocal_api_meta_data.cpp index 1fa4768..1fa234f 100644 --- a/rocAL/source/api/rocal_api_meta_data.cpp +++ b/rocAL/source/api/rocal_api_meta_data.cpp @@ -465,3 +465,14 @@ void *joints_data = (RocalJointsData*)(&(meta_data.second->get_joints_data_batch())); } + +RocalTensor + ROCAL_API_CALL + rocalROIRandomCrop(RocalContext p_context, RocalTensor p_input, std::vector crop_shape, int remove_dim) { + if ((p_context == nullptr) || (p_input == nullptr)) { + ERR("Invalid ROCAL context or invalid input tensor") + } + auto context = static_cast(p_context); + auto input = static_cast(p_input); + return context->master_graph->roi_random_crop(input, crop_shape.data(), remove_dim); +} diff --git a/rocAL/source/augmentations/geometry_augmentations/node_slice.cpp b/rocAL/source/augmentations/geometry_augmentations/node_slice.cpp new file mode 100644 index 0000000..8c757cc --- /dev/null +++ b/rocAL/source/augmentations/geometry_augmentations/node_slice.cpp @@ -0,0 +1,114 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "node_slice.h" + +#include + +#include "exception.h" + +SliceNode::SliceNode(const std::vector &inputs, const std::vector &outputs) : Node(inputs, outputs) {} + +void SliceNode::create_node() { + if (_node) + return; + + create_shape_tensor(); + auto max_shape = _outputs[0]->info().max_shape(); + _slice_roi.resize(_batch_size, std::vector(max_shape.size())); + for (uint i = 0; i < _batch_size; i++) + for (uint j = 0; j < max_shape.size(); j++) + _slice_roi[i][j] = max_shape[j]; + const int buffer_size = _batch_size; + int input_layout = static_cast(_inputs[0]->info().layout()); + int roi_type = static_cast(_inputs[0]->info().roi_type()); + vx_scalar input_layout_vx = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_INT32, &input_layout); + vx_scalar roi_type_vx = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_INT32, &roi_type); + _fill_values_array = vxCreateArray(vxGetContext((vx_reference)_graph->get()), VX_TYPE_FLOAT32, buffer_size); + vx_status status = vxAddArrayItems(_fill_values_array, buffer_size, _fill_values_vec.data(), sizeof(vx_float32)); + if (status != 0) + THROW(" vxAddArrayItems failed in the slice (vxExtRppSlice) node: " + TOSTR(status)); + vx_scalar policy = vxCreateScalar(vxGetContext((vx_reference)_graph->get()), VX_TYPE_UINT32, &_policy); + _node = vxExtRppSlice(_graph->get(), _inputs[0]->handle(), _inputs[0]->get_roi_tensor(), _outputs[0]->handle(), _anchor->handle(), + _shape, _fill_values_array, policy, input_layout_vx, roi_type_vx); + + if ((status = vxGetStatus((vx_reference)_node)) != VX_SUCCESS) + THROW("Adding the slice node (vxRppSlice) failed: " + TOSTR(status)) +} + +void SliceNode::update_node() { + // if fill values passed by user less than what is required, replicate the values + if (_fill_values.size() == 1) { + std::fill(_fill_values_vec.begin(), _fill_values_vec.end(), _fill_values[0]); + } + vx_status status = VX_SUCCESS; + _outputs[0]->update_tensor_roi(_slice_roi); + status = vxCopyArrayRange((vx_array)_fill_values_array, 0, _batch_size, sizeof(vx_float32), _fill_values_vec.data(), VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST); + if (status != 0) + WRN("ERROR: vxCopyArrayRange failed in the slice node (vxExtRppSlice) node: " + TOSTR(status)) + int* shape_arr = (int *) _shape_array; + // replicate shape values for all samples in a batch + for (uint i = 0; i < _batch_size; i++) { + int sample_idx = i * _shape_vec.size(); + memcpy(&(shape_arr[sample_idx]), _shape_vec.data(), _shape_vec.size() * sizeof(int)); + } +} + +void SliceNode::init(Tensor *anchor, std::vector shape, std::vector &fill_values, RocalOutOfBoundsPolicy policy) { + _policy = policy; + _anchor = anchor; + _shape_vec = shape; + _fill_values = fill_values; + _fill_values_vec.resize(_batch_size); +} + +// Create vx_tensor for the shape coordinates +void SliceNode::create_shape_tensor() { + vx_size num_of_dims = 2; + vx_size stride[num_of_dims]; + std::vector _shape_tensor_dims = {_batch_size, _shape_vec.size()}; + stride[0] = sizeof(vx_int32); + stride[1] = stride[0] * _shape_tensor_dims[0]; + vx_enum mem_type = VX_MEMORY_TYPE_HOST; + if (_inputs[0]->info().mem_type() == RocalMemType::HIP) + mem_type = VX_MEMORY_TYPE_HIP; + allocate_host_or_pinned_mem(&_shape_array, stride[1] * _shape_vec.size(), _inputs[0]->info().mem_type()); + + _shape = vxCreateTensorFromHandle(vxGetContext((vx_reference)_graph->get()), num_of_dims, _shape_tensor_dims.data(), VX_TYPE_INT32, 0, + stride, reinterpret_cast(_shape_array), mem_type); + vx_status status; + if ((status = vxGetStatus((vx_reference)_shape)) != VX_SUCCESS) + THROW("Error: vxCreateTensorFromHandle(_shape: failed " + TOSTR(status)) +} + +SliceNode::~SliceNode() { + if (_inputs[0]->info().mem_type() == RocalMemType::HIP) { +#if ENABLE_HIP + hipError_t err = hipHostFree(_shape_array); + if (err != hipSuccess) + std::cerr << "\n[ERR] hipFree failed " << std::to_string(err) << "\n"; +#endif + } else { + if (_shape_array) free(_shape_array); + } + if (_shape) vxReleaseTensor(&_shape); +} diff --git a/rocAL/source/pipeline/master_graph.cpp b/rocAL/source/pipeline/master_graph.cpp index 85d5f1a..c38718e 100644 --- a/rocAL/source/pipeline/master_graph.cpp +++ b/rocAL/source/pipeline/master_graph.cpp @@ -346,6 +346,21 @@ void MasterGraph::release() { _output_tensor_list.release(); // It will call the vxReleaseTensor internally in the destructor for each tensor in the list for (auto tensor_list : _metadata_output_tensor_list) dynamic_cast(tensor_list)->release(); // It will call the vxReleaseTensor internally in the destructor for each tensor in the list + if(_is_roi_random_crop) + { + // delete _roi_random_crop_tensor; + if(_crop_shape_batch != nullptr) + delete[] _crop_shape_batch; + if(_roi_random_crop_buf != nullptr) { + if (_affinity == RocalAffinity::GPU) { + #if ENABLE_HIP + hipError_t err = hipHostFree(_roi_random_crop_buf); + if (err != hipSuccess) + std::cerr << "\n[ERR] hipFree failed " << std::to_string(err) << "\n"; + #endif + } else { free(_roi_random_crop_buf); } + } + } if (_graph != nullptr) _graph->release(); @@ -932,6 +947,29 @@ void MasterGraph::output_routine() { _meta_data_graph->process(_augmented_meta_data, output_meta_data); } } + + if(_is_roi_random_crop) + { + // get the roi_begin and roi_end values from random_object_bbox + int *roi_begin_batch = new int[_user_batch_size * _input_dims]; + int *roi_end_batch = new int[_user_batch_size * _input_dims]; + for(uint i = 0; i < _user_batch_size; i++) + { + int sample_idx = i * _input_dims; + int *roi_begin = &roi_begin_batch[sample_idx]; + int *roi_end = &roi_end_batch[sample_idx]; + int *input_shape = &_roi_batch[sample_idx * 2 + _input_dims]; + for(uint j = 0; j < _input_dims; j++) + { + roi_begin[j] = std::rand() % (_input_dims * 5); + roi_end[j] = std::min(roi_begin[j] + (std::rand() % input_shape[j]), input_shape[j]); + } + } + update_roi_random_crop(_crop_shape_batch, roi_begin_batch, roi_end_batch); + delete[] roi_begin_batch; + delete[] roi_end_batch; + } + _process_time.start(); _graph->process(); _process_time.end(); @@ -1381,6 +1419,120 @@ TensorList *MasterGraph::mask_meta_data() { return &_mask_tensor_list; } +class BatchRNGUniform { + public: + /** + * @brief Used to keep batch of RNGs, so Operators can be immune to order of sample processing + * while using randomness + * + * @param seed Used to generate seed_seq to initialize batch of RNGs + * @param batch_size How many RNGs to store + * @param state_size How many seed are used to initialize one RNG. Used to lower probablity of + * collisions between seeds used to initialize RNGs in different operators. + */ + BatchRNGUniform(int64_t seed, int batch_size, int state_size = 4) + : seed_(seed) { + std::seed_seq seq{seed_}; + std::vector seeds(batch_size * state_size); + seq.generate(seeds.begin(), seeds.end()); + rngs_.reserve(batch_size); + for (int i = 0; i < batch_size * state_size; i += state_size) { + std::seed_seq s(seeds.begin() + i, seeds.begin() + i + state_size); + rngs_.emplace_back(s); + } + } + + + /** + * Returns engine corresponding to given sample ID + */ + std::mt19937 &operator[](int sample) noexcept { + return rngs_[sample]; + } + + private: + int64_t seed_; + std::vector rngs_; +}; + +Tensor* MasterGraph::roi_random_crop(Tensor *input, int *crop_shape, int remove_dim) +{ + _is_roi_random_crop = true; + if(input->info().is_image()) + _input_dims = input->num_of_dims() - 2; // TO be changed later when generic roi changes are added + else + _input_dims = input->num_of_dims() - 1; + + _output_dims = remove_dim >= 0 ? _input_dims - 1 : _input_dims; + _roi_dim_to_remove = remove_dim >= 0 ? remove_dim : _input_dims; // Setting roi_dim_to_remove as _input_dims if remove_dim is not passed + + _roi_batch = reinterpret_cast(input->info().roi().get_ptr()); + _crop_shape_batch = new int[_input_dims * _user_batch_size]; // TODO handle this case later when different crop_shape is given for each tensor + + // replicate crop_shape values for all samples in a batch + for(uint i = 0; i < _user_batch_size; i++) + { + int sample_idx = i * _input_dims; + memcpy(&(_crop_shape_batch[sample_idx]), crop_shape, _input_dims * sizeof(int)); + } + + // create new instance of tensor class + std::vector dims = {_user_batch_size, _output_dims}; + auto info = TensorInfo(std::move(dims), input->info().mem_type(), RocalTensorDataType::INT32); + _roi_random_crop_tensor = new Tensor(info); + + // allocate memory for the raw buffer pointer in tensor object + allocate_host_or_pinned_mem(&_roi_random_crop_buf, _user_batch_size * _output_dims * sizeof(int), input->info().mem_type()); + _roi_random_crop_tensor->create_from_ptr(_context, _roi_random_crop_buf); + return _roi_random_crop_tensor; +} + +void MasterGraph::update_roi_random_crop(int *crop_shape_batch, int *roi_begin_batch, int *roi_end_batch) { + int *crop_begin_batch = static_cast(_roi_random_crop_buf); + uint seed = std::time(0); + + BatchRNGUniform _rng = {seed, static_cast(_user_batch_size)}; + for(uint i = 0; i < _user_batch_size; i++) { + int sample_idx = i * _input_dims; + int *crop_shape = &crop_shape_batch[sample_idx]; + int *roi_begin = &roi_begin_batch[sample_idx]; + int *input_shape = &_roi_batch[sample_idx * 2 + _input_dims]; + int *roi_end = &roi_end_batch[sample_idx]; + int *crop_begin = &crop_begin_batch[i * _output_dims]; + + for(uint j = 0, k = 0; j < _input_dims; j++) { + if (j == _roi_dim_to_remove) + continue; + // check if crop_shape, roi_end is greater than input_shape + if(crop_shape[j] > input_shape[j]) + THROW("crop shape cannot be greater than input shape"); + if (roi_end[j] > input_shape[j]) + THROW("ROI shape cannot be greater than input shape"); + + int roi_length = roi_end[j] - roi_begin[j]; + int crop_length = crop_shape[j]; + if (roi_length == crop_length) { + crop_begin[k++] = roi_begin[j]; + } else { + int64_t start_range[2] = {roi_begin[j], roi_end[j] - crop_length}; + + // swap range values if start_range[0] > start_range[1] + if (start_range[0] > start_range[1]) { + int64_t temp = start_range[0]; + start_range[0] = start_range[1]; + start_range[1] = temp; + } + + // check if range is within the bounds of input + start_range[0] = std::max(0, start_range[0]); + start_range[1] = std::min(input_shape[j] - crop_length, start_range[1]); + + auto dist = std::uniform_int_distribution(start_range[0], start_range[1]); + crop_begin[k++] = dist(_rng[i]); + } + } + } +} void MasterGraph::notify_user_thread() { if (_output_routine_finished_processing) diff --git a/rocAL/source/pipeline/tensor.cpp b/rocAL/source/pipeline/tensor.cpp index 7ae0340..5f0a53a 100644 --- a/rocAL/source/pipeline/tensor.cpp +++ b/rocAL/source/pipeline/tensor.cpp @@ -77,6 +77,10 @@ vx_enum interpret_tensor_data_type(RocalTensorDataType data_type) { return VX_TYPE_FLOAT16; case RocalTensorDataType::UINT8: return VX_TYPE_UINT8; + case RocalTensorDataType::UINT32: + return VX_TYPE_UINT32; + case RocalTensorDataType::INT32: + return VX_TYPE_INT32; default: THROW("Unsupported Tensor type " + TOSTR(data_type)) } @@ -108,23 +112,14 @@ bool operator==(const TensorInfo &rhs, const TensorInfo &lhs) { void TensorInfo::reset_tensor_roi_buffers() { unsigned *roi_buf; - auto roi_no_of_dims = _is_image ? 2 : (_num_of_dims - 2); + auto roi_no_of_dims = _is_image ? 2 : (_num_of_dims - 1); auto roi_size = (_layout == RocalTensorlayout::NFCHW || _layout == RocalTensorlayout::NFHWC) ? _dims[0] * _dims[1] : _batch_size; // For Sequences pre allocating the ROI to N * F to replicate in OpenVX extensions allocate_host_or_pinned_mem((void **)&roi_buf, roi_size * roi_no_of_dims * 2 * sizeof(unsigned), _mem_type); _roi.set_ptr(roi_buf, _mem_type, roi_size, roi_no_of_dims); - if (_layout == RocalTensorlayout::NCDHW) { + if (_layout == RocalTensorlayout::NCDHW || _layout == RocalTensorlayout::NDHWC) { for (unsigned i = 0; i < _batch_size; i++) { unsigned *tensor_shape = _roi[i].end; - tensor_shape[2] = _max_shape[1]; - tensor_shape[1] = _max_shape[2]; - tensor_shape[0] = _max_shape[3]; - } - } else if (_layout == RocalTensorlayout::NDHWC) { - for (unsigned i = 0; i < _batch_size; i++) { - unsigned *tensor_shape = _roi[i].end; - tensor_shape[2] = _max_shape[0]; - tensor_shape[1] = _max_shape[1]; - tensor_shape[0] = _max_shape[2]; + tensor_shape[i] = _max_shape[i]; } } else if (_is_image) { Roi2DCords *roi = _roi.get_2D_roi(); @@ -226,14 +221,10 @@ void Tensor::update_tensor_roi(const std::vector> &shape) THROW("The number of dims to be updated and the num of dims of tensor info does not match") unsigned *tensor_shape = _info.roi()[i].end; - if (_info.layout() == RocalTensorlayout::NCDHW) { - tensor_shape[2] = shape[i][1] > max_shape[1] ? max_shape[1] : shape[i][1]; - tensor_shape[1] = shape[i][2] > max_shape[2] ? max_shape[2] : shape[i][2]; - tensor_shape[0] = shape[i][3] > max_shape[3] ? max_shape[3] : shape[i][3]; - } else if (_info.layout() == RocalTensorlayout::NDHWC) { - tensor_shape[2] = shape[i][0] > max_shape[0] ? max_shape[0] : shape[i][0]; - tensor_shape[1] = shape[i][1] > max_shape[1] ? max_shape[1] : shape[i][1]; - tensor_shape[0] = shape[i][2] > max_shape[2] ? max_shape[2] : shape[i][2]; + if (_info.layout() == RocalTensorlayout::NCDHW || _info.layout() == RocalTensorlayout::NDHWC) { + for (unsigned j = 0; j < max_shape.size(); j++) { + tensor_shape[j] = shape[i][j] > max_shape[j] ? max_shape[j] : shape[i][j]; + } } } } @@ -294,6 +285,30 @@ int Tensor::create_from_handle(vx_context context) { return 0; } +int Tensor::create_from_ptr(vx_context context, void *ptr) { + if (_vx_handle) { + WRN("Tensor object create method is already called ") + return -1; + } + + _context = context; + vx_enum tensor_data_type = interpret_tensor_data_type(_info.data_type()); + unsigned num_of_dims = _info.num_of_dims(); + vx_size stride[num_of_dims]; + + stride[0] = tensor_data_size(_info.data_type()); + for (unsigned i = 1; i < num_of_dims; i++) + stride[i] = stride[i - 1] * _info.dims().at(i - 1); + + _vx_handle = vxCreateTensorFromHandle(_context, _info.num_of_dims(), _info.dims().data(), tensor_data_type, 0, stride, ptr, vx_mem_type(_info._mem_type)); + vx_status status; + if ((status = vxGetStatus((vx_reference)_vx_handle)) != VX_SUCCESS) + THROW("Error: vxCreateTensorFromHandle(input: failed " + TOSTR(status)) + _info._type = TensorInfo::Type::HANDLE; + _mem_handle = ptr; + return 0; +} + int Tensor::create(vx_context context) { if (_vx_handle) { WRN("Tensor object create method is already called ") diff --git a/rocAL_pybind/amd/rocal/fn.py b/rocAL_pybind/amd/rocal/fn.py index 3397796..642455c 100644 --- a/rocAL_pybind/amd/rocal/fn.py +++ b/rocAL_pybind/amd/rocal/fn.py @@ -825,6 +825,32 @@ def crop(*inputs, crop=[0, 0], crop_pos_x=0.5, crop_pos_y=0.5, crop_pos_z=0.5, return (cropped_image) +def slice(*inputs, anchor = [], shape = [], dtype = types.FLOAT, end = [], fill_values = [0.0], out_of_bounds_policy = types.PAD, device=None, output_layout=types.NHWC, output_dtype=types.UINT8): + """ + The slice can be specified by proving the start and end coordinates, or start coordinates and shape of the slice. Both coordinates and shapes can be provided in absolute or relative terms. + + The slice arguments can be specified by the following named arguments: + + start: Slice start coordinates (absolute) + + rel_start: Slice start coordinates (relative) + + end: Slice end coordinates (absolute) + + rel_end: Slice end coordinates (relative) + + shape: Slice shape (absolute) + + rel_shape: Slice shape (relative) + + """ + + kwargs_pybind = {"input": inputs[0], "is_output": False, "anchor": anchor, "shape": shape, "fill_values": fill_values, + "out_of_bounds_policy": out_of_bounds_policy, "output_layout": output_layout, "output_dtype": output_dtype} + slice_output = b.slice(Pipeline._current_pipeline._handle ,*(kwargs_pybind.values())) + return slice_output + + def color_twist(*inputs, brightness=1.0, contrast=1.0, hue=0.0, saturation=1.0, device=None, output_layout=types.NHWC, output_dtype=types.UINT8): """!Adjusts the brightness, hue and saturation of the images. @@ -1099,3 +1125,9 @@ def gaussian_noise(*inputs, mean=0.0, std_dev=1.0, seed=0, device=None, output_l noise_added_image = b.gaussianNoise( Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) return (noise_added_image) + +def roi_random_crop(*inputs, crop_shape=None, remove_dim=-1): + # pybind call arguments + kwargs_pybind = {"input_image": inputs[0], "crop_shape": crop_shape, "remove_dim": remove_dim} + roi_random_crop = b.roiRandomCrop(Pipeline._current_pipeline._handle, *(kwargs_pybind.values())) + return (roi_random_crop) diff --git a/rocAL_pybind/amd/rocal/types.py b/rocAL_pybind/amd/rocal/types.py index 3a0698b..9f891db 100644 --- a/rocAL_pybind/amd/rocal/types.py +++ b/rocAL_pybind/amd/rocal/types.py @@ -90,6 +90,9 @@ from rocal_pybind.types import GAUSSIAN_INTERPOLATION from rocal_pybind.types import TRIANGULAR_INTERPOLATION +from rocal_pybind.types import TRIMTOSHAPE +from rocal_pybind.types import PAD + _known_types = { OK: ("OK", OK), @@ -146,6 +149,9 @@ SCALING_MODE_NOT_SMALLER: ("SCALING_MODE_NOT_SMALLER", SCALING_MODE_NOT_SMALLER), SCALING_MODE_NOT_LARGER: ("SCALING_MODE_NOT_LARGER", SCALING_MODE_NOT_LARGER), + TRIMTOSHAPE: ("TRIMTOSHAPE", TRIMTOSHAPE), + PAD: ("PAD", PAD), + } diff --git a/rocAL_pybind/examples/rocAL_api_numpy_reader.py b/rocAL_pybind/examples/rocAL_api_numpy_reader.py index e62c063..5d6b7d8 100644 --- a/rocAL_pybind/examples/rocAL_api_numpy_reader.py +++ b/rocAL_pybind/examples/rocAL_api_numpy_reader.py @@ -45,10 +45,12 @@ def main(): with pipeline: numpy_reader_output = fn.readers.numpy(file_root=data_path, shard_id=local_rank, num_shards=world_size) new_output = fn.set_layout(numpy_reader_output, output_layout=types.NCDHW) - brightness_output = fn.brightness(new_output, brightness=1.25, brightness_shift=0.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) - flip_output = fn.flip(brightness_output, horizontal=0, vertical=1, depth=1, output_layout=types.NCDHW, output_dtype=types.FLOAT) - # noise_output = fn.gaussian_noise(flip_output, mean=0.0, std_dev=1.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) - pipeline.set_outputs(flip_output) + anchor = fn.roi_random_crop(new_output, crop_shape=(1, 128, 128, 128), remove_dim=0) + sliced_output = fn.slice(new_output, anchor=anchor, shape=(128,128,128), output_layout=types.NCDHW, output_dtype=types.FLOAT) + flip_output = fn.flip(sliced_output, horizontal=0, vertical=1, depth=1, output_layout=types.NCDHW, output_dtype=types.FLOAT) + brightness_output = fn.brightness(flip_output, brightness=1.25, brightness_shift=0.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) + # noise_output = fn.gaussian_noise(brightness_output, mean=0.0, std_dev=1.0, output_layout=types.NCDHW, output_dtype=types.FLOAT) + pipeline.set_outputs(brightness_output) pipeline.build() @@ -62,7 +64,7 @@ def main(): for j in range(batch_size): arr = np.load(files_list[cnt]) shape = arr.shape - print(np.array_equal(np.flip(arr * 1.25, axis=[1,2]), it[j].cpu().numpy()[:, :shape[1], :shape[2], :shape[3]])) + print(np.array_equal(np.flip(arr[:, :128, :128, :128], axis=[1,2]) * 1.25, it[j].cpu().numpy())) cnt += 1 print("************************************** i *************************************",i) numpyIteratorPipeline.reset() diff --git a/rocAL_pybind/rocal_pybind.cpp b/rocAL_pybind/rocal_pybind.cpp index ce5e4ad..eb48046 100644 --- a/rocAL_pybind/rocal_pybind.cpp +++ b/rocAL_pybind/rocal_pybind.cpp @@ -348,6 +348,10 @@ PYBIND11_MODULE(rocal_pybind, m) { .value("DECODER_VIDEO_FFMPEG_SW", ROCAL_DECODER_VIDEO_FFMPEG_SW) .value("DECODER_VIDEO_FFMPEG_HW", ROCAL_DECODER_VIDEO_FFMPEG_HW) .export_values(); + py::enum_(types_m, "RocalOutOfBoundsPolicy", "Rocal Out of Bounds Policy Type") + .value("TRIMTOSHAPE", TRIMTOSHAPE) + .value("PAD", PAD) + .export_values(); // rocal_api_info.h m.def("getRemainingImages", &rocalGetRemainingImages); m.def("getImageName", &wrapper_image_name); @@ -381,6 +385,7 @@ PYBIND11_MODULE(rocal_pybind, m) { int *ptr = static_cast(buf.ptr); rocalGetImageSizes(context, ptr); }); + m.def("roiRandomCrop", &rocalROIRandomCrop); // rocal_api_parameter.h m.def("setSeed", &rocalSetSeed); m.def("getSeed", &rocalGetSeed); @@ -613,5 +618,7 @@ PYBIND11_MODULE(rocal_pybind, m) { py::return_value_policy::reference); m.def("gaussianNoise", &rocalGaussianNoise, py::return_value_policy::reference); + m.def("slice", &rocalSlice, + py::return_value_policy::reference); } } // namespace rocal