• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_CPU_OPERATION_UTILS_H
18 #define ANDROID_FRAMEWORKS_ML_NN_COMMON_CPU_OPERATION_UTILS_H
19 
20 #include <android-base/logging.h>
21 #include <tensorflow/lite/kernels/internal/types.h>
22 
23 #include <algorithm>
24 #include <cmath>
25 #include <limits>
26 #include <vector>
27 
28 #include "OperationsUtils.h"
29 
30 namespace android {
31 namespace nn {
32 
33 // The implementations in tflite/kernels/internal/ take a Dims<4> object
34 // even if the original tensors were not 4D.
convertShapeToDims(const Shape & shape)35 inline tflite::Dims<4> convertShapeToDims(const Shape& shape) {
36     CHECK_LE(shape.dimensions.size(), 4u);
37     tflite::Dims<4> dims;
38 
39     // The dimensions are reversed in Dims<4>.
40     for (int i = 0; i < 4; ++i) {
41         int src = static_cast<int>(shape.dimensions.size()) - i - 1;
42         if (src >= 0) {
43             dims.sizes[i] = static_cast<int>(getSizeOfDimension(shape, src));
44         } else {
45             dims.sizes[i] = 1;
46         }
47     }
48 
49     dims.strides[0] = 1;
50     for (int i = 1; i < 4; i++) {
51         dims.strides[i] = dims.strides[i - 1] * dims.sizes[i - 1];
52     }
53     return dims;
54 }
55 
convertShapeToTflshape(const Shape & shape)56 inline tflite::RuntimeShape convertShapeToTflshape(const Shape& shape) {
57     std::vector<int32_t> tflShapeDim(shape.dimensions.begin(), shape.dimensions.end());
58     return tflite::RuntimeShape(tflShapeDim.size(), tflShapeDim.data());
59 }
60 
convertFloat16ToFloat32(const _Float16 * input,std::vector<float> * output)61 inline void convertFloat16ToFloat32(const _Float16* input, std::vector<float>* output) {
62     CHECK(input != nullptr);
63     CHECK(output != nullptr);
64     for (int i = 0; i < output->size(); ++i) {
65         (*output)[i] = static_cast<float>(input[i]);
66     }
67 }
68 
convertFloat32ToFloat16(const std::vector<float> & input,_Float16 * output)69 inline void convertFloat32ToFloat16(const std::vector<float>& input, _Float16* output) {
70     CHECK(output != nullptr);
71     for (int i = 0; i < input.size(); ++i) {
72         output[i] = input[i];
73     }
74 }
75 
76 // Convert int8 quantized values to uint8 assuming that the scale is the same
77 // and the distance between offsets is 128.
convertInt8ToUInt8(const int8_t * input,std::vector<uint8_t> * output)78 inline void convertInt8ToUInt8(const int8_t* input, std::vector<uint8_t>* output) {
79     CHECK(input != nullptr);
80     CHECK(output != nullptr);
81     for (int i = 0; i < output->size(); ++i) {
82         (*output)[i] = static_cast<uint8_t>(static_cast<int32_t>(input[i]) + 128);
83     }
84 }
85 
86 // Convert uint8 quantized values to int8 assuming that the scale is the same
87 // and the distance between offsets is 128.
convertUInt8ToInt8(const std::vector<uint8_t> & input,int8_t * output)88 inline void convertUInt8ToInt8(const std::vector<uint8_t>& input, int8_t* output) {
89     CHECK(output != nullptr);
90     for (int i = 0; i < input.size(); ++i) {
91         output[i] = static_cast<int8_t>(static_cast<int32_t>(input[i]) - 128);
92     }
93 }
94 
95 template <typename T>
convertQuantToFloat32(const T * input,float scale,int32_t zeroPoint,std::vector<float> * output)96 inline void convertQuantToFloat32(const T* input, float scale, int32_t zeroPoint,
97                                   std::vector<float>* output) {
98     CHECK(input != nullptr);
99     CHECK(output != nullptr);
100     for (int i = 0; i < output->size(); ++i) {
101         (*output)[i] = (static_cast<float>(input[i]) - zeroPoint) * scale;
102     }
103 }
104 
105 template <typename T>
convertFloat32ToQuant(const std::vector<float> & input,float scale,int32_t zeroPoint,T * output)106 inline void convertFloat32ToQuant(const std::vector<float>& input, float scale, int32_t zeroPoint,
107                                   T* output) {
108     CHECK(output != nullptr);
109     for (int i = 0; i < input.size(); ++i) {
110         int32_t intVal = std::round(input[i] / scale + zeroPoint);
111         intVal = std::min<int32_t>(std::max<int32_t>(intVal, std::numeric_limits<T>::min()),
112                                    std::numeric_limits<T>::max());
113         output[i] = static_cast<T>(intVal);
114     }
115 }
116 
117 template <typename T>
convertNchwToNhwc(const T * nchw,const Shape & nchwShape,std::vector<T> * nhwc,Shape * nhwcShape)118 inline bool convertNchwToNhwc(const T* nchw, const Shape& nchwShape, std::vector<T>* nhwc,
119                               Shape* nhwcShape) {
120     NN_RET_CHECK_EQ(getNumberOfDimensions(nchwShape), 4)
121             << "Error converting a non-4-D tensor to NHWC layout";
122     *nhwcShape = nchwShape;
123     const auto& fromDim = nchwShape.dimensions;
124     nhwcShape->dimensions = {fromDim[0], fromDim[2], fromDim[3], fromDim[1]};
125     nhwc->resize(getNumberOfElements(nchwShape));
126     auto to = nhwc->data();
127     uint32_t spatialSize = fromDim[2] * fromDim[3];
128     for (uint32_t n = 0; n < fromDim[0]; n++) {
129         for (uint32_t hw = 0; hw < spatialSize; hw++) {
130             for (uint32_t c = 0; c < fromDim[1]; c++) {
131                 uint32_t fromIndex = n * fromDim[1] * spatialSize + c * spatialSize + hw;
132                 *to++ = nchw[fromIndex];
133             }
134         }
135     }
136     return true;
137 }
138 
139 template <typename T>
convertNhwcToNchw(const std::vector<T> & nhwc,const Shape & nhwcShape,T * nchw)140 inline bool convertNhwcToNchw(const std::vector<T>& nhwc, const Shape& nhwcShape, T* nchw) {
141     NN_RET_CHECK_EQ(getNumberOfDimensions(nhwcShape), 4)
142             << "Error converting a non-4-D tensor to NCHW layout";
143     const auto& fromDim = nhwcShape.dimensions;
144     const auto from = nhwc.data();
145     uint32_t spatialSize = fromDim[1] * fromDim[2];
146     for (uint32_t n = 0; n < fromDim[0]; n++) {
147         for (uint32_t c = 0; c < fromDim[3]; c++) {
148             for (uint32_t hw = 0; hw < spatialSize; hw++) {
149                 uint32_t fromIndex = n * spatialSize * fromDim[3] + hw * fromDim[3] + c;
150                 *nchw++ = from[fromIndex];
151             }
152         }
153     }
154     return true;
155 }
156 
157 template <typename T>
158 class InputWithLayout {
159    public:
InputWithLayout(bool useNchw)160     InputWithLayout(bool useNchw) : mDataOriginal(nullptr), mUseNchw(useNchw) {}
161 
initialize(const T * data,const Shape & shape)162     bool initialize(const T* data, const Shape& shape) {
163         mDataOriginal = data;
164         mShape = shape;
165         if (mUseNchw) {
166             return convertNchwToNhwc(mDataOriginal, shape, &mDataNhwc, &mShape);
167         }
168         return true;
169     }
170 
getNhwcBuffer()171     const T* getNhwcBuffer() { return mUseNchw ? mDataNhwc.data() : mDataOriginal; }
getNhwcShape()172     const Shape& getNhwcShape() { return mShape; }
173 
174    private:
175     const T* mDataOriginal;
176     std::vector<T> mDataNhwc;
177     Shape mShape;
178     bool mUseNchw;
179 };
180 
181 template <typename T>
182 class OutputWithLayout {
183    public:
OutputWithLayout(bool useNchw)184     OutputWithLayout(bool useNchw) : mDataOriginal(nullptr), mUseNchw(useNchw) {}
185 
initialize(T * data,const Shape & shape)186     bool initialize(T* data, const Shape& shape) {
187         NN_RET_CHECK_EQ(getNumberOfDimensions(shape), 4);
188         mDataOriginal = data;
189         mShape = shape;
190         if (mUseNchw) {
191             const auto& dim = shape.dimensions;
192             mShape.dimensions = {dim[0], dim[2], dim[3], dim[1]};
193             mDataNhwc.resize(getNumberOfElements(shape));
194         }
195         return true;
196     }
197 
getNhwcBuffer()198     T* getNhwcBuffer() { return mUseNchw ? mDataNhwc.data() : mDataOriginal; }
getNhwcShape()199     const Shape& getNhwcShape() { return mShape; }
commit()200     bool commit() {
201         if (mUseNchw) {
202             return convertNhwcToNchw(mDataNhwc, mShape, mDataOriginal);
203         }
204         return true;
205     }
206 
207    private:
208     T* mDataOriginal;
209     std::vector<T> mDataNhwc;
210     Shape mShape;
211     bool mUseNchw;
212 };
213 
214 template <typename T>
215 inline void CalculateActivationRange(int32_t activation, const Shape& outputShape,
216                                      int32_t* outputActivationMin, int32_t* outputActivationMax);
217 
218 template <>
219 inline void CalculateActivationRange<uint8_t>(int32_t activation, const Shape& outputShape,
220                                               int32_t* outputActivationMin,
221                                               int32_t* outputActivationMax) {
222     CalculateActivationRangeUint8(activation, outputShape, outputActivationMin,
223                                   outputActivationMax);
224 }
225 
226 template <>
227 inline void CalculateActivationRange<int8_t>(int32_t activation, const Shape& outputShape,
228                                              int32_t* outputActivationMin,
229                                              int32_t* outputActivationMax) {
230     CalculateActivationRangeInt8(activation, outputShape, outputActivationMin, outputActivationMax);
231 }
232 
233 }  // namespace nn
234 }  // namespace android
235 
236 #endif  // ANDROID_FRAMEWORKS_ML_NN_COMMON_CPU_OPERATION_UTILS_H
237