mirror of
https://github.com/Fishwaldo/bl_mcu_sdk.git
synced 2025-03-15 19:31:43 +00:00
[feat] add tensorflowlite component and demo
This commit is contained in:
parent
57bda6c48b
commit
a65f86a713
576 changed files with 175425 additions and 0 deletions
|
@ -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
5
components/TensorFlowLite/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/bazel-*
|
||||
*.swp
|
||||
.vscode/
|
||||
*audio_frontend*
|
||||
*google*
|
25
components/TensorFlowLite/CMakeLists.txt
Normal file
25
components/TensorFlowLite/CMakeLists.txt
Normal 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>)
|
202
components/TensorFlowLite/LICENSE
Normal file
202
components/TensorFlowLite/LICENSE
Normal 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.
|
75
components/TensorFlowLite/README.md
Normal file
75
components/TensorFlowLite/README.md
Normal 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) | [](https://github.com/tensorflow/tflite-micro/actions/workflows/ci.yml?query=event%3Aschedule) |
|
||||
Code Sync | [](https://github.com/tensorflow/tflite-micro/actions/workflows/sync.yml) |
|
||||
|
||||
## Community Supported Builds
|
||||
Build Type | Status |
|
||||
----------- | --------------|
|
||||
Arduino | [](https://github.com/tensorflow/tflite-micro/actions/workflows/arduino.yml) [](https://github.com/antmicro/tensorflow-arduino-examples/actions/workflows/test_examples.yml) |
|
||||
Cortex-M | [](https://github.com/tensorflow/tflite-micro/actions/workflows/cortex_m.yml) |
|
||||
Sparkfun Edge | [](https://github.com/tensorflow/tflite-micro/actions/workflows/sparkfun_edge.yml) |
|
||||
Xtensa | [](https://github.com/tensorflow/tflite-micro/actions/workflows/xtensa.yml?query=event%3Aschedule) [](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)
|
504
components/TensorFlowLite/tensorflow/lite/c/builtin_op_data.h
Normal file
504
components/TensorFlowLite/tensorflow/lite/c/builtin_op_data.h
Normal 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_
|
105
components/TensorFlowLite/tensorflow/lite/c/c_api_types.h
Normal file
105
components/TensorFlowLite/tensorflow/lite/c/c_api_types.h
Normal 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_
|
274
components/TensorFlowLite/tensorflow/lite/c/common.c
Normal file
274
components/TensorFlowLite/tensorflow/lite/c/common.c
Normal 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;
|
||||
}
|
952
components/TensorFlowLite/tensorflow/lite/c/common.h
Normal file
952
components/TensorFlowLite/tensorflow/lite/c/common.h
Normal 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, ®);
|
||||
// }
|
||||
// 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, ®);
|
||||
// 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, ¶ms_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_
|
|
@ -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
|
|
@ -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
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -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_
|
|
@ -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));
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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));
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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 };
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
1080
components/TensorFlowLite/tensorflow/lite/kernels/internal/common.h
Normal file
1080
components/TensorFlowLite/tensorflow/lite/kernels/internal/common.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms,
|
||||
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 ¶ms =
|
||||
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_
|
|
@ -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 ¶ms,
|
||||
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_
|
|
@ -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_
|
|
@ -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 ¶ms,
|
||||
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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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 ¶ms,
|
||||
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 ¶ms,
|
||||
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_
|
|
@ -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 ¶ms, 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 ¶ms, 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 ¶ms, 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_
|
|
@ -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 ¶ms, 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_
|
|
@ -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_
|
|
@ -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 ¶ms, 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_
|
|
@ -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 ¶ms, 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 ¶ms, 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 ¶ms, 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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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
Loading…
Add table
Reference in a new issue