You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.en.md 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. # Demo of Object Detection
  2. The following describes how to use the MindSpore Lite C++ APIs (Android JNIs) and MindSpore Lite object detection models to perform on-device inference, detect the content captured by a device camera, and display the most possible detection result on the application's image preview screen.
  3. ## Running Dependencies
  4. - Android Studio 3.2 or later (Android 4.0 or later is recommended.)
  5. ## Building and Running
  6. 1. Load the sample source code to Android Studio.
  7. ![start_home](images/home.png)
  8. Start Android Studio, click `File > Settings > System Settings > Android SDK`, and select the corresponding `SDK Tools`. As shown in the following figure, select an SDK and click `OK`. Android Studio automatically installs the SDK.
  9. ![start_sdk](images/sdk_management.jpg)
  10. > Android SDK Tools is the default installation. You can see this by unchecking the `Hide Obsolete Packages`box.
  11. >
  12. > If you have any Android Studio configuration problem when trying this demo, please refer to item 4 to resolve it.
  13. 2. Connect to an Android device and runs this application.
  14. Connect to the Android device through a USB cable for debugging. Click `Run 'app'` to run the sample project on your device.
  15. ![run_app](images/run_app.PNG)
  16. > Android Studio will automatically download MindSpore Lite, model files and other dependencies during the compilation process. Please be patient during this process.
  17. >
  18. > For details about how to connect the Android Studio to a device for debugging, see <https://developer.android.com/studio/run/device?hl=zh-cn>.
  19. >
  20. > The mobile phone needs to be turn on "USB debugging mode" before Android Studio can recognize the mobile phone. Huawei mobile phones generally turn on "USB debugging model" in Settings -> system and update -> developer Options -> USB debugging.
  21. 3. Continue the installation on the Android device. After the installation is complete, you can view the content captured by a camera and the inference result.
  22. ![result](images/app_result.jpg)
  23. 4. The solutions of configuration problems:
  24. 4.1 Problems of NDK, CMake, JDK Tools:
  25. If the tools installed in Android Studio are not recognized, you can re-download and install them from the corresponding official website, and configure the path.
  26. - NDK >= 21.3 [NDK](https://developer.android.google.cn/ndk/downloads?hl=zh-cn)
  27. - CMake >= 3.10.2 [CMake](https://cmake.org/download)
  28. - Android SDK >= 26 [SDK](https://developer.microsoft.com/zh-cn/windows/downloads/windows-10-sdk/)
  29. - JDK >= 1.8 [JDK](https://www.oracle.com/cn/java/technologies/javase/javase-jdk8-downloads.html)
  30. ![project_structure](images/project_structure.png)
  31. 4.2 NDK version does not match:
  32. Open `Android SDK`, click `Show Package Details`, and select the appropriate NDK version according to the error message.
  33. ![NDK_version](images/NDK_version.jpg)
  34. 4.3 Problem of Android Studio version:
  35. Update the Android Studio version in `Toolbar - Help - Checkout for Updates`.
  36. 4.4 Gradle dependencies installed too slowly:
  37. As shown in the picture, open the Demo root directory `build. Gradle` file, then add huawei mirror source address: `maven {url 'https://developer.huawei.com/repo/'}`, modify the classpath to 4.0.0 and click ` sync ` . Once the download is complete, restore the classpath version and synchronize it again.
  38. ![maven](images/maven.jpg)
  39. ## Detailed Description of the Sample Program
  40. This object detection sample program on the Android device includes a Java layer and a JNI layer. At the Java layer, the Android Camera 2 API is used to enable a camera to obtain image frames and process images. At the JNI layer, the model inference process is completed .
  41. ### Configuring MindSpore Lite Dependencies
  42. When MindSpore C++ APIs are called at the Android JNI layer, related library files are required. You can use MindSpore Lite [source code compilation](https://www.mindspore.cn/tutorial/lite/en/master/use/build.html) to generate the MindSpore Lite version. In this case, you need to use the compile command of generate with image preprocessing module.
  43. In this example, the build process automatically downloads the `mindspore-lite-1.0.1-runtime-arm64-cpu` by the `app/download.gradle` file and saves in the `app/src/main/cpp` directory.
  44. Note: if the automatic download fails, please manually download the relevant library files and put them in the corresponding location.
  45. mindspore-lite-1.0.1-runtime-arm64-cpu.tar.gz [Download link](https://ms-release.obs.cn-north-4.myhuaweicloud.com/1.0.1/lite/android_aarch64/mindspore-lite-1.0.1-runtime-arm64-cpu.tar.gz)
  46. ```text
  47. android{
  48. defaultConfig{
  49. externalNativeBuild{
  50. cmake{
  51. arguments "-DANDROID_STL=c++_shared"
  52. }
  53. }
  54. ndk{
  55. abiFilters 'arm64-v8a'
  56. }
  57. }
  58. }
  59. ```
  60. Create a link to the `.so` library file in the `app/CMakeLists.txt` file:
  61. ```text
  62. # Set MindSpore Lite Dependencies.
  63. set(MINDSPORELITE_VERSION mindspore-lite-1.0.1-runtime-arm64-cpu)
  64. include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION})
  65. add_library(mindspore-lite SHARED IMPORTED )
  66. add_library(minddata-lite SHARED IMPORTED )
  67. set_target_properties(mindspore-lite PROPERTIES IMPORTED_LOCATION
  68. ${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/lib/libmindspore-lite.so)
  69. set_target_properties(minddata-lite PROPERTIES IMPORTED_LOCATION
  70. ${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/lib/libminddata-lite.so)
  71. # Link target library.
  72. target_link_libraries(
  73. ...
  74. mindspore-lite
  75. minddata-lite
  76. ...
  77. )
  78. ```
  79. ### Downloading and Deploying a Model File
  80. In this example, the download.gradle File configuration auto download `ssd.ms`and placed in the 'app / libs / arm64-v8a' directory.
  81. Note: if the automatic download fails, please manually download the relevant library files and put them in the corresponding location.
  82. ssd.ms [ssd.ms]( https://download.mindspore.cn/model_zoo/official/lite/ssd_mobilenetv2_lite/ssd.ms)
  83. ### Compiling On-Device Inference Code
  84. Call MindSpore Lite C++ APIs at the JNI layer to implement on-device inference.
  85. The inference code process is as follows. For details about the complete code, see `src/cpp/MindSporeNetnative.cpp`.
  86. 1. Load the MindSpore Lite model file and build the context, session, and computational graph for inference.
  87. - Load a model file. Create and configure the context for model inference.
  88. ```cpp
  89. // Buffer is the model data passed in by the Java layer
  90. jlong bufferLen = env->GetDirectBufferCapacity(buffer);
  91. char *modelBuffer = CreateLocalModelBuffer(env, buffer);
  92. ```
  93. - Create a session.
  94. ```cpp
  95. void **labelEnv = new void *;
  96. MSNetWork *labelNet = new MSNetWork;
  97. *labelEnv = labelNet;
  98. // Create context.
  99. lite::Context *context = new lite::Context;
  100. context->device_ctx_.type = lite::DT_CPU;
  101. context->thread_num_ = numThread; //Specify the number of threads to run inference
  102. // Create the mindspore session.
  103. labelNet->CreateSessionMS(modelBuffer, bufferLen, "device label", context);
  104. delete(context);
  105. ```
  106. - Load the model file and build a computational graph for inference.
  107. ```cpp
  108. void MSNetWork::CreateSessionMS(char* modelBuffer, size_t bufferLen, std::string name, mindspore::lite::Context* ctx)
  109. {
  110. CreateSession(modelBuffer, bufferLen, ctx);
  111. session = mindspore::session::LiteSession::CreateSession(ctx);
  112. auto model = mindspore::lite::Model::Import(modelBuffer, bufferLen);
  113. int ret = session->CompileGraph(model);
  114. }
  115. ```
  116. 2. Pre-Process the imagedata and convert the input image into the Tensor format of the MindSpore model.
  117. ```cpp
  118. // Convert the Bitmap image passed in from the JAVA layer to Mat for OpenCV processing
  119. LiteMat lite_mat_bgr,lite_norm_mat_cut;
  120. if (!BitmapToLiteMat(env, srcBitmap, lite_mat_bgr)){
  121. MS_PRINT("BitmapToLiteMat error");
  122. return NULL;
  123. }
  124. int srcImageWidth = lite_mat_bgr.width_;
  125. int srcImageHeight = lite_mat_bgr.height_;
  126. if(!PreProcessImageData(lite_mat_bgr, lite_norm_mat_cut)){
  127. MS_PRINT("PreProcessImageData error");
  128. return NULL;
  129. }
  130. ImgDims inputDims;
  131. inputDims.channel =lite_norm_mat_cut.channel_;
  132. inputDims.width = lite_norm_mat_cut.width_;
  133. inputDims.height = lite_norm_mat_cut.height_;
  134. // Get the mindsore inference environment which created in loadModel().
  135. void **labelEnv = reinterpret_cast<void **>(netEnv);
  136. if (labelEnv == nullptr) {
  137. MS_PRINT("MindSpore error, labelEnv is a nullptr.");
  138. return NULL;
  139. }
  140. MSNetWork *labelNet = static_cast<MSNetWork *>(*labelEnv);
  141. auto mSession = labelNet->session;
  142. if (mSession == nullptr) {
  143. MS_PRINT("MindSpore error, Session is a nullptr.");
  144. return NULL;
  145. }
  146. MS_PRINT("MindSpore get session.");
  147. auto msInputs = mSession->GetInputs();
  148. auto inTensor = msInputs.front();
  149. float *dataHWC = reinterpret_cast<float *>(lite_norm_mat_cut.data_ptr_);
  150. // copy input Tensor
  151. memcpy(inTensor->MutableData(), dataHWC,
  152. inputDims.channel * inputDims.width * inputDims.height * sizeof(float));
  153. delete[] (dataHWC);
  154. ```
  155. 3. The input image shall be NHWC(1:300:300:3).
  156. ```cpp
  157. bool PreProcessImageData(const LiteMat &lite_mat_bgr, LiteMat *lite_norm_mat_ptr) {
  158. bool ret = false;
  159. LiteMat lite_mat_resize;
  160. LiteMat &lite_norm_mat_cut = *lite_norm_mat_ptr;
  161. ret = ResizeBilinear(lite_mat_bgr, lite_mat_resize, 300, 300);
  162. if (!ret) {
  163. MS_PRINT("ResizeBilinear error");
  164. return false;
  165. }
  166. LiteMat lite_mat_convert_float;
  167. ret = ConvertTo(lite_mat_resize, lite_mat_convert_float, 1.0 / 255.0);
  168. if (!ret) {
  169. MS_PRINT("ConvertTo error");
  170. return false;
  171. }
  172. float means[3] = {0.485, 0.456, 0.406};
  173. float vars[3] = {1.0 / 0.229, 1.0 / 0.224, 1.0 / 0.225};
  174. SubStractMeanNormalize(lite_mat_convert_float, lite_norm_mat_cut, means, vars);
  175. return true;
  176. }
  177. ```
  178. 4. Perform inference on the input tensor based on the model, obtain the output tensor, and perform post-processing.
  179. Perform graph execution and on-device inference.
  180. ```cpp
  181. // After the model and image tensor data is loaded, run inference.
  182. auto status = mSession->RunGraph();
  183. ```
  184. Obtain the output data.
  185. ```cpp
  186. auto names = mSession->GetOutputTensorNames();
  187. typedef std::unordered_map<std::string,
  188. std::vector<mindspore::tensor::MSTensor *>> Msout;
  189. std::unordered_map<std::string,
  190. mindspore::tensor::MSTensor *> msOutputs;
  191. for (const auto &name : names) {
  192. auto temp_dat =mSession->GetOutputByTensorName(name);
  193. msOutputs.insert(std::pair<std::string, mindspore::tensor::MSTensor *> {name, temp_dat});
  194. }
  195. std::string retStr = ProcessRunnetResult(msOutputs, ret);
  196. ```
  197. The model output the object category scores (1:1917:81) and the object detection location offset (1:1917:4). The location offset can be calcalation the object location in getDefaultBoxes function .
  198. ```cpp
  199. void SSDModelUtil::getDefaultBoxes() {
  200. float fk[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
  201. std::vector<struct WHBox> all_sizes;
  202. struct Product mProductData[19 * 19] = {0};
  203. for (int i = 0; i < 6; i++) {
  204. fk[i] = config.model_input_height / config.steps[i];
  205. }
  206. float scale_rate =
  207. (config.max_scale - config.min_scale) / (sizeof(config.num_default) / sizeof(int) - 1);
  208. float scales[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0};
  209. for (int i = 0; i < sizeof(config.num_default) / sizeof(int); i++) {
  210. scales[i] = config.min_scale + scale_rate * i;
  211. }
  212. for (int idex = 0; idex < sizeof(config.feature_size) / sizeof(int); idex++) {
  213. float sk1 = scales[idex];
  214. float sk2 = scales[idex + 1];
  215. float sk3 = sqrt(sk1 * sk2);
  216. struct WHBox tempWHBox;
  217. all_sizes.clear();
  218. if (idex == 0) {
  219. float w = sk1 * sqrt(2);
  220. float h = sk1 / sqrt(2);
  221. tempWHBox.boxw = 0.1;
  222. tempWHBox.boxh = 0.1;
  223. all_sizes.push_back(tempWHBox);
  224. tempWHBox.boxw = w;
  225. tempWHBox.boxh = h;
  226. all_sizes.push_back(tempWHBox);
  227. tempWHBox.boxw = h;
  228. tempWHBox.boxh = w;
  229. all_sizes.push_back(tempWHBox);
  230. } else {
  231. tempWHBox.boxw = sk1;
  232. tempWHBox.boxh = sk1;
  233. all_sizes.push_back(tempWHBox);
  234. for (int j = 0; j < sizeof(config.aspect_ratios[idex]) / sizeof(int); j++) {
  235. float w = sk1 * sqrt(config.aspect_ratios[idex][j]);
  236. float h = sk1 / sqrt(config.aspect_ratios[idex][j]);
  237. tempWHBox.boxw = w;
  238. tempWHBox.boxh = h;
  239. all_sizes.push_back(tempWHBox);
  240. tempWHBox.boxw = h;
  241. tempWHBox.boxh = w;
  242. all_sizes.push_back(tempWHBox);
  243. }
  244. tempWHBox.boxw = sk3;
  245. tempWHBox.boxh = sk3;
  246. all_sizes.push_back(tempWHBox);
  247. }
  248. for (int i = 0; i < config.feature_size[idex]; i++) {
  249. for (int j = 0; j < config.feature_size[idex]; j++) {
  250. mProductData[i * config.feature_size[idex] + j].x = i;
  251. mProductData[i * config.feature_size[idex] + j].y = j;
  252. }
  253. }
  254. int productLen = config.feature_size[idex] * config.feature_size[idex];
  255. for (int i = 0; i < productLen; i++) {
  256. for (int j = 0; j < all_sizes.size(); j++) {
  257. struct NormalBox tempBox;
  258. float cx = (mProductData[i].y + 0.5) / fk[idex];
  259. float cy = (mProductData[i].x + 0.5) / fk[idex];
  260. tempBox.y = cy;
  261. tempBox.x = cx;
  262. tempBox.h = all_sizes[j].boxh;
  263. tempBox.w = all_sizes[j].boxw;
  264. mDefaultBoxes.push_back(tempBox);
  265. }
  266. }
  267. }
  268. }
  269. ```
  270. - The higher scores and location of category can be calcluted by the nonMaximumSuppression function.
  271. ```cpp
  272. void SSDModelUtil::nonMaximumSuppression(const YXBoxes *const decoded_boxes,
  273. const float *const scores,
  274. const std::vector<int> &in_indexes,
  275. std::vector<int> &out_indexes, const float nmsThreshold,
  276. const int count, const int max_results) {
  277. int nR = 0; //number of results
  278. std::vector<bool> del(count, false);
  279. for (size_t i = 0; i < in_indexes.size(); i++) {
  280. if (!del[in_indexes[i]]) {
  281. out_indexes.push_back(in_indexes[i]);
  282. if (++nR == max_results) {
  283. break;
  284. }
  285. for (size_t j = i + 1; j < in_indexes.size(); j++) {
  286. const auto boxi = decoded_boxes[in_indexes[i]], boxj = decoded_boxes[in_indexes[j]];
  287. float a[4] = {boxi.xmin, boxi.ymin, boxi.xmax, boxi.ymax};
  288. float b[4] = {boxj.xmin, boxj.ymin, boxj.xmax, boxj.ymax};
  289. if (IOU(a, b) > nmsThreshold) {
  290. del[in_indexes[j]] = true;
  291. }
  292. }
  293. }
  294. }
  295. }
  296. ```
  297. - For the targets whose probability is greater than the threshold value, the output rectangle box needs to be restored to the original size after the rectangular box is filtered by NMS algorithm.
  298. ```cpp
  299. std::string SSDModelUtil::getDecodeResult(float *branchScores, float *branchBoxData) {
  300. std::string result = "";
  301. NormalBox tmpBox[1917] = {0};
  302. float mScores[1917][81] = {0};
  303. float outBuff[1917][7] = {0};
  304. float scoreWithOneClass[1917] = {0};
  305. int outBoxNum = 0;
  306. YXBoxes decodedBoxes[1917] = {0};
  307. // Copy branch outputs box data to tmpBox.
  308. for (int i = 0; i < 1917; ++i) {
  309. tmpBox[i].y = branchBoxData[i * 4 + 0];
  310. tmpBox[i].x = branchBoxData[i * 4 + 1];
  311. tmpBox[i].h = branchBoxData[i * 4 + 2];
  312. tmpBox[i].w = branchBoxData[i * 4 + 3];
  313. }
  314. // Copy branch outputs score to mScores.
  315. for (int i = 0; i < 1917; ++i) {
  316. for (int j = 0; j < 81; ++j) {
  317. mScores[i][j] = branchScores[i * 81 + j];
  318. }
  319. }
  320. // NMS processing.
  321. ssd_boxes_decode(tmpBox, decodedBoxes);
  322. // const float nms_threshold = 0.6;
  323. const float nms_threshold = 0.3;
  324. for (int i = 1; i < 81; i++) {
  325. std::vector<int> in_indexes;
  326. for (int j = 0; j < 1917; j++) {
  327. scoreWithOneClass[j] = mScores[j][i];
  328. // if (mScores[j][i] > 0.1) {
  329. if (mScores[j][i] > g_thres_map[i]) {
  330. in_indexes.push_back(j);
  331. }
  332. }
  333. if (in_indexes.size() == 0) {
  334. continue;
  335. }
  336. sort(in_indexes.begin(), in_indexes.end(),
  337. [&](int a, int b) { return scoreWithOneClass[a] > scoreWithOneClass[b]; });
  338. std::vector<int> out_indexes;
  339. nonMaximumSuppression(decodedBoxes, scoreWithOneClass, in_indexes, out_indexes,
  340. nms_threshold);
  341. for (int k = 0; k < out_indexes.size(); k++) {
  342. outBuff[outBoxNum][0] = out_indexes[k]; //image id
  343. outBuff[outBoxNum][1] = i; //labelid
  344. outBuff[outBoxNum][2] = scoreWithOneClass[out_indexes[k]]; //scores
  345. outBuff[outBoxNum][3] =
  346. decodedBoxes[out_indexes[k]].xmin * inputImageWidth / 300;
  347. outBuff[outBoxNum][4] =
  348. decodedBoxes[out_indexes[k]].ymin * inputImageHeight / 300;
  349. outBuff[outBoxNum][5] =
  350. decodedBoxes[out_indexes[k]].xmax * inputImageWidth / 300;
  351. outBuff[outBoxNum][6] =
  352. decodedBoxes[out_indexes[k]].ymax * inputImageHeight / 300;
  353. outBoxNum++;
  354. }
  355. }
  356. MS_PRINT("outBoxNum %d", outBoxNum);
  357. for (int i = 0; i < outBoxNum; ++i) {
  358. std::string tmpid_str = std::to_string(outBuff[i][0]);
  359. result += tmpid_str; // image ID
  360. result += "_";
  361. // tmpid_str = std::to_string(outBuff[i][1]);
  362. MS_PRINT("label_classes i %d, outBuff %d",i, (int) outBuff[i][1]);
  363. tmpid_str = label_classes[(int) outBuff[i][1]];
  364. result += tmpid_str; // label id
  365. result += "_";
  366. tmpid_str = std::to_string(outBuff[i][2]);
  367. result += tmpid_str; // scores
  368. result += "_";
  369. tmpid_str = std::to_string(outBuff[i][3]);
  370. result += tmpid_str; // xmin
  371. result += "_";
  372. tmpid_str = std::to_string(outBuff[i][4]);
  373. result += tmpid_str; // ymin
  374. result += "_";
  375. tmpid_str = std::to_string(outBuff[i][5]);
  376. result += tmpid_str; // xmax
  377. result += "_";
  378. tmpid_str = std::to_string(outBuff[i][6]);
  379. result += tmpid_str; // ymax
  380. result += ";";
  381. }
  382. return result;
  383. }
  384. std::string SSDModelUtil::getDecodeResult(float *branchScores, float *branchBoxData) {
  385. std::string result = "";
  386. NormalBox tmpBox[1917] = {0};
  387. float mScores[1917][81] = {0};
  388. float outBuff[1917][7] = {0};
  389. float scoreWithOneClass[1917] = {0};
  390. int outBoxNum = 0;
  391. YXBoxes decodedBoxes[1917] = {0};
  392. // Copy branch outputs box data to tmpBox.
  393. for (int i = 0; i < 1917; ++i) {
  394. tmpBox[i].y = branchBoxData[i * 4 + 0];
  395. tmpBox[i].x = branchBoxData[i * 4 + 1];
  396. tmpBox[i].h = branchBoxData[i * 4 + 2];
  397. tmpBox[i].w = branchBoxData[i * 4 + 3];
  398. }
  399. // Copy branch outputs score to mScores.
  400. for (int i = 0; i < 1917; ++i) {
  401. for (int j = 0; j < 81; ++j) {
  402. mScores[i][j] = branchScores[i * 81 + j];
  403. }
  404. }
  405. ssd_boxes_decode(tmpBox, decodedBoxes);
  406. const float nms_threshold = 0.3;
  407. for (int i = 1; i < 81; i++) {
  408. std::vector<int> in_indexes;
  409. for (int j = 0; j < 1917; j++) {
  410. scoreWithOneClass[j] = mScores[j][i];
  411. if (mScores[j][i] > g_thres_map[i]) {
  412. in_indexes.push_back(j);
  413. }
  414. }
  415. if (in_indexes.size() == 0) {
  416. continue;
  417. }
  418. sort(in_indexes.begin(), in_indexes.end(),
  419. [&](int a, int b) { return scoreWithOneClass[a] > scoreWithOneClass[b]; });
  420. std::vector<int> out_indexes;
  421. nonMaximumSuppression(decodedBoxes, scoreWithOneClass, in_indexes, out_indexes,
  422. nms_threshold);
  423. for (int k = 0; k < out_indexes.size(); k++) {
  424. outBuff[outBoxNum][0] = out_indexes[k]; //image id
  425. outBuff[outBoxNum][1] = i; //labelid
  426. outBuff[outBoxNum][2] = scoreWithOneClass[out_indexes[k]]; //scores
  427. outBuff[outBoxNum][3] =
  428. decodedBoxes[out_indexes[k]].xmin * inputImageWidth / 300;
  429. outBuff[outBoxNum][4] =
  430. decodedBoxes[out_indexes[k]].ymin * inputImageHeight / 300;
  431. outBuff[outBoxNum][5] =
  432. decodedBoxes[out_indexes[k]].xmax * inputImageWidth / 300;
  433. outBuff[outBoxNum][6] =
  434. decodedBoxes[out_indexes[k]].ymax * inputImageHeight / 300;
  435. outBoxNum++;
  436. }
  437. }
  438. MS_PRINT("outBoxNum %d", outBoxNum);
  439. for (int i = 0; i < outBoxNum; ++i) {
  440. std::string tmpid_str = std::to_string(outBuff[i][0]);
  441. result += tmpid_str; // image ID
  442. result += "_";
  443. // tmpid_str = std::to_string(outBuff[i][1]);
  444. MS_PRINT("label_classes i %d, outBuff %d",i, (int) outBuff[i][1]);
  445. tmpid_str = label_classes[(int) outBuff[i][1]];
  446. result += tmpid_str; // label id
  447. result += "_";
  448. tmpid_str = std::to_string(outBuff[i][2]);
  449. result += tmpid_str; // scores
  450. result += "_";
  451. tmpid_str = std::to_string(outBuff[i][3]);
  452. result += tmpid_str; // xmin
  453. result += "_";
  454. tmpid_str = std::to_string(outBuff[i][4]);
  455. result += tmpid_str; // ymin
  456. result += "_";
  457. tmpid_str = std::to_string(outBuff[i][5]);
  458. result += tmpid_str; // xmax
  459. result += "_";
  460. tmpid_str = std::to_string(outBuff[i][6]);
  461. result += tmpid_str; // ymax
  462. result += ";";
  463. }
  464. return result;
  465. }
  466. ```