1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "norm_processor.h"
17
18 #include <climits>
19 #include <cstdio>
20 #include <cstdlib>
21
22 #include "aie_log.h"
23 #include "aie_macros.h"
24 #include "aie_retcode_inner.h"
25
26 using namespace OHOS::AI::Feature;
27
28 namespace {
29 const char NUMBER_DELIMITER = ' ';
30 const float EPSILON = 1e-6;
31 }
32
LoadInfoFromFile(FILE * & fp,float * & data,size_t length)33 static int32_t LoadInfoFromFile(FILE *&fp, float *&data, size_t length)
34 {
35 size_t count = 0;
36 char c = '\0';
37 std::string token = "";
38 do {
39 size_t readLen = fread(&c, sizeof(char), 1, fp);
40 if (readLen == 0u) {
41 break;
42 }
43 if (c == NUMBER_DELIMITER) {
44 data[count++] = atof(token.c_str());
45 token = "";
46 continue;
47 }
48 token += c;
49 } while (count < length);
50 if (token.empty()) {
51 return count;
52 }
53 if (count >= length) {
54 HILOGW("[NormProcessor]Lost value %s", token.c_str());
55 count = length;
56 } else {
57 data[count++] = atof(token.c_str());
58 }
59 return count;
60 }
61
ReadFixedLenFloatData(const std::string & filePath,float * & data,size_t length)62 static int32_t ReadFixedLenFloatData(const std::string &filePath, float *&data, size_t length)
63 {
64 char realPath[PATH_MAX + 1] = {0};
65 if (realpath(filePath.c_str(), realPath) == nullptr) {
66 HILOGE("[NormProcessor]Invalid filePath [%s]", filePath.c_str());
67 return RETCODE_FAILURE;
68 }
69 FILE *fp = fopen(realPath, "rb");
70 if (fp == nullptr) {
71 HILOGE("[NormProcessor]File [%s] not exists", realPath);
72 return RETCODE_FAILURE;
73 }
74 size_t readLen = LoadInfoFromFile(fp, data, length);
75 fclose(fp);
76 if (readLen != length) {
77 HILOGE("[NormProcessor]The data length is not equal (got %zu, but expected %zu)", readLen, length);
78 return RETCODE_FAILURE;
79 }
80 return RETCODE_SUCCESS;
81 }
82
LoadMeanAndStd(const std::string & meanFilePath,const std::string & stdFilePath,float * & mean,float * & std,size_t length)83 static int32_t LoadMeanAndStd(const std::string &meanFilePath, const std::string &stdFilePath,
84 float *&mean, float *&std, size_t length)
85 {
86 int32_t retCode = ReadFixedLenFloatData(meanFilePath, mean, length);
87 if (retCode != RETCODE_SUCCESS) {
88 HILOGE("[NormProcessor]Fail to load mean from file");
89 return RETCODE_FAILURE;
90 }
91 retCode = ReadFixedLenFloatData(stdFilePath, std, length);
92 if (retCode != RETCODE_SUCCESS) {
93 HILOGE("[NormProcessor]Fail to load std from file");
94 return RETCODE_FAILURE;
95 }
96 return RETCODE_SUCCESS;
97 }
98
NormProcessor()99 NormProcessor::NormProcessor()
100 : isInitialized_(false),
101 workBuffer_(nullptr),
102 mean_(nullptr),
103 std_(nullptr),
104 converter_(nullptr)
105 {
106 config_ = {};
107 }
108
~NormProcessor()109 NormProcessor::~NormProcessor()
110 {
111 Release();
112 }
113
Init(const FeatureProcessorConfig * config)114 int32_t NormProcessor::Init(const FeatureProcessorConfig *config)
115 {
116 if (isInitialized_) {
117 HILOGE("[NormProcessor]Fail to initialize more than once. Release it, then try again");
118 return RETCODE_FAILURE;
119 }
120 if (config == nullptr) {
121 HILOGE("[NormProcessor]Fail with null config pointer");
122 return RETCODE_FAILURE;
123 }
124 config_ = *(static_cast<const NormProcessorConfig *>(config));
125 if (config_.inputSize == 0 || config_.numChannels == 0) {
126 HILOGE("[NormProcessor]Illegal config, the inputSize or numChannels is 0");
127 return RETCODE_FAILURE;
128 }
129 if (config_.inputSize % config_.numChannels != 0) {
130 HILOGE("[NormProcessor]The inputSize cannot be divided by numChannels");
131 return RETCODE_FAILURE;
132 }
133 TypeConverterConfig convertConfig = {FLOAT, (config_.inputSize)};
134 converter_ = std::unique_ptr<TypeConverter>(new (std::nothrow) TypeConverter());
135 if (converter_ == nullptr) {
136 HILOGE("[NormProcessor]Fail to create typeConverter");
137 return RETCODE_FAILURE;
138 }
139 if (converter_->Init(&convertConfig) != RETCODE_SUCCESS) {
140 HILOGE("[NormProcessor]Fail to initialize typeConverter");
141 return RETCODE_FAILURE;
142 }
143 if (config_.numChannels > MAX_SAMPLE_SIZE || config_.inputSize > MAX_SAMPLE_SIZE) {
144 HILOGE("[NormProcessor]The required memory size is larger than MAX_SAMPLE_SIZE[%zu]", MAX_SAMPLE_SIZE);
145 return RETCODE_FAILURE;
146 }
147 AIE_NEW(mean_, float[config_.numChannels]);
148 AIE_NEW(std_, float[config_.numChannels]);
149 AIE_NEW(workBuffer_, float[config_.inputSize]);
150 if (mean_ == nullptr || std_ == nullptr || workBuffer_ == nullptr) {
151 HILOGE("[NormProcessor]Fail to allocate memory");
152 Release();
153 return RETCODE_FAILURE;
154 }
155 int32_t retCode = LoadMeanAndStd(config_.meanFilePath, config_.stdFilePath, mean_, std_, config_.numChannels);
156 if (retCode != RETCODE_SUCCESS) {
157 HILOGE("[NormProcessor]Fail to load mean and std");
158 Release();
159 return RETCODE_FAILURE;
160 }
161 isInitialized_ = true;
162 return RETCODE_SUCCESS;
163 }
164
Release()165 void NormProcessor::Release()
166 {
167 AIE_DELETE_ARRAY(workBuffer_);
168 AIE_DELETE_ARRAY(mean_);
169 AIE_DELETE_ARRAY(std_);
170 converter_ = nullptr;
171 isInitialized_ = false;
172 }
173
Process(const FeatureData & input,FeatureData & output)174 int32_t NormProcessor::Process(const FeatureData &input, FeatureData &output)
175 {
176 if (!isInitialized_) {
177 HILOGE("[NormProcessor]Fail to process without successfully init");
178 return RETCODE_FAILURE;
179 }
180 if (output.data != nullptr || output.size != 0) {
181 HILOGE("[NormProcessor]Fail with non-empty output");
182 return RETCODE_FAILURE;
183 }
184 if (input.data == nullptr || input.size == 0) {
185 HILOGE("[NormProcessor]Fail to process with empty input");
186 return RETCODE_FAILURE;
187 }
188 if (input.dataType == UNKNOWN) {
189 HILOGE("[NormProcessor]Fail to process with [UNKNOWN] dataType");
190 return RETCODE_FAILURE;
191 }
192 if (input.size != config_.inputSize) {
193 HILOGE("[NormProcessor]Fail with illegal input size");
194 return RETCODE_FAILURE;
195 }
196 if (input.dataType == FLOAT) {
197 output = input;
198 } else {
199 converter_->Process(input, output);
200 }
201 float *data = reinterpret_cast<float *>(output.data);
202 for (size_t i = 0; i < output.size; ++i) {
203 float stdVal = std_[i % config_.numChannels];
204 float meanVal = mean_[i % config_.numChannels];
205 workBuffer_[i] = (std::abs(stdVal) < EPSILON) ? 0.0f : ((data[i] - meanVal) / stdVal) * config_.scale;
206 }
207 output.data = static_cast<void *>(workBuffer_);
208 output.dataType = FLOAT;
209 return RETCODE_SUCCESS;
210 }