blob: 181e6e2fcd752ea48a6d1a8e68db033c47c28124 [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//
Teresa Charlinf69ae562023-04-27 14:42:23 +01005
6#pragma once
7
8#include <OpaqueDelegateUtils.hpp>
9
10namespace armnnOpaqueDelegate
11{
12
13TfLiteStatus VisitL2NormalizationOperator(DelegateData& delegateData,
14 TfLiteOpaqueContext* tfLiteContext,
15 TfLiteOpaqueNode* tfLiteNode,
16 int nodeIndex,
17 int32_t tfLiteL2NormalizationOperatorCode)
18{
19 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
20 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
21
22 // Gather input indices and use to get input tensor.
23 int numInputs = 0;
24 const int* inputTensors;
25 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
26 {
27 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
28 tfLiteContext,
29 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
30 nodeIndex);
31 return kTfLiteError;
32 }
33 // Use input indices to get input tensor.
34 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
35 if (!IsValid(tfLiteContext, tfLiteInputTensor, tfLiteL2NormalizationOperatorCode, nodeIndex))
36 {
37 return kTfLiteError;
38 }
39 // Gather output indices and use to get output tensor.
40 int numOutputs = 0;
41 const int* outputTensors;
42 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
43 {
44 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
45 tfLiteContext,
46 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
47 nodeIndex);
48 return kTfLiteError;
49 }
50 // Use output indices to get output tensor.
51 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
52 if (!IsValid(tfLiteContext, tfLiteOutputTensor, tfLiteL2NormalizationOperatorCode, nodeIndex))
53 {
54 return kTfLiteError;
55 }
56
57 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
58 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
59
60 armnn::L2NormalizationDescriptor descriptor;
61 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
62
63 bool isSupported = false;
64 armnn::BackendId setBackend;
65 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
66 {
67 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("L2_NORMALIZATION",
68 tfLiteContext,
69 IsL2NormalizationSupported,
70 delegateData.m_Backends,
71 isSupported,
72 setBackend,
73 inputTensorInfo,
74 outInfo,
75 descriptor);
76 };
77
78 if (!delegateData.m_Network)
79 {
80 validateFunc(outputTensorInfo, isSupported);
81 return isSupported ? kTfLiteOk : kTfLiteError;
82 }
83
84 // Add a L2Normalization layer
Mike Kellya2806502023-08-03 10:42:11 +010085 auto layerName = GetName(armnn::LayerType::L2Normalization, nodeIndex);
86 armnn::IConnectableLayer* layer = delegateData.m_Network->AddL2NormalizationLayer(descriptor, layerName.c_str());
Teresa Charlinf69ae562023-04-27 14:42:23 +010087 layer->SetBackendId(setBackend);
88 ARMNN_ASSERT(layer != nullptr);
89
90 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
91 outputSlot.SetTensorInfo(outputTensorInfo);
92
93 // try to connect the Constant Inputs if there are any
Mike Kellya2806502023-08-03 10:42:11 +010094 if (ProcessInputs(layer, delegateData, tfLiteContext, tfLiteNode, nodeIndex) != kTfLiteOk)
Teresa Charlinf69ae562023-04-27 14:42:23 +010095 {
96 return kTfLiteError;
97 }
98
99 // Connect
100 return Connect(layer, tfLiteContext, tfLiteNode, delegateData);
101}
102
103
104TfLiteStatus VisitLocalResponseNormalizationOperator(DelegateData& delegateData,
105 TfLiteOpaqueContext* tfLiteContext,
106 TfLiteOpaqueNode* tfLiteNode,
107 int nodeIndex,
108 int32_t tfLiteNormalizationOperatorCode)
109{
110 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
111 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
112
113 // Gather input indices and use to get input tensor.
114 int numInputs = 0;
115 const int* inputTensors;
116 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
117 {
118 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
119 tfLiteContext,
120 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
121 nodeIndex);
122 return kTfLiteError;
123 }
124 // Use input indices to get input tensor.
125 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
126 if (!IsValid(tfLiteContext, tfLiteInputTensor, tfLiteNormalizationOperatorCode, nodeIndex))
127 {
128 return kTfLiteError;
129 }
130 // Gather output indices and use to get output tensor.
131 int numOutputs = 0;
132 const int* outputTensors;
133 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
134 {
135 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
136 tfLiteContext,
137 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
138 nodeIndex);
139 return kTfLiteError;
140 }
141 // Use output indices to get output tensor.
142 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
143 if (!IsValid(tfLiteContext, tfLiteOutputTensor, tfLiteNormalizationOperatorCode, nodeIndex))
144 {
145 return kTfLiteError;
146 }
147
148 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
149 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
150
151 armnn::NormalizationDescriptor descriptor;
152 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
153 descriptor.m_NormChannelType = armnn::NormalizationAlgorithmChannel::Across;
154 descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness;
155
156 auto* nodeParams = reinterpret_cast<TfLiteLocalResponseNormParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
157 descriptor.m_NormSize = nodeParams->radius;
158 descriptor.m_K = nodeParams->bias;
159 descriptor.m_Alpha = nodeParams->alpha;
160 descriptor.m_Beta = nodeParams->beta;
161
162 // ArmNN expects normSize to be the full size of the normalization window
163 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
164
165 bool isSupported = false;
166 armnn::BackendId setBackend;
167 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
168 {
169 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("NORMALIZATION",
170 tfLiteContext,
171 IsNormalizationSupported,
172 delegateData.m_Backends,
173 isSupported,
174 setBackend,
175 inputTensorInfo,
176 outInfo,
177 descriptor);
178 };
179
180 if (!delegateData.m_Network)
181 {
182 validateFunc(outputTensorInfo, isSupported);
183 return isSupported ? kTfLiteOk : kTfLiteError;
184 }
185
186 // Add a Normalization layer
Mike Kellya2806502023-08-03 10:42:11 +0100187 auto layerName = GetName(armnn::LayerType::Normalization, nodeIndex);
188 armnn::IConnectableLayer* layer = delegateData.m_Network->AddNormalizationLayer(descriptor, layerName.c_str());
Teresa Charlinf69ae562023-04-27 14:42:23 +0100189 layer->SetBackendId(setBackend);
190 ARMNN_ASSERT(layer != nullptr);
191
192 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
193 outputSlot.SetTensorInfo(outputTensorInfo);
194
195 // try to connect the Constant Inputs if there are any
Mike Kellya2806502023-08-03 10:42:11 +0100196 if (ProcessInputs(layer, delegateData, tfLiteContext, tfLiteNode, nodeIndex) != kTfLiteOk)
Teresa Charlinf69ae562023-04-27 14:42:23 +0100197 {
198 return kTfLiteError;
199 }
200
201 // Connect
202 return Connect(layer, tfLiteContext, tfLiteNode, delegateData);
203}
204
205} // namespace armnnOpaqueDelegate