[feat] add tensorflowlite component and demo

This commit is contained in:
jzlv 2022-11-18 16:06:20 +08:00
parent 57bda6c48b
commit a65f86a713
576 changed files with 175425 additions and 0 deletions

View File

@ -8,3 +8,5 @@ sdk_add_subdirectory_ifdef(CONFIG_LVGL lvgl)
sdk_add_subdirectory_ifdef(CONFIG_LWIP lwip)
sdk_add_subdirectory_ifdef(CONFIG_BLE ble)
sdk_add_subdirectory_ifdef(CONFIG_XZ xz)
sdk_add_subdirectory_ifdef(CONFIG_TINYMAIX TinyMaix)
sdk_add_subdirectory_ifdef(CONFIG_TENSORFLOWLITE TensorFlowLite)

5
components/TensorFlowLite/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/bazel-*
*.swp
.vscode/
*audio_frontend*
*google*

View File

@ -0,0 +1,25 @@
sdk_generate_library()
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/c sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/core/api sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/kernels sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/kernels/internal sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/micro sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/micro/kernels sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/micro/memory_planner sources)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite/schema sources)
sdk_library_add_sources(${sources})
sdk_add_include_directories(third_party/flatbuffers/include)
sdk_add_include_directories(third_party/gemmlowp)
sdk_add_include_directories(third_party/ruy)
sdk_add_include_directories(.)
sdk_add_compile_definitions(
-DTF_LITE_USE_GLOBAL_CMATH_FUNCTIONS
-DTF_LITE_USE_GLOBAL_MIN
-DTF_LITE_USE_GLOBAL_MAX
-DTF_LITE_STATIC_MEMORY
)
sdk_add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-threadsafe-statics>)

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,75 @@
<!--ts-->
* [TensorFlow Lite for Microcontrollers](#tensorflow-lite-for-microcontrollers)
* [Build Status](#build-status)
* [Official Builds](#official-builds)
* [Community Supported Builds](#community-supported-builds)
* [Contributing](#contributing)
* [Getting Help](#getting-help)
* [Additional Documentation](#additional-documentation)
* [RFCs](#rfcs)
<!-- Added by: advaitjain, at: Thu 17 Jun 2021 09:33:15 AM PDT -->
<!--te-->
# TensorFlow Lite for Microcontrollers
TensorFlow Lite for Microcontrollers is a port of TensorFlow Lite designed to
run machine learning models on DSPs, microcontrollers and other devices with
limited memory.
Additional Links:
* [Tensorflow github repository](https://github.com/tensorflow/tensorflow/)
* [TFLM at tensorflow.org](https://www.tensorflow.org/lite/microcontrollers)
# Build Status
* [GitHub Status](https://www.githubstatus.com/)
## Official Builds
Build Type | Status |
----------- | --------------|
CI (Linux) | [![CI](https://github.com/tensorflow/tflite-micro/actions/workflows/ci.yml/badge.svg?event=schedule)](https://github.com/tensorflow/tflite-micro/actions/workflows/ci.yml?query=event%3Aschedule) |
Code Sync | [![Sync from Upstream TF](https://github.com/tensorflow/tflite-micro/actions/workflows/sync.yml/badge.svg)](https://github.com/tensorflow/tflite-micro/actions/workflows/sync.yml) |
## Community Supported Builds
Build Type | Status |
----------- | --------------|
Arduino | [![Arduino](https://github.com/tensorflow/tflite-micro/actions/workflows/arduino.yml/badge.svg)](https://github.com/tensorflow/tflite-micro/actions/workflows/arduino.yml) [![Antmicro](https://github.com/antmicro/tensorflow-arduino-examples/actions/workflows/test_examples.yml/badge.svg)](https://github.com/antmicro/tensorflow-arduino-examples/actions/workflows/test_examples.yml) |
Cortex-M | [![Cortex-M](https://github.com/tensorflow/tflite-micro/actions/workflows/cortex_m.yml/badge.svg)](https://github.com/tensorflow/tflite-micro/actions/workflows/cortex_m.yml) |
Sparkfun Edge | [![Sparkfun Edge](https://github.com/tensorflow/tflite-micro/actions/workflows/sparkfun_edge.yml/badge.svg)](https://github.com/tensorflow/tflite-micro/actions/workflows/sparkfun_edge.yml) |
Xtensa | [![Xtensa](https://github.com/tensorflow/tflite-micro/actions/workflows/xtensa.yml/badge.svg?event=schedule)](https://github.com/tensorflow/tflite-micro/actions/workflows/xtensa.yml?query=event%3Aschedule) [![Xtensa](https://raw.githubusercontent.com/advaitjain/tflite-micro/local-continuous-builds/tensorflow/lite/micro/docs/local_continuous_builds/xtensa-build-status.svg)](https://github.com/advaitjain/tflite-micro/tree/local-continuous-builds/tensorflow/lite/micro/docs/local_continuous_builds/xtensa.md#summary) |
# Contributing
See our [contribution documentation](CONTRIBUTING.md).
# Getting Help
A [Github issue](https://github.com/tensorflow/tflite-micro/issues/new/choose)
should be the primary method of getting in touch with the TensorFlow Lite Micro
(TFLM) team.
The following resources may also be useful:
1. SIG Micro [email group](https://groups.google.com/a/tensorflow.org/g/micro)
and
[monthly meetings](http://doc/1YHq9rmhrOUdcZnrEnVCWvd87s2wQbq4z17HbeRl-DBc).
1. SIG Micro [gitter chat room](https://gitter.im/tensorflow/sig-micro).
# Additional Documentation
* [Continuous Integration](docs/continuous_integration.md)
* [Benchmarks](tensorflow/lite/micro/benchmarks/README.md)
* [Profiling](tensorflow/lite/micro/docs/profiling.md)
* [Memory Management](tensorflow/lite/micro/docs/memory_management.md)
* [Optimized Kernel Implementations](tensorflow/lite/micro/docs/optimized_kernel_implementations.md)
* [New Platform Support](tensorflow/lite/micro/docs/new_platform_support.md)
* [Software Emulation with Renode](tensorflow/lite/micro/docs/renode.md)
# RFCs
1. [Pre-allocated tensors](tensorflow/lite/micro/docs/rfc/001_preallocated_tensors.md)
1. [TensorFlow Lite for Microcontrollers Port of 16x8 Quantized Operators](tensorflow/lite/micro/docs/rfc/002_16x8_quantization_port.md)

View File

@ -0,0 +1,504 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_C_BUILTIN_OP_DATA_H_
#define TENSORFLOW_LITE_C_BUILTIN_OP_DATA_H_
#include <stdint.h>
#include "tensorflow/lite/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// TfLiteReshapeParams can't have dynamic data so we fix the maximum possible
// number of dimensions.
#define TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT 8
// TODO(aselle): Consider using "if this then that" for testing.
// Useful placeholder to put in otherwise empty structs to avoid size warnings.
typedef struct {
char dummy;
} EmptyStructPlaceholder;
// IMPORTANT: All new members of structs must be added at the end to ensure
// backwards compatibility.
// Possible padding types (for convolutions)
typedef enum {
kTfLitePaddingUnknown = 0,
kTfLitePaddingSame,
kTfLitePaddingValid,
} TfLitePadding;
typedef enum {
kTfLiteMirrorPaddingUnknown = 0,
kTfLiteMirrorPaddingReflect,
kTfLiteMirrorPaddingSymmetric,
} TfLiteMirrorPaddingMode;
// TODO(b/130259536): We should move this out of builtin_op_data.
typedef struct {
int width;
int height;
int width_offset;
int height_offset;
} TfLitePaddingValues;
typedef struct {
TfLiteMirrorPaddingMode mode;
} TfLiteMirrorPaddingParams;
// Possible fused activation functions.
typedef enum {
kTfLiteActNone = 0,
kTfLiteActRelu,
kTfLiteActReluN1To1, // min(max(-1, x), 1)
kTfLiteActRelu6, // min(max(0, x), 6)
kTfLiteActTanh,
kTfLiteActSignBit,
kTfLiteActSigmoid,
} TfLiteFusedActivation;
typedef struct {
// Parameters for CONV_2D version 1.
TfLitePadding padding;
int stride_width;
int stride_height;
TfLiteFusedActivation activation;
// Parameters for CONV_2D version 2.
// Note: Version 2 supports dilation values not equal to 1.
int dilation_width_factor;
int dilation_height_factor;
} TfLiteConvParams;
typedef struct {
TfLitePadding padding;
int stride_width;
int stride_height;
int stride_depth;
int dilation_width_factor;
int dilation_height_factor;
int dilation_depth_factor;
TfLiteFusedActivation activation;
} TfLiteConv3DParams;
typedef TfLiteConv3DParams TfLiteConv3DTransposeParams;
typedef struct {
TfLitePadding padding;
int stride_width;
int stride_height;
int filter_width;
int filter_height;
TfLiteFusedActivation activation;
struct {
TfLitePaddingValues padding;
} computed;
} TfLitePoolParams;
typedef struct {
// Parameters for DepthwiseConv version 1 or above.
TfLitePadding padding;
int stride_width;
int stride_height;
// `depth_multiplier` is redundant. It's used by CPU kernels in
// TensorFlow 2.0 or below, but ignored in versions above.
//
// The information can be deduced from the shape of input and the shape of
// weights. Since the TFLiteConverter toolchain doesn't support partially
// specified shapes, relying on `depth_multiplier` stops us from supporting
// graphs with dynamic shape tensors.
//
// Note: Some of the delegates (e.g. NNAPI, GPU) are still relying on this
// field.
int depth_multiplier;
TfLiteFusedActivation activation;
// Parameters for DepthwiseConv version 2 or above.
int dilation_width_factor;
int dilation_height_factor;
} TfLiteDepthwiseConvParams;
typedef struct {
int rank;
TfLiteFusedActivation activation;
// Parameter for SVDF version 4.
bool asymmetric_quantize_inputs;
} TfLiteSVDFParams;
typedef struct {
TfLiteFusedActivation activation;
// Parameter for RNN version 3.
bool asymmetric_quantize_inputs;
} TfLiteRNNParams;
typedef struct {
bool time_major;
TfLiteFusedActivation activation;
// Parameter for Sequence RNN version 3.
bool asymmetric_quantize_inputs;
} TfLiteSequenceRNNParams;
typedef struct {
bool time_major;
TfLiteFusedActivation activation;
bool merge_outputs;
// Parameter for Bidirectional RNN verison 3.
bool asymmetric_quantize_inputs;
} TfLiteBidirectionalSequenceRNNParams;
typedef enum {
kTfLiteFullyConnectedWeightsFormatDefault = 0,
kTfLiteFullyConnectedWeightsFormatShuffled4x16Int8 = 1,
} TfLiteFullyConnectedWeightsFormat;
typedef struct {
// Parameters for FullyConnected version 1 or above.
TfLiteFusedActivation activation;
// Parameters for FullyConnected version 2 or above.
TfLiteFullyConnectedWeightsFormat weights_format;
// Parameters for FullyConnected version 5 or above.
// If set to true, then the number of dimensions in the input and the output
// tensors are the same. Furthermore, all but the last dimension of the input
// and output shapes will be equal.
bool keep_num_dims;
// Parameters for FullyConnected version 7 or above.
// If set to true and the weights are quantized, then non constant inputs
// are quantized at evaluation time with asymmetric quantization.
bool asymmetric_quantize_inputs;
} TfLiteFullyConnectedParams;
typedef enum {
kTfLiteLshProjectionUnknown = 0,
kTfLiteLshProjectionSparse = 1,
kTfLiteLshProjectionDense = 2,
} TfLiteLSHProjectionType;
typedef struct {
TfLiteLSHProjectionType type;
} TfLiteLSHProjectionParams;
typedef struct {
float beta;
} TfLiteSoftmaxParams;
typedef struct {
int axis;
TfLiteFusedActivation activation;
} TfLiteConcatenationParams;
typedef struct {
TfLiteFusedActivation activation;
// Parameter added for the version 4.
bool pot_scale_int16;
} TfLiteAddParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLiteSpaceToBatchNDParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLiteBatchToSpaceNDParams;
typedef struct {
bool adj_x;
bool adj_y;
// Parameters for BatchMatMul version 4 or above.
// If set to true and the weights are quantized, then non constant inputs
// are quantized at evaluation time with asymmetric quantization.
bool asymmetric_quantize_inputs;
} TfLiteBatchMatMulParams;
typedef struct {
TfLiteFusedActivation activation;
} TfLiteMulParams;
typedef struct {
TfLiteFusedActivation activation;
// Parameter added for the version 5.
bool pot_scale_int16;
} TfLiteSubParams;
typedef struct {
TfLiteFusedActivation activation;
} TfLiteDivParams;
typedef struct {
TfLiteFusedActivation activation;
} TfLiteL2NormParams;
typedef struct {
int radius;
float bias;
float alpha;
float beta;
} TfLiteLocalResponseNormParams;
typedef enum {
kTfLiteLSTMFullKernel = 0,
kTfLiteLSTMBasicKernel
} TfLiteLSTMKernelType;
typedef struct {
// Parameters for LSTM version 1.
TfLiteFusedActivation activation;
float cell_clip;
float proj_clip;
// Parameters for LSTM version 2.
// kTfLiteLSTMBasicKernel is only supported in version 2 or above.
TfLiteLSTMKernelType kernel_type;
// Parameters for LSTM version 4.
bool asymmetric_quantize_inputs;
} TfLiteLSTMParams;
typedef struct {
// Parameters needed for the underlying LSTM.
TfLiteFusedActivation activation;
float cell_clip;
float proj_clip;
// If set to true then the first dimension is time, otherwise batch.
bool time_major;
// Parameter for unidirectional sequence RNN version 3.
bool asymmetric_quantize_inputs;
} TfLiteUnidirectionalSequenceLSTMParams;
typedef struct {
// Parameters supported by version 1:
// Parameters inherited for the LSTM kernel.
TfLiteFusedActivation activation;
float cell_clip;
float proj_clip;
// If true, store the outputs of both directions in the first output.
bool merge_outputs;
// Parameters supported by version 2:
// If set to true then the first dimension is time, otherwise batch.
bool time_major;
// Parameters supported by version 4:
// If set to true, then hybrid ops use asymmetric quantization for inputs.
bool asymmetric_quantize_inputs;
} TfLiteBidirectionalSequenceLSTMParams;
typedef struct {
bool align_corners;
// half_pixel_centers assumes pixels are of half the actual dimensions, and
// yields more accurate resizes. Corresponds to the same argument for the
// original TensorFlow op in TF2.0.
bool half_pixel_centers;
} TfLiteResizeBilinearParams;
typedef struct {
bool align_corners;
bool half_pixel_centers;
} TfLiteResizeNearestNeighborParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLitePadParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLitePadV2Params;
typedef struct {
// These fields are only used in old models for backward compatibility.
// In the current implementation, we use the 2nd input of the op as the shape,
// and these fields are unused.
int shape[TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT];
int num_dimensions;
} TfLiteReshapeParams;
typedef struct {
int ngram_size;
int max_skip_size;
bool include_all_ngrams;
} TfLiteSkipGramParams;
typedef struct {
int block_size;
} TfLiteSpaceToDepthParams;
typedef struct {
int block_size;
} TfLiteDepthToSpaceParams;
typedef struct {
TfLiteType in_data_type;
TfLiteType out_data_type;
} TfLiteCastParams;
typedef enum {
kTfLiteCombinerTypeSum = 0,
kTfLiteCombinerTypeMean = 1,
kTfLiteCombinerTypeSqrtn = 2,
} TfLiteCombinerType;
typedef struct {
TfLiteCombinerType combiner;
} TfLiteEmbeddingLookupSparseParams;
typedef struct {
int axis;
int batch_dims;
} TfLiteGatherParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLiteTransposeParams;
typedef struct {
bool keep_dims;
} TfLiteReducerParams;
typedef struct {
int num_splits;
} TfLiteSplitParams;
typedef struct {
int num_splits;
} TfLiteSplitVParams;
typedef struct {
// TODO(ahentz): We can't have dynamic data in this struct, at least not yet.
// For now we will fix the maximum possible number of dimensions.
int squeeze_dims[8];
int num_squeeze_dims;
} TfLiteSqueezeParams;
typedef struct {
int begin_mask;
int end_mask;
int ellipsis_mask;
int new_axis_mask;
int shrink_axis_mask;
} TfLiteStridedSliceParams;
typedef struct {
TfLiteType output_type;
} TfLiteArgMaxParams;
typedef struct {
TfLiteType output_type;
} TfLiteArgMinParams;
typedef struct {
TfLitePadding padding;
int stride_width;
int stride_height;
} TfLiteTransposeConvParams;
typedef struct {
bool validate_indices;
} TfLiteSparseToDenseParams;
typedef struct {
TfLiteType out_type;
} TfLiteShapeParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLiteRankParams;
typedef struct {
// Parameters supported by version 1:
float min;
float max;
int num_bits;
// Parameters supported by version 2:
bool narrow_range;
} TfLiteFakeQuantParams;
typedef struct {
int values_count;
int axis;
} TfLitePackParams;
typedef struct {
int axis;
} TfLiteOneHotParams;
typedef struct {
int num;
int axis;
} TfLiteUnpackParams;
typedef struct {
float alpha;
} TfLiteLeakyReluParams;
typedef struct {
TfLiteType index_out_type;
} TfLiteUniqueParams;
typedef struct {
int seq_dim;
int batch_dim;
} TfLiteReverseSequenceParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLiteMatrixDiagParams;
typedef struct {
EmptyStructPlaceholder placeholder;
} TfLiteMatrixSetDiagParams;
typedef struct {
int then_subgraph_index;
int else_subgraph_index;
} TfLiteIfParams;
typedef struct {
int cond_subgraph_index;
int body_subgraph_index;
} TfLiteWhileParams;
typedef struct {
bool exclusive;
bool reverse;
} TfLiteCumsumParams;
typedef struct {
int init_subgraph_index;
} TfLiteCallOnceParams;
typedef struct {
int table_id;
TfLiteType key_dtype;
TfLiteType value_dtype;
} TfLiteHashtableParams;
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_C_BUILTIN_OP_DATA_H_

View File

@ -0,0 +1,105 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
// This file declares types used by the pure C inference API defined in c_api.h,
// some of which are also used in the C++ and C kernel and interpreter APIs.
#ifndef TENSORFLOW_LITE_C_C_API_TYPES_H_
#define TENSORFLOW_LITE_C_C_API_TYPES_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Define TFL_CAPI_EXPORT macro to export a function properly with a shared
// library.
#ifdef SWIG
#define TFL_CAPI_EXPORT
#elif defined(TFL_STATIC_LIBRARY_BUILD)
#define TFL_CAPI_EXPORT
#else // not definded TFL_STATIC_LIBRARY_BUILD
#if defined(_WIN32)
#ifdef TFL_COMPILE_LIBRARY
#define TFL_CAPI_EXPORT __declspec(dllexport)
#else
#define TFL_CAPI_EXPORT __declspec(dllimport)
#endif // TFL_COMPILE_LIBRARY
#else
#define TFL_CAPI_EXPORT __attribute__((visibility("default")))
#endif // _WIN32
#endif // SWIG
typedef enum TfLiteStatus {
kTfLiteOk = 0,
// Generally referring to an error in the runtime (i.e. interpreter)
kTfLiteError = 1,
// Generally referring to an error from a TfLiteDelegate itself.
kTfLiteDelegateError = 2,
// Generally referring to an error in applying a delegate due to
// incompatibility between runtime and delegate, e.g., this error is returned
// when trying to apply a TfLite delegate onto a model graph that's already
// immutable.
kTfLiteApplicationError = 3,
// Generally referring to serialized delegate data not being found.
// See tflite::delegates::Serialization.
kTfLiteDelegateDataNotFound = 4,
// Generally referring to data-writing issues in delegate serialization.
// See tflite::delegates::Serialization.
kTfLiteDelegateDataWriteError = 5,
} TfLiteStatus;
// Types supported by tensor
typedef enum {
kTfLiteNoType = 0,
kTfLiteFloat32 = 1,
kTfLiteInt32 = 2,
kTfLiteUInt8 = 3,
kTfLiteInt64 = 4,
kTfLiteString = 5,
kTfLiteBool = 6,
kTfLiteInt16 = 7,
kTfLiteComplex64 = 8,
kTfLiteInt8 = 9,
kTfLiteFloat16 = 10,
kTfLiteFloat64 = 11,
kTfLiteComplex128 = 12,
kTfLiteUInt64 = 13,
kTfLiteResource = 14,
kTfLiteVariant = 15,
kTfLiteUInt32 = 16,
} TfLiteType;
// Legacy. Will be deprecated in favor of TfLiteAffineQuantization.
// If per-layer quantization is specified this field will still be populated in
// addition to TfLiteAffineQuantization.
// Parameters for asymmetric quantization. Quantized values can be converted
// back to float using:
// real_value = scale * (quantized_value - zero_point)
typedef struct TfLiteQuantizationParams {
float scale;
int32_t zero_point;
} TfLiteQuantizationParams;
#ifdef __cplusplus
} // extern C
#endif
#endif // TENSORFLOW_LITE_C_C_API_TYPES_H_

View File

@ -0,0 +1,274 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/c/c_api_types.h"
#ifndef TF_LITE_STATIC_MEMORY
#include <stdlib.h>
#include <string.h>
#endif // TF_LITE_STATIC_MEMORY
int TfLiteIntArrayGetSizeInBytes(int size)
{
static TfLiteIntArray dummy;
return sizeof(dummy) + sizeof(dummy.data[0]) * size;
}
int TfLiteIntArrayEqual(const TfLiteIntArray *a, const TfLiteIntArray *b)
{
if (a == b)
return 1;
if (a == NULL || b == NULL)
return 0;
return TfLiteIntArrayEqualsArray(a, b->size, b->data);
}
int TfLiteIntArrayEqualsArray(const TfLiteIntArray *a, int b_size,
const int b_data[])
{
if (a == NULL)
return (b_size == 0);
if (a->size != b_size)
return 0;
int i = 0;
for (; i < a->size; i++)
if (a->data[i] != b_data[i])
return 0;
return 1;
}
#ifndef TF_LITE_STATIC_MEMORY
TfLiteIntArray *TfLiteIntArrayCreate(int size)
{
int alloc_size = TfLiteIntArrayGetSizeInBytes(size);
if (alloc_size <= 0)
return NULL;
TfLiteIntArray *ret = (TfLiteIntArray *)malloc(alloc_size);
if (!ret)
return ret;
ret->size = size;
return ret;
}
TfLiteIntArray *TfLiteIntArrayCopy(const TfLiteIntArray *src)
{
if (!src)
return NULL;
TfLiteIntArray *ret = TfLiteIntArrayCreate(src->size);
if (ret) {
memcpy(ret->data, src->data, src->size * sizeof(int));
}
return ret;
}
void TfLiteIntArrayFree(TfLiteIntArray *a)
{
free(a);
}
#endif // TF_LITE_STATIC_MEMORY
int TfLiteFloatArrayGetSizeInBytes(int size)
{
static TfLiteFloatArray dummy;
return sizeof(dummy) + sizeof(dummy.data[0]) * size;
}
#ifndef TF_LITE_STATIC_MEMORY
TfLiteFloatArray *TfLiteFloatArrayCreate(int size)
{
TfLiteFloatArray *ret =
(TfLiteFloatArray *)malloc(TfLiteFloatArrayGetSizeInBytes(size));
ret->size = size;
return ret;
}
void TfLiteFloatArrayFree(TfLiteFloatArray *a)
{
free(a);
}
void TfLiteTensorDataFree(TfLiteTensor *t)
{
if (t->allocation_type == kTfLiteDynamic ||
t->allocation_type == kTfLitePersistentRo) {
free(t->data.raw);
}
t->data.raw = NULL;
}
void TfLiteQuantizationFree(TfLiteQuantization *quantization)
{
if (quantization->type == kTfLiteAffineQuantization) {
TfLiteAffineQuantization *q_params =
(TfLiteAffineQuantization *)(quantization->params);
if (q_params->scale) {
TfLiteFloatArrayFree(q_params->scale);
q_params->scale = NULL;
}
if (q_params->zero_point) {
TfLiteIntArrayFree(q_params->zero_point);
q_params->zero_point = NULL;
}
free(q_params);
}
quantization->params = NULL;
quantization->type = kTfLiteNoQuantization;
}
void TfLiteSparsityFree(TfLiteSparsity *sparsity)
{
if (sparsity == NULL) {
return;
}
if (sparsity->traversal_order) {
TfLiteIntArrayFree(sparsity->traversal_order);
sparsity->traversal_order = NULL;
}
if (sparsity->block_map) {
TfLiteIntArrayFree(sparsity->block_map);
sparsity->block_map = NULL;
}
if (sparsity->dim_metadata) {
int i = 0;
for (; i < sparsity->dim_metadata_size; i++) {
TfLiteDimensionMetadata metadata = sparsity->dim_metadata[i];
if (metadata.format == kTfLiteDimSparseCSR) {
TfLiteIntArrayFree(metadata.array_segments);
metadata.array_segments = NULL;
TfLiteIntArrayFree(metadata.array_indices);
metadata.array_indices = NULL;
}
}
free(sparsity->dim_metadata);
sparsity->dim_metadata = NULL;
}
free(sparsity);
}
void TfLiteTensorFree(TfLiteTensor *t)
{
TfLiteTensorDataFree(t);
if (t->dims)
TfLiteIntArrayFree(t->dims);
t->dims = NULL;
if (t->dims_signature) {
TfLiteIntArrayFree((TfLiteIntArray *)t->dims_signature);
}
t->dims_signature = NULL;
TfLiteQuantizationFree(&t->quantization);
TfLiteSparsityFree(t->sparsity);
t->sparsity = NULL;
}
void TfLiteTensorReset(TfLiteType type, const char *name, TfLiteIntArray *dims,
TfLiteQuantizationParams quantization, char *buffer,
size_t size, TfLiteAllocationType allocation_type,
const void *allocation, bool is_variable,
TfLiteTensor *tensor)
{
TfLiteTensorFree(tensor);
tensor->type = type;
tensor->name = name;
tensor->dims = dims;
tensor->params = quantization;
tensor->data.raw = buffer;
tensor->bytes = size;
tensor->allocation_type = allocation_type;
tensor->allocation = allocation;
tensor->is_variable = is_variable;
tensor->quantization.type = kTfLiteNoQuantization;
tensor->quantization.params = NULL;
}
void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor *tensor)
{
if (tensor->allocation_type != kTfLiteDynamic &&
tensor->allocation_type != kTfLitePersistentRo) {
return;
}
// TODO(b/145340303): Tensor data should be aligned.
if (!tensor->data.raw) {
tensor->data.raw = (char *)malloc(num_bytes);
} else if (num_bytes > tensor->bytes) {
tensor->data.raw = (char *)realloc(tensor->data.raw, num_bytes);
}
tensor->bytes = num_bytes;
}
#endif // TF_LITE_STATIC_MEMORY
const char *TfLiteTypeGetName(TfLiteType type)
{
switch (type) {
case kTfLiteNoType:
return "NOTYPE";
case kTfLiteFloat32:
return "FLOAT32";
case kTfLiteInt16:
return "INT16";
case kTfLiteInt32:
return "INT32";
case kTfLiteUInt32:
return "UINT32";
case kTfLiteUInt8:
return "UINT8";
case kTfLiteInt8:
return "INT8";
case kTfLiteInt64:
return "INT64";
case kTfLiteUInt64:
return "UINT64";
case kTfLiteBool:
return "BOOL";
case kTfLiteComplex64:
return "COMPLEX64";
case kTfLiteComplex128:
return "COMPLEX128";
case kTfLiteString:
return "STRING";
case kTfLiteFloat16:
return "FLOAT16";
case kTfLiteFloat64:
return "FLOAT64";
case kTfLiteResource:
return "RESOURCE";
case kTfLiteVariant:
return "VARIANT";
}
return "Unknown type";
}
TfLiteDelegate TfLiteDelegateCreate(void)
{
TfLiteDelegate d = {
.data_ = NULL,
.Prepare = NULL,
.CopyFromBufferHandle = NULL,
.CopyToBufferHandle = NULL,
.FreeBufferHandle = NULL,
.flags = kTfLiteDelegateFlagsNone,
};
return d;
}

View File

@ -0,0 +1,952 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
// This file defines common C types and APIs for implementing operations,
// delegates and other constructs in TensorFlow Lite. The actual operations and
// delegates can be defined using C++, but the interface between the interpreter
// and the operations are C.
//
// Summary of abstractions
// TF_LITE_ENSURE - Self-sufficient error checking
// TfLiteStatus - Status reporting
// TfLiteIntArray - stores tensor shapes (dims),
// TfLiteContext - allows an op to access the tensors
// TfLiteTensor - tensor (a multidimensional array)
// TfLiteNode - a single node or operation
// TfLiteRegistration - the implementation of a conceptual operation.
// TfLiteDelegate - allows delegation of nodes to alternative backends.
//
// Some abstractions in this file are created and managed by Interpreter.
//
// NOTE: The order of values in these structs are "semi-ABI stable". New values
// should be added only to the end of structs and never reordered.
#ifndef TENSORFLOW_LITE_C_COMMON_H_
#define TENSORFLOW_LITE_C_COMMON_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "tensorflow/lite/c/c_api_types.h" // IWYU pragma: export
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// The list of external context types known to TF Lite. This list exists solely
// to avoid conflicts and to ensure ops can share the external contexts they
// need. Access to the external contexts is controlled by one of the
// corresponding support files.
typedef enum TfLiteExternalContextType {
kTfLiteEigenContext = 0, // include eigen_support.h to use.
kTfLiteGemmLowpContext = 1, // include gemm_support.h to use.
kTfLiteEdgeTpuContext = 2, // Placeholder for Edge TPU support.
kTfLiteCpuBackendContext = 3, // include cpu_backend_context.h to use.
kTfLiteMaxExternalContexts = 4
} TfLiteExternalContextType;
// Forward declare so dependent structs and methods can reference these types
// prior to the struct definitions.
struct TfLiteContext;
struct TfLiteDelegate;
struct TfLiteRegistration;
// An external context is a collection of information unrelated to the TF Lite
// framework, but useful to a subset of the ops. TF Lite knows very little
// about the actual contexts, but it keeps a list of them, and is able to
// refresh them if configurations like the number of recommended threads
// change.
typedef struct TfLiteExternalContext {
TfLiteExternalContextType type;
TfLiteStatus (*Refresh)(struct TfLiteContext *context);
} TfLiteExternalContext;
#define kTfLiteOptionalTensor (-1)
// Fixed size list of integers. Used for dimensions and inputs/outputs tensor
// indices
typedef struct TfLiteIntArray {
int size;
// gcc 6.1+ have a bug where flexible members aren't properly handled
// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c
#if (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \
__GNUC_MINOR__ >= 1) || \
defined(HEXAGON) || \
(defined(__clang__) && __clang_major__ == 7 && __clang_minor__ == 1)
int data[0];
#else
int data[];
#endif
} TfLiteIntArray;
// Given the size (number of elements) in a TfLiteIntArray, calculate its size
// in bytes.
int TfLiteIntArrayGetSizeInBytes(int size);
#ifndef TF_LITE_STATIC_MEMORY
// Create a array of a given `size` (uninitialized entries).
// This returns a pointer, that you must free using TfLiteIntArrayFree().
TfLiteIntArray *TfLiteIntArrayCreate(int size);
#endif
// Check if two intarrays are equal. Returns 1 if they are equal, 0 otherwise.
int TfLiteIntArrayEqual(const TfLiteIntArray *a, const TfLiteIntArray *b);
// Check if an intarray equals an array. Returns 1 if equals, 0 otherwise.
int TfLiteIntArrayEqualsArray(const TfLiteIntArray *a, int b_size,
const int b_data[]);
#ifndef TF_LITE_STATIC_MEMORY
// Create a copy of an array passed as `src`.
// You are expected to free memory with TfLiteIntArrayFree
TfLiteIntArray *TfLiteIntArrayCopy(const TfLiteIntArray *src);
// Free memory of array `a`.
void TfLiteIntArrayFree(TfLiteIntArray *a);
#endif // TF_LITE_STATIC_MEMORY
// Fixed size list of floats. Used for per-channel quantization.
typedef struct TfLiteFloatArray {
int size;
// gcc 6.1+ have a bug where flexible members aren't properly handled
// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c
// This also applies to the toolchain used for Qualcomm Hexagon DSPs.
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \
__GNUC_MINOR__ >= 1
float data[0];
#else
float data[];
#endif
} TfLiteFloatArray;
// Given the size (number of elements) in a TfLiteFloatArray, calculate its size
// in bytes.
int TfLiteFloatArrayGetSizeInBytes(int size);
#ifndef TF_LITE_STATIC_MEMORY
// Create a array of a given `size` (uninitialized entries).
// This returns a pointer, that you must free using TfLiteFloatArrayFree().
TfLiteFloatArray *TfLiteFloatArrayCreate(int size);
// Free memory of array `a`.
void TfLiteFloatArrayFree(TfLiteFloatArray *a);
#endif // TF_LITE_STATIC_MEMORY
// Since we must not depend on any libraries, define a minimal subset of
// error macros while avoiding names that have pre-conceived meanings like
// assert and check.
// Try to make all reporting calls through TF_LITE_KERNEL_LOG rather than
// calling the context->ReportError function directly, so that message strings
// can be stripped out if the binary size needs to be severely optimized.
#ifndef TF_LITE_STRIP_ERROR_STRINGS
#define TF_LITE_KERNEL_LOG(context, ...) \
do { \
(context)->ReportError((context), __VA_ARGS__); \
} while (false)
#define TF_LITE_MAYBE_KERNEL_LOG(context, ...) \
do { \
if ((context) != nullptr) { \
(context)->ReportError((context), __VA_ARGS__); \
} \
} while (false)
#else // TF_LITE_STRIP_ERROR_STRINGS
#define TF_LITE_KERNEL_LOG(context, ...)
#define TF_LITE_MAYBE_KERNEL_LOG(context, ...)
#endif // TF_LITE_STRIP_ERROR_STRINGS
// Check whether value is true, and if not return kTfLiteError from
// the current function (and report the error string msg).
#define TF_LITE_ENSURE_MSG(context, value, msg) \
do { \
if (!(value)) { \
TF_LITE_KERNEL_LOG((context), __FILE__ " " msg); \
return kTfLiteError; \
} \
} while (0)
// Check whether the value `a` is true, and if not return kTfLiteError from
// the current function, while also reporting the location of the error.
#define TF_LITE_ENSURE(context, a) \
do { \
if (!(a)) { \
TF_LITE_KERNEL_LOG((context), "%s:%d %s was not true.", __FILE__, \
__LINE__, #a); \
return kTfLiteError; \
} \
} while (0)
#define TF_LITE_ENSURE_STATUS(a) \
do { \
const TfLiteStatus s = (a); \
if (s != kTfLiteOk) { \
return s; \
} \
} while (0)
// Check whether the value `a == b` is true, and if not return kTfLiteError from
// the current function, while also reporting the location of the error.
// `a` and `b` may be evaluated more than once, so no side effects or
// extremely expensive computations should be done.
// NOTE: Use TF_LITE_ENSURE_TYPES_EQ if comparing TfLiteTypes.
#define TF_LITE_ENSURE_EQ(context, a, b) \
do { \
if ((a) != (b)) { \
TF_LITE_KERNEL_LOG((context), "%s:%d %s != %s (%d != %d)", __FILE__, \
__LINE__, #a, #b, (a), (b)); \
return kTfLiteError; \
} \
} while (0)
#define TF_LITE_ENSURE_TYPES_EQ(context, a, b) \
do { \
if ((a) != (b)) { \
TF_LITE_KERNEL_LOG((context), "%s:%d %s != %s (%s != %s)", __FILE__, \
__LINE__, #a, #b, TfLiteTypeGetName(a), \
TfLiteTypeGetName(b)); \
return kTfLiteError; \
} \
} while (0)
#define TF_LITE_ENSURE_NEAR(context, a, b, epsilon) \
do { \
auto delta = ((a) > (b)) ? ((a) - (b)) : ((b) - (a)); \
if (delta > epsilon) { \
TF_LITE_KERNEL_LOG((context), "%s:%d %s not near %s (%f != %f)", \
__FILE__, __LINE__, #a, #b, static_cast<double>(a), \
static_cast<double>(b)); \
return kTfLiteError; \
} \
} while (0)
#define TF_LITE_ENSURE_OK(context, status) \
do { \
const TfLiteStatus s = (status); \
if ((s) != kTfLiteOk) { \
return s; \
} \
} while (0)
// Single-precision complex data type compatible with the C99 definition.
typedef struct TfLiteComplex64 {
float re, im; // real and imaginary parts, respectively.
} TfLiteComplex64;
// Double-precision complex data type compatible with the C99 definition.
typedef struct TfLiteComplex128 {
double re, im; // real and imaginary parts, respectively.
} TfLiteComplex128;
// Half precision data type compatible with the C99 definition.
typedef struct TfLiteFloat16 {
uint16_t data;
} TfLiteFloat16;
// Return the name of a given type, for error reporting purposes.
const char *TfLiteTypeGetName(TfLiteType type);
// SupportedQuantizationTypes.
typedef enum TfLiteQuantizationType {
// No quantization.
kTfLiteNoQuantization = 0,
// Affine quantization (with support for per-channel quantization).
// Corresponds to TfLiteAffineQuantization.
kTfLiteAffineQuantization = 1,
} TfLiteQuantizationType;
// Structure specifying the quantization used by the tensor, if-any.
typedef struct TfLiteQuantization {
// The type of quantization held by params.
TfLiteQuantizationType type;
// Holds an optional reference to a quantization param structure. The actual
// type depends on the value of the `type` field (see the comment there for
// the values and corresponding types).
void *params;
} TfLiteQuantization;
// Parameters for asymmetric quantization across a dimension (i.e per output
// channel quantization).
// quantized_dimension specifies which dimension the scales and zero_points
// correspond to.
// For a particular value in quantized_dimension, quantized values can be
// converted back to float using:
// real_value = scale * (quantized_value - zero_point)
typedef struct TfLiteAffineQuantization {
TfLiteFloatArray *scale;
TfLiteIntArray *zero_point;
int32_t quantized_dimension;
} TfLiteAffineQuantization;
/* A union of pointers that points to memory for a given tensor. */
typedef union TfLitePtrUnion {
/* Do not access these members directly, if possible, use
* GetTensorData<TYPE>(tensor) instead, otherwise only access .data, as other
* members are deprecated. */
int32_t *i32;
uint32_t *u32;
int64_t *i64;
uint64_t *u64;
float *f;
TfLiteFloat16 *f16;
double *f64;
char *raw;
const char *raw_const;
uint8_t *uint8;
bool *b;
int16_t *i16;
TfLiteComplex64 *c64;
TfLiteComplex128 *c128;
int8_t *int8;
/* Only use this member. */
void *data;
} TfLitePtrUnion;
// Memory allocation strategies.
// * kTfLiteMmapRo: Read-only memory-mapped data, or data externally allocated.
// * kTfLiteArenaRw: Arena allocated with no guarantees about persistence,
// and available during eval.
// * kTfLiteArenaRwPersistent: Arena allocated but persistent across eval, and
// only available during eval.
// * kTfLiteDynamic: Allocated during eval, or for string tensors.
// * kTfLitePersistentRo: Allocated and populated during prepare. This is
// useful for tensors that can be computed during prepare and treated
// as constant inputs for downstream ops (also in prepare).
// * kTfLiteCustom: Custom memory allocation provided by the user. See
// TfLiteCustomAllocation below.
typedef enum TfLiteAllocationType {
kTfLiteMemNone = 0,
kTfLiteMmapRo,
kTfLiteArenaRw,
kTfLiteArenaRwPersistent,
kTfLiteDynamic,
kTfLitePersistentRo,
kTfLiteCustom,
} TfLiteAllocationType;
// The delegates should use zero or positive integers to represent handles.
// -1 is reserved from unallocated status.
typedef int TfLiteBufferHandle;
enum {
kTfLiteNullBufferHandle = -1,
};
// Storage format of each dimension in a sparse tensor.
typedef enum TfLiteDimensionType {
kTfLiteDimDense = 0,
kTfLiteDimSparseCSR,
} TfLiteDimensionType;
// Metadata to encode each dimension in a sparse tensor.
typedef struct TfLiteDimensionMetadata {
TfLiteDimensionType format;
int dense_size;
TfLiteIntArray *array_segments;
TfLiteIntArray *array_indices;
} TfLiteDimensionMetadata;
// Parameters used to encode a sparse tensor. For detailed explanation of each
// field please refer to lite/schema/schema.fbs.
typedef struct TfLiteSparsity {
TfLiteIntArray *traversal_order;
TfLiteIntArray *block_map;
TfLiteDimensionMetadata *dim_metadata;
int dim_metadata_size;
} TfLiteSparsity;
// Defines a custom memory allocation not owned by the runtime.
// `data` should be aligned to kDefaultTensorAlignment defined in
// lite/util.h. (Currently 64 bytes)
// NOTE: See Interpreter.SetCustomAllocationForTensor for details on usage.
typedef struct TfLiteCustomAllocation {
void *data;
size_t bytes;
} TfLiteCustomAllocation;
// The flags used in `Interpreter::SetCustomAllocationForTensor`.
// Note that this is a bitmask, so the values should be 1, 2, 4, 8, ...etc.
typedef enum TfLiteCustomAllocationFlags {
kTfLiteCustomAllocationFlagsNone = 0,
// Skips checking whether allocation.data points to an aligned buffer as
// expected by the TFLite runtime.
// NOTE: Setting this flag can cause crashes when calling Invoke().
// Use with caution.
kTfLiteCustomAllocationFlagsSkipAlignCheck = 1,
} TfLiteCustomAllocationFlags;
// A tensor in the interpreter system which is a wrapper around a buffer of
// data including a dimensionality (or NULL if not currently defined).
#ifndef TF_LITE_STATIC_MEMORY
typedef struct TfLiteTensor {
// The data type specification for data stored in `data`. This affects
// what member of `data` union should be used.
TfLiteType type;
// A union of data pointers. The appropriate type should be used for a typed
// tensor based on `type`.
TfLitePtrUnion data;
// A pointer to a structure representing the dimensionality interpretation
// that the buffer should have. NOTE: the product of elements of `dims`
// and the element datatype size should be equal to `bytes` below.
TfLiteIntArray *dims;
// Quantization information.
TfLiteQuantizationParams params;
// How memory is mapped
// kTfLiteMmapRo: Memory mapped read only.
// i.e. weights
// kTfLiteArenaRw: Arena allocated read write memory
// (i.e. temporaries, outputs).
TfLiteAllocationType allocation_type;
// The number of bytes required to store the data of this Tensor. I.e.
// (bytes of each element) * dims[0] * ... * dims[n-1]. For example, if
// type is kTfLiteFloat32 and dims = {3, 2} then
// bytes = sizeof(float) * 3 * 2 = 4 * 3 * 2 = 24.
size_t bytes;
// An opaque pointer to a tflite::MMapAllocation
const void *allocation;
// Null-terminated name of this tensor.
const char *name;
// The delegate which knows how to handle `buffer_handle`.
// WARNING: This is an experimental interface that is subject to change.
struct TfLiteDelegate *delegate;
// An integer buffer handle that can be handled by `delegate`.
// The value is valid only when delegate is not null.
// WARNING: This is an experimental interface that is subject to change.
TfLiteBufferHandle buffer_handle;
// If the delegate uses its own buffer (e.g. GPU memory), the delegate is
// responsible to set data_is_stale to true.
// `delegate->CopyFromBufferHandle` can be called to copy the data from
// delegate buffer.
// WARNING: This is an // experimental interface that is subject to change.
bool data_is_stale;
// True if the tensor is a variable.
bool is_variable;
// Quantization information. Replaces params field above.
TfLiteQuantization quantization;
// Parameters used to encode a sparse tensor.
// This is optional. The field is NULL if a tensor is dense.
// WARNING: This is an experimental interface that is subject to change.
TfLiteSparsity *sparsity;
// Optional. Encodes shapes with unknown dimensions with -1. This field is
// only populated when unknown dimensions exist in a read-write tensor (i.e.
// an input or output tensor). (e.g. `dims` contains [1, 1, 1, 3] and
// `dims_signature` contains [1, -1, -1, 3]).
const TfLiteIntArray *dims_signature;
} TfLiteTensor;
// A structure representing an instance of a node.
// This structure only exhibits the inputs, outputs, user defined data and some
// node properties (like statefulness), not other features like the type.
typedef struct TfLiteNode {
// Inputs to this node expressed as indices into the simulator's tensors.
TfLiteIntArray *inputs;
// Outputs to this node expressed as indices into the simulator's tensors.
TfLiteIntArray *outputs;
// intermediate tensors to this node expressed as indices into the simulator's
// tensors.
TfLiteIntArray *intermediates;
// Temporary tensors uses during the computations. This usually contains no
// tensors, but ops are allowed to change that if they need scratch space of
// any sort.
TfLiteIntArray *temporaries;
// Opaque data provided by the node implementer through `Registration.init`.
void *user_data;
// Opaque data provided to the node if the node is a builtin. This is usually
// a structure defined in builtin_op_data.h
void *builtin_data;
// Custom initial data. This is the opaque data provided in the flatbuffer.
// WARNING: This is an experimental interface that is subject to change.
const void *custom_initial_data;
int custom_initial_data_size;
// The pointer to the delegate. This is non-null only when the node is
// created by calling `interpreter.ModifyGraphWithDelegate`.
// WARNING: This is an experimental interface that is subject to change.
struct TfLiteDelegate *delegate;
// Whether this op might have side effect (e.g. stateful op).
bool might_have_side_effect;
} TfLiteNode;
#else // defined(TF_LITE_STATIC_MEMORY)?
// NOTE: This flag is opt-in only at compile time.
//
// Specific reduced TfLiteTensor struct for TF Micro runtime. This struct
// contains only the minimum fields required to initialize and prepare a micro
// inference graph. The fields in this struct have been ordered from
// largest-to-smallest for optimal struct sizeof.
//
// This struct does not use:
// - allocation
// - buffer_handle
// - data_is_stale
// - delegate
// - dims_signature
// - name
// - sparsity
typedef struct TfLiteTensor {
// TODO(b/155784997): Consider consolidating these quantization fields:
// Quantization information. Replaces params field above.
TfLiteQuantization quantization;
// Quantization information.
TfLiteQuantizationParams params;
// A union of data pointers. The appropriate type should be used for a typed
// tensor based on `type`.
TfLitePtrUnion data;
// A pointer to a structure representing the dimensionality interpretation
// that the buffer should have. NOTE: the product of elements of `dims`
// and the element datatype size should be equal to `bytes` below.
TfLiteIntArray *dims;
// The number of bytes required to store the data of this Tensor. I.e.
// (bytes of each element) * dims[0] * ... * dims[n-1]. For example, if
// type is kTfLiteFloat32 and dims = {3, 2} then
// bytes = sizeof(float) * 3 * 2 = 4 * 3 * 2 = 24.
size_t bytes;
// The data type specification for data stored in `data`. This affects
// what member of `data` union should be used.
TfLiteType type;
// How memory is mapped
// kTfLiteMmapRo: Memory mapped read only.
// i.e. weights
// kTfLiteArenaRw: Arena allocated read write memory
// (i.e. temporaries, outputs).
TfLiteAllocationType allocation_type;
// True if the tensor is a variable.
bool is_variable;
} TfLiteTensor;
// Specific reduced TfLiteNode struct for TF Micro runtime. This struct contains
// only the minimum fields required to represent a node.
//
// This struct does not use:
// - delegate
// - intermediates
// - temporaries
typedef struct TfLiteNode {
// Inputs to this node expressed as indices into the simulator's tensors.
TfLiteIntArray *inputs;
// Outputs to this node expressed as indices into the simulator's tensors.
TfLiteIntArray *outputs;
// Opaque data provided by the node implementer through `Registration.init`.
void *user_data;
// Opaque data provided to the node if the node is a builtin. This is usually
// a structure defined in builtin_op_data.h
void *builtin_data;
// Custom initial data. This is the opaque data provided in the flatbuffer.
// WARNING: This is an experimental interface that is subject to change.
const void *custom_initial_data;
int custom_initial_data_size;
} TfLiteNode;
#endif // TF_LITE_STATIC_MEMORY
// Light-weight tensor struct for TF Micro runtime. Provides the minimal amount
// of information required for a kernel to run during TfLiteRegistration::Eval.
// TODO(b/160955687): Move this field into TF_LITE_STATIC_MEMORY when TFLM
// builds with this flag by default internally.
typedef struct TfLiteEvalTensor {
// A union of data pointers. The appropriate type should be used for a typed
// tensor based on `type`.
TfLitePtrUnion data;
// A pointer to a structure representing the dimensionality interpretation
// that the buffer should have.
TfLiteIntArray *dims;
// The data type specification for data stored in `data`. This affects
// what member of `data` union should be used.
TfLiteType type;
} TfLiteEvalTensor;
#ifndef TF_LITE_STATIC_MEMORY
// Free data memory of tensor `t`.
void TfLiteTensorDataFree(TfLiteTensor *t);
// Free quantization data.
void TfLiteQuantizationFree(TfLiteQuantization *quantization);
// Free sparsity parameters.
void TfLiteSparsityFree(TfLiteSparsity *sparsity);
// Free memory of tensor `t`.
void TfLiteTensorFree(TfLiteTensor *t);
// Set all of a tensor's fields (and free any previously allocated data).
void TfLiteTensorReset(TfLiteType type, const char *name, TfLiteIntArray *dims,
TfLiteQuantizationParams quantization, char *buffer,
size_t size, TfLiteAllocationType allocation_type,
const void *allocation, bool is_variable,
TfLiteTensor *tensor);
// Resize the allocated data of a (dynamic) tensor. Tensors with allocation
// types other than kTfLiteDynamic will be ignored.
void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor *tensor);
#endif // TF_LITE_STATIC_MEMORY
// WARNING: This is an experimental interface that is subject to change.
//
// Currently, TfLiteDelegateParams has to be allocated in a way that it's
// trivially destructable. It will be stored as `builtin_data` field in
// `TfLiteNode` of the delegate node.
//
// See also the `CreateDelegateParams` function in `interpreter.cc` details.
typedef struct TfLiteDelegateParams {
struct TfLiteDelegate *delegate;
TfLiteIntArray *nodes_to_replace;
TfLiteIntArray *input_tensors;
TfLiteIntArray *output_tensors;
} TfLiteDelegateParams;
typedef struct TfLiteContext {
// Number of tensors in the context.
size_t tensors_size;
// The execution plan contains a list of the node indices in execution
// order. execution_plan->size is the current number of nodes. And,
// execution_plan->data[0] is the first node that needs to be run.
// TfLiteDelegates can traverse the current execution plan by iterating
// through each member of this array and using GetNodeAndRegistration() to
// access details about a node. i.e.
//
// TfLiteIntArray* execution_plan;
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
// for (int exec_index = 0; exec_index < execution_plan->size; exec_index++) {
// int node_index = execution_plan->data[exec_index];
// TfLiteNode* node;
// TfLiteRegistration* reg;
// context->GetNodeAndRegistration(context, node_index, &node, &reg);
// }
// Note: the memory pointed by '`*execution_plan` is OWNED by TfLite runtime.
// Future calls to GetExecutionPlan invalidates earlier outputs. The following
// code snippet shows the issue of such an invocation pattern. After calling
// CheckNode, subsequent access to `plan_1st` is undefined.
//
// void CheckNode(const TfLiteNode* node) {
// ...
// TfLiteIntArray* plan_2nd;
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan_2nd));
// ...
// }
//
// TfLiteIntArray* plan_1st;
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan_1st));
// for (int exec_index = 0; exec_index < plan_1st->size; exec_index++) {
// int node_index = plan_1st->data[exec_index];
// TfLiteNode* node;
// TfLiteRegistration* reg;
// context->GetNodeAndRegistration(context, node_index, &node, &reg);
// CheckNode(node);
// }
//
// WARNING: This is an experimental interface that is subject to change.
TfLiteStatus (*GetExecutionPlan)(struct TfLiteContext *context,
TfLiteIntArray **execution_plan);
// An array of tensors in the interpreter context (of length `tensors_size`)
TfLiteTensor *tensors;
// opaque full context ptr (an opaque c++ data structure)
void *impl_;
// Request memory pointer be resized. Updates dimensions on the tensor.
// NOTE: ResizeTensor takes ownership of newSize.
TfLiteStatus (*ResizeTensor)(struct TfLiteContext *, TfLiteTensor *tensor,
TfLiteIntArray *new_size);
// Request that an error be reported with format string msg.
void (*ReportError)(struct TfLiteContext *, const char *msg, ...);
// Add `tensors_to_add` tensors, preserving pre-existing Tensor entries. If
// non-null, the value pointed to by `first_new_tensor_index` will be set to
// the index of the first new tensor.
TfLiteStatus (*AddTensors)(struct TfLiteContext *, int tensors_to_add,
int *first_new_tensor_index);
// Get a Tensor node by node_index.
// WARNING: This is an experimental interface that is subject to change.
TfLiteStatus (*GetNodeAndRegistration)(
struct TfLiteContext *, int node_index, TfLiteNode **node,
struct TfLiteRegistration **registration);
// Replace ops with one or more stub delegate operations. This function
// does not take ownership of `nodes_to_replace`.
TfLiteStatus (*ReplaceNodeSubsetsWithDelegateKernels)(
struct TfLiteContext *, struct TfLiteRegistration registration,
const TfLiteIntArray *nodes_to_replace, struct TfLiteDelegate *delegate);
// Number of threads that are recommended to subsystems like gemmlowp and
// eigen.
int recommended_num_threads;
// Access external contexts by type.
// WARNING: This is an experimental interface that is subject to change.
TfLiteExternalContext *(*GetExternalContext)(struct TfLiteContext *,
TfLiteExternalContextType);
// Set the value of a external context. Does not take ownership of the
// pointer.
// WARNING: This is an experimental interface that is subject to change.
void (*SetExternalContext)(struct TfLiteContext *, TfLiteExternalContextType,
TfLiteExternalContext *);
// Flag for allowing float16 precision for FP32 calculation.
// default: false.
// WARNING: This is an experimental API and subject to change.
bool allow_fp32_relax_to_fp16;
// Pointer to the op-level profiler, if set; nullptr otherwise.
void *profiler;
// Allocate persistent buffer which has the same life time as the interpreter.
// Returns nullptr on failure.
// The memory is allocated from heap for TFL, and from tail in TFLM.
// This method is only available in Init or Prepare stage.
// WARNING: This is an experimental interface that is subject to change.
void *(*AllocatePersistentBuffer)(struct TfLiteContext *ctx, size_t bytes);
// Allocate a buffer which will be deallocated right after invoke phase.
// The memory is allocated from heap in TFL, and from volatile arena in TFLM.
// This method is only available in invoke stage.
// NOTE: If possible use RequestScratchBufferInArena method to avoid memory
// allocation during inference time.
// WARNING: This is an experimental interface that is subject to change.
TfLiteStatus (*AllocateBufferForEval)(struct TfLiteContext *ctx, size_t bytes,
void **ptr);
// Request a scratch buffer in the arena through static memory planning.
// This method is only available in Prepare stage and the buffer is allocated
// by the interpreter between Prepare and Eval stage. In Eval stage,
// GetScratchBuffer API can be used to fetch the address.
// WARNING: This is an experimental interface that is subject to change.
TfLiteStatus (*RequestScratchBufferInArena)(struct TfLiteContext *ctx,
size_t bytes, int *buffer_idx);
// Get the scratch buffer pointer.
// This method is only available in Eval stage.
// WARNING: This is an experimental interface that is subject to change.
void *(*GetScratchBuffer)(struct TfLiteContext *ctx, int buffer_idx);
// Resize the memory pointer of the `tensor`. This method behaves the same as
// `ResizeTensor`, except that it makes a copy of the shape array internally
// so the shape array could be deallocated right afterwards.
// WARNING: This is an experimental interface that is subject to change.
TfLiteStatus (*ResizeTensorExplicit)(struct TfLiteContext *ctx,
TfLiteTensor *tensor, int dims,
const int *shape);
// This method provides a preview of post-delegation partitioning. Each
// TfLiteDelegateParams in the referenced array corresponds to one instance of
// the delegate kernel.
// Example usage:
//
// TfLiteIntArray* nodes_to_replace = ...;
// TfLiteDelegateParams* params_array;
// int num_partitions = 0;
// TF_LITE_ENSURE_STATUS(context->PreviewDelegatePartitioning(
// context, delegate, nodes_to_replace, &params_array, &num_partitions));
// for (int idx = 0; idx < num_partitions; idx++) {
// const auto& partition_params = params_array[idx];
// ...
// }
//
// NOTE: The context owns the memory referenced by partition_params_array. It
// will be cleared with another call to PreviewDelegateParitioning, or after
// TfLiteDelegateParams::Prepare returns.
//
// WARNING: This is an experimental interface that is subject to change.
TfLiteStatus (*PreviewDelegatePartitioning)(
struct TfLiteContext *context, const TfLiteIntArray *nodes_to_replace,
TfLiteDelegateParams **partition_params_array, int *num_partitions);
// Returns a TfLiteTensor struct for a given index.
// WARNING: This is an experimental interface that is subject to change.
// WARNING: This method may not be available on all platforms.
TfLiteTensor *(*GetTensor)(const struct TfLiteContext *context,
int tensor_idx);
// Returns a TfLiteEvalTensor struct for a given index.
// WARNING: This is an experimental interface that is subject to change.
// WARNING: This method may not be available on all platforms.
TfLiteEvalTensor *(*GetEvalTensor)(const struct TfLiteContext *context,
int tensor_idx);
} TfLiteContext;
typedef struct TfLiteRegistration {
// Initializes the op from serialized data.
// If a built-in op:
// `buffer` is the op's params data (TfLiteLSTMParams*).
// `length` is zero.
// If custom op:
// `buffer` is the op's `custom_options`.
// `length` is the size of the buffer.
//
// Returns a type-punned (i.e. void*) opaque data (e.g. a primitive pointer
// or an instance of a struct).
//
// The returned pointer will be stored with the node in the `user_data` field,
// accessible within prepare and invoke functions below.
// NOTE: if the data is already in the desired format, simply implement this
// function to return `nullptr` and implement the free function to be a no-op.
void *(*init)(TfLiteContext *context, const char *buffer, size_t length);
// The pointer `buffer` is the data previously returned by an init invocation.
void (*free)(TfLiteContext *context, void *buffer);
// prepare is called when the inputs this node depends on have been resized.
// context->ResizeTensor() can be called to request output tensors to be
// resized.
//
// Returns kTfLiteOk on success.
TfLiteStatus (*prepare)(TfLiteContext *context, TfLiteNode *node);
// Execute the node (should read node->inputs and output to node->outputs).
// Returns kTfLiteOk on success.
TfLiteStatus (*invoke)(TfLiteContext *context, TfLiteNode *node);
// profiling_string is called during summarization of profiling information
// in order to group executions together. Providing a value here will cause a
// given op to appear multiple times is the profiling report. This is
// particularly useful for custom ops that can perform significantly
// different calculations depending on their `user-data`.
const char *(*profiling_string)(const TfLiteContext *context,
const TfLiteNode *node);
// Builtin codes. If this kernel refers to a builtin this is the code
// of the builtin. This is so we can do marshaling to other frameworks like
// NN API.
// Note: It is the responsibility of the registration binder to set this
// properly.
int32_t builtin_code;
// Custom op name. If the op is a builtin, this will be null.
// Note: It is the responsibility of the registration binder to set this
// properly.
// WARNING: This is an experimental interface that is subject to change.
const char *custom_name;
// The version of the op.
// Note: It is the responsibility of the registration binder to set this
// properly.
int version;
} TfLiteRegistration;
// The flags used in `TfLiteDelegate`. Note that this is a bitmask, so the
// values should be 1, 2, 4, 8, ...etc.
typedef enum TfLiteDelegateFlags {
kTfLiteDelegateFlagsNone = 0,
// The flag is set if the delegate can handle dynamic sized tensors.
// For example, the output shape of a `Resize` op with non-constant shape
// can only be inferred when the op is invoked.
// In this case, the Delegate is responsible for calling
// `SetTensorToDynamic` to mark the tensor as a dynamic tensor, and calling
// `ResizeTensor` when invoking the op.
//
// If the delegate isn't capable to handle dynamic tensors, this flag need
// to be set to false.
kTfLiteDelegateFlagsAllowDynamicTensors = 1,
// This flag can be used by delegates (that allow dynamic tensors) to ensure
// applicable tensor shapes are automatically propagated in the case of tensor
// resizing.
// This means that non-dynamic (allocation_type != kTfLiteDynamic) I/O tensors
// of a delegate kernel will have correct shapes before its Prepare() method
// is called. The runtime leverages TFLite builtin ops in the original
// execution plan to propagate shapes.
//
// A few points to note:
// 1. This requires kTfLiteDelegateFlagsAllowDynamicTensors. If that flag is
// false, this one is redundant since the delegate kernels are re-initialized
// every time tensors are resized.
// 2. Enabling this flag adds some overhead to AllocateTensors(), since extra
// work is required to prepare the original execution plan.
// 3. This flag requires that the original execution plan only have ops with
// valid registrations (and not 'dummy' custom ops like with Flex).
// WARNING: This feature is experimental and subject to change.
kTfLiteDelegateFlagsRequirePropagatedShapes = 2
} TfLiteDelegateFlags;
// WARNING: This is an experimental interface that is subject to change.
typedef struct TfLiteDelegate {
// Data that delegate needs to identify itself. This data is owned by the
// delegate. The delegate is owned in the user code, so the delegate is
// responsible for doing this when it is destroyed.
void *data_;
// Invoked by ModifyGraphWithDelegate. This prepare is called, giving the
// delegate a view of the current graph through TfLiteContext*. It typically
// will look at the nodes and call ReplaceNodeSubsetsWithDelegateKernels()
// to ask the TensorFlow lite runtime to create macro-nodes to represent
// delegated subgraphs of the original graph.
TfLiteStatus (*Prepare)(TfLiteContext *context,
struct TfLiteDelegate *delegate);
// Copy the data from delegate buffer handle into raw memory of the given
// 'tensor'. Note that the delegate is allowed to allocate the raw bytes as
// long as it follows the rules for kTfLiteDynamic tensors, in which case this
// cannot be null.
TfLiteStatus (*CopyFromBufferHandle)(TfLiteContext *context,
struct TfLiteDelegate *delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor *tensor);
// Copy the data from raw memory of the given 'tensor' to delegate buffer
// handle. This can be null if the delegate doesn't use its own buffer.
TfLiteStatus (*CopyToBufferHandle)(TfLiteContext *context,
struct TfLiteDelegate *delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor *tensor);
// Free the Delegate Buffer Handle. Note: This only frees the handle, but
// this doesn't release the underlying resource (e.g. textures). The
// resources are either owned by application layer or the delegate.
// This can be null if the delegate doesn't use its own buffer.
void (*FreeBufferHandle)(TfLiteContext *context,
struct TfLiteDelegate *delegate,
TfLiteBufferHandle *handle);
// Bitmask flags. See the comments in `TfLiteDelegateFlags`.
int64_t flags;
} TfLiteDelegate;
// Build a 'null' delegate, with all the fields properly set to their default
// values.
TfLiteDelegate TfLiteDelegateCreate(void);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_C_COMMON_H_

View File

@ -0,0 +1,40 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/core/api/error_reporter.h"
#include <cstdarg>
namespace tflite {
int ErrorReporter::Report(const char *format, ...)
{
va_list args;
va_start(args, format);
int code = Report(format, args);
va_end(args);
return code;
}
// TODO(aselle): Make the name of ReportError on context the same, so
// we can use the ensure functions w/o a context and w/ a reporter.
int ErrorReporter::ReportError(void *, const char *format, ...)
{
va_list args;
va_start(args, format);
int code = Report(format, args);
va_end(args);
return code;
}
} // namespace tflite

View File

@ -0,0 +1,60 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
#define TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
#include <cstdarg>
namespace tflite {
/// A functor that reports error to supporting system. Invoked similar to
/// printf.
///
/// Usage:
/// ErrorReporter foo;
/// foo.Report("test %d", 5);
/// or
/// va_list args;
/// foo.Report("test %d", args); // where args is va_list
///
/// Subclass ErrorReporter to provide another reporting destination.
/// For example, if you have a GUI program, you might redirect to a buffer
/// that drives a GUI error log box.
class ErrorReporter {
public:
virtual ~ErrorReporter()
{
}
virtual int Report(const char *format, va_list args) = 0;
int Report(const char *format, ...);
int ReportError(void *, const char *format, ...);
};
} // namespace tflite
// You should not make bare calls to the error reporter, instead use the
// TF_LITE_REPORT_ERROR macro, since this allows message strings to be
// stripped when the binary size has to be optimized. If you are looking to
// reduce binary size, define TF_LITE_STRIP_ERROR_STRINGS when compiling and
// every call will be stubbed out, taking no memory.
#ifndef TF_LITE_STRIP_ERROR_STRINGS
#define TF_LITE_REPORT_ERROR(reporter, ...) \
do { \
static_cast<tflite::ErrorReporter *>(reporter)->Report(__VA_ARGS__); \
} while (false)
#else // TF_LITE_STRIP_ERROR_STRINGS
#define TF_LITE_REPORT_ERROR(reporter, ...)
#endif // TF_LITE_STRIP_ERROR_STRINGS
#endif // TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,360 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
#define TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
// These functions transform codes and data structures that are defined in the
// flatbuffer serialization format into in-memory values that are used by the
// runtime API and interpreter.
#include <cstddef>
#include <new>
#include <type_traits>
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
// Interface class for builtin data allocations.
class BuiltinDataAllocator {
public:
virtual void *Allocate(size_t size, size_t alignment_hint) = 0;
virtual void Deallocate(void *data) = 0;
// Allocate a structure, but make sure it is a POD structure that doesn't
// require constructors to run. The reason we do this, is that Interpreter's C
// extension part will take ownership so destructors will not be run during
// deallocation.
template <typename T>
T *AllocatePOD()
{
// TODO(b/154346074): Change this to is_trivially_destructible when all
// platform targets support that properly.
static_assert(std::is_pod<T>::value, "Builtin data structure must be POD.");
void *allocated_memory = this->Allocate(sizeof(T), alignof(T));
return new (allocated_memory) T();
}
virtual ~BuiltinDataAllocator()
{
}
};
// Parse the appropriate data out of the op.
//
// This handles builtin data explicitly as there are flatbuffer schemas.
// If it returns kTfLiteOk, it passes the data out with `builtin_data`. The
// calling function has to pass in an allocator object, and this allocator
// will be called to reserve space for the output data. If the calling
// function's allocator reserves memory on the heap, then it's the calling
// function's responsibility to free it.
// If it returns kTfLiteError, `builtin_data` will be `nullptr`.
TfLiteStatus ParseOpData(const Operator *op, BuiltinOperator op_type,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
// Converts the tensor data type used in the flat buffer to the representation
// used by the runtime.
TfLiteStatus ConvertTensorType(TensorType tensor_type, TfLiteType *type,
ErrorReporter *error_reporter);
TfLiteStatus ParseAbs(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseAdd(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseAddN(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseArgMax(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseArgMin(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseBatchMatMul(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseBatchToSpaceNd(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseCeil(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseCast(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseConcatenation(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseConv2D(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseCos(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseCumsum(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseDepthToSpace(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseDepthwiseConv2D(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseDequantize(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseDiv(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseElu(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseEqual(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseExp(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseExpandDims(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseFill(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseFloor(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseFloorDiv(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseFloorMod(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseFullyConnected(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseGather(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseGatherNd(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseGreater(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseGreaterEqual(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseHardSwish(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseIf(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseL2Normalization(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLeakyRelu(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLess(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseLessEqual(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLog(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseLogicalAnd(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLogicalNot(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLogicalOr(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLogistic(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseLogSoftmax(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseMaximum(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseMinimum(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseMul(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseNeg(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseNotEqual(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParsePack(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParsePad(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParsePadV2(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParsePool(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParsePow(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParsePrelu(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseQuantize(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseReducer(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseRelu(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseRelu6(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseReshape(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseResizeBilinear(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseResizeNearestNeighbor(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseRound(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseRsqrt(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseShape(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSin(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSoftmax(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSpaceToBatchNd(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseSpaceToDepth(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseSplit(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSplitV(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSqueeze(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSqrt(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSquare(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseStridedSlice(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseSub(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseSvdf(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseTanh(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseTranspose(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseTransposeConv(const Operator *op,
ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
TfLiteStatus ParseUnpack(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator, void **builtin_data);
TfLiteStatus ParseZerosLike(const Operator *op, ErrorReporter *error_reporter,
BuiltinDataAllocator *allocator,
void **builtin_data);
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_

View File

@ -0,0 +1,70 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/core/api/op_resolver.h"
#include "flatbuffers/flatbuffers.h" // from @flatbuffers
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/schema/schema_utils.h"
namespace tflite {
TfLiteStatus GetRegistrationFromOpCode(
const OperatorCode *opcode, const OpResolver &op_resolver,
ErrorReporter *error_reporter, const TfLiteRegistration **registration)
{
TfLiteStatus status = kTfLiteOk;
*registration = nullptr;
auto builtin_code = GetBuiltinCode(opcode);
int version = opcode->version();
if (builtin_code > BuiltinOperator_MAX ||
builtin_code < BuiltinOperator_MIN) {
TF_LITE_REPORT_ERROR(
error_reporter,
"Op builtin_code out of range: %d. Are you using old TFLite binary "
"with newer model?",
builtin_code);
status = kTfLiteError;
} else if (builtin_code != BuiltinOperator_CUSTOM) {
*registration = op_resolver.FindOp(builtin_code, version);
if (*registration == nullptr) {
TF_LITE_REPORT_ERROR(
error_reporter,
"Didn't find op for builtin opcode '%s' version '%d'. "
"An older version of this builtin might be supported. "
"Are you using an old TFLite binary with a newer model?\n",
EnumNameBuiltinOperator(builtin_code), version);
status = kTfLiteError;
}
} else if (!opcode->custom_code()) {
TF_LITE_REPORT_ERROR(
error_reporter,
"Operator with CUSTOM builtin_code has no custom_code.\n");
status = kTfLiteError;
} else {
const char *name = opcode->custom_code()->c_str();
*registration = op_resolver.FindOp(name, version);
if (*registration == nullptr) {
// Do not report error for unresolved custom op, we do the final check
// while preparing ops.
status = kTfLiteError;
}
}
return status;
}
} // namespace tflite

View File

@ -0,0 +1,82 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
#define TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
#include <memory>
#include <vector>
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
/// Abstract interface that returns TfLiteRegistrations given op codes or custom
/// op names. This is the mechanism that ops being referenced in the flatbuffer
/// model are mapped to executable function pointers (TfLiteRegistrations).
class OpResolver {
public:
/// Finds the op registration for a builtin operator by enum code.
virtual const TfLiteRegistration *FindOp(tflite::BuiltinOperator op,
int version) const = 0;
/// Finds the op registration of a custom operator by op name.
virtual const TfLiteRegistration *FindOp(const char *op,
int version) const = 0;
// Returns optional delegates for resolving and handling ops in the flatbuffer
// model. This may be used in addition to the standard TfLiteRegistration
// lookup for graph resolution.
using TfLiteDelegatePtrVector =
std::vector<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate *)> >;
virtual TfLiteDelegatePtrVector GetDelegates(int num_threads) const
{
return TfLiteDelegatePtrVector();
}
virtual ~OpResolver()
{
}
private:
/// Returns true if this OpResolver may contain any "user defined" ops.
/// By "user defined" ops, we mean any op definitions other than those
/// contained in tflite::ops::builtin::BuiltinOpResolver.
///
/// If this method returns true, it doesn't necessarily mean that the
/// OpResolver contains a user-defined op, just that the absence of
/// user-defined ops can't be guaranteed.
///
/// Note that "user-defined" ops are not the same as "custom" ops;
/// BuiltinOpResolver may support certain "custom" ops, in addition to
/// "builtin" ops, and may not support all of the "builtin" op enum values.
virtual bool MayContainUserDefinedOps() const
{
return true;
}
friend class OpResolverInternal;
};
// Handles the logic for converting between an OperatorCode structure extracted
// from a flatbuffer and information about a registered operator
// implementation.
TfLiteStatus GetRegistrationFromOpCode(const OperatorCode *opcode,
const OpResolver &op_resolver,
ErrorReporter *error_reporter,
const TfLiteRegistration **registration);
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_

View File

@ -0,0 +1,51 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/core/api/tensor_utils.h"
#include <string.h>
#include "tensorflow/lite/c/common.h"
namespace tflite {
TfLiteStatus ResetVariableTensor(TfLiteTensor *tensor)
{
if (!tensor->is_variable) {
return kTfLiteOk;
}
// TODO(b/115961645): Implement - If a variable tensor has a buffer, reset it
// to the value of the buffer.
int value = 0;
if (tensor->type == kTfLiteInt8) {
value = tensor->params.zero_point;
}
// TODO(b/139446230): Provide a platform header to better handle these
// specific scenarios.
#if __ANDROID__ || defined(__x86_64__) || defined(__i386__) || \
defined(__i386) || defined(__x86__) || defined(__X86__) || \
defined(_X86_) || defined(_M_IX86) || defined(_M_X64)
memset(tensor->data.raw, value, tensor->bytes);
#else
char *raw_ptr = tensor->data.raw;
for (size_t i = 0; i < tensor->bytes; ++i) {
*raw_ptr = value;
raw_ptr++;
}
#endif
return kTfLiteOk;
}
} // namespace tflite

View File

@ -0,0 +1,27 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
#define TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
#include "tensorflow/lite/c/common.h"
namespace tflite {
// Resets a variable tensor to the default value.
TfLiteStatus ResetVariableTensor(TfLiteTensor *tensor);
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_

View File

@ -0,0 +1,6 @@
This directory contains the subset of functionality that is needed to run the
micro_speech example with TFLM.
The source of truth for the experimental microfrontend in TfLite is at:
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/microfrontend

View File

@ -0,0 +1,65 @@
# Audio "frontend" library for feature generation
A feature generation library (also called frontend) that receives raw audio
input, and produces filter banks (a vector of values).
The raw audio input is expected to be 16-bit PCM features, with a configurable
sample rate. More specifically the audio signal goes through a pre-emphasis
filter (optionally); then gets sliced into (potentially overlapping) frames and
a window function is applied to each frame; afterwards, we do a Fourier
transform on each frame (or more specifically a Short-Time Fourier Transform)
and calculate the power spectrum; and subsequently compute the filter banks.
By default the library is configured with a set of defaults to perform the
different processing tasks. This takes place with the frontend_util.c function:
```c++
void FrontendFillConfigWithDefaults(struct FrontendConfig* config)
```
A single invocation looks like:
```c++
struct FrontendConfig frontend_config;
FrontendFillConfigWithDefaults(&frontend_config);
int sample_rate = 16000;
FrontendPopulateState(&frontend_config, &frontend_state, sample_rate);
int16_t* audio_data = ; // PCM audio samples at 16KHz.
size_t audio_size = ; // Number of audio samples.
size_t num_samples_read; // How many samples were processed.
struct FrontendOutput output =
FrontendProcessSamples(
&frontend_state, audio_data, audio_size, &num_samples_read);
for (i = 0; i < output.size; ++i) {
printf("%d ", output.values[i]); // Print the feature vector.
}
```
Something to note in the above example is that the frontend consumes as many
samples needed from the audio data to produce a single feature vector (according
to the frontend configuration). If not enough samples were available to generate
a feature vector, the returned size will be 0 and the values pointer will be
`NULL`.
An example of how to use the frontend is provided in frontend_main.cc and its
binary frontend_main. This example, expects a path to a file containing `int16`
PCM features at a sample rate of 16KHz, and upon execution will printing out
the coefficients according to the frontend default configuration.
## Extra features
Extra features of this frontend library include a noise reduction module, as
well as a gain control module.
**Noise cancellation**. Removes stationary noise from each channel of the signal
using a low pass filter.
**Gain control**. A novel automatic gain control based dynamic compression to
replace the widely used static (such as log or root) compression. Disabled
by default.
## Memory map
The binary frontend_memmap_main shows a sample usage of how to avoid all the
initialization code in your application, by first running
"frontend_generate_memmap" to create a header/source file that uses a baked in
frontend state. This command could be automated as part of your build process,
or you can just use the output directly.

View File

@ -0,0 +1,115 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
#ifdef __cplusplus
#include <cstdint>
extern "C" {
#endif
static inline int CountLeadingZeros32Slow(uint64_t n)
{
int zeroes = 28;
if (n >> 16)
zeroes -= 16, n >>= 16;
if (n >> 8)
zeroes -= 8, n >>= 8;
if (n >> 4)
zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
static inline int CountLeadingZeros32(uint32_t n)
{
#if defined(_MSC_VER)
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse(&result, n)) {
return 31 - result;
}
return 32;
#elif defined(__GNUC__)
// Handle 0 as a special case because __builtin_clz(0) is undefined.
if (n == 0) {
return 32;
}
return __builtin_clz(n);
#else
return CountLeadingZeros32Slow(n);
#endif
}
static inline int MostSignificantBit32(uint32_t n)
{
return 32 - CountLeadingZeros32(n);
}
static inline int CountLeadingZeros64Slow(uint64_t n)
{
int zeroes = 60;
if (n >> 32)
zeroes -= 32, n >>= 32;
if (n >> 16)
zeroes -= 16, n >>= 16;
if (n >> 8)
zeroes -= 8, n >>= 8;
if (n >> 4)
zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
static inline int CountLeadingZeros64(uint64_t n)
{
#if defined(_MSC_VER) && defined(_M_X64)
// MSVC does not have __builtin_clzll. Use _BitScanReverse64.
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse64(&result, n)) {
return 63 - result;
}
return 64;
#elif defined(_MSC_VER)
// MSVC does not have __builtin_clzll. Compose two calls to _BitScanReverse
unsigned long result = 0; // NOLINT(runtime/int)
if ((n >> 32) && _BitScanReverse(&result, n >> 32)) {
return 31 - result;
}
if (_BitScanReverse(&result, n)) {
return 63 - result;
}
return 64;
#elif defined(__GNUC__)
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
if (n == 0) {
return 64;
}
return __builtin_clzll(n);
#else
return CountLeadingZeros64Slow(n);
#endif
}
static inline int MostSignificantBit64(uint64_t n)
{
return 64 - CountLeadingZeros64(n);
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_

View File

@ -0,0 +1,56 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
#include <string.h>
#define FIXED_POINT 16
#include "kiss_fft.h"
#include "tools/kiss_fftr.h"
void FftCompute(struct FftState *state, const int16_t *input,
int input_scale_shift)
{
const size_t input_size = state->input_size;
const size_t fft_size = state->fft_size;
int16_t *fft_input = state->input;
// First, scale the input by the given shift.
size_t i;
for (i = 0; i < input_size; ++i) {
fft_input[i] = static_cast<int16_t>(static_cast<uint16_t>(input[i])
<< input_scale_shift);
}
// Zero out whatever else remains in the top part of the input.
for (; i < fft_size; ++i) {
fft_input[i] = 0;
}
// Apply the FFT.
kiss_fftr(reinterpret_cast<kiss_fftr_cfg>(state->scratch),
state->input,
reinterpret_cast<kiss_fft_cpx *>(state->output));
}
void FftInit(struct FftState *state)
{
// All the initialization is done in FftPopulateState()
}
void FftReset(struct FftState *state)
{
memset(state->input, 0, state->fft_size * sizeof(*state->input));
memset(state->output, 0, (state->fft_size / 2 + 1) * sizeof(*state->output));
}

View File

@ -0,0 +1,50 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
struct complex_int16_t {
int16_t real;
int16_t imag;
};
struct FftState {
int16_t *input;
struct complex_int16_t *output;
size_t fft_size;
size_t input_size;
void *scratch;
size_t scratch_size;
};
void FftCompute(struct FftState *state, const int16_t *input,
int input_scale_shift);
void FftInit(struct FftState *state);
void FftReset(struct FftState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_

View File

@ -0,0 +1,35 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/fft_io.h"
void FftWriteMemmapPreamble(FILE *fp, const struct FftState *state)
{
fprintf(fp, "static int16_t fft_input[%zu];\n", state->fft_size);
fprintf(fp, "static struct complex_int16_t fft_output[%zu];\n",
state->fft_size / 2 + 1);
fprintf(fp, "static char fft_scratch[%zu];\n", state->scratch_size);
fprintf(fp, "\n");
}
void FftWriteMemmap(FILE *fp, const struct FftState *state,
const char *variable)
{
fprintf(fp, "%s->input = fft_input;\n", variable);
fprintf(fp, "%s->output = fft_output;\n", variable);
fprintf(fp, "%s->fft_size = %zu;\n", variable, state->fft_size);
fprintf(fp, "%s->input_size = %zu;\n", variable, state->input_size);
fprintf(fp, "%s->scratch = fft_scratch;\n", variable);
fprintf(fp, "%s->scratch_size = %zu;\n", variable, state->scratch_size);
}

View File

@ -0,0 +1,34 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_IO_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_IO_H_
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
#ifdef __cplusplus
extern "C" {
#endif
void FftWriteMemmapPreamble(FILE *fp, const struct FftState *state);
void FftWriteMemmap(FILE *fp, const struct FftState *state,
const char *variable);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_IO_H_

View File

@ -0,0 +1,55 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int16_t kFakeWindow[] = {
0, 1151, 0, -5944, 0, 13311, 0, -21448, 0, 28327, 0, -32256, 0, 32255,
0, -28328, 0, 21447, 0, -13312, 0, 5943, 0, -1152, 0
};
const int kScaleShift = 0;
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(FftTest_CheckOutputValues)
{
struct FftState state;
TF_LITE_MICRO_EXPECT(
FftPopulateState(&state, sizeof(kFakeWindow) / sizeof(kFakeWindow[0])));
FftInit(&state);
FftCompute(&state, kFakeWindow, kScaleShift);
const struct complex_int16_t expected[] = {
{ 0, 0 }, { -10, 9 }, { -20, 0 }, { -9, -10 }, { 0, 25 }, { -119, 119 }, { -887, 0 }, { 3000, 3000 }, { 0, -6401 }, { -3000, 3000 }, { 886, 0 }, { 118, 119 }, { 0, 25 }, { 9, -10 }, { 19, 0 }, { 9, 9 }, { 0, 0 }
};
TF_LITE_MICRO_EXPECT_EQ(state.fft_size / 2 + 1,
sizeof(expected) / sizeof(expected[0]));
unsigned int i;
for (i = 0; i <= state.fft_size / 2; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.output[i].real, expected[i].real);
TF_LITE_MICRO_EXPECT_EQ(state.output[i].imag, expected[i].imag);
}
FftFreeStateContents(&state);
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,74 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
#include <stdio.h>
#define FIXED_POINT 16
#include "kiss_fft.h"
#include "tools/kiss_fftr.h"
int FftPopulateState(struct FftState *state, size_t input_size)
{
state->input_size = input_size;
state->fft_size = 1;
while (state->fft_size < state->input_size) {
state->fft_size <<= 1;
}
state->input = reinterpret_cast<int16_t *>(
malloc(state->fft_size * sizeof(*state->input)));
if (state->input == nullptr) {
fprintf(stderr, "Failed to alloc fft input buffer\n");
return 0;
}
state->output = reinterpret_cast<complex_int16_t *>(
malloc((state->fft_size / 2 + 1) * sizeof(*state->output) * 2));
if (state->output == nullptr) {
fprintf(stderr, "Failed to alloc fft output buffer\n");
return 0;
}
// Ask kissfft how much memory it wants.
size_t scratch_size = 0;
kiss_fftr_cfg kfft_cfg = kiss_fftr_alloc(
state->fft_size, 0, nullptr, &scratch_size);
if (kfft_cfg != nullptr) {
fprintf(stderr, "Kiss memory sizing failed.\n");
return 0;
}
state->scratch = malloc(scratch_size);
if (state->scratch == nullptr) {
fprintf(stderr, "Failed to alloc fft scratch buffer\n");
return 0;
}
state->scratch_size = scratch_size;
// Let kissfft configure the scratch space we just allocated
kfft_cfg = kiss_fftr_alloc(state->fft_size, 0,
state->scratch, &scratch_size);
if (kfft_cfg != state->scratch) {
fprintf(stderr, "Kiss memory preallocation strategy failed.\n");
return 0;
}
return 1;
}
void FftFreeStateContents(struct FftState *state)
{
free(state->input);
free(state->output);
free(state->scratch);
}

View File

@ -0,0 +1,34 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
#ifdef __cplusplus
extern "C" {
#endif
// Prepares and FFT for the given input size.
int FftPopulateState(struct FftState *state, size_t input_size);
// Frees any allocated buffers.
void FftFreeStateContents(struct FftState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_

View File

@ -0,0 +1,140 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
#include <string.h>
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
void FilterbankConvertFftComplexToEnergy(struct FilterbankState *state,
struct complex_int16_t *fft_output,
int32_t *energy)
{
const int end_index = state->end_index;
int i;
energy += state->start_index;
fft_output += state->start_index;
for (i = state->start_index; i < end_index; ++i) {
const int32_t real = fft_output->real;
const int32_t imag = fft_output->imag;
fft_output++;
const uint32_t mag_squared = (real * real) + (imag * imag);
*energy++ = mag_squared;
}
}
void FilterbankAccumulateChannels(struct FilterbankState *state,
const int32_t *energy)
{
uint64_t *work = state->work;
uint64_t weight_accumulator = 0;
uint64_t unweight_accumulator = 0;
const int16_t *channel_frequency_starts = state->channel_frequency_starts;
const int16_t *channel_weight_starts = state->channel_weight_starts;
const int16_t *channel_widths = state->channel_widths;
int num_channels_plus_1 = state->num_channels + 1;
int i;
for (i = 0; i < num_channels_plus_1; ++i) {
const int32_t *magnitudes = energy + *channel_frequency_starts++;
const int16_t *weights = state->weights + *channel_weight_starts;
const int16_t *unweights = state->unweights + *channel_weight_starts++;
const int width = *channel_widths++;
int j;
for (j = 0; j < width; ++j) {
weight_accumulator += *weights++ * ((uint64_t)*magnitudes);
unweight_accumulator += *unweights++ * ((uint64_t)*magnitudes);
++magnitudes;
}
*work++ = weight_accumulator;
weight_accumulator = unweight_accumulator;
unweight_accumulator = 0;
}
}
static uint16_t Sqrt32(uint32_t num)
{
if (num == 0) {
return 0;
}
uint32_t res = 0;
int max_bit_number = 32 - MostSignificantBit32(num);
max_bit_number |= 1;
uint32_t bit = 1U << (31 - max_bit_number);
int iterations = (31 - max_bit_number) / 2 + 1;
while (iterations--) {
if (num >= res + bit) {
num -= res + bit;
res = (res >> 1U) + bit;
} else {
res >>= 1U;
}
bit >>= 2U;
}
// Do rounding - if we have the bits.
if (num > res && res != 0xFFFF) {
++res;
}
return res;
}
static uint32_t Sqrt64(uint64_t num)
{
// Take a shortcut and just use 32 bit operations if the upper word is all
// clear. This will cause a slight off by one issue for numbers close to 2^32,
// but it probably isn't going to matter (and gives us a big performance win).
if ((num >> 32) == 0) {
return Sqrt32((uint32_t)num);
}
uint64_t res = 0;
int max_bit_number = 64 - MostSignificantBit64(num);
max_bit_number |= 1;
uint64_t bit = 1ULL << (63 - max_bit_number);
int iterations = (63 - max_bit_number) / 2 + 1;
while (iterations--) {
if (num >= res + bit) {
num -= res + bit;
res = (res >> 1U) + bit;
} else {
res >>= 1U;
}
bit >>= 2U;
}
// Do rounding - if we have the bits.
if (num > res && res != 0xFFFFFFFFLL) {
++res;
}
return res;
}
uint32_t *FilterbankSqrt(struct FilterbankState *state, int scale_down_shift)
{
const int num_channels = state->num_channels;
const uint64_t *work = state->work + 1;
// Reuse the work buffer since we're fine clobbering it at this point to hold
// the output.
uint32_t *output = (uint32_t *)state->work;
int i;
for (i = 0; i < num_channels; ++i) {
*output++ = Sqrt64(*work++) >> scale_down_shift;
}
return (uint32_t *)state->work;
}
void FilterbankReset(struct FilterbankState *state)
{
memset(state->work, 0, (state->num_channels + 1) * sizeof(*state->work));
}

View File

@ -0,0 +1,63 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
#include <stdint.h>
#include <stdlib.h>
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
#define kFilterbankBits 12
#ifdef __cplusplus
extern "C" {
#endif
struct FilterbankState {
int num_channels;
int start_index;
int end_index;
int16_t *channel_frequency_starts;
int16_t *channel_weight_starts;
int16_t *channel_widths;
int16_t *weights;
int16_t *unweights;
uint64_t *work;
};
// Converts the relevant complex values of an FFT output into energy (the
// square magnitude).
void FilterbankConvertFftComplexToEnergy(struct FilterbankState *state,
struct complex_int16_t *fft_output,
int32_t *energy);
// Computes the mel-scale filterbank on the given energy array. Output is cached
// internally - to fetch it, you need to call FilterbankSqrt.
void FilterbankAccumulateChannels(struct FilterbankState *state,
const int32_t *energy);
// Applies an integer square root to the 64 bit intermediate values of the
// filterbank, and returns a pointer to them. Memory will be invalidated the
// next time FilterbankAccumulateChannels is called.
uint32_t *FilterbankSqrt(struct FilterbankState *state, int scale_down_shift);
void FilterbankReset(struct FilterbankState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_

View File

@ -0,0 +1,70 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_io.h"
static void PrintArray(FILE *fp, const char *name, const int16_t *values,
size_t size)
{
fprintf(fp, "static int16_t filterbank_%s[] = {", name);
int i;
for (i = 0; i < size; ++i) {
fprintf(fp, "%d", values[i]);
if (i < size - 1) {
fprintf(fp, ", ");
}
}
fprintf(fp, "};\n");
}
void FilterbankWriteMemmapPreamble(FILE *fp,
const struct FilterbankState *state)
{
const int num_channels_plus_1 = state->num_channels + 1;
PrintArray(fp, "channel_frequency_starts", state->channel_frequency_starts,
num_channels_plus_1);
PrintArray(fp, "channel_weight_starts", state->channel_weight_starts,
num_channels_plus_1);
PrintArray(fp, "channel_widths", state->channel_widths, num_channels_plus_1);
int num_weights = 0;
int i;
for (i = 0; i < num_channels_plus_1; ++i) {
num_weights += state->channel_widths[i];
}
PrintArray(fp, "weights", state->weights, num_weights);
PrintArray(fp, "unweights", state->unweights, num_weights);
fprintf(fp, "static uint64_t filterbank_work[%d];\n", num_channels_plus_1);
fprintf(fp, "\n");
}
void FilterbankWriteMemmap(FILE *fp, const struct FilterbankState *state,
const char *variable)
{
fprintf(fp, "%s->num_channels = %d;\n", variable, state->num_channels);
fprintf(fp, "%s->start_index = %d;\n", variable, state->start_index);
fprintf(fp, "%s->end_index = %d;\n", variable, state->end_index);
fprintf(
fp,
"%s->channel_frequency_starts = filterbank_channel_frequency_starts;\n",
variable);
fprintf(fp, "%s->channel_weight_starts = filterbank_channel_weight_starts;\n",
variable);
fprintf(fp, "%s->channel_widths = filterbank_channel_widths;\n", variable);
fprintf(fp, "%s->weights = filterbank_weights;\n", variable);
fprintf(fp, "%s->unweights = filterbank_unweights;\n", variable);
fprintf(fp, "%s->work = filterbank_work;\n", variable);
}

View File

@ -0,0 +1,35 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_IO_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_IO_H_
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
#ifdef __cplusplus
extern "C" {
#endif
void FilterbankWriteMemmapPreamble(FILE *fp,
const struct FilterbankState *state);
void FilterbankWriteMemmap(FILE *fp, const struct FilterbankState *state,
const char *variable);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_IO_H_

View File

@ -0,0 +1,229 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
#include <cstring>
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int kSampleRate = 1000;
const int kSpectrumSize = 17;
const int kStartIndex = 1;
const int kEndIndex = 15;
const int32_t kEnergy[] = { -1, 181, 400, 181, 625, 28322,
786769, 18000000, 40972801, 18000000, 784996, 28085,
625, 181, 361, -1, -1 };
const uint64_t kWork[] = { 1835887, 61162970173, 258694800000 };
const int kScaleShift = 0;
// Test filterbank generation using scaled-down defaults.
class FilterbankTestConfig {
public:
FilterbankTestConfig()
{
config_.num_channels = 2;
config_.lower_band_limit = 8.0;
config_.upper_band_limit = 450.0;
}
struct FilterbankConfig config_;
};
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(FilterbankTest_CheckStartIndex)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
TF_LITE_MICRO_EXPECT_EQ(state.start_index, kStartIndex);
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckEndIndex)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
TF_LITE_MICRO_EXPECT_EQ(state.end_index, kEndIndex);
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckChannelFrequencyStarts)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
const int16_t expected[] = { 0, 4, 8 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels + 1,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i <= state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.channel_frequency_starts[i], expected[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckChannelWeightStarts)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
const int16_t expected[] = { 0, 8, 16 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels + 1,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i <= state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.channel_weight_starts[i], expected[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckChannelWidths)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
const int16_t expected[] = { 8, 8, 8 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels + 1,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i <= state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.channel_widths[i], expected[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckWeights)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
const int16_t expected[] = { 0, 3277, 2217, 1200, 222, 0, 0, 0,
0, 3376, 2468, 1591, 744, 0, 0, 0,
0, 4020, 3226, 2456, 1708, 983, 277, 0 };
TF_LITE_MICRO_EXPECT_EQ(state.channel_weight_starts[state.num_channels] +
state.channel_widths[state.num_channels],
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.weights[i], expected[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckUnweights)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
const int16_t expected[] = { 0, 819, 1879, 2896, 3874, 0, 0, 0,
0, 720, 1628, 2505, 3352, 0, 0, 0,
0, 76, 870, 1640, 2388, 3113, 3819, 0 };
TF_LITE_MICRO_EXPECT_EQ(state.channel_weight_starts[state.num_channels] +
state.channel_widths[state.num_channels],
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.unweights[i], expected[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckConvertFftComplexToEnergy)
{
struct FilterbankState state;
state.start_index = kStartIndex;
state.end_index = kEndIndex;
struct complex_int16_t fake_fft[] = {
{ 0, 0 }, { -10, 9 }, { -20, 0 }, { -9, -10 }, { 0, 25 }, { -119, 119 }, { -887, 0 }, { 3000, 3000 }, { 0, -6401 }, { -3000, 3000 }, { 886, 0 }, { 118, 119 }, { 0, 25 }, { 9, -10 }, { 19, 0 }, { 9, 9 }, { 0, 0 }
};
int32_t *energy = reinterpret_cast<int32_t *>(fake_fft);
FilterbankConvertFftComplexToEnergy(&state, fake_fft, energy);
int i;
for (i = state.start_index; i < state.end_index; ++i) {
TF_LITE_MICRO_EXPECT_EQ(energy[i], kEnergy[i]);
}
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckAccumulateChannels)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
FilterbankAccumulateChannels(&state, kEnergy);
TF_LITE_MICRO_EXPECT_EQ(state.num_channels + 1,
sizeof(kWork) / sizeof(kWork[0]));
int i;
for (i = 0; i <= state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.work[i], kWork[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FilterbankTest_CheckSqrt)
{
FilterbankTestConfig config;
struct FilterbankState state;
TF_LITE_MICRO_EXPECT(FilterbankPopulateState(&config.config_, &state,
kSampleRate, kSpectrumSize));
std::memcpy(state.work, kWork, sizeof(kWork));
uint32_t *scaled_filterbank = FilterbankSqrt(&state, kScaleShift);
const uint32_t expected[] = { 247311, 508620 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(scaled_filterbank[i], expected[i]);
}
FilterbankFreeStateContents(&state);
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,226 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#define kFilterbankIndexAlignment 4
#define kFilterbankChannelBlockSize 4
void FilterbankFillConfigWithDefaults(struct FilterbankConfig *config)
{
config->num_channels = 32;
config->lower_band_limit = 125.0f;
config->upper_band_limit = 7500.0f;
config->output_scale_shift = 7;
}
static float FreqToMel(float freq)
{
return 1127.0 * log1p(freq / 700.0);
}
static void CalculateCenterFrequencies(const int num_channels,
const float lower_frequency_limit,
const float upper_frequency_limit,
float *center_frequencies)
{
assert(lower_frequency_limit >= 0.0f);
assert(upper_frequency_limit > lower_frequency_limit);
const float mel_low = FreqToMel(lower_frequency_limit);
const float mel_hi = FreqToMel(upper_frequency_limit);
const float mel_span = mel_hi - mel_low;
const float mel_spacing = mel_span / ((float)num_channels);
int i;
for (i = 0; i < num_channels; ++i) {
center_frequencies[i] = mel_low + (mel_spacing * (i + 1));
}
}
static void QuantizeFilterbankWeights(const float float_weight, int16_t *weight,
int16_t *unweight)
{
*weight = floor(float_weight * (1 << kFilterbankBits) + 0.5);
*unweight = floor((1.0 - float_weight) * (1 << kFilterbankBits) + 0.5);
}
int FilterbankPopulateState(const struct FilterbankConfig *config,
struct FilterbankState *state, int sample_rate,
int spectrum_size)
{
state->num_channels = config->num_channels;
const int num_channels_plus_1 = config->num_channels + 1;
// How should we align things to index counts given the byte alignment?
const int index_alignment =
(kFilterbankIndexAlignment < sizeof(int16_t) ? 1 : kFilterbankIndexAlignment / sizeof(int16_t));
state->channel_frequency_starts =
malloc(num_channels_plus_1 * sizeof(*state->channel_frequency_starts));
state->channel_weight_starts =
malloc(num_channels_plus_1 * sizeof(*state->channel_weight_starts));
state->channel_widths =
malloc(num_channels_plus_1 * sizeof(*state->channel_widths));
state->work = malloc(num_channels_plus_1 * sizeof(*state->work));
float *center_mel_freqs =
malloc(num_channels_plus_1 * sizeof(*center_mel_freqs));
int16_t *actual_channel_starts =
malloc(num_channels_plus_1 * sizeof(*actual_channel_starts));
int16_t *actual_channel_widths =
malloc(num_channels_plus_1 * sizeof(*actual_channel_widths));
if (state->channel_frequency_starts == NULL ||
state->channel_weight_starts == NULL || state->channel_widths == NULL ||
center_mel_freqs == NULL || actual_channel_starts == NULL ||
actual_channel_widths == NULL) {
free(center_mel_freqs);
free(actual_channel_starts);
free(actual_channel_widths);
fprintf(stderr, "Failed to allocate channel buffers\n");
return 0;
}
CalculateCenterFrequencies(num_channels_plus_1, config->lower_band_limit,
config->upper_band_limit, center_mel_freqs);
// Always exclude DC.
const float hz_per_sbin = 0.5 * sample_rate / ((float)spectrum_size - 1);
state->start_index = 1.5 + config->lower_band_limit / hz_per_sbin;
state->end_index = 0; // Initialized to zero here, but actually set below.
// For each channel, we need to figure out what frequencies belong to it, and
// how much padding we need to add so that we can efficiently multiply the
// weights and unweights for accumulation. To simplify the multiplication
// logic, all channels will have some multiplication to do (even if there are
// no frequencies that accumulate to that channel) - they will be directed to
// a set of zero weights.
int chan_freq_index_start = state->start_index;
int weight_index_start = 0;
int needs_zeros = 0;
int chan;
for (chan = 0; chan < num_channels_plus_1; ++chan) {
// Keep jumping frequencies until we overshoot the bound on this channel.
int freq_index = chan_freq_index_start;
while (FreqToMel((freq_index)*hz_per_sbin) <= center_mel_freqs[chan]) {
++freq_index;
}
const int width = freq_index - chan_freq_index_start;
actual_channel_starts[chan] = chan_freq_index_start;
actual_channel_widths[chan] = width;
if (width == 0) {
// This channel doesn't actually get anything from the frequencies, it's
// always zero. We need then to insert some 'zero' weights into the
// output, and just redirect this channel to do a single multiplication at
// this point. For simplicity, the zeros are placed at the beginning of
// the weights arrays, so we have to go and update all the other
// weight_starts to reflect this shift (but only once).
state->channel_frequency_starts[chan] = 0;
state->channel_weight_starts[chan] = 0;
state->channel_widths[chan] = kFilterbankChannelBlockSize;
if (!needs_zeros) {
needs_zeros = 1;
int j;
for (j = 0; j < chan; ++j) {
state->channel_weight_starts[j] += kFilterbankChannelBlockSize;
}
weight_index_start += kFilterbankChannelBlockSize;
}
} else {
// How far back do we need to go to ensure that we have the proper
// alignment?
const int aligned_start =
(chan_freq_index_start / index_alignment) * index_alignment;
const int aligned_width = (chan_freq_index_start - aligned_start + width);
const int padded_width =
(((aligned_width - 1) / kFilterbankChannelBlockSize) + 1) *
kFilterbankChannelBlockSize;
state->channel_frequency_starts[chan] = aligned_start;
state->channel_weight_starts[chan] = weight_index_start;
state->channel_widths[chan] = padded_width;
weight_index_start += padded_width;
}
chan_freq_index_start = freq_index;
}
// Allocate the two arrays to store the weights - weight_index_start contains
// the index of what would be the next set of weights that we would need to
// add, so that's how many weights we need to allocate.
state->weights = calloc(weight_index_start, sizeof(*state->weights));
state->unweights = calloc(weight_index_start, sizeof(*state->unweights));
// If the alloc failed, we also need to nuke the arrays.
if (state->weights == NULL || state->unweights == NULL) {
free(center_mel_freqs);
free(actual_channel_starts);
free(actual_channel_widths);
fprintf(stderr, "Failed to allocate weights or unweights\n");
return 0;
}
// Next pass, compute all the weights. Since everything has been memset to
// zero, we only need to fill in the weights that correspond to some frequency
// for a channel.
const float mel_low = FreqToMel(config->lower_band_limit);
for (chan = 0; chan < num_channels_plus_1; ++chan) {
int frequency = actual_channel_starts[chan];
const int num_frequencies = actual_channel_widths[chan];
const int frequency_offset =
frequency - state->channel_frequency_starts[chan];
const int weight_start = state->channel_weight_starts[chan];
const float denom_val = (chan == 0) ? mel_low : center_mel_freqs[chan - 1];
int j;
for (j = 0; j < num_frequencies; ++j, ++frequency) {
const float weight =
(center_mel_freqs[chan] - FreqToMel(frequency * hz_per_sbin)) /
(center_mel_freqs[chan] - denom_val);
// Make the float into an integer for the weights (and unweights).
const int weight_index = weight_start + frequency_offset + j;
QuantizeFilterbankWeights(weight, state->weights + weight_index,
state->unweights + weight_index);
}
if (frequency > state->end_index) {
state->end_index = frequency;
}
}
free(center_mel_freqs);
free(actual_channel_starts);
free(actual_channel_widths);
if (state->end_index >= spectrum_size) {
fprintf(stderr, "Filterbank end_index is above spectrum size.\n");
return 0;
}
return 1;
}
void FilterbankFreeStateContents(struct FilterbankState *state)
{
free(state->channel_frequency_starts);
free(state->channel_weight_starts);
free(state->channel_widths);
free(state->weights);
free(state->unweights);
free(state->work);
}

View File

@ -0,0 +1,50 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
#ifdef __cplusplus
extern "C" {
#endif
struct FilterbankConfig {
// number of frequency channel buckets for filterbank
int num_channels;
// maximum frequency to include
float upper_band_limit;
// minimum frequency to include
float lower_band_limit;
// unused
int output_scale_shift;
};
// Fills the frontendConfig with "sane" defaults.
void FilterbankFillConfigWithDefaults(struct FilterbankConfig *config);
// Allocates any buffers.
int FilterbankPopulateState(const struct FilterbankConfig *config,
struct FilterbankState *state, int sample_rate,
int spectrum_size);
// Frees any allocated buffers.
void FilterbankFreeStateContents(struct FilterbankState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_

View File

@ -0,0 +1,74 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
struct FrontendOutput FrontendProcessSamples(struct FrontendState *state,
const int16_t *samples,
size_t num_samples,
size_t *num_samples_read)
{
struct FrontendOutput output;
output.values = NULL;
output.size = 0;
// Try to apply the window - if it fails, return and wait for more data.
if (!WindowProcessSamples(&state->window, samples, num_samples,
num_samples_read)) {
return output;
}
// Apply the FFT to the window's output (and scale it so that the fixed point
// FFT can have as much resolution as possible).
int input_shift =
15 - MostSignificantBit32(state->window.max_abs_output_value);
FftCompute(&state->fft, state->window.output, input_shift);
// We can re-ruse the fft's output buffer to hold the energy.
int32_t *energy = (int32_t *)state->fft.output;
FilterbankConvertFftComplexToEnergy(&state->filterbank, state->fft.output,
energy);
FilterbankAccumulateChannels(&state->filterbank, energy);
uint32_t *scaled_filterbank = FilterbankSqrt(&state->filterbank, input_shift);
// Apply noise reduction.
NoiseReductionApply(&state->noise_reduction, scaled_filterbank);
if (state->pcan_gain_control.enable_pcan) {
PcanGainControlApply(&state->pcan_gain_control, scaled_filterbank);
}
// Apply the log and scale.
int correction_bits =
MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2);
uint16_t *logged_filterbank =
LogScaleApply(&state->log_scale, scaled_filterbank,
state->filterbank.num_channels, correction_bits);
output.size = state->filterbank.num_channels;
output.values = logged_filterbank;
return output;
}
void FrontendReset(struct FrontendState *state)
{
WindowReset(&state->window);
FftReset(&state->fft);
FilterbankReset(&state->filterbank);
NoiseReductionReset(&state->noise_reduction);
}

View File

@ -0,0 +1,64 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
#include <stdint.h>
#include <stdlib.h>
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
#ifdef __cplusplus
extern "C" {
#endif
struct FrontendState {
struct WindowState window;
struct FftState fft;
struct FilterbankState filterbank;
struct NoiseReductionState noise_reduction;
struct PcanGainControlState pcan_gain_control;
struct LogScaleState log_scale;
};
struct FrontendOutput {
const uint16_t *values;
size_t size;
};
// Main entry point to processing frontend samples. Updates num_samples_read to
// contain the number of samples that have been consumed from the input array.
// Returns a struct containing the generated output. If not enough samples were
// added to generate a feature vector, the returned size will be 0 and the
// values pointer will be NULL. Note that the output pointer will be invalidated
// as soon as FrontendProcessSamples is called again, so copy the contents
// elsewhere if you need to use them later.
struct FrontendOutput FrontendProcessSamples(struct FrontendState *state,
const int16_t *samples,
size_t num_samples,
size_t *num_samples_read);
void FrontendReset(struct FrontendState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_

View File

@ -0,0 +1,70 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_io.h"
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/fft_io.h"
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_io.h"
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_io.h"
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_io.h"
#include "tensorflow/lite/experimental/microfrontend/lib/window_io.h"
int WriteFrontendStateMemmap(const char *header, const char *source,
const struct FrontendState *state)
{
// Write a header that just has our init function.
FILE *fp = fopen(header, "w");
if (!fp) {
fprintf(stderr, "Failed to open header '%s' for write\n", header);
return 0;
}
fprintf(fp, "#ifndef FRONTEND_STATE_MEMMAP_H_\n");
fprintf(fp, "#define FRONTEND_STATE_MEMMAP_H_\n");
fprintf(fp, "\n");
fprintf(fp, "#include \"frontend.h\"\n");
fprintf(fp, "\n");
fprintf(fp, "struct FrontendState* GetFrontendStateMemmap();\n");
fprintf(fp, "\n");
fprintf(fp, "#endif // FRONTEND_STATE_MEMMAP_H_\n");
fclose(fp);
// Write out the source file that actually has everything in it.
fp = fopen(source, "w");
if (!fp) {
fprintf(stderr, "Failed to open source '%s' for write\n", source);
return 0;
}
fprintf(fp, "#include \"%s\"\n", header);
fprintf(fp, "\n");
WindowWriteMemmapPreamble(fp, &state->window);
FftWriteMemmapPreamble(fp, &state->fft);
FilterbankWriteMemmapPreamble(fp, &state->filterbank);
NoiseReductionWriteMemmapPreamble(fp, &state->noise_reduction);
fprintf(fp, "static struct FrontendState state;\n");
fprintf(fp, "struct FrontendState* GetFrontendStateMemmap() {\n");
WindowWriteMemmap(fp, &state->window, " (&state.window)");
FftWriteMemmap(fp, &state->fft, " (&state.fft)");
FilterbankWriteMemmap(fp, &state->filterbank, " (&state.filterbank)");
NoiseReductionWriteMemmap(fp, &state->noise_reduction,
" (&state.noise_reduction)");
LogScaleWriteMemmap(fp, &state->log_scale, " (&state.log_scale)");
fprintf(fp, " FftInit(&state.fft);\n");
fprintf(fp, " FrontendReset(&state);\n");
fprintf(fp, " return &state;\n");
fprintf(fp, "}\n");
fclose(fp);
return 1;
}

View File

@ -0,0 +1,31 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_IO_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_IO_H_
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
#ifdef __cplusplus
extern "C" {
#endif
int WriteFrontendStateMemmap(const char *header, const char *source,
const struct FrontendState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_IO_H_

View File

@ -0,0 +1,72 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
int main(int argc, char **argv)
{
struct FrontendConfig frontend_config;
FrontendFillConfigWithDefaults(&frontend_config);
char *filename = argv[1];
int sample_rate = 16000;
struct FrontendState frontend_state;
if (!FrontendPopulateState(&frontend_config, &frontend_state, sample_rate)) {
fprintf(stderr, "Failed to populate frontend state\n");
FrontendFreeStateContents(&frontend_state);
return 1;
}
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open %s for read\n", filename);
return 1;
}
fseek(fp, 0L, SEEK_END);
size_t audio_file_size = ftell(fp) / sizeof(int16_t);
fseek(fp, 0L, SEEK_SET);
int16_t *audio_data = malloc(audio_file_size * sizeof(int16_t));
int16_t *original_audio_data = audio_data;
if (audio_file_size !=
fread(audio_data, sizeof(int16_t), audio_file_size, fp)) {
fprintf(stderr, "Failed to read in all audio data\n");
fclose(fp);
return 1;
}
while (audio_file_size > 0) {
size_t num_samples_read;
struct FrontendOutput output = FrontendProcessSamples(
&frontend_state, audio_data, audio_file_size, &num_samples_read);
audio_data += num_samples_read;
audio_file_size -= num_samples_read;
if (output.values != NULL) {
int i;
for (i = 0; i < output.size; ++i) {
printf("%d ", output.values[i]);
}
printf("\n");
}
}
FrontendFreeStateContents(&frontend_state);
free(original_audio_data);
fclose(fp);
return 0;
}

View File

@ -0,0 +1,49 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_io.h"
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr,
"%s requires exactly two parameters - the names of the header and "
"source files to save\n",
argv[0]);
return 1;
}
struct FrontendConfig frontend_config;
FrontendFillConfigWithDefaults(&frontend_config);
int sample_rate = 16000;
struct FrontendState frontend_state;
if (!FrontendPopulateState(&frontend_config, &frontend_state, sample_rate)) {
fprintf(stderr, "Failed to populate frontend state\n");
FrontendFreeStateContents(&frontend_state);
return 1;
}
if (!WriteFrontendStateMemmap(argv[1], argv[2], &frontend_state)) {
fprintf(stderr, "Failed to write memmap\n");
FrontendFreeStateContents(&frontend_state);
return 1;
}
FrontendFreeStateContents(&frontend_state);
return 0;
}

View File

@ -0,0 +1,61 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stdio.h>
#include "memmap.h"
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
int main(int argc, char **argv)
{
struct FrontendState *frontend_state = GetFrontendStateMemmap();
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open %s for read\n", filename);
return 1;
}
fseek(fp, 0L, SEEK_END);
size_t audio_file_size = ftell(fp) / sizeof(int16_t);
fseek(fp, 0L, SEEK_SET);
int16_t *audio_data = malloc(audio_file_size * sizeof(int16_t));
int16_t *original_audio_data = audio_data;
if (audio_file_size !=
fread(audio_data, sizeof(int16_t), audio_file_size, fp)) {
fprintf(stderr, "Failed to read in all audio data\n");
fclose(fp);
return 1;
}
while (audio_file_size > 0) {
size_t num_samples_read;
struct FrontendOutput output = FrontendProcessSamples(
frontend_state, audio_data, audio_file_size, &num_samples_read);
audio_data += num_samples_read;
audio_file_size -= num_samples_read;
if (output.values != NULL) {
int i;
for (i = 0; i < output.size; ++i) {
printf("%d ", output.values[i]);
}
printf("\n");
}
}
free(original_audio_data);
fclose(fp);
return 0;
}

View File

@ -0,0 +1,136 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int kSampleRate = 1000;
const int kWindowSamples = 25;
const int kStepSamples = 10;
const int16_t kFakeAudioData[] = {
0, 32767, 0, -32768, 0, 32767, 0, -32768, 0, 32767, 0, -32768,
0, 32767, 0, -32768, 0, 32767, 0, -32768, 0, 32767, 0, -32768,
0, 32767, 0, -32768, 0, 32767, 0, -32768, 0, 32767, 0, -32768
};
// Test end-to-end frontend behaviors.
class FrontendTestConfig {
public:
FrontendTestConfig()
{
config_.window.size_ms = 25;
config_.window.step_size_ms = 10;
config_.noise_reduction.smoothing_bits = 10;
config_.filterbank.num_channels = 2;
config_.filterbank.lower_band_limit = 8.0;
config_.filterbank.upper_band_limit = 450.0;
config_.noise_reduction.smoothing_bits = 10;
config_.noise_reduction.even_smoothing = 0.025;
config_.noise_reduction.odd_smoothing = 0.06;
config_.noise_reduction.min_signal_remaining = 0.05;
config_.pcan_gain_control.enable_pcan = true;
config_.pcan_gain_control.strength = 0.95;
config_.pcan_gain_control.offset = 80.0;
config_.pcan_gain_control.gain_bits = 21;
config_.log_scale.enable_log = true;
config_.log_scale.scale_shift = 6;
}
struct FrontendConfig config_;
};
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(FrontendTest_CheckOutputValues)
{
FrontendTestConfig config;
struct FrontendState state;
TF_LITE_MICRO_EXPECT(
FrontendPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
struct FrontendOutput output = FrontendProcessSamples(
&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]), &num_samples_read);
const uint16_t expected[] = { 479, 425 };
TF_LITE_MICRO_EXPECT_EQ(output.size, sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < output.size; ++i) {
TF_LITE_MICRO_EXPECT_EQ(output.values[i], expected[i]);
}
FrontendFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FrontendTest_CheckConsecutiveWindow)
{
FrontendTestConfig config;
struct FrontendState state;
TF_LITE_MICRO_EXPECT(
FrontendPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
FrontendProcessSamples(&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]),
&num_samples_read);
struct FrontendOutput output = FrontendProcessSamples(
&state, kFakeAudioData + kWindowSamples,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) - kWindowSamples,
&num_samples_read);
const int16_t expected[] = { 436, 378 };
TF_LITE_MICRO_EXPECT_EQ(output.size, sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < output.size; ++i) {
TF_LITE_MICRO_EXPECT_EQ(output.values[i], expected[i]);
}
FrontendFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(FrontendTest_CheckNotEnoughSamples)
{
FrontendTestConfig config;
struct FrontendState state;
TF_LITE_MICRO_EXPECT(
FrontendPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
FrontendProcessSamples(&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]),
&num_samples_read);
FrontendProcessSamples(
&state, kFakeAudioData + kWindowSamples,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) - kWindowSamples,
&num_samples_read);
struct FrontendOutput output = FrontendProcessSamples(
&state, kFakeAudioData + kWindowSamples + kStepSamples,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) - kWindowSamples -
kStepSamples,
&num_samples_read);
TF_LITE_MICRO_EXPECT_EQ(output.size, 0);
TF_LITE_MICRO_EXPECT(output.values == nullptr);
FrontendFreeStateContents(&state);
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,88 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
#include <stdio.h>
#include <string.h>
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
void FrontendFillConfigWithDefaults(struct FrontendConfig *config)
{
WindowFillConfigWithDefaults(&config->window);
FilterbankFillConfigWithDefaults(&config->filterbank);
NoiseReductionFillConfigWithDefaults(&config->noise_reduction);
PcanGainControlFillConfigWithDefaults(&config->pcan_gain_control);
LogScaleFillConfigWithDefaults(&config->log_scale);
}
int FrontendPopulateState(const struct FrontendConfig *config,
struct FrontendState *state, int sample_rate)
{
memset(state, 0, sizeof(*state));
if (!WindowPopulateState(&config->window, &state->window, sample_rate)) {
fprintf(stderr, "Failed to populate window state\n");
return 0;
}
if (!FftPopulateState(&state->fft, state->window.size)) {
fprintf(stderr, "Failed to populate fft state\n");
return 0;
}
FftInit(&state->fft);
if (!FilterbankPopulateState(&config->filterbank, &state->filterbank,
sample_rate, state->fft.fft_size / 2 + 1)) {
fprintf(stderr, "Failed to populate filterbank state\n");
return 0;
}
if (!NoiseReductionPopulateState(&config->noise_reduction,
&state->noise_reduction,
state->filterbank.num_channels)) {
fprintf(stderr, "Failed to populate noise reduction state\n");
return 0;
}
int input_correction_bits =
MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2);
if (!PcanGainControlPopulateState(
&config->pcan_gain_control, &state->pcan_gain_control,
state->noise_reduction.estimate, state->filterbank.num_channels,
state->noise_reduction.smoothing_bits, input_correction_bits)) {
fprintf(stderr, "Failed to populate pcan gain control state\n");
return 0;
}
if (!LogScalePopulateState(&config->log_scale, &state->log_scale)) {
fprintf(stderr, "Failed to populate log scale state\n");
return 0;
}
FrontendReset(state);
// All good, return a true value.
return 1;
}
void FrontendFreeStateContents(struct FrontendState *state)
{
WindowFreeStateContents(&state->window);
FftFreeStateContents(&state->fft);
FilterbankFreeStateContents(&state->filterbank);
NoiseReductionFreeStateContents(&state->noise_reduction);
PcanGainControlFreeStateContents(&state->pcan_gain_control);
}

View File

@ -0,0 +1,52 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
#ifdef __cplusplus
extern "C" {
#endif
struct FrontendConfig {
struct WindowConfig window;
struct FilterbankConfig filterbank;
struct NoiseReductionConfig noise_reduction;
struct PcanGainControlConfig pcan_gain_control;
struct LogScaleConfig log_scale;
};
// Fills the frontendConfig with "sane" defaults.
void FrontendFillConfigWithDefaults(struct FrontendConfig *config);
// Allocates any buffers.
int FrontendPopulateState(const struct FrontendConfig *config,
struct FrontendState *state, int sample_rate);
// Frees any allocated buffers.
void FrontendFreeStateContents(struct FrontendState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_

View File

@ -0,0 +1,30 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/log_lut.h"
const uint16_t kLogLut[]
#ifndef _MSC_VER
__attribute__((aligned(4)))
#endif // _MSV_VER
= { 0, 224, 442, 654, 861, 1063, 1259, 1450, 1636, 1817, 1992, 2163,
2329, 2490, 2646, 2797, 2944, 3087, 3224, 3358, 3487, 3611, 3732, 3848,
3960, 4068, 4172, 4272, 4368, 4460, 4549, 4633, 4714, 4791, 4864, 4934,
5001, 5063, 5123, 5178, 5231, 5280, 5326, 5368, 5408, 5444, 5477, 5507,
5533, 5557, 5578, 5595, 5610, 5622, 5631, 5637, 5640, 5641, 5638, 5633,
5626, 5615, 5602, 5586, 5568, 5547, 5524, 5498, 5470, 5439, 5406, 5370,
5332, 5291, 5249, 5203, 5156, 5106, 5054, 5000, 4944, 4885, 4825, 4762,
4697, 4630, 4561, 4490, 4416, 4341, 4264, 4184, 4103, 4020, 3935, 3848,
3759, 3668, 3575, 3481, 3384, 3286, 3186, 3084, 2981, 2875, 2768, 2659,
2549, 2437, 2323, 2207, 2090, 1971, 1851, 1729, 1605, 1480, 1353, 1224,
1094, 963, 830, 695, 559, 421, 282, 142, 0, 0 };

View File

@ -0,0 +1,40 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Number of segments in the log lookup table. The table will be kLogSegments+1
// in length (with some padding).
#define kLogSegments 128
#define kLogSegmentsLog2 7
// Scale used by lookup table.
#define kLogScale 65536
#define kLogScaleLog2 16
#define kLogCoeff 45426
extern const uint16_t kLogLut[];
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_

View File

@ -0,0 +1,86 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
#include "tensorflow/lite/experimental/microfrontend/lib/log_lut.h"
#define kuint16max 0x0000FFFF
// The following functions implement integer logarithms of various sizes. The
// approximation is calculated according to method described in
// www.inti.gob.ar/electronicaeinformatica/instrumentacion/utic/
// publicaciones/SPL2007/Log10-spl07.pdf
// It first calculates log2 of the input and then converts it to natural
// logarithm.
static uint32_t Log2FractionPart(const uint32_t x, const uint32_t log2x)
{
// Part 1
int32_t frac = x - (1LL << log2x);
if (log2x < kLogScaleLog2) {
frac <<= kLogScaleLog2 - log2x;
} else {
frac >>= log2x - kLogScaleLog2;
}
// Part 2
const uint32_t base_seg = frac >> (kLogScaleLog2 - kLogSegmentsLog2);
const uint32_t seg_unit =
(((uint32_t)1) << kLogScaleLog2) >> kLogSegmentsLog2;
const int32_t c0 = kLogLut[base_seg];
const int32_t c1 = kLogLut[base_seg + 1];
const int32_t seg_base = seg_unit * base_seg;
const int32_t rel_pos = ((c1 - c0) * (frac - seg_base)) >> kLogScaleLog2;
return frac + c0 + rel_pos;
}
static uint32_t Log(const uint32_t x, const uint32_t scale_shift)
{
const uint32_t integer = MostSignificantBit32(x) - 1;
const uint32_t fraction = Log2FractionPart(x, integer);
const uint32_t log2 = (integer << kLogScaleLog2) + fraction;
const uint32_t round = kLogScale / 2;
const uint32_t loge = (((uint64_t)kLogCoeff) * log2 + round) >> kLogScaleLog2;
// Finally scale to our output scale
const uint32_t loge_scaled = ((loge << scale_shift) + round) >> kLogScaleLog2;
return loge_scaled;
}
uint16_t *LogScaleApply(struct LogScaleState *state, uint32_t *signal,
int signal_size, int correction_bits)
{
const int scale_shift = state->scale_shift;
uint16_t *output = (uint16_t *)signal;
uint16_t *ret = output;
int i;
for (i = 0; i < signal_size; ++i) {
uint32_t value = *signal++;
if (state->enable_log) {
if (correction_bits < 0) {
value >>= -correction_bits;
} else {
value <<= correction_bits;
}
if (value > 1) {
value = Log(value, scale_shift);
} else {
value = 0;
}
}
*output++ = (value < kuint16max) ? value : kuint16max;
}
return ret;
}

View File

@ -0,0 +1,39 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
struct LogScaleState {
int enable_log;
int scale_shift;
};
// Applies a fixed point logarithm to the signal and converts it to 16 bit. Note
// that the signal array will be modified.
uint16_t *LogScaleApply(struct LogScaleState *state, uint32_t *signal,
int signal_size, int correction_bits);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_

View File

@ -0,0 +1,22 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_io.h"
void LogScaleWriteMemmap(FILE *fp, const struct LogScaleState *state,
const char *variable)
{
fprintf(fp, "%s->enable_log = %d;\n", variable, state->enable_log);
fprintf(fp, "%s->scale_shift = %d;\n", variable, state->scale_shift);
}

View File

@ -0,0 +1,33 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_IO_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_IO_H_
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
#ifdef __cplusplus
extern "C" {
#endif
void LogScaleWriteMemmap(FILE *fp, const struct LogScaleState *state,
const char *variable);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_IO_H_

View File

@ -0,0 +1,65 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int kScaleShift = 6;
const int kCorrectionBits = -1;
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(LogScaleTest_CheckOutputValues)
{
struct LogScaleState state;
state.enable_log = true;
state.scale_shift = kScaleShift;
uint32_t fake_signal[] = { 3578, 1533 };
uint16_t *output = LogScaleApply(&state, fake_signal,
sizeof(fake_signal) / sizeof(fake_signal[0]),
kCorrectionBits);
const uint16_t expected[] = { 479, 425 };
int i;
for (i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) {
TF_LITE_MICRO_EXPECT_EQ(output[i], expected[i]);
}
}
TF_LITE_MICRO_TEST(LogScaleTest_CheckOutputValuesNoLog)
{
struct LogScaleState state;
state.enable_log = false;
state.scale_shift = kScaleShift;
uint32_t fake_signal[] = { 85964, 45998 };
uint16_t *output = LogScaleApply(&state, fake_signal,
sizeof(fake_signal) / sizeof(fake_signal[0]),
kCorrectionBits);
const uint16_t expected[] = { 65535, 45998 };
int i;
for (i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) {
TF_LITE_MICRO_EXPECT_EQ(output[i], expected[i]);
}
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,29 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
void LogScaleFillConfigWithDefaults(struct LogScaleConfig *config)
{
config->enable_log = 1;
config->scale_shift = 6;
}
int LogScalePopulateState(const struct LogScaleConfig *config,
struct LogScaleState *state)
{
state->enable_log = config->enable_log;
state->scale_shift = config->scale_shift;
return 1;
}

View File

@ -0,0 +1,45 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
#include <stdint.h>
#include <stdlib.h>
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
#ifdef __cplusplus
extern "C" {
#endif
struct LogScaleConfig {
// set to false (0) to disable this module
int enable_log;
// scale results by 2^(scale_shift)
int scale_shift;
};
// Populates the LogScaleConfig with "sane" default values.
void LogScaleFillConfigWithDefaults(struct LogScaleConfig *config);
// Allocates any buffers.
int LogScalePopulateState(const struct LogScaleConfig *config,
struct LogScaleState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_

View File

@ -0,0 +1,53 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
#include <string.h>
void NoiseReductionApply(struct NoiseReductionState *state, uint32_t *signal)
{
int i;
for (i = 0; i < state->num_channels; ++i) {
const uint32_t smoothing =
((i & 1) == 0) ? state->even_smoothing : state->odd_smoothing;
const uint32_t one_minus_smoothing = (1 << kNoiseReductionBits) - smoothing;
// Update the estimate of the noise.
const uint32_t signal_scaled_up = signal[i] << state->smoothing_bits;
uint32_t estimate =
(((uint64_t)signal_scaled_up * smoothing) +
((uint64_t)state->estimate[i] * one_minus_smoothing)) >>
kNoiseReductionBits;
state->estimate[i] = estimate;
// Make sure that we can't get a negative value for the signal - estimate.
if (estimate > signal_scaled_up) {
estimate = signal_scaled_up;
}
const uint32_t floor =
((uint64_t)signal[i] * state->min_signal_remaining) >>
kNoiseReductionBits;
const uint32_t subtracted =
(signal_scaled_up - estimate) >> state->smoothing_bits;
const uint32_t output = subtracted > floor ? subtracted : floor;
signal[i] = output;
}
}
void NoiseReductionReset(struct NoiseReductionState *state)
{
memset(state->estimate, 0, sizeof(*state->estimate) * state->num_channels);
}

View File

@ -0,0 +1,46 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
#define kNoiseReductionBits 14
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
struct NoiseReductionState {
int smoothing_bits;
uint16_t even_smoothing;
uint16_t odd_smoothing;
uint16_t min_signal_remaining;
int num_channels;
uint32_t *estimate;
};
// Removes stationary noise from each channel of the signal using a low pass
// filter.
void NoiseReductionApply(struct NoiseReductionState *state, uint32_t *signal);
void NoiseReductionReset(struct NoiseReductionState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_

View File

@ -0,0 +1,36 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_io.h"
void NoiseReductionWriteMemmapPreamble(
FILE *fp, const struct NoiseReductionState *state)
{
fprintf(fp, "static uint32_t noise_reduction_estimate[%zu];\n",
state->num_channels);
fprintf(fp, "\n");
}
void NoiseReductionWriteMemmap(FILE *fp,
const struct NoiseReductionState *state,
const char *variable)
{
fprintf(fp, "%s->even_smoothing = %d;\n", variable, state->even_smoothing);
fprintf(fp, "%s->odd_smoothing = %d;\n", variable, state->odd_smoothing);
fprintf(fp, "%s->min_signal_remaining = %d;\n", variable,
state->min_signal_remaining);
fprintf(fp, "%s->num_channels = %d;\n", variable, state->num_channels);
fprintf(fp, "%s->estimate = noise_reduction_estimate;\n", variable);
}

View File

@ -0,0 +1,36 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_IO_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_IO_H_
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
#ifdef __cplusplus
extern "C" {
#endif
void NoiseReductionWriteMemmapPreamble(FILE *fp,
const struct NoiseReductionState *state);
void NoiseReductionWriteMemmap(FILE *fp,
const struct NoiseReductionState *state,
const char *variable);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_IO_H_

View File

@ -0,0 +1,84 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int kNumChannels = 2;
// Test noise reduction using default config values.
class NoiseReductionTestConfig {
public:
NoiseReductionTestConfig()
{
config_.smoothing_bits = 10;
config_.even_smoothing = 0.025;
config_.odd_smoothing = 0.06;
config_.min_signal_remaining = 0.05;
}
struct NoiseReductionConfig config_;
};
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(NoiseReductionTest_TestNoiseReductionEstimate)
{
NoiseReductionTestConfig config;
struct NoiseReductionState state;
TF_LITE_MICRO_EXPECT(
NoiseReductionPopulateState(&config.config_, &state, kNumChannels));
uint32_t signal[] = { 247311, 508620 };
NoiseReductionApply(&state, signal);
const uint32_t expected[] = { 6321887, 31248341 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.estimate[i], expected[i]);
}
NoiseReductionFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(NoiseReductionTest_TestNoiseReduction)
{
NoiseReductionTestConfig config;
struct NoiseReductionState state;
TF_LITE_MICRO_EXPECT(
NoiseReductionPopulateState(&config.config_, &state, kNumChannels));
uint32_t signal[] = { 247311, 508620 };
NoiseReductionApply(&state, signal);
const uint32_t expected[] = { 241137, 478104 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(signal[i], expected[i]);
}
NoiseReductionFreeStateContents(&state);
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,48 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
#include <stdio.h>
void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig *config)
{
config->smoothing_bits = 10;
config->even_smoothing = 0.025;
config->odd_smoothing = 0.06;
config->min_signal_remaining = 0.05;
}
int NoiseReductionPopulateState(const struct NoiseReductionConfig *config,
struct NoiseReductionState *state,
int num_channels)
{
state->smoothing_bits = config->smoothing_bits;
state->odd_smoothing = config->odd_smoothing * (1 << kNoiseReductionBits);
state->even_smoothing = config->even_smoothing * (1 << kNoiseReductionBits);
state->min_signal_remaining =
config->min_signal_remaining * (1 << kNoiseReductionBits);
state->num_channels = num_channels;
state->estimate = calloc(state->num_channels, sizeof(*state->estimate));
if (state->estimate == NULL) {
fprintf(stderr, "Failed to alloc estimate buffer\n");
return 0;
}
return 1;
}
void NoiseReductionFreeStateContents(struct NoiseReductionState *state)
{
free(state->estimate);
}

View File

@ -0,0 +1,50 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
#ifdef __cplusplus
extern "C" {
#endif
struct NoiseReductionConfig {
// scale the signal up by 2^(smoothing_bits) before reduction
int smoothing_bits;
// smoothing coefficient for even-numbered channels
float even_smoothing;
// smoothing coefficient for odd-numbered channels
float odd_smoothing;
// fraction of signal to preserve (1.0 disables this module)
float min_signal_remaining;
};
// Populates the NoiseReductionConfig with "sane" default values.
void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig *config);
// Allocates any buffers.
int NoiseReductionPopulateState(const struct NoiseReductionConfig *config,
struct NoiseReductionState *state,
int num_channels);
// Frees any allocated buffers.
void NoiseReductionFreeStateContents(struct NoiseReductionState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_

View File

@ -0,0 +1,59 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
int16_t WideDynamicFunction(const uint32_t x, const int16_t *lut)
{
if (x <= 2) {
return lut[x];
}
const int16_t interval = MostSignificantBit32(x);
lut += 4 * interval - 6;
const int16_t frac =
((interval < 11) ? (x << (11 - interval)) : (x >> (interval - 11))) &
0x3FF;
int32_t result = ((int32_t)lut[2] * frac) >> 5;
result += (int32_t)((uint32_t)lut[1] << 5);
result *= frac;
result = (result + (1 << 14)) >> 15;
result += lut[0];
return (int16_t)result;
}
uint32_t PcanShrink(const uint32_t x)
{
if (x < (2 << kPcanSnrBits)) {
return (x * x) >> (2 + 2 * kPcanSnrBits - kPcanOutputBits);
} else {
return (x >> (kPcanSnrBits - kPcanOutputBits)) - (1 << kPcanOutputBits);
}
}
void PcanGainControlApply(struct PcanGainControlState *state,
uint32_t *signal)
{
int i;
for (i = 0; i < state->num_channels; ++i) {
const uint32_t gain =
WideDynamicFunction(state->noise_estimate[i], state->gain_lut);
const uint32_t snr = ((uint64_t)signal[i] * gain) >> state->snr_shift;
signal[i] = PcanShrink(snr);
}
}

View File

@ -0,0 +1,47 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
#include <stdint.h>
#include <stdlib.h>
#define kPcanSnrBits 12
#define kPcanOutputBits 6
#ifdef __cplusplus
extern "C" {
#endif
// Details at https://research.google/pubs/pub45911.pdf
struct PcanGainControlState {
int enable_pcan;
uint32_t *noise_estimate;
int num_channels;
int16_t *gain_lut;
int32_t snr_shift;
};
int16_t WideDynamicFunction(const uint32_t x, const int16_t *lut);
uint32_t PcanShrink(const uint32_t x);
void PcanGainControlApply(struct PcanGainControlState *state, uint32_t *signal);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_

View File

@ -0,0 +1,67 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int kNumChannels = 2;
const int kSmoothingBits = 10;
const int kCorrectionBits = -1;
// Test pcan auto gain control using default config values.
class PcanGainControlTestConfig {
public:
PcanGainControlTestConfig()
{
config_.enable_pcan = 1;
config_.strength = 0.95;
config_.offset = 80.0;
config_.gain_bits = 21;
}
struct PcanGainControlConfig config_;
};
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(PcanGainControlTest_TestPcanGainControl)
{
uint32_t estimate[] = { 6321887, 31248341 };
PcanGainControlTestConfig config;
struct PcanGainControlState state;
TF_LITE_MICRO_EXPECT(PcanGainControlPopulateState(
&config.config_, &state, estimate, kNumChannels, kSmoothingBits,
kCorrectionBits));
uint32_t signal[] = { 241137, 478104 };
PcanGainControlApply(&state, signal);
const uint32_t expected[] = { 3578, 1533 };
TF_LITE_MICRO_EXPECT_EQ(state.num_channels,
sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.num_channels; ++i) {
TF_LITE_MICRO_EXPECT_EQ(signal[i], expected[i]);
}
PcanGainControlFreeStateContents(&state);
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,96 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
#include <math.h>
#include <stdio.h>
#define kint16max 0x00007FFF
void PcanGainControlFillConfigWithDefaults(
struct PcanGainControlConfig *config)
{
config->enable_pcan = 0;
config->strength = 0.95;
config->offset = 80.0;
config->gain_bits = 21;
}
int16_t PcanGainLookupFunction(const struct PcanGainControlConfig *config,
int32_t input_bits, uint32_t x)
{
const float x_as_float = ((float)x) / ((uint32_t)1 << input_bits);
const float gain_as_float =
((uint32_t)1 << config->gain_bits) *
powf(x_as_float + config->offset, -config->strength);
if (gain_as_float > kint16max) {
return kint16max;
}
return (int16_t)(gain_as_float + 0.5f);
}
int PcanGainControlPopulateState(const struct PcanGainControlConfig *config,
struct PcanGainControlState *state,
uint32_t *noise_estimate,
const int num_channels,
const uint16_t smoothing_bits,
const int32_t input_correction_bits)
{
state->enable_pcan = config->enable_pcan;
if (!state->enable_pcan) {
return 1;
}
state->noise_estimate = noise_estimate;
state->num_channels = num_channels;
state->gain_lut = malloc(kWideDynamicFunctionLUTSize * sizeof(int16_t));
if (state->gain_lut == NULL) {
fprintf(stderr, "Failed to allocate gain LUT\n");
return 0;
}
state->snr_shift = config->gain_bits - input_correction_bits - kPcanSnrBits;
const int32_t input_bits = smoothing_bits - input_correction_bits;
state->gain_lut[0] = PcanGainLookupFunction(config, input_bits, 0);
state->gain_lut[1] = PcanGainLookupFunction(config, input_bits, 1);
state->gain_lut -= 6;
int interval;
for (interval = 2; interval <= kWideDynamicFunctionBits; ++interval) {
const uint32_t x0 = (uint32_t)1 << (interval - 1);
const uint32_t x1 = x0 + (x0 >> 1);
const uint32_t x2 =
(interval == kWideDynamicFunctionBits) ? x0 + (x0 - 1) : 2 * x0;
const int16_t y0 = PcanGainLookupFunction(config, input_bits, x0);
const int16_t y1 = PcanGainLookupFunction(config, input_bits, x1);
const int16_t y2 = PcanGainLookupFunction(config, input_bits, x2);
const int32_t diff1 = (int32_t)y1 - y0;
const int32_t diff2 = (int32_t)y2 - y0;
const int32_t a1 = 4 * diff1 - diff2;
const int32_t a2 = diff2 - a1;
state->gain_lut[4 * interval] = y0;
state->gain_lut[4 * interval + 1] = (int16_t)a1;
state->gain_lut[4 * interval + 2] = (int16_t)a2;
}
state->gain_lut += 6;
return 1;
}
void PcanGainControlFreeStateContents(struct PcanGainControlState *state)
{
free(state->gain_lut);
}

View File

@ -0,0 +1,57 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
#define kWideDynamicFunctionBits 32
#define kWideDynamicFunctionLUTSize (4 * kWideDynamicFunctionBits - 3)
#ifdef __cplusplus
extern "C" {
#endif
struct PcanGainControlConfig {
// set to false (0) to disable this module
int enable_pcan;
// gain normalization exponent (0.0 disables, 1.0 full strength)
float strength;
// positive value added in the normalization denominator
float offset;
// number of fractional bits in the gain
int gain_bits;
};
void PcanGainControlFillConfigWithDefaults(
struct PcanGainControlConfig *config);
int16_t PcanGainLookupFunction(const struct PcanGainControlConfig *config,
int32_t input_bits, uint32_t x);
int PcanGainControlPopulateState(const struct PcanGainControlConfig *config,
struct PcanGainControlState *state,
uint32_t *noise_estimate,
const int num_channels,
const uint16_t smoothing_bits,
const int32_t input_correction_bits);
void PcanGainControlFreeStateContents(struct PcanGainControlState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_

View File

@ -0,0 +1,72 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
#include <string.h>
int WindowProcessSamples(struct WindowState *state, const int16_t *samples,
size_t num_samples, size_t *num_samples_read)
{
const int size = state->size;
// Copy samples from the samples buffer over to our local input.
size_t max_samples_to_copy = state->size - state->input_used;
if (max_samples_to_copy > num_samples) {
max_samples_to_copy = num_samples;
}
memcpy(state->input + state->input_used, samples,
max_samples_to_copy * sizeof(*samples));
*num_samples_read = max_samples_to_copy;
state->input_used += max_samples_to_copy;
if (state->input_used < state->size) {
// We don't have enough samples to compute a window.
return 0;
}
// Apply the window to the input.
const int16_t *coefficients = state->coefficients;
const int16_t *input = state->input;
int16_t *output = state->output;
int i;
int16_t max_abs_output_value = 0;
for (i = 0; i < size; ++i) {
int16_t new_value =
(((int32_t)*input++) * *coefficients++) >> kFrontendWindowBits;
*output++ = new_value;
if (new_value < 0) {
new_value = -new_value;
}
if (new_value > max_abs_output_value) {
max_abs_output_value = new_value;
}
}
// Shuffle the input down by the step size, and update how much we have used.
memmove(state->input, state->input + state->step,
sizeof(*state->input) * (state->size - state->step));
state->input_used -= state->step;
state->max_abs_output_value = max_abs_output_value;
// Indicate that the output buffer is valid for the next stage.
return 1;
}
void WindowReset(struct WindowState *state)
{
memset(state->input, 0, state->size * sizeof(*state->input));
memset(state->output, 0, state->size * sizeof(*state->output));
state->input_used = 0;
state->max_abs_output_value = 0;
}

View File

@ -0,0 +1,49 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
#include <stdint.h>
#include <stdlib.h>
#define kFrontendWindowBits 12
#ifdef __cplusplus
extern "C" {
#endif
struct WindowState {
size_t size;
int16_t *coefficients;
size_t step;
int16_t *input;
size_t input_used;
int16_t *output;
int16_t max_abs_output_value;
};
// Applies a window to the samples coming in, stepping forward at the given
// rate.
int WindowProcessSamples(struct WindowState *state, const int16_t *samples,
size_t num_samples, size_t *num_samples_read);
void WindowReset(struct WindowState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_

View File

@ -0,0 +1,45 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/window_io.h"
void WindowWriteMemmapPreamble(FILE *fp, const struct WindowState *state)
{
fprintf(fp, "static int16_t window_coefficients[] = {\n");
int i;
for (i = 0; i < state->size; ++i) {
fprintf(fp, "%d", state->coefficients[i]);
if (i < state->size - 1) {
fprintf(fp, ", ");
}
}
fprintf(fp, "};\n");
fprintf(fp, "static int16_t window_input[%zu];\n", state->size);
fprintf(fp, "static int16_t window_output[%zu];\n", state->size);
fprintf(fp, "\n");
}
void WindowWriteMemmap(FILE *fp, const struct WindowState *state,
const char *variable)
{
fprintf(fp, "%s->size = %zu;\n", variable, state->size);
fprintf(fp, "%s->coefficients = window_coefficients;\n", variable);
fprintf(fp, "%s->step = %zu;\n", variable, state->step);
fprintf(fp, "%s->input = window_input;\n", variable);
fprintf(fp, "%s->input_used = %zu;\n", variable, state->input_used);
fprintf(fp, "%s->output = window_output;\n", variable);
fprintf(fp, "%s->max_abs_output_value = %d;\n", variable,
state->max_abs_output_value);
}

View File

@ -0,0 +1,34 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_IO_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_IO_H_
#include <stdio.h>
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
#ifdef __cplusplus
extern "C" {
#endif
void WindowWriteMemmapPreamble(FILE *fp, const struct WindowState *state);
void WindowWriteMemmap(FILE *fp, const struct WindowState *state,
const char *variable);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_IO_H_

View File

@ -0,0 +1,187 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace {
const int kSampleRate = 1000;
const int kWindowSamples = 25;
const int kStepSamples = 10;
const int16_t kFakeAudioData[] = {
0, 32767, 0, -32768, 0, 32767, 0, -32768, 0, 32767, 0, -32768,
0, 32767, 0, -32768, 0, 32767, 0, -32768, 0, 32767, 0, -32768,
0, 32767, 0, -32768, 0, 32767, 0, -32768, 0, 32767, 0, -32768
};
// Test window function behaviors using default config values.
class WindowTestConfig {
public:
WindowTestConfig()
{
config_.size_ms = 25;
config_.step_size_ms = 10;
}
struct WindowConfig config_;
};
} // namespace
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(WindowState_CheckCoefficients)
{
WindowTestConfig config;
struct WindowState state;
TF_LITE_MICRO_EXPECT(
WindowPopulateState(&config.config_, &state, kSampleRate));
const int16_t expected[] = { 16, 144, 391, 743, 1176, 1664, 2177,
2681, 3145, 3541, 3843, 4032, 4096, 4032,
3843, 3541, 3145, 2681, 2177, 1664, 1176,
743, 391, 144, 16 };
TF_LITE_MICRO_EXPECT_EQ(state.size, sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.size; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.coefficients[i], expected[i]);
}
WindowFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(WindowState_CheckResidualInput)
{
WindowTestConfig config;
struct WindowState state;
TF_LITE_MICRO_EXPECT(
WindowPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]), &num_samples_read));
int i;
for (i = kStepSamples; i < kWindowSamples; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.input[i - kStepSamples], kFakeAudioData[i]);
}
WindowFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(WindowState_CheckOutputValues)
{
WindowTestConfig config;
struct WindowState state;
TF_LITE_MICRO_EXPECT(
WindowPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]), &num_samples_read));
const int16_t expected[] = {
0, 1151, 0, -5944, 0, 13311, 0, -21448, 0, 28327, 0, -32256, 0, 32255,
0, -28328, 0, 21447, 0, -13312, 0, 5943, 0, -1152, 0
};
TF_LITE_MICRO_EXPECT_EQ(state.size, sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.size; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.output[i], expected[i]);
}
WindowFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(WindowState_CheckMaxAbsValue)
{
WindowTestConfig config;
struct WindowState state;
TF_LITE_MICRO_EXPECT(
WindowPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]), &num_samples_read));
TF_LITE_MICRO_EXPECT_EQ(state.max_abs_output_value, 32256);
WindowFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(WindowState_CheckConsecutiveWindow)
{
WindowTestConfig config;
struct WindowState state;
TF_LITE_MICRO_EXPECT(
WindowPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]), &num_samples_read));
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData + kWindowSamples,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) - kWindowSamples,
&num_samples_read));
const int16_t expected[] = {
0, -1152, 0, 5943, 0, -13312, 0, 21447, 0, -28328, 0, 32255, 0, -32256,
0, 28327, 0, -21448, 0, 13311, 0, -5944, 0, 1151, 0
};
TF_LITE_MICRO_EXPECT_EQ(state.size, sizeof(expected) / sizeof(expected[0]));
int i;
for (i = 0; i < state.size; ++i) {
TF_LITE_MICRO_EXPECT_EQ(state.output[i], expected[i]);
}
WindowFreeStateContents(&state);
}
TF_LITE_MICRO_TEST(WindowState_CheckNotEnoughSamples)
{
WindowTestConfig config;
struct WindowState state;
TF_LITE_MICRO_EXPECT(
WindowPopulateState(&config.config_, &state, kSampleRate));
size_t num_samples_read;
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]), &num_samples_read));
TF_LITE_MICRO_EXPECT(WindowProcessSamples(
&state, kFakeAudioData + kWindowSamples,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) - kWindowSamples,
&num_samples_read));
TF_LITE_MICRO_EXPECT_EQ(
false, WindowProcessSamples(
&state, kFakeAudioData + kWindowSamples + kStepSamples,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) -
kWindowSamples - kStepSamples,
&num_samples_read));
TF_LITE_MICRO_EXPECT_EQ(
state.input_used,
sizeof(kFakeAudioData) / sizeof(kFakeAudioData[0]) - 2 * kStepSamples);
WindowFreeStateContents(&state);
}
TF_LITE_MICRO_TESTS_END

View File

@ -0,0 +1,76 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Some platforms don't have M_PI
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void WindowFillConfigWithDefaults(struct WindowConfig *config)
{
config->size_ms = 25;
config->step_size_ms = 10;
}
int WindowPopulateState(const struct WindowConfig *config,
struct WindowState *state, int sample_rate)
{
state->size = config->size_ms * sample_rate / 1000;
state->step = config->step_size_ms * sample_rate / 1000;
state->coefficients = malloc(state->size * sizeof(*state->coefficients));
if (state->coefficients == NULL) {
fprintf(stderr, "Failed to allocate window coefficients\n");
return 0;
}
// Populate the window values.
const float arg = M_PI * 2.0 / ((float)state->size);
int i;
for (i = 0; i < state->size; ++i) {
float float_value = 0.5 - (0.5 * cos(arg * (i + 0.5)));
// Scale it to fixed point and round it.
state->coefficients[i] =
floor(float_value * (1 << kFrontendWindowBits) + 0.5);
}
state->input_used = 0;
state->input = malloc(state->size * sizeof(*state->input));
if (state->input == NULL) {
fprintf(stderr, "Failed to allocate window input\n");
return 0;
}
state->output = malloc(state->size * sizeof(*state->output));
if (state->output == NULL) {
fprintf(stderr, "Failed to allocate window output\n");
return 0;
}
return 1;
}
void WindowFreeStateContents(struct WindowState *state)
{
free(state->coefficients);
free(state->input);
free(state->output);
}

View File

@ -0,0 +1,45 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
#ifdef __cplusplus
extern "C" {
#endif
struct WindowConfig {
// length of window frame in milliseconds
size_t size_ms;
// length of step for next frame in milliseconds
size_t step_size_ms;
};
// Populates the WindowConfig with "sane" default values.
void WindowFillConfigWithDefaults(struct WindowConfig *config);
// Allocates any buffers.
int WindowPopulateState(const struct WindowConfig *config,
struct WindowState *state, int sample_rate);
// Frees any allocated buffers.
void WindowFreeStateContents(struct WindowState *state);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_COMPATIBILITY_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_COMPATIBILITY_H_
#include <cstdint>
#include "tensorflow/lite/kernels/op_macros.h"
#ifndef TFLITE_DCHECK
#define TFLITE_DCHECK(condition) (condition) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
#ifndef TFLITE_DCHECK_EQ
#define TFLITE_DCHECK_EQ(x, y) ((x) == (y)) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
#ifndef TFLITE_DCHECK_NE
#define TFLITE_DCHECK_NE(x, y) ((x) != (y)) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
#ifndef TFLITE_DCHECK_GE
#define TFLITE_DCHECK_GE(x, y) ((x) >= (y)) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
#ifndef TFLITE_DCHECK_GT
#define TFLITE_DCHECK_GT(x, y) ((x) > (y)) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
#ifndef TFLITE_DCHECK_LE
#define TFLITE_DCHECK_LE(x, y) ((x) <= (y)) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
#ifndef TFLITE_DCHECK_LT
#define TFLITE_DCHECK_LT(x, y) ((x) < (y)) ? (void)0 : TFLITE_ASSERT_FALSE
#endif
// TODO(ahentz): Clean up: We should stick to the DCHECK versions.
#ifndef TFLITE_CHECK
#define TFLITE_CHECK(condition) (condition) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TFLITE_CHECK_EQ
#define TFLITE_CHECK_EQ(x, y) ((x) == (y)) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TFLITE_CHECK_NE
#define TFLITE_CHECK_NE(x, y) ((x) != (y)) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TFLITE_CHECK_GE
#define TFLITE_CHECK_GE(x, y) ((x) >= (y)) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TFLITE_CHECK_GT
#define TFLITE_CHECK_GT(x, y) ((x) > (y)) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TFLITE_CHECK_LE
#define TFLITE_CHECK_LE(x, y) ((x) <= (y)) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TFLITE_CHECK_LT
#define TFLITE_CHECK_LT(x, y) ((x) < (y)) ? (void)0 : TFLITE_ABORT
#endif
#ifndef TF_LITE_STATIC_MEMORY
// TODO(b/162019032): Consider removing these type-aliases.
using int8 = std::int8_t;
using uint8 = std::uint8_t;
using int16 = std::int16_t;
using uint16 = std::uint16_t;
using int32 = std::int32_t;
using uint32 = std::uint32_t;
#endif // !defined(TF_LITE_STATIC_MEMORY)
// TFLITE_DEPRECATED()
//
// Duplicated from absl/base/macros.h to avoid pulling in that library.
// Marks a deprecated class, struct, enum, function, method and variable
// declarations. The macro argument is used as a custom diagnostic message (e.g.
// suggestion of a better alternative).
//
// Example:
//
// class TFLITE_DEPRECATED("Use Bar instead") Foo {...};
// TFLITE_DEPRECATED("Use Baz instead") void Bar() {...}
//
// Every usage of a deprecated entity will trigger a warning when compiled with
// clang's `-Wdeprecated-declarations` option. This option is turned off by
// default, but the warnings will be reported by clang-tidy.
#if defined(__clang__) && __cplusplus >= 201103L
#define TFLITE_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#ifndef TFLITE_DEPRECATED
#define TFLITE_DEPRECATED(message)
#endif
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_COMPATIBILITY_H_

View File

@ -0,0 +1,41 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_CPPMATH_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_CPPMATH_H_
#include <cmath>
namespace tflite {
#if defined(TF_LITE_USE_GLOBAL_CMATH_FUNCTIONS) || \
(defined(__ANDROID__) && !defined(__NDK_MAJOR__)) || defined(ARDUINO) || \
defined(__ZEPHYR__)
#define TF_LITE_GLOBAL_STD_PREFIX
#else
#define TF_LITE_GLOBAL_STD_PREFIX std
#endif
#define DECLARE_STD_GLOBAL_SWITCH1(tf_name, std_name) \
template <class T> \
inline T tf_name(const T x) \
{ \
return TF_LITE_GLOBAL_STD_PREFIX::std_name(x); \
}
DECLARE_STD_GLOBAL_SWITCH1(TfLiteRound, round);
DECLARE_STD_GLOBAL_SWITCH1(TfLiteExpm1, expm1);
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_CPPMATH_H_

View File

@ -0,0 +1,36 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_MAX_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_MAX_H_
#include <cmath>
namespace tflite {
#if defined(TF_LITE_USE_GLOBAL_MAX) || defined(__ZEPHYR__)
inline float TfLiteMax(const float &x, const float &y)
{
return std::max(x, y);
}
#else
template <class T>
inline T TfLiteMax(const T &x, const T &y)
{
return std::fmax(x, y);
}
#endif
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_MAX_H_

View File

@ -0,0 +1,36 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_MIN_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_MIN_H_
#include <cmath>
namespace tflite {
#if defined(TF_LITE_USE_GLOBAL_MIN) || defined(__ZEPHYR__)
inline float TfLiteMin(const float &x, const float &y)
{
return std::min(x, y);
}
#else
template <class T>
inline T TfLiteMin(const T &x, const T &y)
{
return std::fmin(x, y);
}
#endif
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_MIN_H_

View File

@ -0,0 +1,20 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
// TFLM does not need to utilize any Neon optimizations.
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_

View File

@ -0,0 +1,143 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_PORTABLE_TENSOR_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_PORTABLE_TENSOR_H_
#include <vector>
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
inline RuntimeShape GetTensorShape(std::vector<int32_t> data)
{
return RuntimeShape(data.size(), data.data());
}
// A list of tensors in a format that can be used by kernels like split and
// concatenation.
template <typename T>
class VectorOfTensors {
public:
// Build with the tensors in 'tensor_list'.
VectorOfTensors(const TfLiteContext &context,
const TfLiteIntArray &tensor_list)
{
int num_tensors = tensor_list.size;
all_data_.reserve(num_tensors);
all_shape_.reserve(num_tensors);
all_shape_ptr_.reserve(num_tensors);
for (int i = 0; i < num_tensors; ++i) {
TfLiteTensor *t = &context.tensors[tensor_list.data[i]];
all_data_.push_back(GetTensorData<T>(t));
all_shape_.push_back(GetTensorShape(t));
}
// Taking the pointer from inside a std::vector is only OK if the vector is
// never modified, so we populate all_shape in the previous loop and then we
// are free to grab iterators here.
for (int i = 0; i < num_tensors; ++i) {
all_shape_ptr_.push_back(&all_shape_[i]);
}
}
// Return a pointer to the data pointers of all tensors in the list. For
// example:
// float* const* f = v.data();
// f[0][1] is the second element of the first tensor.
T *const *data() const
{
return all_data_.data();
}
// Return a pointer the shape pointers of all tensors in the list. For
// example:
// const RuntimeShape* const* d = v.dims();
// dims[1] are the dimensions of the second tensor in the list.
const RuntimeShape *const *shapes() const
{
return all_shape_ptr_.data();
}
private:
std::vector<T *> all_data_;
std::vector<RuntimeShape> all_shape_;
std::vector<RuntimeShape *> all_shape_ptr_;
};
// A list of quantized tensors in a format that can be used by kernels like
// split and concatenation.
class VectorOfQuantizedTensors : public VectorOfTensors<uint8_t> {
public:
// Build with the tensors in 'tensor_list'.
VectorOfQuantizedTensors(const TfLiteContext &context,
const TfLiteIntArray &tensor_list)
: VectorOfTensors<uint8_t>(context, tensor_list)
{
for (int i = 0; i < tensor_list.size; ++i) {
TfLiteTensor *t = &context.tensors[tensor_list.data[i]];
zero_point_.push_back(t->params.zero_point);
scale_.push_back(t->params.scale);
}
}
const float *scale() const
{
return scale_.data();
}
const int32_t *zero_point() const
{
return zero_point_.data();
}
private:
std::vector<int32_t> zero_point_;
std::vector<float> scale_;
};
// Writes randomly accessed values from `input` sequentially into `output`.
template <typename T>
class SequentialTensorWriter {
public:
SequentialTensorWriter(const TfLiteTensor *input, TfLiteTensor *output)
{
input_data_ = GetTensorData<T>(input);
output_ptr_ = GetTensorData<T>(output);
}
SequentialTensorWriter(const T *input_data, T *output_data)
: input_data_(input_data), output_ptr_(output_data)
{
}
void Write(int position)
{
*output_ptr_++ = input_data_[position];
}
void WriteN(int position, int len)
{
memcpy(output_ptr_, &input_data_[position], sizeof(T) * len);
output_ptr_ += len;
}
private:
const T *input_data_;
T *output_ptr_;
};
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_PORTABLE_TENSOR_H_

View File

@ -0,0 +1,409 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/kernels/internal/quantization_util.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/cppmath.h"
namespace tflite {
namespace {
// These constants are used to manipulate the binary representation of doubles.
// Double-precision binary64 floating point format is:
// Bit | 63 | 62-52 | 51-0 |
// | Sign | Exponent | Fraction |
// To avoid 64-bit integers as much as possible, I break this into high and
// low 32-bit chunks. High is:
// Bit | 31 | 30-20 | 19-0 |
// | Sign | Exponent | High Fraction |
// Low is:
// Bit | 31-0 |
// | Low Fraction |
// We then access the components through logical bit-wise operations to
// extract the parts needed, with the positions and masks derived from the
// layout shown above.
constexpr uint64_t kSignMask = 0x8000000000000000LL;
constexpr uint64_t kExponentMask = 0x7ff0000000000000LL;
constexpr int32_t kExponentShift = 52;
constexpr int32_t kExponentBias = 1023;
constexpr uint32_t kExponentIsBadNum = 0x7ff;
constexpr uint64_t kFractionMask = 0x000fffffffc00000LL;
constexpr uint32_t kFractionShift = 22;
constexpr uint32_t kFractionRoundingMask = 0x003fffff;
constexpr uint32_t kFractionRoundingThreshold = 0x00200000;
} // namespace
void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier,
int *shift)
{
if (double_multiplier == 0.) {
*quantized_multiplier = 0;
*shift = 0;
return;
}
#ifdef TFLITE_EMULATE_FLOAT
// If we're trying to avoid the use of floating-point instructions (for
// example on microcontrollers) then use an alternative implementation
// that only requires integer and bitwise operations. To enable this, you
// need to set the define during the build process for your platform.
int64_t q_fixed = IntegerFrExp(double_multiplier, shift);
#else // TFLITE_EMULATE_FLOAT
const double q = std::frexp(double_multiplier, shift);
auto q_fixed = static_cast<int64_t>(TfLiteRound(q * (1ll << 31)));
#endif // TFLITE_EMULATE_FLOAT
TFLITE_CHECK(q_fixed <= (1ll << 31));
if (q_fixed == (1ll << 31)) {
q_fixed /= 2;
++*shift;
}
TFLITE_CHECK_LE(q_fixed, std::numeric_limits<int32_t>::max());
// A shift amount smaller than -31 would cause all bits to be shifted out
// and thus all results would be zero. We implement that instead with
// q_fixed==0, so as to avoid hitting issues with right-shift
// operations with shift amounts greater than 31. Note that this happens
// roughly when abs(double_multiplier) < 2^-31 and the present handling means
// that we're effectively flushing tiny double_multiplier's to zero.
// We could conceivably handle values in the range (roughly) [32, 63]
// as 'denormals' i.e. (shift==0, q_fixed < 2^30). In that point of view
// the present handling is just doing 'flush denormals to zero'. We could
// reconsider and actually generate nonzero denormals if a need arises.
if (*shift < -31) {
*shift = 0;
q_fixed = 0;
}
*quantized_multiplier = static_cast<int32_t>(q_fixed);
}
void QuantizeMultiplierGreaterThanOne(double double_multiplier,
int32_t *quantized_multiplier,
int *left_shift)
{
TFLITE_CHECK_GT(double_multiplier, 1.);
QuantizeMultiplier(double_multiplier, quantized_multiplier, left_shift);
TFLITE_CHECK_GE(*left_shift, 0);
}
void QuantizeMultiplierSmallerThanOneExp(double double_multiplier,
int32_t *quantized_multiplier,
int *left_shift)
{
TFLITE_CHECK_LT(double_multiplier, 1.);
TFLITE_CHECK_GT(double_multiplier, 0.);
int shift;
QuantizeMultiplier(double_multiplier, quantized_multiplier, &shift);
TFLITE_CHECK_LE(shift, 0);
*left_shift = shift;
}
int64_t IntegerFrExp(double input, int *shift)
{
// Make sure our assumptions about the double layout hold.
TFLITE_CHECK_EQ(8, sizeof(double));
// We want to access the bits of the input double value directly, which is
// tricky to do safely, so use a union to handle the casting.
union {
double double_value;
uint64_t double_as_uint;
} cast_union;
cast_union.double_value = input;
const uint64_t u = cast_union.double_as_uint;
// If the bitfield is all zeros apart from the sign bit, this is a normalized
// zero value, so return standard values for this special case.
if ((u & ~kSignMask) == 0) {
*shift = 0;
return 0;
}
// Deal with NaNs and Infs, which are always indicated with a fixed pattern in
// the exponent, and distinguished by whether the fractions are zero or
// non-zero.
const uint32_t exponent_part = ((u & kExponentMask) >> kExponentShift);
if (exponent_part == kExponentIsBadNum) {
*shift = std::numeric_limits<int>::max();
if (u & kFractionMask) {
// NaN, so just return zero (with the exponent set to INT_MAX).
return 0;
} else {
// Infinity, so return +/- INT_MAX.
if (u & kSignMask) {
return std::numeric_limits<int64_t>::min();
} else {
return std::numeric_limits<int64_t>::max();
}
}
}
// The shift is fairly easy to extract from the high bits of the double value,
// just by masking it out and applying a bias. The std::frexp() implementation
// always returns values between 0.5 and 1.0 though, whereas the exponent
// assumes 1.0 to 2.0 is the standard range, so I add on one to match that
// interface.
*shift = (exponent_part - kExponentBias) + 1;
// There's an implicit high bit in the double format definition, so make sure
// we include that at the top, and then reconstruct the rest of the fractional
// value from the remaining fragments.
int64_t fraction = 0x40000000 + ((u & kFractionMask) >> kFractionShift);
// We're cutting off some bits at the bottom, so to exactly match the standard
// frexp implementation here we'll apply rounding by adding one to the least
// significant bit of the result if the discarded portion is over half of the
// maximum.
if ((u & kFractionRoundingMask) > kFractionRoundingThreshold) {
fraction += 1;
}
// Negate the fraction if the sign bit was set.
if (u & kSignMask) {
fraction *= -1;
}
return fraction;
}
double DoubleFromFractionAndShift(int64_t fraction, int shift)
{
union {
double double_value;
uint64_t double_as_uint;
} result;
// Detect NaNs and infinities.
if (shift == std::numeric_limits<int>::max()) {
if (fraction == 0) {
return std::numeric_limits<double>::quiet_NaN();
} else if (fraction > 0) {
return std::numeric_limits<double>::infinity();
} else {
return -std::numeric_limits<double>::infinity();
}
}
// Return a normalized zero for a zero fraction.
if (fraction == 0) {
result.double_as_uint = 0;
return result.double_value;
}
bool is_negative = (fraction < 0);
int64_t encoded_fraction = is_negative ? -fraction : fraction;
int64_t encoded_shift = (shift - 1);
while (encoded_fraction < 0x40000000) {
encoded_fraction *= 2;
encoded_shift -= 1;
}
while (encoded_fraction > 0x80000000) {
encoded_fraction /= 2;
encoded_shift += 1;
}
encoded_fraction -= 0x40000000;
if (encoded_shift < -1022) {
encoded_shift = -1023;
} else if (encoded_shift > 1022) {
encoded_shift = 1023;
}
encoded_shift += kExponentBias;
uint64_t encoded_sign = is_negative ? kSignMask : 0;
result.double_as_uint = encoded_sign | (encoded_shift << kExponentShift) |
(encoded_fraction << kFractionShift);
return result.double_value;
}
double IntegerDoubleMultiply(double a, double b)
{
int a_shift;
const int64_t a_fraction = IntegerFrExp(a, &a_shift);
int b_shift;
const int64_t b_fraction = IntegerFrExp(b, &b_shift);
// Detect NaNs and infinities.
if (a_shift == std::numeric_limits<int>::max() ||
(b_shift == std::numeric_limits<int>::max())) {
return std::numeric_limits<double>::quiet_NaN();
}
const int result_shift = a_shift + b_shift + 1;
const int64_t result_fraction = (a_fraction * b_fraction) >> 32;
return DoubleFromFractionAndShift(result_fraction, result_shift);
}
int IntegerDoubleCompare(double a, double b)
{
int a_shift;
const int64_t a_fraction = IntegerFrExp(a, &a_shift);
int b_shift;
const int64_t b_fraction = IntegerFrExp(b, &b_shift);
// Detect NaNs and infinities.
if (a_shift == std::numeric_limits<int>::max() ||
(b_shift == std::numeric_limits<int>::max())) {
return 1;
}
if ((a_fraction == 0) && (b_fraction < 0)) {
return 1;
} else if ((a_fraction < 0) && (b_fraction == 0)) {
return -1;
} else if (a_shift < b_shift) {
return -1;
} else if (a_shift > b_shift) {
return 1;
} else if (a_fraction < b_fraction) {
return -1;
} else if (a_fraction > b_fraction) {
return 1;
} else {
return 0;
}
}
void PreprocessSoftmaxScaling(double beta, double input_scale,
int input_integer_bits,
int32_t *quantized_multiplier, int *left_shift)
{
// If the overall multiplier (input and beta) is large, then exp() of an
// input difference of 1 scaled by this will be large. In other words, we
// can cap the multiplier and know that, when it is used, the output will be
// (round to) zero wherever the input is not at the maximum value.
// If the overall scale is less than one, and input_integer_bits=0, then the
// result is double equivalent of Q0.31 (actually with more precision). Thus
// this generates a Q(input_integer_bits).(31-input_integer_bits)
// representation.
#ifdef TFLITE_EMULATE_FLOAT
const double input_beta = IntegerDoubleMultiply(beta, input_scale);
int shift;
int64_t fraction = IntegerFrExp(input_beta, &shift);
shift += (31 - input_integer_bits);
double input_beta_real_multiplier =
DoubleFromFractionAndShift(fraction, shift);
if (IntegerDoubleCompare(input_beta_real_multiplier, (1ll << 31) - 1.0) > 0) {
input_beta_real_multiplier = (1ll << 31) - 1.0;
}
#else // TFLITE_EMULATE_FLOAT
const double input_beta_real_multiplier = std::min<double>(
beta * input_scale * (1 << (31 - input_integer_bits)), (1ll << 31) - 1.0);
#endif // TFLITE_EMULATE_FLOAT
QuantizeMultiplierGreaterThanOne(input_beta_real_multiplier,
quantized_multiplier, left_shift);
}
void PreprocessLogSoftmaxScalingExp(double beta, double input_scale,
int input_integer_bits,
int32_t *quantized_multiplier,
int *left_shift,
int32_t *reverse_scaling_divisor,
int *reverse_scaling_left_shift)
{
PreprocessSoftmaxScaling(beta, input_scale, input_integer_bits,
quantized_multiplier, left_shift);
// Also calculate what amounts to the inverse scaling factor for the input.
const double real_reverse_scaling_divisor =
(1 << (31 - *left_shift)) / static_cast<double>(*quantized_multiplier);
tflite::QuantizeMultiplierSmallerThanOneExp(real_reverse_scaling_divisor,
reverse_scaling_divisor,
reverse_scaling_left_shift);
}
int CalculateInputRadius(int input_integer_bits, int input_left_shift,
int total_signed_bits)
{
#ifdef TFLITE_EMULATE_FLOAT
int64_t result = (1 << input_integer_bits) - 1;
result <<= (total_signed_bits - input_integer_bits);
result >>= input_left_shift;
return result;
#else // TFLITE_EMULATE_FLOAT
const double max_input_rescaled =
1.0 * ((1 << input_integer_bits) - 1) *
(1ll << (total_signed_bits - input_integer_bits)) /
(1ll << input_left_shift);
// Tighten bound using floor. Suppose that we could use the exact value.
// After scaling the difference, the result would be at the maximum. Thus we
// must ensure that our value has lower magnitude.
return static_cast<int>(std::floor(max_input_rescaled));
#endif // TFLITE_EMULATE_FLOAT
}
void NudgeQuantizationRange(const float min, const float max,
const int quant_min, const int quant_max,
float *nudged_min, float *nudged_max,
float *nudged_scale)
{
// This code originates from tensorflow/core/kernels/fake_quant_ops_functor.h.
const float quant_min_float = static_cast<float>(quant_min);
const float quant_max_float = static_cast<float>(quant_max);
*nudged_scale = (max - min) / (quant_max_float - quant_min_float);
const float zero_point_from_min = quant_min_float - min / *nudged_scale;
uint16_t nudged_zero_point;
if (zero_point_from_min < quant_min_float) {
nudged_zero_point = static_cast<uint16_t>(quant_min);
} else if (zero_point_from_min > quant_max_float) {
nudged_zero_point = static_cast<uint16_t>(quant_max);
} else {
nudged_zero_point = static_cast<uint16_t>(TfLiteRound(zero_point_from_min));
}
*nudged_min = (quant_min_float - nudged_zero_point) * (*nudged_scale);
*nudged_max = (quant_max_float - nudged_zero_point) * (*nudged_scale);
}
void FakeQuantizeArray(const float nudged_scale, const float nudged_min,
const float nudged_max, const float *input_data,
float *output_data, const float size)
{
// This code originates from tensorflow/core/kernels/fake_quant_ops_functor.h.
const float inv_nudged_scale = 1.0f / nudged_scale;
for (int i = 0; i < size; i++) {
const float src_val = input_data[i];
const float clamped = std::min(nudged_max, std::max(nudged_min, src_val));
const float clamped_shifted = clamped - nudged_min;
const float dst_val =
TfLiteRound(clamped_shifted * inv_nudged_scale) * nudged_scale +
nudged_min;
output_data[i] = dst_val;
}
}
bool CheckedLog2(const float x, int *log2_result)
{
// Using TfLiteRound instead of std::round and std::log instead of
// std::log2 to work around these functions being missing in a toolchain
// used in some TensorFlow tests as of May 2018.
const float x_log2 = std::log(x) * (1.0f / std::log(2.0f));
const float x_log2_rounded = TfLiteRound(x_log2);
const float x_log2_fracpart = x_log2 - x_log2_rounded;
*log2_result = static_cast<int>(x_log2_rounded);
return std::abs(x_log2_fracpart) < 1e-3f;
}
void QuantizeMultiplierArray(const double *effective_scales, size_t size,
int32_t *effective_scale_significand,
int *effective_shift)
{
for (size_t i = 0; i < size; ++i) {
QuantizeMultiplier(effective_scales[i], &effective_scale_significand[i],
&effective_shift[i]);
}
}
} // namespace tflite

View File

@ -0,0 +1,290 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_QUANTIZATION_UTIL_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_QUANTIZATION_UTIL_H_
#include <cmath>
#include <cstdint>
#include <limits>
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/cppmath.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
// Given the min and max values of a float array, return
// reasonable quantization parameters to use for this array.
template <typename T>
QuantizationParams ChooseQuantizationParams(double rmin, double rmax,
bool narrow_range)
{
const T qmin = std::numeric_limits<T>::min() + (narrow_range ? 1 : 0);
const T qmax = std::numeric_limits<T>::max();
const double qmin_double = qmin;
const double qmax_double = qmax;
// 0 should always be a representable value. Let's assume that the initial
// min,max range contains 0.
TFLITE_CHECK_LE(rmin, 0.);
TFLITE_CHECK_GE(rmax, 0.);
if (rmin == rmax) {
// Special case where the min,max range is a point. Should be {0}.
TFLITE_CHECK_EQ(rmin, 0.);
TFLITE_CHECK_EQ(rmax, 0.);
QuantizationParams quantization_params;
quantization_params.zero_point = 0;
quantization_params.scale = 0.;
return quantization_params;
}
// General case.
//
// First determine the scale.
const double scale = (rmax - rmin) / (qmax_double - qmin_double);
// Zero-point computation.
// First the initial floating-point computation. The zero-point can be
// determined from solving an affine equation for any known pair
// (real value, corresponding quantized value).
// We know two such pairs: (rmin, qmin) and (rmax, qmax).
// The arithmetic error on the zero point computed from either pair
// will be roughly machine_epsilon * (sum of absolute values of terms)
// so we want to use the variant that adds the smaller terms.
const double zero_point_from_min = qmin_double - rmin / scale;
const double zero_point_from_max = qmax_double - rmax / scale;
const double zero_point_from_min_error =
std::abs(qmin_double) + std::abs(rmin / scale);
const double zero_point_from_max_error =
std::abs(qmax_double) + std::abs(rmax / scale);
const double zero_point_double =
zero_point_from_min_error < zero_point_from_max_error ? zero_point_from_min : zero_point_from_max;
// Now we need to nudge the zero point to be an integer
// (our zero points are integer, and this is motivated by the requirement
// to be able to represent the real value "0" exactly as a quantized value,
// which is required in multiple places, for example in Im2col with SAME
// padding).
T nudged_zero_point = 0;
if (zero_point_double < qmin_double) {
nudged_zero_point = qmin;
} else if (zero_point_double > qmax_double) {
nudged_zero_point = qmax;
} else {
nudged_zero_point = static_cast<T>(round(zero_point_double));
}
// The zero point should always be in the range of quantized value,
// [qmin, qmax].
TFLITE_CHECK_GE(nudged_zero_point, qmin);
TFLITE_CHECK_LE(nudged_zero_point, qmax);
// Finally, store the result nudged quantization params.
QuantizationParams quantization_params;
quantization_params.zero_point = nudged_zero_point;
quantization_params.scale = scale;
return quantization_params;
}
template <typename T>
QuantizationParams ChooseQuantizationParams(double rmin, double rmax)
{
return ChooseQuantizationParams<T>(rmin, rmax, false);
}
// Converts a floating-point number to an integer. For all inputs x where
// static_cast<IntOut>(x) is legal according to the C++ standard, the result
// is identical to that cast (i.e. the result is x with its fractional part
// truncated whenever that is representable as IntOut).
//
// static_cast would cause undefined behavior for the following cases, which
// have well-defined behavior for this function:
//
// 1. If x is NaN, the result is zero.
//
// 2. If the truncated form of x is above the representable range of IntOut,
// the result is std::numeric_limits<IntOut>::max().
//
// 3. If the truncated form of x is below the representable range of IntOut,
// the result is std::numeric_limits<IntOut>::min().
//
// Note that cases #2 and #3 cover infinities as well as finite numbers.
//
// The range of FloatIn must include the range of IntOut, otherwise
// the results are undefined.
// TODO(sfeuz): Replace by absl::SafeCast once available.
template <class IntOut, class FloatIn>
IntOut SafeCast(FloatIn x)
{
static_assert(!std::numeric_limits<FloatIn>::is_integer,
"FloatIn is integer");
static_assert(std::numeric_limits<IntOut>::is_integer,
"IntOut is not integer");
static_assert(std::numeric_limits<IntOut>::radix == 2, "IntOut is base 2");
// Special case NaN, for which the logic below doesn't work.
if (std::isnan(x)) {
return 0;
}
// Negative values all clip to zero for unsigned results.
if (!std::numeric_limits<IntOut>::is_signed && x < 0) {
return 0;
}
// Handle infinities.
if (std::isinf(x)) {
return x < 0 ? std::numeric_limits<IntOut>::min() : std::numeric_limits<IntOut>::max();
}
// Set exp such that x == f * 2^exp for some f with |f| in [0.5, 1.0),
// unless x is zero in which case exp == 0. Note that this implies that the
// magnitude of x is strictly less than 2^exp.
int exp = 0;
std::frexp(x, &exp);
// Let N be the number of non-sign bits in the representation of IntOut. If
// the magnitude of x is strictly less than 2^N, the truncated version of x
// is representable as IntOut. The only representable integer for which this
// is not the case is kMin for signed types (i.e. -2^N), but that is covered
// by the fall-through below.
if (exp <= std::numeric_limits<IntOut>::digits) {
return x;
}
// Handle numbers with magnitude >= 2^N.
return x < 0 ? std::numeric_limits<IntOut>::min() : std::numeric_limits<IntOut>::max();
}
// Decompose a double multiplier into a Q0.31 int32 representation of its
// significand, and shift representation of NEGATIVE its exponent ---
// this is intended as a RIGHT-shift.
//
// Restricted to the case where the multiplier < 1 (and non-negative).
void QuantizeMultiplierSmallerThanOneExp(double double_multiplier,
int32_t *quantized_multiplier,
int *left_shift);
// Decompose a double multiplier into a Q0.31 int32 representation of its
// significand, and shift representation of its exponent.
//
// Restricted to the case where the multiplier > 1.
void QuantizeMultiplierGreaterThanOne(double double_multiplier,
int32_t *quantized_multiplier,
int *left_shift);
// Decompose a double multiplier into a Q0.31 int32 representation of its
// significand, and shift representation of its exponent.
//
// Handles an arbitrary positive multiplier. The 'shift' output-value is
// basically the 'floating-point exponent' of the multiplier:
// Negative for a right-shift (when the multiplier is <1), positive for a
// left-shift (when the multiplier is >1)
void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier,
int *shift);
// Splits a double input value into a returned fraction, and a shift value from
// the exponent, using only bitwise and integer operations to support
// microcontrollers and other environments without floating-point support.
//
// This is designed to be a replacement for how std::frexp() is used within the
// QuantizeMultiplier() function, and so has a different signature than the
// standard version, returning a 64-bit integer rather than a double. This
// result has a maximum value of 1<<31, with the fraction expressed as a
// proportion of that maximum.
//
// std::frexp() returns NaNs and infinities unmodified, but since we're
// returning integers that can't represent those values, instead we return
// a shift of std::numeric_limits<int>::max() for all bad numbers, with an int64
// result of 0 for NaNs, std:numeric_limits<int64_t>::max() for +INFINITY, and
// std::numeric_limits<int64_t>::min() for -INFINITY. Denormalized inputs will
// result in return values that end up truncating some bits at the end,
// reflecting the loss of precision inherent in denormalization.
int64_t IntegerFrExp(double input, int *shift);
// Converts an integer fraction in the format produced by IntegerFrExp (where
// 0x40000000 is 1.0) and an exponent shift (between -1022 and +1022) into an
// IEEE binary64 double format result. The implementation uses only integer and
// bitwise operators, so no floating point hardware support or emulation is
// needed. This is here so quantized operations can run non-time-critical
// preparation calculations on microcontrollers and other platforms without
// float support.
double DoubleFromFractionAndShift(int64_t fraction, int shift);
// Performs a multiplication of two numbers in double format, using only integer
// and bitwise instructions. This is aimed at supporting housekeeping functions
// for quantized operations on microcontrollers without floating-point hardware.
double IntegerDoubleMultiply(double a, double b);
// Returns -1 if a is less than b, 0 if a and b are equal, and +1 if a is
// greater than b. It is implemented using only integer and logical instructions
// so that it can be easily run on microcontrollers for quantized operations.
int IntegerDoubleCompare(double a, double b);
// This first creates a multiplier in a double equivalent of
// Q(input_integer_bits).(31-input_integer_bits) representation, with extra
// precision in the double's fractional bits. It then splits the result into
// significand and exponent.
void PreprocessSoftmaxScaling(double beta, double input_scale,
int input_integer_bits,
int32_t *quantized_multiplier, int *left_shift);
// Like PreprocessSoftmaxScaling, but inverse scaling factors also calculated.
void PreprocessLogSoftmaxScalingExp(double beta, double input_scale,
int input_integer_bits,
int32_t *quantized_multiplier,
int *left_shift,
int32_t *reverse_scaling_divisor,
int *reverse_scaling_left_shift);
// Calculate the largest input that will result in a within-bounds intermediate
// result within MultiplyByQuantizedMultiplierGreaterThanOne. In other words,
// it must not overflow before we reduce the value by multiplication by the
// input multiplier. The negative radius is used as the minimum difference in
// Softmax.
int CalculateInputRadius(int input_integer_bits, int input_left_shift,
int total_signed_bits = 31);
// Nudges a min/max quantization range to ensure zero is zero.
// Gymnastics with nudged zero point is to ensure that real zero maps to
// an integer, which is required for e.g. zero-padding in convolutional layers.
// Outputs nudged_min, nudged_max, nudged_scale.
void NudgeQuantizationRange(const float min, const float max,
const int quant_min, const int quant_max,
float *nudged_min, float *nudged_max,
float *nudged_scale);
// Fake quantizes (quantizes and dequantizes) input_data using the scale,
// nudged_min, and nudged_max from NudgeQuantizationRange. This matches the code
// in TensorFlow's FakeQuantizeWithMinMaxVarsFunctor.
void FakeQuantizeArray(const float nudged_scale, const float nudged_min,
const float nudged_max, const float *input_data,
float *output_data, const float size);
// If x is approximately a power of two (with any positive or negative
// exponent), stores that exponent (i.e. log2(x)) in *log2_result, otherwise
// returns false.
bool CheckedLog2(const float x, int *log2_result);
// Decomposes an array of double multipliers into a Q0.31 int32 representation
// of its significand, and shift representation of its exponent.
//
// Handles an arbitrary multiplier. The 'shift' output-value is
// basically the 'floating-point exponent' of the multiplier:
// Negative for a right-shift (when the multiplier is <1), positive for a
// left-shift (when the multiplier is >1)
void QuantizeMultiplierArray(const double *effective_scales, size_t size,
int32_t *effective_scale_significand,
int *effective_shift);
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_QUANTIZATION_UTIL_H_

View File

@ -0,0 +1,406 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_
#include <type_traits>
#include "fixedpoint/fixedpoint.h"
#include "tensorflow/lite/kernels/internal/common.h"
namespace tflite {
namespace reference_ops {
template <typename T>
inline void Add(const ArithmeticParams &params,
const RuntimeShape &input1_shape, const T *input1_data,
const RuntimeShape &input2_shape, const T *input2_data,
const RuntimeShape &output_shape, T *output_data)
{
T activation_min, activation_max;
GetActivationParams(params, &activation_min, &activation_max);
const int flat_size =
MatchingElementsSize(input1_shape, input2_shape, output_shape);
for (int i = 0; i < flat_size; ++i) {
output_data[i] = ActivationFunctionWithMinMax(
input1_data[i] + input2_data[i], activation_min, activation_max);
}
}
// Element-wise add that can often be used for inner loop of broadcast add as
// well as the non-broadcast add.
// This function is used for 8-bit as well as for 16-bit, but the accumulator
// is 32-bit for both cases. The overflow does not happen due to the
// choice of the shift (20 or 15, accordingly - see add.cc for more comments).
template <typename T>
inline void AddElementwise(int size, const ArithmeticParams &params,
const T *input1_data, const T *input2_data,
T *output_data)
{
TFLITE_DCHECK_GT(params.input1_offset, -std::numeric_limits<T>::max());
TFLITE_DCHECK_GT(params.input2_offset, -std::numeric_limits<T>::max());
TFLITE_DCHECK_LT(params.input1_offset, std::numeric_limits<T>::max());
TFLITE_DCHECK_LT(params.input2_offset, std::numeric_limits<T>::max());
for (int i = 0; i < size; ++i) {
const int32_t input1_val = params.input1_offset + input1_data[i];
const int32_t input2_val = params.input2_offset + input2_data[i];
const int32_t shifted_input1_val = input1_val * (1 << params.left_shift);
const int32_t shifted_input2_val = input2_val * (1 << params.left_shift);
const int32_t scaled_input1_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input1_val, params.input1_multiplier, params.input1_shift);
const int32_t scaled_input2_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input2_val, params.input2_multiplier, params.input2_shift);
const int32_t raw_sum = scaled_input1_val + scaled_input2_val;
const int32_t raw_output =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
raw_sum, params.output_multiplier, params.output_shift) +
params.output_offset;
const int32_t clamped_output =
std::min(params.quantized_activation_max,
std::max(params.quantized_activation_min, raw_output));
output_data[i] = static_cast<T>(clamped_output);
}
}
// Scalar-broadcast add that can be used for inner loop of more general
// broadcast add, so that, for example, scalar-broadcast with batch will still
// be fast.
inline void AddScalarBroadcast(int size, const ArithmeticParams &params,
uint8_t input1_data, const uint8_t *input2_data,
uint8_t *output_data)
{
TFLITE_DCHECK_GT(params.input1_offset, -256);
TFLITE_DCHECK_GT(params.input2_offset, -256);
TFLITE_DCHECK_LT(params.input1_offset, 256);
TFLITE_DCHECK_LT(params.input2_offset, 256);
const int32_t input1_val = params.input1_offset + input1_data;
const int32_t shifted_input1_val = input1_val * (1 << params.left_shift);
const int32_t scaled_input1_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input1_val, params.input1_multiplier, params.input1_shift);
for (int i = 0; i < size; ++i) {
const int32_t input2_val = params.input2_offset + input2_data[i];
const int32_t shifted_input2_val = input2_val * (1 << params.left_shift);
const int32_t scaled_input2_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input2_val, params.input2_multiplier, params.input2_shift);
const int32_t raw_sum = scaled_input1_val + scaled_input2_val;
const int32_t raw_output =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
raw_sum, params.output_multiplier, params.output_shift) +
params.output_offset;
const int32_t clamped_output =
std::min(params.quantized_activation_max,
std::max(params.quantized_activation_min, raw_output));
output_data[i] = static_cast<uint8_t>(clamped_output);
}
}
inline void Add(const ArithmeticParams &params,
const RuntimeShape &input1_shape, const uint8_t *input1_data,
const RuntimeShape &input2_shape, const uint8_t *input2_data,
const RuntimeShape &output_shape, uint8_t *output_data)
{
TFLITE_DCHECK_LE(params.quantized_activation_min,
params.quantized_activation_max);
const int flat_size =
MatchingElementsSize(input1_shape, input2_shape, output_shape);
TFLITE_DCHECK_GT(params.input1_offset, -256);
TFLITE_DCHECK_GT(params.input2_offset, -256);
TFLITE_DCHECK_LT(params.input1_offset, 256);
TFLITE_DCHECK_LT(params.input2_offset, 256);
AddElementwise(flat_size, params, input1_data, input2_data, output_data);
}
inline void AddGeneralParamScale(const ArithmeticParams &params,
const RuntimeShape &input1_shape,
const int16_t *input1_data,
const RuntimeShape &input2_shape,
const int16_t *input2_data,
const RuntimeShape &output_shape,
int16_t *output_data)
{
TFLITE_DCHECK_LE(params.quantized_activation_min,
params.quantized_activation_max);
const int flat_size =
MatchingElementsSize(input1_shape, input2_shape, output_shape);
int max_value = std::numeric_limits<int16_t>::max();
TFLITE_DCHECK_GT(params.input1_offset, -max_value);
TFLITE_DCHECK_GT(params.input2_offset, -max_value);
TFLITE_DCHECK_LT(params.input1_offset, max_value);
TFLITE_DCHECK_LT(params.input2_offset, max_value);
AddElementwise(flat_size, params, input1_data, input2_data, output_data);
}
inline void Add(const ArithmeticParams &params,
const RuntimeShape &input1_shape, const int16_t *input1_data,
const RuntimeShape &input2_shape, const int16_t *input2_data,
const RuntimeShape &output_shape, int16_t *output_data,
bool pot_scale = true)
{
if (!pot_scale) {
AddGeneralParamScale(params, input1_shape, input1_data, input2_shape,
input2_data, output_shape, output_data);
return;
}
TFLITE_DCHECK_LE(params.quantized_activation_min,
params.quantized_activation_max);
const int input1_shift = params.input1_shift;
const int flat_size =
MatchingElementsSize(input1_shape, input2_shape, output_shape);
const int16_t output_activation_min = params.quantized_activation_min;
const int16_t output_activation_max = params.quantized_activation_max;
TFLITE_DCHECK(input1_shift == 0 || params.input2_shift == 0);
TFLITE_DCHECK_LE(input1_shift, 0);
TFLITE_DCHECK_LE(params.input2_shift, 0);
const int16_t *not_shift_input =
input1_shift == 0 ? input1_data : input2_data;
const int16_t *shift_input = input1_shift == 0 ? input2_data : input1_data;
const int input_right_shift =
input1_shift == 0 ? -params.input2_shift : -input1_shift;
for (int i = 0; i < flat_size; i++) {
// F0 uses 0 integer bits, range [-1, 1].
using F0 = gemmlowp::FixedPoint<std::int16_t, 0>;
F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]);
F0 scaled_input = F0::FromRaw(
gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift));
F0 result = gemmlowp::SaturatingAdd(scaled_input, input_ready_scaled);
const int16_t raw_output = result.raw();
const int16_t clamped_output = std::min(
output_activation_max, std::max(output_activation_min, raw_output));
output_data[i] = clamped_output;
}
}
template <typename T>
inline typename std::enable_if<!is_small_integer<T>::value, void>::type
BroadcastAdd4DSlow(const ArithmeticParams &params,
const RuntimeShape &input1_shape, const T *input1_data,
const RuntimeShape &input2_shape, const T *input2_data,
const RuntimeShape &output_shape, T *output_data)
{
NdArrayDesc<4> desc1;
NdArrayDesc<4> desc2;
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
&desc2);
const RuntimeShape extended_output_shape =
RuntimeShape::ExtendedShape(4, output_shape);
T activation_min, activation_max;
GetActivationParams(params, &activation_min, &activation_max);
// In Tensorflow, the dimensions are canonically named (batch_number, row,
// col, channel), with extents (batches, height, width, depth), with the
// trailing dimension changing most rapidly (channels has the smallest stride,
// typically 1 element).
//
// In generated C code, we store arrays with the dimensions reversed. The
// first dimension has smallest stride.
//
// We name our variables by their Tensorflow convention, but generate C code
// nesting loops such that the innermost loop has the smallest stride for the
// best cache behavior.
for (int b = 0; b < extended_output_shape.Dims(0); ++b) {
for (int y = 0; y < extended_output_shape.Dims(1); ++y) {
for (int x = 0; x < extended_output_shape.Dims(2); ++x) {
for (int c = 0; c < extended_output_shape.Dims(3); ++c) {
output_data[Offset(extended_output_shape, b, y, x, c)] =
ActivationFunctionWithMinMax<T>(
input1_data[SubscriptToIndex(desc1, b, y, x, c)] +
input2_data[SubscriptToIndex(desc2, b, y, x, c)],
activation_min, activation_max);
}
}
}
}
}
// This function is used for 8-bit as well as for 16-bit, but the accumulator
// is 32-bit for both cases. The overflow does not happen due to the
// choice of the shift (20 or 15, accordingly - see add.cc for more comments).
template <typename T>
inline typename std::enable_if<is_small_integer<T>::value, void>::type
BroadcastAdd4DSlow(const ArithmeticParams &params,
const RuntimeShape &input1_shape, const T *input1_data,
const RuntimeShape &input2_shape, const T *input2_data,
const RuntimeShape &output_shape, T *output_data)
{
NdArrayDesc<4> desc1;
NdArrayDesc<4> desc2;
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
&desc2);
const RuntimeShape extended_output_shape =
RuntimeShape::ExtendedShape(4, output_shape);
// In Tensorflow, the dimensions are canonically named (batch_number, row,
// col, channel), with extents (batches, height, width, depth), with the
// trailing dimension changing most rapidly (channels has the smallest stride,
// typically 1 element).
//
// In generated C code, we store arrays with the dimensions reversed. The
// first dimension has smallest stride.
//
// We name our variables by their Tensorflow convention, but generate C code
// nesting loops such that the innermost loop has the smallest stride for the
// best cache behavior.
for (int b = 0; b < extended_output_shape.Dims(0); ++b) {
for (int y = 0; y < extended_output_shape.Dims(1); ++y) {
for (int x = 0; x < extended_output_shape.Dims(2); ++x) {
for (int c = 0; c < extended_output_shape.Dims(3); ++c) {
const int32_t input1_val =
params.input1_offset +
input1_data[SubscriptToIndex(desc1, b, y, x, c)];
const int32_t input2_val =
params.input2_offset +
input2_data[SubscriptToIndex(desc2, b, y, x, c)];
const int32_t shifted_input1_val =
input1_val * (1 << params.left_shift);
const int32_t shifted_input2_val =
input2_val * (1 << params.left_shift);
const int32_t scaled_input1_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input1_val, params.input1_multiplier,
params.input1_shift);
const int32_t scaled_input2_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input2_val, params.input2_multiplier,
params.input2_shift);
const int32_t raw_sum = scaled_input1_val + scaled_input2_val;
const int32_t raw_output =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
raw_sum, params.output_multiplier, params.output_shift) +
params.output_offset;
const int32_t clamped_output =
std::min(params.quantized_activation_max,
std::max(params.quantized_activation_min, raw_output));
output_data[Offset(extended_output_shape, b, y, x, c)] =
static_cast<T>(clamped_output);
}
}
}
}
}
inline void BroadcastAddFivefold(const ArithmeticParams &unswitched_params,
const RuntimeShape &unswitched_input1_shape,
const uint8_t *unswitched_input1_data,
const RuntimeShape &unswitched_input2_shape,
const uint8_t *unswitched_input2_data,
const RuntimeShape &output_shape,
uint8_t *output_data)
{
ArithmeticParams switched_params = unswitched_params;
switched_params.input1_offset = unswitched_params.input2_offset;
switched_params.input1_multiplier = unswitched_params.input2_multiplier;
switched_params.input1_shift = unswitched_params.input2_shift;
switched_params.input2_offset = unswitched_params.input1_offset;
switched_params.input2_multiplier = unswitched_params.input1_multiplier;
switched_params.input2_shift = unswitched_params.input1_shift;
const bool use_unswitched =
unswitched_params.broadcast_category ==
tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast;
const ArithmeticParams &params =
use_unswitched ? unswitched_params : switched_params;
const uint8_t *input1_data =
use_unswitched ? unswitched_input1_data : unswitched_input2_data;
const uint8_t *input2_data =
use_unswitched ? unswitched_input2_data : unswitched_input1_data;
// Fivefold nested loops. The second input resets its position for each
// iteration of the second loop. The first input resets its position at the
// beginning of the fourth loop. The innermost loop is an elementwise add of
// sections of the arrays.
uint8_t *output_data_ptr = output_data;
const uint8_t *input1_data_ptr = input1_data;
const uint8_t *input2_data_reset = input2_data;
// In the fivefold pattern, y0, y2 and y4 are not broadcast, and so shared
// between input shapes. y3 for input 1 is always broadcast, and so the
// dimension there is 1, whereas optionally y1 might be broadcast for input 2.
// Put another way,
// input1.shape.FlatSize = y0 * y1 * y2 * y4,
// input2.shape.FlatSize = y0 * y2 * y3 * y4.
int y0 = params.broadcast_shape[0];
int y1 = params.broadcast_shape[1];
int y2 = params.broadcast_shape[2];
int y3 = params.broadcast_shape[3];
int y4 = params.broadcast_shape[4];
if (y4 > 1) {
// General fivefold pattern, with y4 > 1 so there is a non-broadcast inner
// dimension.
for (int i0 = 0; i0 < y0; ++i0) {
const uint8_t *input2_data_ptr;
for (int i1 = 0; i1 < y1; ++i1) {
input2_data_ptr = input2_data_reset;
for (int i2 = 0; i2 < y2; ++i2) {
for (int i3 = 0; i3 < y3; ++i3) {
AddElementwise(y4, params, input1_data_ptr, input2_data_ptr,
output_data_ptr);
input2_data_ptr += y4;
output_data_ptr += y4;
}
// We have broadcast y4 of input1 data y3 times, and now move on.
input1_data_ptr += y4;
}
}
// We have broadcast y2*y3*y4 of input2 data y1 times, and now move on.
input2_data_reset = input2_data_ptr;
}
} else {
// Special case of y4 == 1, in which the innermost loop is a single element
// and can be combined with the next (y3) as an inner broadcast.
//
// Note that this handles the case of pure scalar broadcast when
// y0 == y1 == y2 == 1. With low overhead it handles cases such as scalar
// broadcast with batch (as y2 > 1).
//
// NOTE The process is the same as the above general case except simplified
// for y4 == 1 and the loop over y3 is contained within the
// AddScalarBroadcast function.
for (int i0 = 0; i0 < y0; ++i0) {
const uint8_t *input2_data_ptr;
for (int i1 = 0; i1 < y1; ++i1) {
input2_data_ptr = input2_data_reset;
for (int i2 = 0; i2 < y2; ++i2) {
AddScalarBroadcast(y3, params, *input1_data_ptr, input2_data_ptr,
output_data_ptr);
input2_data_ptr += y3;
output_data_ptr += y3;
input1_data_ptr += 1;
}
}
input2_data_reset = input2_data_ptr;
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_

View File

@ -0,0 +1,87 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_
#include <algorithm>
#include <limits>
#include "tensorflow/lite/kernels/internal/common.h"
namespace tflite {
namespace reference_ops {
// T is expected to be either float or int.
template <typename T>
inline void AddN(const RuntimeShape &input_shape, const size_t num_inputs,
const T *const *input_data, T *output_data)
{
// All inputs and output should have the same shape, this is checked during
// Prepare stage.
const size_t size = input_shape.FlatSize();
for (size_t i = 0; i < size; ++i) {
T x = 0;
for (size_t j = 0; j < num_inputs; ++j) {
x += input_data[j][i];
}
output_data[i] = x;
}
}
inline void AddN(const ArithmeticParams &params,
const RuntimeShape &input_shape, const size_t num_inputs,
const int8_t *const *input_data, int8_t *output_data)
{
TFLITE_DCHECK_LE(params.quantized_activation_min,
params.quantized_activation_max);
// Input offset is negative input zero point. Activation tensors are
// asymmetric quantized so they span the full int8 range.
// All inputs should have same zero-point and scale, this is checked during
// Prepare stage.
TFLITE_DCHECK_GE(-params.input1_offset, std::numeric_limits<int8_t>::min());
TFLITE_DCHECK_LE(-params.input1_offset, std::numeric_limits<int8_t>::max());
// All inputs and output should have the same shape, this is checked during
// Prepare stage.
const size_t size = input_shape.FlatSize();
for (size_t i = 0; i < size; ++i) {
// accumulate in scaled_x before clamping to avoid overflow
const int32_t x = params.input1_offset; // x = 0
const int32_t shifted_x = x * (1 << params.left_shift);
int32_t scaled_x = MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_x, params.input1_multiplier, params.input1_shift);
for (size_t j = 0; j < num_inputs; ++j) {
const int32_t y = params.input1_offset + input_data[j][i];
const int32_t shifted_y = y * (1 << params.left_shift);
int32_t scaled_y = MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_y, params.input1_multiplier, params.input1_shift);
scaled_x += scaled_y;
}
const int32_t raw_output =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
scaled_x, params.output_multiplier, params.output_shift) +
params.output_offset;
const int32_t clamped_output =
std::min(params.quantized_activation_max,
std::max(params.quantized_activation_min, raw_output));
output_data[i] = static_cast<int8_t>(clamped_output);
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_

View File

@ -0,0 +1,89 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ARG_MIN_MAX_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ARG_MIN_MAX_H_
#include <functional>
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename T>
std::function<bool(T, T)> GetComparefunction(bool is_arg_max)
{
if (is_arg_max) {
return std::greater<T>();
} else {
return std::less<T>();
}
}
template <typename T1, typename T2, typename T3, typename Cmp>
void ArgMinMax(const RuntimeShape &input1_shape, const T1 *input1_data,
const T3 *input2_data, const RuntimeShape &output_shape,
T2 *output_data, const Cmp &cmp)
{
TFLITE_DCHECK_GT(input1_shape.DimensionsCount(), 0);
TFLITE_DCHECK_EQ(input1_shape.DimensionsCount() - 1,
output_shape.DimensionsCount());
int axis = input2_data[0];
if (axis < 0) {
axis += input1_shape.DimensionsCount();
}
const int axis_size = input1_shape.Dims(axis);
int outer_size = 1;
for (int i = 0; i < axis; ++i) {
TFLITE_DCHECK_EQ(input1_shape.Dims(i), output_shape.Dims(i));
outer_size *= input1_shape.Dims(i);
}
int inner_size = 1;
const int dims_count = input1_shape.DimensionsCount();
for (int i = axis + 1; i < dims_count; ++i) {
TFLITE_DCHECK_EQ(input1_shape.Dims(i), output_shape.Dims(i - 1));
inner_size *= input1_shape.Dims(i);
}
for (int outer = 0; outer < outer_size; ++outer) {
for (int inner = 0; inner < inner_size; ++inner) {
auto min_max_value = input1_data[outer * axis_size * inner_size + inner];
T2 min_max_index = 0;
for (int i = 1; i < axis_size; ++i) {
const auto &curr_value =
input1_data[(outer * axis_size + i) * inner_size + inner];
if (cmp(curr_value, min_max_value)) {
min_max_value = curr_value;
min_max_index = static_cast<T2>(i);
}
}
output_data[outer * inner_size + inner] = min_max_index;
}
}
}
template <typename T1, typename T2, typename T3>
void ArgMinMax(const RuntimeShape &input1_shape, const T1 *input1_data,
const T3 *input2_data, const RuntimeShape &output_shape,
T2 *output_data, const bool is_arg_max)
{
ArgMinMax(input1_shape, input1_data, input2_data, output_shape, output_data,
GetComparefunction<T1>(is_arg_max));
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ARG_MIN_MAX_H_

View File

@ -0,0 +1,281 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_MATMUL_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_MATMUL_H_
#include <algorithm>
#include <cstdint>
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/tensor_utils_common.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
namespace batch_matmul {
// Determine which dimension is the broadcast dimension.
inline int broadcast_dim(int lhs_dim, int rhs_dim)
{
if (lhs_dim == rhs_dim)
return lhs_dim;
if (lhs_dim == 1)
return rhs_dim;
TFLITE_DCHECK_EQ(rhs_dim, 1);
return lhs_dim;
}
// Compute the "extent" for iterating on this dimension.
// If we are broadcasting, then don't advance (i.e return 0).
inline int extent(const RuntimeShape &shape, int x)
{
if (shape.Dims(x) == 1) {
return 0;
}
int prod = 1;
for (int i = x + 1; i < shape.DimensionsCount(); ++i) {
prod *= shape.Dims(i);
}
return prod;
}
} // namespace batch_matmul
template <typename Ta, typename Tb, typename Tout>
inline void BatchMatMul(const RuntimeShape &lhs_shape, const Ta *lhs_data,
const RuntimeShape &rhs_shape, const Tb *rhs_data,
const RuntimeShape &output_shape, Tout *output_data)
{
const RuntimeShape extended_lhs_shape =
RuntimeShape::ExtendedShape(5, lhs_shape);
const RuntimeShape extended_rhs_shape =
RuntimeShape::ExtendedShape(5, rhs_shape);
const int batch_dim0 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(0), extended_rhs_shape.Dims(0));
const int batch_dim1 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(1), extended_rhs_shape.Dims(1));
const int batch_dim2 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(2), extended_rhs_shape.Dims(2));
const int lhs_ext0 = batch_matmul::extent(extended_lhs_shape, 0);
const int lhs_ext1 = batch_matmul::extent(extended_lhs_shape, 1);
const int lhs_ext2 = batch_matmul::extent(extended_lhs_shape, 2);
const int rhs_ext0 = batch_matmul::extent(extended_rhs_shape, 0);
const int rhs_ext1 = batch_matmul::extent(extended_rhs_shape, 1);
const int rhs_ext2 = batch_matmul::extent(extended_rhs_shape, 2);
// Set params for each matrix multiply.
const int lhs_rows = extended_lhs_shape.Dims(3);
const int rhs_cols = extended_rhs_shape.Dims(4);
const int accum_depth = extended_lhs_shape.Dims(4);
for (int b0 = 0; b0 < batch_dim0; ++b0) {
const Ta *lhs_ptr0 = lhs_data + (b0 * lhs_ext0);
const Tb *rhs_ptr0 = rhs_data + (b0 * rhs_ext0);
for (int b1 = 0; b1 < batch_dim1; ++b1) {
const Ta *lhs_ptr1 = lhs_ptr0 + b1 * lhs_ext1;
const Tb *rhs_ptr1 = rhs_ptr0 + b1 * rhs_ext1;
for (int b2 = 0; b2 < batch_dim2; ++b2) {
const Ta *lhs_ptr2 = lhs_ptr1 + b2 * lhs_ext2;
const Tb *rhs_ptr2 = rhs_ptr1 + b2 * rhs_ext2;
Tout *out_ptr = output_data + ((b0 * batch_dim1 * batch_dim2) +
b1 * batch_dim2 + b2) *
lhs_rows * rhs_cols;
for (int j = 0; j < rhs_cols; ++j) {
for (int i = 0; i < lhs_rows; ++i) {
Tout total = 0;
for (int k = 0; k < accum_depth; ++k) {
total += static_cast<Tout>(lhs_ptr2[accum_depth * i + k]) *
static_cast<Tout>(rhs_ptr2[j * accum_depth + k]);
}
int idx = lhs_rows * j + i;
out_ptr[idx] = total;
}
}
}
}
}
}
inline void BatchMatMul(const RuntimeShape &lhs_shape, const int8_t *lhs_data,
const RuntimeShape &rhs_shape, const int8_t *rhs_data,
const float *scaling_factors,
const int32_t *input_offset, int32_t *row_sums,
const RuntimeShape &output_shape, float *output_data,
bool *compute_row_sums)
{
const RuntimeShape extended_lhs_shape =
RuntimeShape::ExtendedShape(5, lhs_shape);
const RuntimeShape extended_rhs_shape =
RuntimeShape::ExtendedShape(5, rhs_shape);
const int batch_dim0 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(0), extended_rhs_shape.Dims(0));
const int batch_dim1 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(1), extended_rhs_shape.Dims(1));
const int batch_dim2 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(2), extended_rhs_shape.Dims(2));
const int lhs_ext0 = batch_matmul::extent(extended_lhs_shape, 0);
const int lhs_ext1 = batch_matmul::extent(extended_lhs_shape, 1);
const int lhs_ext2 = batch_matmul::extent(extended_lhs_shape, 2);
const int rhs_ext0 = batch_matmul::extent(extended_rhs_shape, 0);
const int rhs_ext1 = batch_matmul::extent(extended_rhs_shape, 1);
const int rhs_ext2 = batch_matmul::extent(extended_rhs_shape, 2);
// Set params for each matrix multiply.
const int lhs_rows = extended_lhs_shape.Dims(3);
const int rhs_cols = extended_rhs_shape.Dims(4);
const int accum_depth = extended_lhs_shape.Dims(4);
const int ioff_ext0 = rhs_ext0 == 0 ? 0 : rhs_cols;
const int ioff_ext1 = rhs_ext1 == 0 ? 0 : rhs_cols;
const int ioff_ext2 = rhs_ext2 == 0 ? 0 : rhs_cols;
const int woff_ext0 = lhs_ext0 == 0 ? 0 : lhs_rows;
const int woff_ext1 = lhs_ext1 == 0 ? 0 : lhs_rows;
const int woff_ext2 = lhs_ext2 == 0 ? 0 : lhs_rows;
if (!compute_row_sums || *compute_row_sums) {
int num_weights_matrices = 1;
for (int i = 1; i < extended_lhs_shape.DimensionsCount() - 2; ++i) {
num_weights_matrices *= extended_lhs_shape.Dims(i);
}
tensor_utils::ReductionSumVector(
lhs_data, row_sums, num_weights_matrices * lhs_rows, accum_depth);
if (compute_row_sums) {
*compute_row_sums = false;
}
}
for (int b0 = 0; b0 < batch_dim0; ++b0) {
const int8_t *lhs_ptr0 = lhs_data + (b0 * lhs_ext0);
const int8_t *rhs_ptr0 = rhs_data + (b0 * rhs_ext0);
const int32_t *ioff_ptr0 = input_offset + (b0 * ioff_ext0);
const float *scale_ptr0 = scaling_factors + (b0 * ioff_ext0);
const int32_t *woff_ptr0 = row_sums + (b0 * woff_ext0);
for (int b1 = 0; b1 < batch_dim1; ++b1) {
const int8_t *lhs_ptr1 = lhs_ptr0 + b1 * lhs_ext1;
const int8_t *rhs_ptr1 = rhs_ptr0 + b1 * rhs_ext1;
const int32_t *ioff_ptr1 = ioff_ptr0 + (b1 * ioff_ext1);
const float *scale_ptr1 = scale_ptr0 + (b1 * ioff_ext1);
const int32_t *woff_ptr1 = woff_ptr0 + (b1 * woff_ext1);
for (int b2 = 0; b2 < batch_dim2; ++b2) {
const int8_t *lhs_ptr2 = lhs_ptr1 + b2 * lhs_ext2;
const int8_t *rhs_ptr2 = rhs_ptr1 + b2 * rhs_ext2;
const int32_t *ioff_ptr2 = ioff_ptr1 + (b2 * ioff_ext2);
const float *scale_ptr2 = scale_ptr1 + (b2 * ioff_ext2);
const int32_t *woff_ptr2 = woff_ptr1 + (b2 * woff_ext2);
float *out_ptr = output_data + ((b0 * batch_dim1 * batch_dim2) +
b1 * batch_dim2 + b2) *
lhs_rows * rhs_cols;
for (int j = 0; j < rhs_cols; ++j) {
const float batch_scaling_factor = scale_ptr2[j];
const float batch_offset = static_cast<float>(ioff_ptr2[j]);
for (int i = 0; i < lhs_rows; ++i) {
int32_t total = 0;
for (int k = 0; k < accum_depth; ++k) {
total +=
lhs_ptr2[accum_depth * i + k] * rhs_ptr2[j * accum_depth + k];
}
int32_t row_sum = woff_ptr2[i];
total -= row_sum * batch_offset;
int idx = lhs_rows * j + i;
out_ptr[idx] += batch_scaling_factor * total;
}
}
}
}
}
}
template <typename T, typename AccumT>
inline void BatchMatMul(const FullyConnectedParams &params,
const RuntimeShape &lhs_shape, const T *lhs_data,
const RuntimeShape &rhs_shape, const T *rhs_data,
const RuntimeShape &output_shape, T *output_data)
{
const RuntimeShape extended_lhs_shape =
RuntimeShape::ExtendedShape(5, lhs_shape);
const RuntimeShape extended_rhs_shape =
RuntimeShape::ExtendedShape(5, rhs_shape);
const int batch_dim0 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(0), extended_rhs_shape.Dims(0));
const int batch_dim1 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(1), extended_rhs_shape.Dims(1));
const int batch_dim2 = batch_matmul::broadcast_dim(
extended_lhs_shape.Dims(2), extended_rhs_shape.Dims(2));
const int lhs_ext0 = batch_matmul::extent(extended_lhs_shape, 0);
const int lhs_ext1 = batch_matmul::extent(extended_lhs_shape, 1);
const int lhs_ext2 = batch_matmul::extent(extended_lhs_shape, 2);
const int rhs_ext0 = batch_matmul::extent(extended_rhs_shape, 0);
const int rhs_ext1 = batch_matmul::extent(extended_rhs_shape, 1);
const int rhs_ext2 = batch_matmul::extent(extended_rhs_shape, 2);
// Set params for each matrix multiply.
const int lhs_rows = extended_lhs_shape.Dims(3);
const int rhs_cols = extended_rhs_shape.Dims(4);
const int accum_depth = extended_lhs_shape.Dims(4);
const int32_t input_offset = params.input_offset;
const int32_t filter_offset = params.weights_offset;
const int32_t output_offset = params.output_offset;
const int32_t output_multiplier = params.output_multiplier;
const int output_shift = params.output_shift;
const int32_t output_activation_min = params.quantized_activation_min;
const int32_t output_activation_max = params.quantized_activation_max;
TFLITE_DCHECK_LE(output_activation_min, output_activation_max);
for (int b0 = 0; b0 < batch_dim0; ++b0) {
const T *lhs_ptr0 = lhs_data + (b0 * lhs_ext0);
const T *rhs_ptr0 = rhs_data + (b0 * rhs_ext0);
for (int b1 = 0; b1 < batch_dim1; ++b1) {
const T *lhs_ptr1 = lhs_ptr0 + b1 * lhs_ext1;
const T *rhs_ptr1 = rhs_ptr0 + b1 * rhs_ext1;
for (int b2 = 0; b2 < batch_dim2; ++b2) {
const T *lhs_ptr2 = lhs_ptr1 + b2 * lhs_ext2;
const T *rhs_ptr2 = rhs_ptr1 + b2 * rhs_ext2;
T *out_ptr = output_data +
((b0 * batch_dim1 * batch_dim2) + b1 * batch_dim2 + b2) *
lhs_rows * rhs_cols;
for (int j = 0; j < rhs_cols; ++j) {
for (int i = 0; i < lhs_rows; ++i) {
AccumT total = 0;
for (int k = 0; k < accum_depth; ++k) {
AccumT lhs_val = lhs_ptr2[accum_depth * i + k];
AccumT rhs_val = rhs_ptr2[accum_depth * j + k];
total += (lhs_val + filter_offset) * (rhs_val + input_offset);
}
int32_t total_scaled = MultiplyByQuantizedMultiplier(
total, output_multiplier, output_shift);
total_scaled += output_offset;
total_scaled = std::max(total_scaled, output_activation_min);
total_scaled = std::min(total_scaled, output_activation_max);
const int idx = lhs_rows * j + i;
out_ptr[idx] = static_cast<T>(total_scaled);
}
}
}
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_MATMUL_H_

View File

@ -0,0 +1,102 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_TO_SPACE_ND_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_TO_SPACE_ND_H_
#include <cmath>
#include "ruy/profiler/instrumentation.h" // from @ruy
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
// TODO(b/135760455): Move this method anonymous namespace in a cc file.
inline RuntimeShape ExtendShapeBatchToSpace(const RuntimeShape &shape)
{
if (shape.DimensionsCount() == 4) {
return shape;
}
RuntimeShape new_shape(4, 1);
new_shape.SetDim(0, shape.Dims(0));
new_shape.SetDim(1, shape.Dims(1));
new_shape.SetDim(3, shape.Dims(2));
return new_shape;
}
template <typename T>
inline void BatchToSpaceND(const RuntimeShape &unextended_input1_shape,
const T *input1_data,
const RuntimeShape &unextended_input2_shape,
const int32_t *block_shape_data,
const RuntimeShape &unextended_input3_shape,
const int32_t *crops_data,
const RuntimeShape &unextended_output_shape,
T *output_data)
{
ruy::profiler::ScopeLabel label("BatchToSpaceND");
TFLITE_DCHECK_GE(unextended_input1_shape.DimensionsCount(), 3);
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(unextended_input1_shape.DimensionsCount(),
unextended_output_shape.DimensionsCount());
const RuntimeShape input1_shape =
ExtendShapeBatchToSpace(unextended_input1_shape);
const RuntimeShape output_shape =
ExtendShapeBatchToSpace(unextended_output_shape);
const int output_width = output_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_batch_size = output_shape.Dims(0);
const int depth = input1_shape.Dims(3);
const int input_width = input1_shape.Dims(2);
const int input_height = input1_shape.Dims(1);
const int input_batch_size = input1_shape.Dims(0);
const int block_shape_height = block_shape_data[0];
const int block_shape_width =
unextended_input1_shape.DimensionsCount() == 4 ? block_shape_data[1] : 1;
const int crops_top = crops_data[0];
const int crops_left =
unextended_input1_shape.DimensionsCount() == 4 ? crops_data[2] : 0;
for (int in_batch = 0; in_batch < input_batch_size; ++in_batch) {
const int out_batch = in_batch % output_batch_size;
const int spatial_offset = in_batch / output_batch_size;
for (int in_h = 0; in_h < input_height; ++in_h) {
const int out_h = in_h * block_shape_height +
spatial_offset / block_shape_width - crops_top;
if (out_h < 0 || out_h >= output_height) {
continue;
}
for (int in_w = 0; in_w < input_width; ++in_w) {
const int out_w = in_w * block_shape_width +
spatial_offset % block_shape_width - crops_left;
if (out_w < 0 || out_w >= output_width) {
continue;
}
T *out = output_data + Offset(output_shape, out_batch, out_h, out_w, 0);
const T *in =
input1_data + Offset(input1_shape, in_batch, in_h, in_w, 0);
memcpy(out, in, depth * sizeof(T));
}
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_TO_SPACE_ND_H_

View File

@ -0,0 +1,80 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BINARY_FUNCTION_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BINARY_FUNCTION_H_
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
// Also appears to duplicate MinimumMaximum.
//
// R: Result type. T1: Input 1 type. T2: Input 2 type.
template <typename R, typename T1, typename T2>
inline void BroadcastBinaryFunction4DSlow(
const RuntimeShape &unextended_input1_shape, const T1 *input1_data,
const RuntimeShape &unextended_input2_shape, const T2 *input2_data,
const RuntimeShape &unextended_output_shape, R *output_data,
R (*func)(T1, T2))
{
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
const RuntimeShape output_shape =
RuntimeShape::ExtendedShape(4, unextended_output_shape);
NdArrayDesc<4> desc1;
NdArrayDesc<4> desc2;
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
unextended_input2_shape, &desc1, &desc2);
for (int b = 0; b < output_shape.Dims(0); ++b) {
for (int y = 0; y < output_shape.Dims(1); ++y) {
for (int x = 0; x < output_shape.Dims(2); ++x) {
for (int c = 0; c < output_shape.Dims(3); ++c) {
auto out_idx = Offset(output_shape, b, y, x, c);
auto in1_idx = SubscriptToIndex(desc1, b, y, x, c);
auto in2_idx = SubscriptToIndex(desc2, b, y, x, c);
auto in1_val = input1_data[in1_idx];
auto in2_val = input2_data[in2_idx];
output_data[out_idx] = func(in1_val, in2_val);
}
}
}
}
}
// R: Result type. T1: Input 1 type. T2: Input 2 type.
template <typename R, typename T1, typename T2>
inline void BinaryFunction(const RuntimeShape &input1_shape,
const T1 *input1_data,
const RuntimeShape &input2_shape,
const T2 *input2_data,
const RuntimeShape &output_shape, R *output_data,
R (*func)(T1, T2))
{
const int flat_size =
MatchingFlatSize(input1_shape, input2_shape, output_shape);
for (int i = 0; i < flat_size; ++i) {
output_data[i] = func(input1_data[i], input2_data[i]);
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BINARY_FUNCTION_H_

View File

@ -0,0 +1,36 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CEIL_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CEIL_H_
#include <cmath>
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
inline void Ceil(const RuntimeShape &input_shape, const float *input_data,
const RuntimeShape &output_shape, float *output_data)
{
const int flat_size = MatchingFlatSize(input_shape, output_shape);
for (int i = 0; i < flat_size; ++i) {
output_data[i] = std::ceil(input_data[i]);
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CEIL_H_

View File

@ -0,0 +1,297 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_COMPARISONS_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_COMPARISONS_H_
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename T>
inline bool EqualFn(T lhs, T rhs)
{
return lhs == rhs;
}
template <typename T>
inline bool NotEqualFn(T lhs, T rhs)
{
return lhs != rhs;
}
template <typename T>
inline bool GreaterFn(T lhs, T rhs)
{
return lhs > rhs;
}
template <typename T>
inline bool GreaterEqualFn(T lhs, T rhs)
{
return lhs >= rhs;
}
template <typename T>
inline bool LessFn(T lhs, T rhs)
{
return lhs < rhs;
}
template <typename T>
inline bool LessEqualFn(T lhs, T rhs)
{
return lhs <= rhs;
}
template <typename T>
using ComparisonFn = bool (*)(T, T);
template <typename T, ComparisonFn<T> F>
inline void ComparisonImpl(
const ComparisonParams &op_params, const RuntimeShape &input1_shape,
const T *input1_data, const RuntimeShape &input2_shape,
const T *input2_data, const RuntimeShape &output_shape, bool *output_data)
{
const int64_t flatsize =
MatchingFlatSize(input1_shape, input2_shape, output_shape);
for (int64_t i = 0; i < flatsize; ++i) {
output_data[i] = F(input1_data[i], input2_data[i]);
}
}
template <ComparisonFn<float> F>
inline void Comparison(const ComparisonParams &op_params,
const RuntimeShape &input1_shape,
const float *input1_data,
const RuntimeShape &input2_shape,
const float *input2_data,
const RuntimeShape &output_shape, bool *output_data)
{
ComparisonImpl<float, F>(op_params, input1_shape, input1_data, input2_shape,
input2_data, output_shape, output_data);
}
template <typename T, ComparisonFn<int32_t> F>
inline void ComparisonWithScaling(
const ComparisonParams &op_params, const RuntimeShape &input1_shape,
const T *input1_data, const RuntimeShape &input2_shape,
const T *input2_data, const RuntimeShape &output_shape, bool *output_data)
{
int left_shift = op_params.left_shift;
int32_t input1_offset = op_params.input1_offset;
int32_t input1_multiplier = op_params.input1_multiplier;
int input1_shift = op_params.input1_shift;
int32_t input2_offset = op_params.input2_offset;
int32_t input2_multiplier = op_params.input2_multiplier;
int input2_shift = op_params.input2_shift;
const int64_t flatsize =
MatchingFlatSize(input1_shape, input2_shape, output_shape);
for (int64_t i = 0; i < flatsize; ++i) {
const int32_t input1_val = input1_offset + input1_data[i];
const int32_t input2_val = input2_offset + input2_data[i];
const int32_t shifted_input1_val = input1_val * (1 << left_shift);
const int32_t shifted_input2_val = input2_val * (1 << left_shift);
const int32_t scaled_input1_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input1_val, input1_multiplier, input1_shift);
const int32_t scaled_input2_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input2_val, input2_multiplier, input2_shift);
output_data[i] = F(scaled_input1_val, scaled_input2_val);
}
}
struct BroadcastComparison4DSlowCommon {
const RuntimeShape output_shape;
NdArrayDesc<4> desc1;
NdArrayDesc<4> desc2;
};
inline BroadcastComparison4DSlowCommon BroadcastComparison4DSlowPreprocess(
const RuntimeShape &unextended_input1_shape,
const RuntimeShape &unextended_input2_shape,
const RuntimeShape &unextended_output_shape)
{
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
NdArrayDesc<4> desc1;
NdArrayDesc<4> desc2;
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
unextended_input2_shape, &desc1, &desc2);
return { RuntimeShape::ExtendedShape(4, unextended_output_shape), desc1,
desc2 };
}
template <typename T, ComparisonFn<T> F>
inline void BroadcastComparison4DSlowImpl(
const ComparisonParams &op_params,
const RuntimeShape &unextended_input1_shape, const T *input1_data,
const RuntimeShape &unextended_input2_shape, const T *input2_data,
const RuntimeShape &unextended_output_shape, bool *output_data)
{
const BroadcastComparison4DSlowCommon dims =
BroadcastComparison4DSlowPreprocess(unextended_input1_shape,
unextended_input2_shape,
unextended_output_shape);
for (int b = 0; b < dims.output_shape.Dims(0); ++b) {
for (int y = 0; y < dims.output_shape.Dims(1); ++y) {
for (int x = 0; x < dims.output_shape.Dims(2); ++x) {
for (int c = 0; c < dims.output_shape.Dims(3); ++c) {
output_data[Offset(dims.output_shape, b, y, x, c)] =
F(input1_data[SubscriptToIndex(dims.desc1, b, y, x, c)],
input2_data[SubscriptToIndex(dims.desc2, b, y, x, c)]);
}
}
}
}
}
template <ComparisonFn<float> F>
inline void BroadcastComparison4DSlow(const ComparisonParams &op_params,
const RuntimeShape &input1_shape,
const float *input1_data,
const RuntimeShape &input2_shape,
const float *input2_data,
const RuntimeShape &output_shape,
bool *output_data)
{
BroadcastComparison4DSlowImpl<float, F>(op_params, input1_shape, input1_data,
input2_shape, input2_data,
output_shape, output_data);
}
template <typename T, ComparisonFn<int32_t> F>
inline void BroadcastComparison4DSlowWithScaling(
const ComparisonParams &op_params,
const RuntimeShape &unextended_input1_shape, const T *input1_data,
const RuntimeShape &unextended_input2_shape, const T *input2_data,
const RuntimeShape &unextended_output_shape, bool *output_data)
{
const BroadcastComparison4DSlowCommon dims =
BroadcastComparison4DSlowPreprocess(unextended_input1_shape,
unextended_input2_shape,
unextended_output_shape);
int left_shift = op_params.left_shift;
int32_t input1_offset = op_params.input1_offset;
int32_t input1_multiplier = op_params.input1_multiplier;
int input1_shift = op_params.input1_shift;
int32_t input2_offset = op_params.input2_offset;
int32_t input2_multiplier = op_params.input2_multiplier;
int input2_shift = op_params.input2_shift;
for (int b = 0; b < dims.output_shape.Dims(0); ++b) {
for (int y = 0; y < dims.output_shape.Dims(1); ++y) {
for (int x = 0; x < dims.output_shape.Dims(2); ++x) {
for (int c = 0; c < dims.output_shape.Dims(3); ++c) {
const int32_t input1_val =
input1_offset +
input1_data[SubscriptToIndex(dims.desc1, b, y, x, c)];
const int32_t input2_val =
input2_offset +
input2_data[SubscriptToIndex(dims.desc2, b, y, x, c)];
const int32_t shifted_input1_val = input1_val * (1 << left_shift);
const int32_t shifted_input2_val = input2_val * (1 << left_shift);
const int32_t scaled_input1_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input1_val, input1_multiplier, input1_shift);
const int32_t scaled_input2_val =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_input2_val, input2_multiplier, input2_shift);
output_data[Offset(dims.output_shape, b, y, x, c)] =
F(scaled_input1_val, scaled_input2_val);
}
}
}
}
}
#define TFLITE_COMPARISON_OP(name) \
inline void name(const ComparisonParams &op_params, \
const RuntimeShape &input1_shape, const float *input1_data, \
const RuntimeShape &input2_shape, const float *input2_data, \
const RuntimeShape &output_shape, bool *output_data) \
{ \
Comparison<name##Fn>(op_params, input1_shape, input1_data, input2_shape, \
input2_data, output_shape, output_data); \
} \
template <typename T> \
inline void name##NoScaling( \
const ComparisonParams &op_params, const RuntimeShape &input1_shape, \
const T *input1_data, const RuntimeShape &input2_shape, \
const T *input2_data, const RuntimeShape &output_shape, \
bool *output_data) \
{ \
ComparisonImpl<T, name##Fn>(op_params, input1_shape, input1_data, \
input2_shape, input2_data, output_shape, \
output_data); \
} \
template <typename T> \
inline void name##WithScaling( \
const ComparisonParams &op_params, const RuntimeShape &input1_shape, \
const T *input1_data, const RuntimeShape &input2_shape, \
const T *input2_data, const RuntimeShape &output_shape, \
bool *output_data) \
{ \
ComparisonWithScaling<T, name##Fn>(op_params, input1_shape, input1_data, \
input2_shape, input2_data, \
output_shape, output_data); \
} \
template <typename T> \
inline void Broadcast4DSlow##name##NoScaling( \
const ComparisonParams &op_params, const RuntimeShape &input1_shape, \
const T *input1_data, const RuntimeShape &input2_shape, \
const T *input2_data, const RuntimeShape &output_shape, \
bool *output_data) \
{ \
BroadcastComparison4DSlowImpl<T, name##Fn>( \
op_params, input1_shape, input1_data, input2_shape, input2_data, \
output_shape, output_data); \
} \
inline void Broadcast4DSlow##name( \
const ComparisonParams &op_params, const RuntimeShape &input1_shape, \
const float *input1_data, const RuntimeShape &input2_shape, \
const float *input2_data, const RuntimeShape &output_shape, \
bool *output_data) \
{ \
BroadcastComparison4DSlow<name##Fn>(op_params, input1_shape, input1_data, \
input2_shape, input2_data, \
output_shape, output_data); \
} \
template <typename T> \
inline void Broadcast4DSlow##name##WithScaling( \
const ComparisonParams &op_params, const RuntimeShape &input1_shape, \
const T *input1_data, const RuntimeShape &input2_shape, \
const T *input2_data, const RuntimeShape &output_shape, \
bool *output_data) \
{ \
BroadcastComparison4DSlowWithScaling<T, name##Fn>( \
op_params, input1_shape, input1_data, input2_shape, input2_data, \
output_shape, output_data); \
}
TFLITE_COMPARISON_OP(Equal);
TFLITE_COMPARISON_OP(NotEqual);
TFLITE_COMPARISON_OP(Greater);
TFLITE_COMPARISON_OP(GreaterEqual);
TFLITE_COMPARISON_OP(Less);
TFLITE_COMPARISON_OP(LessEqual);
#undef TFLITE_COMPARISON_OP
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_COMPARISONS_H_

View File

@ -0,0 +1,140 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONCATENATION_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONCATENATION_H_
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/cppmath.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename Scalar>
inline void Concatenation(const ConcatenationParams &params,
const RuntimeShape *const *input_shapes,
const Scalar *const *input_data,
const RuntimeShape &output_shape,
Scalar *output_data)
{
int axis = params.axis;
int inputs_count = params.inputs_count;
const int concat_dimensions = output_shape.DimensionsCount();
TFLITE_DCHECK_LT(axis, concat_dimensions);
int64_t concat_size = 0;
for (int i = 0; i < inputs_count; i++) {
TFLITE_DCHECK_EQ(input_shapes[i]->DimensionsCount(), concat_dimensions);
for (int j = 0; j < concat_dimensions; j++) {
if (j != axis) {
MatchingDim(*input_shapes[i], j, output_shape, j);
}
}
concat_size += input_shapes[i]->Dims(axis);
}
TFLITE_DCHECK_EQ(concat_size, output_shape.Dims(axis));
int64_t outer_size = 1;
for (int i = 0; i < axis; ++i) {
outer_size *= output_shape.Dims(i);
}
// For all input arrays,
// FlatSize() = outer_size * Dims(axis) * base_inner_size;
int64_t base_inner_size = 1;
for (int i = axis + 1; i < concat_dimensions; ++i) {
base_inner_size *= output_shape.Dims(i);
}
Scalar *output_ptr = output_data;
for (int k = 0; k < outer_size; k++) {
for (int i = 0; i < inputs_count; ++i) {
const int copy_size = input_shapes[i]->Dims(axis) * base_inner_size;
const Scalar *input_ptr = input_data[i] + k * copy_size;
memcpy(output_ptr, input_ptr, copy_size * sizeof(Scalar));
output_ptr += copy_size;
}
}
}
// TODO(b/174275780): The quantized implementation of concatentation isn't fully
// quantized as it takes scale as a floating point value. This should be fixed
// when optimizng this routine further.
inline void ConcatenationWithScaling(const ConcatenationParams &params,
const RuntimeShape *const *input_shapes,
const uint8_t *const *input_data,
const RuntimeShape &output_shape,
uint8_t *output_data)
{
int axis = params.axis;
const int32_t *input_zeropoint = params.input_zeropoint;
const float *input_scale = params.input_scale;
int inputs_count = params.inputs_count;
const int32_t output_zeropoint = params.output_zeropoint;
const float output_scale = params.output_scale;
const int concat_dimensions = output_shape.DimensionsCount();
TFLITE_DCHECK_LT(axis, concat_dimensions);
int64_t concat_size = 0;
for (int i = 0; i < inputs_count; i++) {
TFLITE_DCHECK_EQ(input_shapes[i]->DimensionsCount(), concat_dimensions);
for (int j = 0; j < concat_dimensions; j++) {
if (j != axis) {
MatchingDim(*input_shapes[i], j, output_shape, j);
}
}
concat_size += input_shapes[i]->Dims(axis);
}
TFLITE_DCHECK_EQ(concat_size, output_shape.Dims(axis));
int64_t outer_size = 1;
for (int i = 0; i < axis; ++i) {
outer_size *= output_shape.Dims(i);
}
// For all input arrays,
// FlatSize() = outer_size * Dims(axis) * base_inner_size;
int64_t base_inner_size = 1;
for (int i = axis + 1; i < concat_dimensions; ++i) {
base_inner_size *= output_shape.Dims(i);
}
const float inverse_output_scale = 1.f / output_scale;
uint8_t *output_ptr = output_data;
for (int k = 0; k < outer_size; k++) {
for (int i = 0; i < inputs_count; ++i) {
const int copy_size = input_shapes[i]->Dims(axis) * base_inner_size;
const uint8_t *input_ptr = input_data[i] + k * copy_size;
if (input_zeropoint[i] == output_zeropoint &&
input_scale[i] == output_scale) {
memcpy(output_ptr, input_ptr, copy_size);
} else {
const float scale = input_scale[i] * inverse_output_scale;
const float bias = -input_zeropoint[i] * scale;
for (int j = 0; j < copy_size; ++j) {
const int32_t value = static_cast<int32_t>(tflite::TfLiteRound(
input_ptr[j] * scale + bias)) +
output_zeropoint;
output_ptr[j] = static_cast<uint8_t>(
std::max<int32_t>(std::min<int32_t>(255, value), 0));
}
}
output_ptr += copy_size;
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONCATENATION_H_

View File

@ -0,0 +1,265 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONV_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONV_H_
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
inline void Conv(const ConvParams &params, const RuntimeShape &input_shape,
const float *input_data, const RuntimeShape &filter_shape,
const float *filter_data, const RuntimeShape &bias_shape,
const float *bias_data, const RuntimeShape &output_shape,
float *output_data, const RuntimeShape &im2col_shape,
float *im2col_data)
{
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int dilation_width_factor = params.dilation_width_factor;
const int dilation_height_factor = params.dilation_height_factor;
const int pad_width = params.padding_values.width;
const int pad_height = params.padding_values.height;
const float output_activation_min = params.float_activation_min;
const float output_activation_max = params.float_activation_max;
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
(void)im2col_data; // only used in optimized code.
(void)im2col_shape; // only used in optimized code.
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int input_depth = MatchingDim(input_shape, 3, filter_shape, 3);
const int output_depth = MatchingDim(filter_shape, 0, output_shape, 3);
if (bias_data) {
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
}
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
for (int batch = 0; batch < batches; ++batch) {
for (int out_y = 0; out_y < output_height; ++out_y) {
const int in_y_origin = (out_y * stride_height) - pad_height;
for (int out_x = 0; out_x < output_width; ++out_x) {
const int in_x_origin = (out_x * stride_width) - pad_width;
for (int out_channel = 0; out_channel < output_depth; ++out_channel) {
float total = 0.f;
for (int filter_y = 0; filter_y < filter_height; ++filter_y) {
const int in_y = in_y_origin + dilation_height_factor * filter_y;
for (int filter_x = 0; filter_x < filter_width; ++filter_x) {
const int in_x = in_x_origin + dilation_width_factor * filter_x;
// Zero padding by omitting the areas outside the image.
const bool is_point_inside_image =
(in_x >= 0) && (in_x < input_width) && (in_y >= 0) &&
(in_y < input_height);
if (!is_point_inside_image) {
continue;
}
for (int in_channel = 0; in_channel < input_depth; ++in_channel) {
float input_value = input_data[Offset(input_shape, batch, in_y,
in_x, in_channel)];
float filter_value = filter_data[Offset(
filter_shape, out_channel, filter_y, filter_x, in_channel)];
total += (input_value * filter_value);
}
}
}
float bias_value = 0.0f;
if (bias_data) {
bias_value = bias_data[out_channel];
}
output_data[Offset(output_shape, batch, out_y, out_x, out_channel)] =
ActivationFunctionWithMinMax(total + bias_value,
output_activation_min,
output_activation_max);
}
}
}
}
}
inline void Conv(const ConvParams &params, const RuntimeShape &input_shape,
const uint8_t *input_data, const RuntimeShape &filter_shape,
const uint8_t *filter_data, const RuntimeShape &bias_shape,
const int32_t *bias_data, const RuntimeShape &output_shape,
uint8_t *output_data, const RuntimeShape &im2col_shape,
uint8_t *im2col_data, void *cpu_backend_context)
{
(void)cpu_backend_context; // only used in optimized code.
(void)im2col_data; // only used in optimized code.
(void)im2col_shape; // only used in optimized code.
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int dilation_width_factor = params.dilation_width_factor;
const int dilation_height_factor = params.dilation_height_factor;
const int pad_width = params.padding_values.width;
const int pad_height = params.padding_values.height;
const int32_t input_offset = params.input_offset;
const int32_t filter_offset = params.weights_offset;
const int32_t output_offset = params.output_offset;
const int32_t output_multiplier = params.output_multiplier;
const int output_shift = params.output_shift;
const int32_t output_activation_min = params.quantized_activation_min;
const int32_t output_activation_max = params.quantized_activation_max;
TFLITE_DCHECK_LE(output_activation_min, output_activation_max);
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int input_depth = MatchingDim(input_shape, 3, filter_shape, 3);
const int output_depth = MatchingDim(filter_shape, 0, output_shape, 3);
if (bias_data) {
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
}
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
for (int batch = 0; batch < batches; ++batch) {
for (int out_y = 0; out_y < output_height; ++out_y) {
const int in_y_origin = (out_y * stride_height) - pad_height;
for (int out_x = 0; out_x < output_width; ++out_x) {
const int in_x_origin = (out_x * stride_width) - pad_width;
for (int out_channel = 0; out_channel < output_depth; ++out_channel) {
int32_t acc = 0;
for (int filter_y = 0; filter_y < filter_height; ++filter_y) {
const int in_y = in_y_origin + dilation_height_factor * filter_y;
for (int filter_x = 0; filter_x < filter_width; ++filter_x) {
const int in_x = in_x_origin + dilation_width_factor * filter_x;
// Zero padding by omitting the areas outside the image.
const bool is_point_inside_image =
(in_x >= 0) && (in_x < input_width) && (in_y >= 0) &&
(in_y < input_height);
if (!is_point_inside_image) {
continue;
}
for (int in_channel = 0; in_channel < input_depth; ++in_channel) {
int32_t input_val = input_data[Offset(input_shape, batch, in_y,
in_x, in_channel)];
int32_t filter_val = filter_data[Offset(
filter_shape, out_channel, filter_y, filter_x, in_channel)];
acc +=
(filter_val + filter_offset) * (input_val + input_offset);
}
}
}
if (bias_data) {
acc += bias_data[out_channel];
}
acc = MultiplyByQuantizedMultiplier(acc, output_multiplier,
output_shift);
acc += output_offset;
acc = std::max(acc, output_activation_min);
acc = std::min(acc, output_activation_max);
output_data[Offset(output_shape, batch, out_y, out_x, out_channel)] =
static_cast<uint8_t>(acc);
}
}
}
}
}
inline void HybridConvPerChannel(
const ConvParams &params, float *scaling_factors_ptr,
const RuntimeShape &input_shape, const int8_t *input_data,
const RuntimeShape &filter_shape, const int8_t *filter_data,
const RuntimeShape &bias_shape, const float *bias_data,
const RuntimeShape &output_shape, float *output_data,
const RuntimeShape &im2col_shape, int8_t *im2col_data,
const float *per_channel_scale, int32_t *input_offset)
{
(void)im2col_data; // only used in optimized code.
(void)im2col_shape; // only used in optimized code.
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int dilation_width_factor = params.dilation_width_factor;
const int dilation_height_factor = params.dilation_height_factor;
const int pad_width = params.padding_values.width;
const int pad_height = params.padding_values.height;
const float output_activation_min = params.float_activation_min;
const float output_activation_max = params.float_activation_max;
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int input_depth = MatchingDim(input_shape, 3, filter_shape, 3);
const int output_depth = MatchingDim(filter_shape, 0, output_shape, 3);
if (bias_data) {
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
}
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
for (int batch = 0; batch < batches; ++batch) {
for (int out_y = 0; out_y < output_height; ++out_y) {
for (int out_x = 0; out_x < output_width; ++out_x) {
for (int out_channel = 0; out_channel < output_depth; ++out_channel) {
const int in_x_origin = (out_x * stride_width) - pad_width;
const int in_y_origin = (out_y * stride_height) - pad_height;
int32_t acc = 0;
for (int filter_y = 0; filter_y < filter_height; ++filter_y) {
for (int filter_x = 0; filter_x < filter_width; ++filter_x) {
for (int in_channel = 0; in_channel < input_depth; ++in_channel) {
const int in_x = in_x_origin + dilation_width_factor * filter_x;
const int in_y =
in_y_origin + dilation_height_factor * filter_y;
// If the location is outside the bounds of the input image,
// use zero as a default value.
if ((in_x >= 0) && (in_x < input_width) && (in_y >= 0) &&
(in_y < input_height)) {
int32_t input_val = input_data[Offset(
input_shape, batch, in_y, in_x, in_channel)];
int32_t filter_val =
filter_data[Offset(filter_shape, out_channel, filter_y,
filter_x, in_channel)];
acc += filter_val * (input_val - input_offset[batch]);
}
}
}
}
float acc_float =
acc * per_channel_scale[out_channel] * scaling_factors_ptr[batch];
if (bias_data) {
acc_float += bias_data[out_channel];
}
output_data[Offset(output_shape, batch, out_y, out_x, out_channel)] =
ActivationFunctionWithMinMax(acc_float, output_activation_min,
output_activation_max);
}
}
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONV_H_

View File

@ -0,0 +1,176 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CUMSUM_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CUMSUM_H_
#include <algorithm>
#include <cstdint>
#include <limits>
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
namespace tflite {
namespace reference_ops {
template <typename T>
inline void CumSum(const T *input_data, const RuntimeShape &shape, int32_t axis,
bool exclusive, bool reverse, T *output_data)
{
const int32_t rank = shape.DimensionsCount();
TFLITE_DCHECK_GE(rank, 1);
TFLITE_DCHECK_GE(axis, 0);
TFLITE_DCHECK_LT(axis, rank);
size_t inner = 1;
size_t outer = 1;
size_t depth = 1;
for (int32_t i = 0; i < rank; i++) {
if (i < axis)
inner *= shape.Dims(i);
else if (i > axis)
outer *= shape.Dims(i);
else
depth = shape.Dims(i);
}
for (size_t outer_index = 0; outer_index < outer; outer_index++) {
size_t outer_index_adj;
if (reverse)
outer_index_adj = (outer - 1) - outer_index;
else
outer_index_adj = outer_index;
for (size_t inner_index = 0; inner_index < inner; inner_index++) {
T accumulator = 0;
size_t inner_index_adj;
if (reverse)
inner_index_adj = (inner - 1) - inner_index;
else
inner_index_adj = inner_index;
for (size_t depth_index = 0; depth_index < depth; depth_index++) {
size_t depth_index_adj;
if (reverse)
depth_index_adj = (depth - 1) - depth_index;
else
depth_index_adj = depth_index;
size_t index = outer_index_adj;
index += inner_index_adj * depth * outer;
index += depth_index_adj * outer;
if (exclusive) {
output_data[index] = accumulator;
accumulator += input_data[index];
} else {
accumulator += input_data[index];
output_data[index] = accumulator;
}
}
}
}
}
//
// Quantized INT8 CUMSUM
//
inline void CumSum(const ArithmeticParams &params, const int8_t *input_data,
const RuntimeShape &shape, int32_t axis, bool exclusive,
bool reverse, int8_t *output_data)
{
TFLITE_DCHECK_LE(params.quantized_activation_min,
params.quantized_activation_max);
// Input offset is negative input zero point. Activation tensors are
// asymmetric quantized so they span the full int8 range.
// All inputs should have same zero-point and scale, this is checked during
// Prepare stage.
TFLITE_DCHECK_GE(-params.input1_offset, std::numeric_limits<int8_t>::min());
TFLITE_DCHECK_LE(-params.input1_offset, std::numeric_limits<int8_t>::max());
const int32_t rank = shape.DimensionsCount();
TFLITE_DCHECK_GE(rank, 1);
TFLITE_DCHECK_GE(axis, 0);
TFLITE_DCHECK_LT(axis, rank);
size_t inner = 1;
size_t outer = 1;
size_t depth = 1;
for (int32_t i = 0; i < rank; i++) {
if (i < axis)
inner *= shape.Dims(i);
else if (i > axis)
outer *= shape.Dims(i);
else
depth = shape.Dims(i);
}
for (size_t outer_index = 0; outer_index < outer; outer_index++) {
size_t outer_index_adj;
if (reverse)
outer_index_adj = (outer - 1) - outer_index;
else
outer_index_adj = outer_index;
for (size_t inner_index = 0; inner_index < inner; inner_index++) {
int32_t accumulator = params.input1_offset; // accumulator = 0
accumulator *= (1 << params.left_shift);
accumulator = MultiplyByQuantizedMultiplierSmallerThanOneExp(
accumulator, params.input1_multiplier, params.input1_shift);
size_t inner_index_adj;
if (reverse)
inner_index_adj = (inner - 1) - inner_index;
else
inner_index_adj = inner_index;
for (size_t depth_index = 0; depth_index < depth; depth_index++) {
size_t depth_index_adj;
if (reverse)
depth_index_adj = (depth - 1) - depth_index;
else
depth_index_adj = depth_index;
size_t index = outer_index_adj;
index += inner_index_adj * depth * outer;
index += depth_index_adj * outer;
const int32_t y = params.input1_offset + input_data[index];
const int32_t shifted_y = y * (1 << params.left_shift);
const int32_t scaled_y = MultiplyByQuantizedMultiplierSmallerThanOneExp(
shifted_y, params.input1_multiplier, params.input1_shift);
int32_t scaled_output;
if (exclusive) {
scaled_output = accumulator;
accumulator += scaled_y;
} else {
accumulator += scaled_y;
scaled_output = accumulator;
}
const int32_t raw_output =
MultiplyByQuantizedMultiplierSmallerThanOneExp(
scaled_output, params.output_multiplier, params.output_shift) +
params.output_offset;
const int32_t clamped_output =
std::min(params.quantized_activation_max,
std::max(params.quantized_activation_min, raw_output));
output_data[index] = static_cast<int8_t>(clamped_output);
}
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CUMSUM_H_

View File

@ -0,0 +1,79 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTH_TO_SPACE_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTH_TO_SPACE_H_
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename T>
inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params,
const RuntimeShape &unextended_input_shape,
const T *input_data,
const RuntimeShape &unextended_output_shape,
T *output_data)
{
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
const RuntimeShape input_shape =
RuntimeShape::ExtendedShape(4, unextended_input_shape);
const RuntimeShape output_shape =
RuntimeShape::ExtendedShape(4, unextended_output_shape);
const int input_depth = input_shape.Dims(3);
const int input_width = input_shape.Dims(2);
const int input_height = input_shape.Dims(1);
const int input_batch = input_shape.Dims(0);
const int output_depth = output_shape.Dims(3);
const int output_width = output_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_batch = output_shape.Dims(0);
const int32_t block_size = op_params.block_size;
TFLITE_DCHECK_EQ(input_width * block_size, output_width);
TFLITE_DCHECK_EQ(input_height * block_size, output_height);
TFLITE_DCHECK_EQ(input_depth, output_depth * block_size * block_size);
TFLITE_DCHECK_EQ(input_batch, output_batch);
for (int out_b = 0; out_b < output_batch; ++out_b) {
for (int out_h = 0; out_h < output_height; ++out_h) {
for (int out_w = 0; out_w < output_width; ++out_w) {
for (int out_d = 0; out_d < output_depth; ++out_d) {
const int in_d =
out_d + ((out_h % block_size) * block_size + out_w % block_size) *
output_depth;
const int in_w = out_w / block_size;
const int in_h = out_h / block_size;
const int in_b = out_b;
const int input_index = Offset(input_shape, in_b, in_h, in_w, in_d);
const int output_index =
Offset(output_shape, out_b, out_h, out_w, out_d);
output_data[output_index] = input_data[input_index];
}
}
}
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTH_TO_SPACE_H_

View File

@ -0,0 +1,100 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTHWISECONV_FLOAT_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTHWISECONV_FLOAT_H_
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
inline void DepthwiseConv(
const DepthwiseParams &params, const RuntimeShape &input_shape,
const float *input_data, const RuntimeShape &filter_shape,
const float *filter_data, const RuntimeShape &bias_shape,
const float *bias_data, const RuntimeShape &output_shape,
float *output_data)
{
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int dilation_width_factor = params.dilation_width_factor;
const int dilation_height_factor = params.dilation_height_factor;
const int pad_width = params.padding_values.width;
const int pad_height = params.padding_values.height;
const int depth_multiplier = params.depth_multiplier;
const float output_activation_min = params.float_activation_min;
const float output_activation_max = params.float_activation_max;
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3);
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int input_depth = input_shape.Dims(3);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
TFLITE_DCHECK_EQ(output_depth, input_depth * depth_multiplier);
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
for (int b = 0; b < batches; ++b) {
for (int out_y = 0; out_y < output_height; ++out_y) {
for (int out_x = 0; out_x < output_width; ++out_x) {
for (int ic = 0; ic < input_depth; ++ic) {
for (int m = 0; m < depth_multiplier; m++) {
const int oc = m + ic * depth_multiplier;
const int in_x_origin = (out_x * stride_width) - pad_width;
const int in_y_origin = (out_y * stride_height) - pad_height;
float total = 0.f;
for (int filter_y = 0; filter_y < filter_height; ++filter_y) {
for (int filter_x = 0; filter_x < filter_width; ++filter_x) {
const int in_x = in_x_origin + dilation_width_factor * filter_x;
const int in_y =
in_y_origin + dilation_height_factor * filter_y;
// If the location is outside the bounds of the input image,
// use zero as a default value.
if ((in_x >= 0) && (in_x < input_width) && (in_y >= 0) &&
(in_y < input_height)) {
float input_value =
input_data[Offset(input_shape, b, in_y, in_x, ic)];
float filter_value = filter_data[Offset(
filter_shape, 0, filter_y, filter_x, oc)];
total += (input_value * filter_value);
}
}
}
float bias_value = 0.0f;
if (bias_data) {
bias_value = bias_data[oc];
}
output_data[Offset(output_shape, b, out_y, out_x, oc)] =
ActivationFunctionWithMinMax(total + bias_value,
output_activation_min,
output_activation_max);
}
}
}
}
}
}
} // end namespace reference_ops
} // end namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTHWISECONV_FLOAT_H_

View File

@ -0,0 +1,301 @@
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTHWISECONV_UINT8_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTHWISECONV_UINT8_H_
#include <algorithm>
#include "fixedpoint/fixedpoint.h"
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
// Used in tests and template parameters to control which version of depthwise
// convolution is called. Primarily for reference code, and specializations
// forced in tests.
enum class DepthwiseConvImplementation {
// Run all tests against kUseStandardEntry even if also testing another
// kernel, since we need to be sure that the main DepthwiseConv() function in
// optimized_ops.h dispatches to a correctly-executing kernel.
kNone = 0, // The "default" option: use the normal
// DepthwiseConv kernel (entry) function.
kUseGenericKernel, // Forced use of generic kernel.
kUseNeon3x3, // 3x3 kernel that uses NEON when available.
kUseNeon3x3DotProduct, // 3x3 kernel that uses dot-product enabled NEON
// when available.
kUseCModel3x3DotProduct, // 3x3 kernel, reference C model that is intended
// to match overall design NEON code.
kUseUnwound3x3DotProduct, // 3x3 kernel, reference C model with unwound loops
// and some arrays.
kUseIntrinsics3x3DotProduct, // 3x3 kernel using NEON intrinsics.
};
// Category of depthwise convolution output rounding.
enum class DepthwiseConvOutputRounding {
kNone = 0, // Invalid: specific method must be specified.
kAwayFromZero, // Original method: exact halves rounded away from zero.
kUpward, // Halves towards +infinity: adds 0.5 before truncate.
// This is where a future kNearestEven would be placed.
};
// Category of depthwise convolution depth multiplication.
enum class DepthwiseConvDepthMultiplication {
kNoMultiplication = 0, // Depth multiplier = 1.
kUnitInputDepth, // Input depth = 1, output depth = depth multiplier.
};
namespace reference_ops {
namespace depthwise_conv {
template <DepthwiseConvOutputRounding output_rounding>
inline int32_t DepthwiseConvRound(int32_t x, int32_t quantized_multiplier,
int shift)
{
TFLITE_DCHECK_NE(output_rounding, DepthwiseConvOutputRounding::kNone);
return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift);
}
template <>
inline int32_t DepthwiseConvRound<DepthwiseConvOutputRounding::kAwayFromZero>(
int32_t x, int32_t quantized_multiplier, int shift)
{
return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift);
}
template <>
inline int32_t DepthwiseConvRound<DepthwiseConvOutputRounding::kUpward>(
int32_t x, int32_t quantized_multiplier, int shift)
{
using gemmlowp::SaturatingRoundingDoublingHighMul;
const int left_shift = shift > 0 ? shift : 0;
const int right_shift = shift > 0 ? 0 : -shift;
const int rounding_offset = right_shift > 0 ? 1 << (right_shift - 1) : 0;
return (SaturatingRoundingDoublingHighMul(x * (1 << left_shift),
quantized_multiplier) +
rounding_offset) >>
right_shift;
}
template <DepthwiseConvOutputRounding output_rounding>
struct DepthwiseConvBasicKernel {
static inline void Run(
const DepthwiseParams &params, const RuntimeShape &input_shape,
const uint8_t *input_data, const RuntimeShape &filter_shape,
const uint8_t *filter_data, const RuntimeShape &bias_shape,
const int32_t *bias_data, const RuntimeShape &output_shape,
uint8_t *output_data)
{
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int dilation_width_factor = params.dilation_width_factor;
const int dilation_height_factor = params.dilation_height_factor;
const int pad_width = params.padding_values.width;
const int pad_height = params.padding_values.height;
const int depth_multiplier = params.depth_multiplier;
const int32_t output_activation_min = params.quantized_activation_min;
const int32_t output_activation_max = params.quantized_activation_max;
const int32_t input_offset = params.input_offset;
const int32_t filter_offset = params.weights_offset;
const int32_t output_offset = params.output_offset;
const int32_t output_multiplier = params.output_multiplier;
const int output_shift = params.output_shift;
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(output_activation_min, output_activation_max);
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3);
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int input_depth = input_shape.Dims(3);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
TFLITE_DCHECK_EQ(output_depth, input_depth * depth_multiplier);
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
for (int b = 0; b < batches; ++b) {
for (int out_y = 0; out_y < output_height; ++out_y) {
for (int out_x = 0; out_x < output_width; ++out_x) {
for (int ic = 0; ic < input_depth; ++ic) {
for (int m = 0; m < depth_multiplier; m++) {
const int oc = m + ic * depth_multiplier;
const int in_x_origin = (out_x * stride_width) - pad_width;
const int in_y_origin = (out_y * stride_height) - pad_height;
int32_t acc = 0;
for (int filter_y = 0; filter_y < filter_height; ++filter_y) {
for (int filter_x = 0; filter_x < filter_width; ++filter_x) {
const int in_x =
in_x_origin + dilation_width_factor * filter_x;
const int in_y =
in_y_origin + dilation_height_factor * filter_y;
// If the location is outside the bounds of the input image,
// use zero as a default value.
if ((in_x >= 0) && (in_x < input_width) && (in_y >= 0) &&
(in_y < input_height)) {
int32_t input_val =
input_data[Offset(input_shape, b, in_y, in_x, ic)];
int32_t filter_val = filter_data[Offset(
filter_shape, 0, filter_y, filter_x, oc)];
acc += (filter_val + filter_offset) *
(input_val + input_offset);
}
}
}
if (bias_data) {
acc += bias_data[oc];
}
acc = DepthwiseConvRound<output_rounding>(acc, output_multiplier,
output_shift);
acc += output_offset;
acc = std::max(acc, output_activation_min);
acc = std::min(acc, output_activation_max);
output_data[Offset(output_shape, b, out_y, out_x, oc)] =
static_cast<uint8_t>(acc);
}
}
}
}
}
}
// TODO(b/148596273): Reconcile reference versions, perhaps with common
// MultiplyByQuantizedMultiplier or DepthwiseConvRound function.
static inline void RunPerChannel(
const DepthwiseParams &params, const RuntimeShape &input_shape,
const int8_t *input_data, const RuntimeShape &filter_shape,
const int8_t *filter_data, const RuntimeShape &bias_shape,
const int32_t *bias_data, const RuntimeShape &output_shape,
int8_t *output_data)
{
// Get parameters.
// TODO(b/141565753): Re-introduce ScopedProfilingLabel on Micro.
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int dilation_width_factor = params.dilation_width_factor;
const int dilation_height_factor = params.dilation_height_factor;
const int pad_width = params.padding_values.width;
const int pad_height = params.padding_values.height;
const int depth_multiplier = params.depth_multiplier;
const int32_t input_offset = params.input_offset;
const int32_t output_offset = params.output_offset;
const int32_t output_activation_min = params.quantized_activation_min;
const int32_t output_activation_max = params.quantized_activation_max;
const int32_t *output_multiplier = params.output_multiplier_per_channel;
const int32_t *output_shift = params.output_shift_per_channel;
// Check dimensions of the tensors.
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
TFLITE_DCHECK_LE(output_activation_min, output_activation_max);
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3);
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int input_depth = input_shape.Dims(3);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
TFLITE_DCHECK_EQ(output_depth, input_depth * depth_multiplier);
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
for (int batch = 0; batch < batches; ++batch) {
for (int out_y = 0; out_y < output_height; ++out_y) {
for (int out_x = 0; out_x < output_width; ++out_x) {
for (int in_channel = 0; in_channel < input_depth; ++in_channel) {
for (int m = 0; m < depth_multiplier; ++m) {
const int output_channel = m + in_channel * depth_multiplier;
const int in_x_origin = (out_x * stride_width) - pad_width;
const int in_y_origin = (out_y * stride_height) - pad_height;
int32_t acc = 0;
for (int filter_y = 0; filter_y < filter_height; ++filter_y) {
for (int filter_x = 0; filter_x < filter_width; ++filter_x) {
const int in_x =
in_x_origin + dilation_width_factor * filter_x;
const int in_y =
in_y_origin + dilation_height_factor * filter_y;
// Zero padding by omitting the areas outside the image.
const bool is_point_inside_image =
(in_x >= 0) && (in_x < input_width) && (in_y >= 0) &&
(in_y < input_height);
if (is_point_inside_image) {
int32_t input_val = input_data[Offset(
input_shape, batch, in_y, in_x, in_channel)];
int32_t filter_val = filter_data[Offset(
filter_shape, 0, filter_y, filter_x, output_channel)];
// Accumulate with 32 bits accumulator.
// In the nudging process during model quantization, we
// force real value of 0.0 be represented by a quantized
// value. This guarantees that the input_offset is a int8_t,
// even though it is represented using int32_t. int32_t +=
// int8_t
// * (int8_t - int8_t) so the highest value we can get from
// each accumulation is [-127, 127] * ([-128, 127] -
// [-128, 127]), which is [-32512, 32512]. log2(32512)
// = 14.98, which means we can accumulate at least 2^16
// multiplications without overflow. The accumulator is
// applied to a filter so the accumulation logic will hold
// as long as the filter size (filter_y * filter_x *
// in_channel) does not exceed 2^16, which is the case in
// all the models we have seen so far.
acc += filter_val * (input_val + input_offset);
}
}
}
if (bias_data) {
acc += bias_data[output_channel];
}
acc = DepthwiseConvRound<output_rounding>(
acc, output_multiplier[output_channel],
output_shift[output_channel]);
acc += output_offset;
acc = std::max(acc, output_activation_min);
acc = std::min(acc, output_activation_max);
output_data[Offset(output_shape, batch, out_y, out_x,
output_channel)] = static_cast<int8_t>(acc);
}
}
}
}
}
}
};
} // namespace depthwise_conv
inline void DepthwiseConv(
const DepthwiseParams &params, const RuntimeShape &input_shape,
const uint8_t *input_data, const RuntimeShape &filter_shape,
const uint8_t *filter_data, const RuntimeShape &bias_shape,
const int32_t *bias_data, const RuntimeShape &output_shape,
uint8_t *output_data)
{
return depthwise_conv::DepthwiseConvBasicKernel<
DepthwiseConvOutputRounding::kAwayFromZero>::Run(params, input_shape,
input_data, filter_shape,
filter_data, bias_shape,
bias_data, output_shape,
output_data);
}
} // namespace reference_ops
} // end namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTHWISECONV_UINT8_H_

View File

@ -0,0 +1,78 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEQUANTIZE_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEQUANTIZE_H_
#include <limits.h>
#include <vector>
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
// Dequantizes into a float without rounding.
template <typename InputT, typename OutputT>
inline void Dequantize(const tflite::DequantizationParams &op_params,
const RuntimeShape &input_shape,
const InputT *input_data,
const RuntimeShape &output_shape, OutputT *output_data)
{
int32_t zero_point = op_params.zero_point;
const double scale = op_params.scale;
const int flat_size = MatchingFlatSize(input_shape, output_shape);
for (int i = 0; i < flat_size; i++) {
const int32_t val = input_data[i];
const OutputT result = static_cast<OutputT>(scale * (val - zero_point));
output_data[i] = result;
}
}
// Dequantizes per-channel quantized tensor to float.
template <typename T>
inline void PerChannelDequantize(
const tflite::PerChannelDequantizationParams &op_params,
const RuntimeShape &input_shape, const T *input_data,
const RuntimeShape &output_shape, float *output_data)
{
// Ensure flat size is same.
MatchingFlatSize(input_shape, output_shape);
const int32_t *zero_point = op_params.zero_point;
const float *scale = op_params.scale;
const int32_t quantized_dimension = op_params.quantized_dimension;
const int32_t num_dims = input_shape.DimensionsCount();
const int32_t *dims_data = input_shape.DimsData();
std::vector<int> current_dim(num_dims, 0);
do {
size_t offset =
ReducedOutputOffset(num_dims, reinterpret_cast<const int *>(dims_data),
current_dim.data(), 0, nullptr);
const int channel = current_dim[quantized_dimension];
const int32_t val = input_data[offset];
const float result =
static_cast<float>(scale[channel] * (val - zero_point[channel]));
output_data[offset] = result;
} while (NextIndex(num_dims, reinterpret_cast<const int *>(dims_data),
current_dim.data()));
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEQUANTIZE_H_

View File

@ -0,0 +1,36 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ELU_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ELU_H_
#include "tensorflow/lite/kernels/internal/cppmath.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
inline void Elu(const RuntimeShape &input_shape, const float *input_data,
const RuntimeShape &output_shape, float *output_data)
{
const int flat_size = MatchingFlatSize(input_shape, output_shape);
for (int i = 0; i < flat_size; ++i) {
const float val = input_data[i];
output_data[i] = val < 0.0f ? TfLiteExpm1(val) : val;
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ELU_H_

View File

@ -0,0 +1,38 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_EXP_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_EXP_H_
#include <cmath>
#include "ruy/profiler/instrumentation.h" // from @ruy
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename T>
inline void Exp(const T *input_data, const size_t num_elements,
T *output_data)
{
ruy::profiler::ScopeLabel label("Exp");
for (size_t idx = 0; idx < num_elements; ++idx) {
output_data[idx] = std::exp(input_data[idx]);
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_EXP_H_

View File

@ -0,0 +1,38 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FILL_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FILL_H_
#include <cmath>
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename T>
void Fill(const RuntimeShape &value_shape, const T *value_data,
const RuntimeShape &output_shape, T *output_data)
{
TFLITE_DCHECK_EQ(value_shape.DimensionsCount(), 0);
const int flat_size = output_shape.FlatSize();
for (int i = 0; i < flat_size; ++i) {
output_data[i] = *value_data;
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FILL_H_

View File

@ -0,0 +1,38 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_H_
#include <cmath>
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
inline void Floor(const RuntimeShape &input_shape, const float *input_data,
const RuntimeShape &output_shape, float *output_data)
{
const int flat_size = MatchingFlatSize(input_shape, output_shape);
for (int i = 0; i < flat_size; i++) {
int offset = i;
output_data[offset] = std::floor(input_data[offset]);
}
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_H_

Some files were not shown because too many files have changed in this diff Show More