ArmNN
 24.02
ResizeOperator.hpp File Reference
Include dependency graph for ResizeOperator.hpp:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

TosaSerializationBasicBlock * ConvertResizeToTosaOperator (const Layer *inputSize, const std::vector< const TensorInfo * > &outputSize, const std::vector< const TensorInfo * > &scale_n, const ResizeDescriptor *scale_d)
 

Function Documentation

◆ ConvertResizeToTosaOperator()

TosaSerializationBasicBlock* ConvertResizeToTosaOperator ( const Layer inputSize,
const std::vector< const TensorInfo * > &  outputSize,
const std::vector< const TensorInfo * > &  scale_n,
const ResizeDescriptor scale_d 
)

Definition at line 15 of file ResizeOperator.cpp.

19 {
20  ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( inputs.size() == 1,
21  "ConvertResizeToTosaOperator: Resize must have only one input." );
22  ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( resizeDescriptor->m_DataLayout == DataLayout::NHWC,
23  "ConvertResizeToTosaOperator: NCHW not supported.");
24 
25  ResizeMode mode;
26  if (resizeDescriptor->m_Method == ResizeMethod::NearestNeighbor)
27  {
28  mode = tosa::ResizeMode_NEAREST;
29  }
30  else if (resizeDescriptor->m_Method == ResizeMethod::Bilinear)
31  {
32  mode = tosa::ResizeMode_BILINEAR;
33  throw armnn::InvalidArgumentException("ConvertResizeToTosaOperator: Unimplemented Resize method.");
34  }
35  else
36  {
37  throw armnn::InvalidArgumentException("ConvertResizeToTosaOperator: Unsupported Resize method.");
38  }
39 
40  std::string inputName = std::string("input0_");
41  std::string outputName = std::string("output0_");
42  std::string blockName = std::string("Op_RESIZE_block_") + GetUniqueTosaMappingID();
43 
44  // If a layer is present then the block will be used for execution, so input and output names need to be determined
45  // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
46  if(layer != nullptr)
47  {
48  // Get the layers connected to the input slots and determine unique tensor names.
49  Layer& connectedLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer();
50  inputName = GenerateUniqueName(connectedLayer, 0);
51 
52  // Determine unique output tensor name.
53  outputName = GenerateUniqueOutputName(*layer, 0);
54  }
55 
56  int32_t inputHeight = static_cast<int32_t>(inputs[0]->GetShape()[1]);
57  int32_t inputWidth = static_cast<int32_t>(inputs[0]->GetShape()[2]);
58 
59  int32_t outputHeight = static_cast<int32_t>(resizeDescriptor->m_TargetHeight);
60  int32_t outputWidth = static_cast<int32_t>(resizeDescriptor->m_TargetWidth);
61  bool alignCorners = resizeDescriptor->m_AlignCorners;
62  bool halfPixel = resizeDescriptor->m_HalfPixelCenters;
63 
64  // Go from ArmNN parameters (outputShape, halfPixel and alignedCorners)
65  // to TOSA parameters (scale, offset and border)
66  // Align corners sets the scaling ratio to (O - 1)/(I - 1) rather than O / I.
67  auto preprocessResizeParameters = [&](int inputSize, int outputSize, int& scale_n, int& scale_d, int& offset)
68  {
69  // Dimension is length 1, we are just sampling from one value.
70  if (inputSize == 1)
71  {
72  scale_n = outputSize;
73  scale_d = 1;
74  offset = 0;
75  return;
76  }
77 
78  // Apply if aligned and capable to be aligned.
79  // Align corners sets the scaling ratio to (OH - 1)/(IH - 1) rather than OH / IH. Same for width.
80  bool applyAligned = alignCorners && (outputSize > 1);
81  scale_n = applyAligned ? (outputSize - 1) : outputSize;
82  scale_d = applyAligned ? (inputSize - 1) : inputSize;
83 
84  // Simplify the scales, make sure they are even values.
85  int gcd = std::gcd(scale_n, scale_d);
86  scale_n = 2 * scale_n / gcd;
87  scale_d = 2 * scale_d / gcd;
88 
89  // If half pixel centers then input and output sampling positions are offset by 1/2 pixel.
90  offset = halfPixel ? (scale_d / 2 - scale_n / 2) : 0;
91 
92  // Reduce the scaling ratio if possible, we know scale_n and scale_d are even
93  if ((offset & 1) == 0)
94  {
95  scale_n /= 2;
96  scale_d /= 2;
97  offset /= 2;
98  }
99  };
100 
101  int scale_y_n, scale_y_d, offset_y;
102  int scale_x_n, scale_x_d, offset_x;
103  preprocessResizeParameters(inputHeight, outputHeight, scale_y_n, scale_y_d, offset_y);
104  preprocessResizeParameters(inputWidth, outputWidth, scale_x_n, scale_x_d, offset_x);
105 
106  int border_y = scale_y_d * (outputHeight - 1) - scale_y_n * (inputHeight - 1) + offset_y;
107  int border_x = scale_x_d * (outputWidth - 1) - scale_x_n * (inputWidth - 1) + offset_x;
108 
109  // [scale_y_n, scale_y_d, scale_x_n, scale_x_d]
110  std::vector<int16_t> scale = { static_cast<int16_t>(scale_y_n),
111  static_cast<int16_t>(scale_y_d),
112  static_cast<int16_t>(scale_x_n),
113  static_cast<int16_t>(scale_x_d) };
114 
115  // [offset_y, offset_x]
116  std::vector<int16_t> offset = { static_cast<int16_t>(offset_y),
117  static_cast<int16_t>(offset_x) };
118  // [border_y, border_x]
119  std::vector<int16_t> border = { static_cast<int16_t>(border_y),
120  static_cast<int16_t>(border_x) };
121 
122  auto isInt16Range = [](int x)
123  {
124  return (x <= std::numeric_limits<int16_t>::max()) && (x >= std::numeric_limits<int16_t>::min());
125  };
126 
127  if (inputs[0]->IsQuantized())
128  {
129  // It isn't commonly seen these numbers aren't fit within 16 bits, and won't match TFLite reference.
130  if (!isInt16Range(scale_y_n) || !isInt16Range(scale_y_d) ||
131  !isInt16Range(scale_x_n) || !isInt16Range(scale_x_d) ||
132  !isInt16Range(offset_y) || !isInt16Range(offset_x) ||
133  !isInt16Range(border_y) || !isInt16Range(border_x))
134  {
135  throw armnn::Exception("ConvertResizeToTosaOperator: stride or offset out of 16 bit range");
136  }
137  }
138 
139  TosaResizeAttribute resizeAttribute(scale, offset, border, mode);
140 
141  auto* op = new TosaSerializationOperator(Op_RESIZE,
142  Attribute_ResizeAttribute,
143  &resizeAttribute,
144  {inputName},
145  {outputName});
146 
147  std::vector<TosaSerializationTensor*> tensors;
148 
149  // Only add input tensors if connected layer is an input layer.
150  // As intermediate or constant tensors will be created separately.
151  // There also can't be duplicate tensor.
152  if(inputName.find("input0_") != std::string::npos)
153  {
154  std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
155  DType inputDType = ArmNNToDType(inputs[0]->GetDataType());
156 
157  tensors.push_back(new TosaSerializationTensor(inputName, inputShape, inputDType, {}));
158  }
159 
160  std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[0]->GetShape());
161  DType outputDType = ArmNNToDType(outputs[0]->GetDataType());
162 
163  tensors.push_back(new TosaSerializationTensor(outputName, outputShape, outputDType, {}));
164 
165  // operatorInputNames/operatorOutputNames ends up being the same as
166  // blockInputNames/blockOutputNames for one-to-one ArmNN to TOSA mappings
167  return new TosaSerializationBasicBlock(blockName, // name
168  mainName, // region name
169  {op}, // operators
170  tensors, // tensors
171  {inputName}, // inputs
172  {outputName}); // outputs
173 }

