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 }