1 /**
2 * Copyright 2021-2022 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "tools/converter/config_parser/preprocess_parser.h"
18 #include <dirent.h>
19 #include <sys/stat.h>
20 #include <map>
21 #include <vector>
22 #include <algorithm>
23 #ifdef MSLITE_DEPS_OPENCV
24 #include "tools/converter/preprocess/opencv_utils.h"
25 #endif
26 #include "src/common/log_adapter.h"
27 #include "mindspore/lite/tools/common/string_util.h"
28 #include "include/errorcode.h"
29 #include "src/common/file_utils.h"
30
31 namespace mindspore {
32 namespace lite {
33 namespace {
34 constexpr int kMinSize = 0;
35 constexpr int kMaxSize = 65535;
36 } // namespace
ParseInputType(const std::string & input_type_str,preprocess::InputType * input_type)37 int PreprocessParser::ParseInputType(const std::string &input_type_str, preprocess::InputType *input_type) {
38 if (input_type_str == "IMAGE") {
39 (*input_type) = preprocess::IMAGE;
40 } else if (input_type_str == "BIN") {
41 (*input_type) = preprocess::BIN;
42 } else {
43 (*input_type) = preprocess::INPUT_TYPE_MAX;
44 MS_LOG(ERROR) << "INPUT ILLEGAL: input_type must be IMAGE|BIN.";
45 return RET_INPUT_PARAM_INVALID;
46 }
47 return RET_OK;
48 }
49
ParsePreprocess(const DataPreProcessString & data_pre_process_str,preprocess::DataPreProcessParam * data_pre_process)50 int PreprocessParser::ParsePreprocess(const DataPreProcessString &data_pre_process_str,
51 preprocess::DataPreProcessParam *data_pre_process) {
52 int ret;
53 if (!data_pre_process_str.calibrate_path.empty()) {
54 ret = ParseCalibratePath(data_pre_process_str.calibrate_path, &data_pre_process->calibrate_path);
55 if (ret != RET_OK) {
56 MS_LOG(ERROR) << "Parse calibrate path failed.";
57 return ret;
58 }
59 }
60
61 if (!data_pre_process_str.calibrate_size.empty()) {
62 if (!ConvertIntNum(data_pre_process_str.calibrate_size, &data_pre_process->calibrate_size)) {
63 MS_LOG(ERROR) << "calibrate_size should be a valid number.";
64 return RET_INPUT_PARAM_INVALID;
65 }
66 if (data_pre_process->calibrate_size <= kMinSize || data_pre_process->calibrate_size > kMaxSize) {
67 MS_LOG(ERROR) << "calibrate size must pass and the size should in [1, 65535].";
68 return RET_INPUT_PARAM_INVALID;
69 }
70 }
71
72 if (!data_pre_process_str.input_type.empty()) {
73 ret = ParseInputType(data_pre_process_str.input_type, &data_pre_process->input_type);
74 if (ret != RET_OK || data_pre_process->input_type == preprocess::INPUT_TYPE_MAX) {
75 MS_LOG(ERROR) << "input_type must pass IMAGE | BIN.";
76 return RET_INPUT_PARAM_INVALID;
77 }
78 }
79 if (!data_pre_process_str.image_to_format.empty()) {
80 ret =
81 ParseImageToFormat(data_pre_process_str.image_to_format, &data_pre_process->image_pre_process.image_to_format);
82 if (ret != RET_OK) {
83 MS_LOG(ERROR) << "image preprocess parse failed.";
84 return ret;
85 }
86 #ifdef MSLITE_DEPS_OPENCV
87 if (data_pre_process->image_pre_process.image_to_format == preprocess::RGB ||
88 data_pre_process->image_pre_process.image_to_format == preprocess::GRAY) {
89 data_pre_process->image_pre_process.image_to_format_code =
90 preprocess::ConvertColorConversionCodes(data_pre_process->image_pre_process.image_to_format);
91 }
92 #endif
93 }
94 if (!data_pre_process_str.calibrate_path.empty() && !data_pre_process_str.calibrate_size.empty()) {
95 ret = ParseImagePreProcess(data_pre_process_str, &data_pre_process->image_pre_process);
96 if (ret != RET_OK) {
97 MS_LOG(ERROR) << "image preprocess parse failed.";
98 return ret;
99 }
100
101 ret = CollectCalibInputs(data_pre_process->calibrate_path, data_pre_process->calibrate_size,
102 &data_pre_process->calibrate_path_vector);
103 if (ret != RET_OK) {
104 MS_LOG(ERROR) << "collect calibrate inputs failed.";
105 return ret;
106 }
107 }
108 return RET_OK;
109 }
110
ParseCalibratePath(const std::string & str,std::map<std::string,std::string> * value)111 int PreprocessParser::ParseCalibratePath(const std::string &str, std::map<std::string, std::string> *value) {
112 if (value == nullptr) {
113 MS_LOG(ERROR) << "value is nullptr.";
114 return RET_ERROR;
115 }
116 auto key_values = SplitStringToVector(str, ',');
117 for (const auto &key_value : key_values) {
118 auto string_split = SplitStringToVector(key_value, ':');
119 const size_t min_size = 2;
120 if (string_split.size() < min_size) {
121 MS_LOG(ERROR) << "vector need size >= 2, size is " << string_split.size();
122 return RET_INPUT_PARAM_INVALID;
123 }
124 auto string_split_quotation = SplitStringToVector(key_value, '\'');
125 if (string_split_quotation.size() == 1) {
126 auto name = string_split.at(0);
127 for (size_t i = 1; i < string_split.size() - 1; ++i) {
128 name += ":" + string_split.at(i);
129 }
130 if (name.empty()) {
131 MS_LOG(ERROR) << "path is invalid.";
132 return RET_INPUT_PARAM_INVALID;
133 }
134 (*value)[name] = string_split.at(string_split.size() - 1);
135 } else {
136 auto name = string_split.at(0);
137 for (size_t i = 1; i < string_split.size() - 1; ++i) {
138 name += ":" + string_split.at(i);
139 }
140 (*value)[name] = string_split_quotation.at(string_split_quotation.size() - 1);
141 }
142 }
143 return RET_OK;
144 }
145
ParseImagePreProcess(const DataPreProcessString & data_pre_process_str,preprocess::ImagePreProcessParam * image_pre_process)146 int PreprocessParser::ParseImagePreProcess(const DataPreProcessString &data_pre_process_str,
147 preprocess::ImagePreProcessParam *image_pre_process) {
148 auto ret = ParseImageNormalize(data_pre_process_str, image_pre_process);
149 if (ret != RET_OK) {
150 MS_LOG(ERROR) << "Parse image normalize failed.";
151 return ret;
152 }
153 ret = ParseImageCenterCrop(data_pre_process_str, image_pre_process);
154 if (ret != RET_OK) {
155 MS_LOG(ERROR) << "Parse image center crop failed.";
156 return ret;
157 }
158 ret = ParseImageResize(data_pre_process_str, image_pre_process);
159 if (ret != RET_OK) {
160 MS_LOG(ERROR) << "Parse image resize failed.";
161 return ret;
162 }
163 return RET_OK;
164 }
165
ParseImageToFormat(const std::string & image_to_format_str,preprocess::ImageToFormat * image_to_format)166 int PreprocessParser::ParseImageToFormat(const std::string &image_to_format_str,
167 preprocess::ImageToFormat *image_to_format) {
168 if (image_to_format_str == "RGB") {
169 (*image_to_format) = preprocess::RGB;
170 } else if (image_to_format_str == "GRAY") {
171 (*image_to_format) = preprocess::GRAY;
172 } else if (image_to_format_str == "BGR") {
173 (*image_to_format) = preprocess::BGR;
174 } else {
175 (*image_to_format) = preprocess::IMAGE_TO_FORMAT_MAX;
176 MS_LOG(ERROR) << "INPUT ILLEGAL: image_to_format must be RGB|GRAY|BGR.";
177 return RET_INPUT_PARAM_INVALID;
178 }
179 return RET_OK;
180 }
181
CollectCalibInputs(const std::map<std::string,std::string> & calibrate_data_path,size_t limited_count,std::map<std::string,std::vector<std::string>> * inputs)182 int PreprocessParser::CollectCalibInputs(const std::map<std::string, std::string> &calibrate_data_path,
183 size_t limited_count,
184 std::map<std::string, std::vector<std::string>> *inputs) {
185 if (inputs == nullptr) {
186 MS_LOG(ERROR) << "inputs is null";
187 return RET_ERROR;
188 }
189
190 auto AddImage = [&inputs](const std::string &file, const std::string &input_name) {
191 struct stat buf {};
192 if (stat(file.c_str(), &buf) == 0) {
193 (*inputs)[input_name].push_back(file);
194 } else {
195 MS_LOG(WARNING) << "invalid image file path: " << file;
196 }
197 };
198
199 for (const auto &image_path : calibrate_data_path) {
200 std::vector<std::string> file_names;
201 auto ret = ReadDirectory(image_path.second, &file_names);
202 if (ret != RET_OK) {
203 return ret;
204 }
205 MS_ASSERT(file_names.size() >= kDotDirCount);
206 if (file_names.size() < (limited_count + kDotDirCount)) {
207 MS_LOG(ERROR) << "file count less than calibrate size, file count: " << file_names.size()
208 << " limited_count: " << limited_count << " kDotDirCount: " << kDotDirCount;
209 return RET_ERROR;
210 }
211 for (size_t index = 0; index < (limited_count + kDotDirCount); index++) {
212 if (file_names[index] == "." || file_names[index] == "..") {
213 continue;
214 }
215 const std::string file_path = image_path.second + "/" + file_names[index];
216 MS_LOG(DEBUG) << "calibrate file_path: " << file_path;
217 AddImage(file_path, image_path.first);
218 }
219 }
220 return RET_OK;
221 }
222
ParseImageNormalize(const DataPreProcessString & data_pre_process_str,preprocess::ImagePreProcessParam * image_pre_process)223 int PreprocessParser::ParseImageNormalize(const DataPreProcessString &data_pre_process_str,
224 preprocess::ImagePreProcessParam *image_pre_process) {
225 if (!data_pre_process_str.normalize_mean.empty() &&
226 !ConvertDoubleVector(data_pre_process_str.normalize_mean, &image_pre_process->normalize_mean)) {
227 MS_LOG(ERROR) << "Convert normalize_mean failed.";
228 return RET_INPUT_PARAM_INVALID;
229 }
230
231 if (!data_pre_process_str.normalize_std.empty() &&
232 !ConvertDoubleVector(data_pre_process_str.normalize_std, &image_pre_process->normalize_std)) {
233 MS_LOG(ERROR) << "Convert normalize_std failed.";
234 return RET_INPUT_PARAM_INVALID;
235 }
236
237 return RET_OK;
238 }
239
ParseImageResize(const DataPreProcessString & data_pre_process_str,preprocess::ImagePreProcessParam * image_pre_process)240 int PreprocessParser::ParseImageResize(const DataPreProcessString &data_pre_process_str,
241 preprocess::ImagePreProcessParam *image_pre_process) {
242 if (!data_pre_process_str.resize_width.empty()) {
243 if (!ConvertIntNum(data_pre_process_str.resize_width, &image_pre_process->resize_width)) {
244 MS_LOG(ERROR) << "resize_width should be a valid number.";
245 return RET_INPUT_PARAM_INVALID;
246 }
247 if (image_pre_process->resize_width <= kMinSize || image_pre_process->resize_width > kMaxSize) {
248 MS_LOG(ERROR) << "resize_width must be in [1, 65535].";
249 return RET_INPUT_PARAM_INVALID;
250 }
251 }
252 if (!data_pre_process_str.resize_height.empty()) {
253 if (!ConvertIntNum(data_pre_process_str.resize_height, &image_pre_process->resize_height)) {
254 MS_LOG(ERROR) << "resize_height should be a valid number.";
255 return RET_INPUT_PARAM_INVALID;
256 }
257 if (image_pre_process->resize_height <= kMinSize || image_pre_process->resize_height > kMaxSize) {
258 MS_LOG(ERROR) << "resize_height must be in [1, 65535].";
259 return RET_INPUT_PARAM_INVALID;
260 }
261 }
262
263 #ifdef MSLITE_DEPS_OPENCV
264 if (!data_pre_process_str.resize_method.empty()) {
265 image_pre_process->resize_method = preprocess::ConvertResizeMethod(data_pre_process_str.resize_method);
266 if (image_pre_process->resize_method == cv::INTER_MAX) {
267 MS_LOG(ERROR) << "INPUT ILLEGAL: resize_method must be NEAREST|LINEAR|CUBIC.";
268 return RET_INPUT_PARAM_INVALID;
269 }
270 }
271 #endif
272 return RET_OK;
273 }
274
ParseImageCenterCrop(const DataPreProcessString & data_pre_process_str,preprocess::ImagePreProcessParam * image_pre_process)275 int PreprocessParser::ParseImageCenterCrop(const DataPreProcessString &data_pre_process_str,
276 preprocess::ImagePreProcessParam *image_pre_process) {
277 if (!data_pre_process_str.center_crop_width.empty()) {
278 if (!ConvertIntNum(data_pre_process_str.center_crop_width, &image_pre_process->center_crop_width)) {
279 MS_LOG(ERROR) << "center_crop_width should be a valid number.";
280 return RET_INPUT_PARAM_INVALID;
281 }
282 if (image_pre_process->center_crop_width <= kMinSize || image_pre_process->center_crop_width > kMaxSize) {
283 MS_LOG(ERROR) << "center_crop_width must be in [1, 65535].";
284 return RET_INPUT_PARAM_INVALID;
285 }
286 }
287 if (!data_pre_process_str.center_crop_height.empty()) {
288 if (!ConvertIntNum(data_pre_process_str.center_crop_height, &image_pre_process->center_crop_height)) {
289 MS_LOG(ERROR) << "center_crop_height should be a valid number.";
290 return RET_INPUT_PARAM_INVALID;
291 }
292 if (image_pre_process->center_crop_height <= kMinSize || image_pre_process->center_crop_height > kMaxSize) {
293 MS_LOG(ERROR) << "center_crop_height must be in [1, 65535].";
294 return RET_INPUT_PARAM_INVALID;
295 }
296 }
297 return RET_OK;
298 }
299
ReadDirectory(const std::string & path,std::vector<std::string> * file_names)300 int PreprocessParser::ReadDirectory(const std::string &path, std::vector<std::string> *file_names) {
301 if (file_names == nullptr) {
302 MS_LOG(ERROR) << "file_names is null";
303 return RET_ERROR;
304 }
305
306 DIR *dp = opendir(path.empty() ? "." : RealPath(path.c_str()).c_str());
307 if (dp == nullptr) {
308 MS_LOG(ERROR) << "cant open dir: " << path;
309 return RET_PARAM_INVALID;
310 }
311 size_t file_count = 0;
312 while (true) {
313 if (file_count >= kFileCountLimit) {
314 break;
315 }
316 struct dirent *de = readdir(dp);
317 if (de == nullptr) {
318 break;
319 }
320 file_names->push_back(std::string(de->d_name));
321 file_count++;
322 }
323
324 auto ret = closedir(dp);
325 if (ret != 0) {
326 MS_LOG(ERROR) << " close dir failed.";
327 return RET_ERROR;
328 }
329
330 if (file_count >= kFileCountLimit) {
331 MS_LOG(ERROR) << " read calibrate directory failed, files count exceed limit: " << kFileCountLimit;
332 return RET_ERROR;
333 }
334 std::sort(file_names->begin(), file_names->end());
335 return RET_OK;
336 }
337 } // namespace lite
338 } // namespace mindspore
339