• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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