blob: 948b600625d45104ebf9df5ff2a9daff4c833970 [file] [log] [blame]
Francis Murtaghc4fb0dd2023-03-16 17:01:56 +00001//
2// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
John Mcloughlin083586d2023-04-28 18:36:52 +01005
6#pragma once
7
8#include <OpaqueDelegateUtils.hpp>
9
10namespace armnnOpaqueDelegate
11{
12
13TfLiteStatus ValidateResizeOperator(DelegateData& delegateData,
14 TfLiteOpaqueContext* tfLiteContext,
15 const armnn::TensorInfo& inputInfo,
16 const armnn::TensorInfo& outputInfo,
17 const armnn::ResizeDescriptor& descriptor)
18{
19 bool isSupported = false;
20 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("RESIZE",
21 tfLiteContext,
22 IsResizeSupported,
23 delegateData.m_Backends,
24 isSupported,
25 armnn::BackendId(),
26 inputInfo,
27 outputInfo,
28 descriptor);
29
30 return isSupported ? kTfLiteOk : kTfLiteError;
31}
32
33TfLiteStatus VisitResizeOperator(DelegateData& delegateData,
34 TfLiteOpaqueContext* tfLiteContext,
35 TfLiteOpaqueNode* tfLiteNode,
36 int nodeIndex,
37 int32_t resizeOperatorCode)
38{
39 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 2, nodeIndex));
40 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
41
42 // Gather input indices and use to get input tensor.
43 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
44 const int* inputTensors;
45 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
46 {
47 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
48 tfLiteContext,
49 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
50 nodeIndex);
51 return kTfLiteError;
52 }
53
54 // The first input contains the data of the image that should be resized [batch, height, width, channels]
55 const TfLiteOpaqueTensor* tfLiteInputTensor =
56 TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
57 if (IsDynamicTensor(tfLiteInputTensor))
58 {
59 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
60 tfLiteContext,
61 "TfLiteArmnnOpaqueDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
62 resizeOperatorCode, nodeIndex);
63 return kTfLiteError;
64 }
65
66 // The second input contains a size tensor. The size tensor contains two integer values
67 // that describe the new height and width of the image [new_height, new_width]
68 const TfLiteOpaqueTensor* tfLiteSizeTensor =
69 TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]);
70 if (IsDynamicTensor(tfLiteSizeTensor))
71 {
72 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
73 tfLiteContext,
74 "TfLiteArmnnOpaqueDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
75 resizeOperatorCode, nodeIndex);
76 return kTfLiteError;
77 }
78
79 // Gather output indices and use to get output tensors.
80 int numOutputs = 0;
81 const int* outputTensors;
82 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
83 {
84 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
85 tfLiteContext,
86 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
87 nodeIndex);
88 return kTfLiteError;
89 }
90
91 // The output tensor should have the shape [batch, new_height, new_width, channels]
92 const TfLiteOpaqueTensor* tfLiteOutputTensor =
93 TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
94 if (IsDynamicTensor(tfLiteOutputTensor))
95 {
96 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
97 tfLiteContext,
98 "TfLiteArmnnOpaqueDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
99 resizeOperatorCode, nodeIndex);
100 return kTfLiteError;
101 }
102
103 const armnn::TensorInfo& inputTensorInfo =
104 GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
105 const armnn::TensorInfo& outputTensorInfo =
106 GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
107
108 std::string layerName("Resize");
109
110 // Fill descriptor
111 armnn::ResizeDescriptor desc;
112 switch (resizeOperatorCode)
113 {
114 case kTfLiteBuiltinResizeBilinear:
115 {
116 desc.m_Method = armnn::ResizeMethod::Bilinear;
117
118 layerName += "Bilinear:" + std::to_string(nodeIndex);
119
120 TfLiteResizeBilinearParams* bilinearOptions =
121 reinterpret_cast<TfLiteResizeBilinearParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
122
123 desc.m_AlignCorners = bilinearOptions->align_corners;
124 desc.m_HalfPixelCenters = bilinearOptions->half_pixel_centers;
125 break;
126 }
127 case kTfLiteBuiltinResizeNearestNeighbor:
128 {
129 desc.m_Method = armnn::ResizeMethod::NearestNeighbor;
130 layerName += "NearestNeighbor:" + std::to_string(nodeIndex);
131
132 TfLiteResizeNearestNeighborParams* nearestNeighborOptions =
133 reinterpret_cast<TfLiteResizeNearestNeighborParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
134
135 desc.m_AlignCorners = nearestNeighborOptions->align_corners;
136 desc.m_HalfPixelCenters = nearestNeighborOptions->half_pixel_centers;
137 break;
138 }
139 default:
140 {
141 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
142 tfLiteContext,
143 "TfLiteArmnnOpaqueDelegate: Unknown TfLite built in operation for Resize. "
144 "Given operator: #%d node #%d: ",
145 resizeOperatorCode, nodeIndex);
146 return kTfLiteError;
147 }
148 }
149
150 // In Arm NN the values of the size input tensor [new_height, new_width] is saved in the operator
151 // descriptor. We have to read it from the input tensor and write it to the descriptor.
152
153 auto* sizeTensorDataPtr = static_cast<int*>(TfLiteOpaqueTensorData(tfLiteSizeTensor));
154 auto sizeTensorNumDimensions = TfLiteOpaqueTensorNumDims(tfLiteSizeTensor);
155 // The size tensor is only a 1D tensor -> [new_height, new width]
156 if (sizeTensorNumDimensions != 1)
157 {
158 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
159 tfLiteContext,
160 "TfLiteArmnnOpaqueDelegate: The Size-Input-Tensor of the Resize operation is not allowed to be a "
161 "dynamic tensor. Operator: #%d node #%d: ",
162 resizeOperatorCode, nodeIndex);
163 return kTfLiteError;
164 }
165
166 // Get number of values in the size tensor
167 auto sizeTensorNumValues = TfLiteOpaqueTensorDim(tfLiteSizeTensor,0);
168 if (sizeTensorNumValues == 0)
169 {
170 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
171 tfLiteContext,
172 "TfLiteArmnnOpaqueDelegate: The Size-Input-Tensor of the Resize operation is not allowed to be a "
173 "dynamic tensor. Operator: #%d node #%d: ",
174 resizeOperatorCode, nodeIndex);
175 return kTfLiteError;
176 }
177 else if (sizeTensorNumValues != 2)
178 {
179 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
180 tfLiteContext,
181 "TfLiteArmnnOpaqueDelegate: The Size-Input-Tensor of the Resize operation requires to "
182 "have a dimension of 2 [new_height, new width] but a tensor with a dimension of #%d was given. "
183 "Operator: #%d node #%d: ",
184 sizeTensorNumValues, resizeOperatorCode, nodeIndex);
185 return kTfLiteError;
186 }
187 // get size tensor data
188 std::vector<int32_t> sizeTensorData(sizeTensorDataPtr, sizeTensorDataPtr+sizeTensorNumValues);
189
190 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
191 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
192 desc.m_DataLayout = armnn::DataLayout::NHWC;
193
194 // No network pointer indicates that only support for this operator should be checked
195 if (!delegateData.m_Network)
196 {
197 return ValidateResizeOperator(delegateData,
198 tfLiteContext,
199 inputTensorInfo,
200 outputTensorInfo,
201 desc);
202 }
203
204
205 armnn::IConnectableLayer* resizeLayer = nullptr;
Mike Kellya2806502023-08-03 10:42:11 +0100206 layerName += ":";
207 layerName += nodeIndex;
208
John Mcloughlin083586d2023-04-28 18:36:52 +0100209 resizeLayer = delegateData.m_Network->AddResizeLayer(desc, layerName.c_str());
210
211 armnn::IOutputSlot& outputSlot = resizeLayer->GetOutputSlot(0);
212 outputSlot.SetTensorInfo(outputTensorInfo);
213
214 // try to connect the Constant Inputs if there are any
Mike Kellya2806502023-08-03 10:42:11 +0100215 if (ProcessInputs(resizeLayer, delegateData, tfLiteContext, tfLiteNode, nodeIndex) != kTfLiteOk)
John Mcloughlin083586d2023-04-28 18:36:52 +0100216 {
217 return kTfLiteError;
218 }
219
220 ARMNN_ASSERT(resizeLayer != nullptr);
221
222 return Connect(resizeLayer, tfLiteContext, tfLiteNode, delegateData);
223}
224
225} // namespace armnnOpaqueDelegate