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