References ARMNN_THROW_INVALIDARG_MSG_IF_FALSE, GenerateUniqueName(), GenerateUniqueOutputName(), InputSlot::GetConnectedOutputSlot(), Layer::GetInputSlot(), OutputSlot::GetOwningLayer(), GetTosaTensorShape(), GetUniqueTosaMappingID(), ResizeDescriptor::m_AlignCorners, ResizeDescriptor::m_DataLayout, ResizeDescriptor::m_HalfPixelCenters, ResizeDescriptor::m_Method, ResizeDescriptor::m_TargetHeight, and ResizeDescriptor::m_TargetWidth.

Referenced by GetTosaMapping().

armnn::Layer::GetInputSlot
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:337
armnn::Layer
Definition: Layer.hpp:230
mainName
const std::string mainName
Definition: TosaOperatorUtils.hpp:19
armnn::OutputSlot::GetOwningLayer
Layer & GetOwningLayer() const
Definition: Layer.hpp:132
ArmNNToDType
DType ArmNNToDType(const DataType &type)
Definition: TosaOperatorUtils.hpp:22
GenerateUniqueOutputName
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot)
Definition: TosaOperatorUtils.hpp:82
armnn::InvalidArgumentException
Definition: Exceptions.hpp:80
armnn::Exception
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
GenerateUniqueName
std::string GenerateUniqueName(const Layer &layer, uint32_t layerSlot)
Definition: TosaOperatorUtils.hpp:63
GetTosaTensorShape
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
Definition: TosaOperatorUtils.hpp:52
armnn::InputSlot::GetConnectedOutputSlot
const OutputSlot * GetConnectedOutputSlot() const
Definition: Layer.hpp:56
GetUniqueTosaMappingID
std::string GetUniqueTosaMappingID()
Definition: TosaOperatorUtils.hpp:100
ARMNN_THROW_INVALIDARG_MSG_IF_FALSE
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
Definition: Exceptions.hpp:210