1# 使用MindSpore Lite实现图像分类(C/C++) 2 3## 场景说明 4 5开发者可以使用[MindSpore](../../reference/apis-mindspore-lite-kit/_mind_spore.md),在UI代码中直接集成MindSpore Lite能力,快速部署AI算法,进行AI模型推理,实现图像分类的应用。 6 7图像分类可实现对图像中物体的识别,在医学影像分析、自动驾驶、电子商务、人脸识别等有广泛的应用。 8 9## 基本概念 10 11- N-API:用于构建ArkTS本地化组件的一套接口。可利用N-API,将C/C++开发的库封装成ArkTS模块。 12 13## 开发流程 14 151. 选择图像分类模型。 162. 在端侧使用MindSpore Lite推理模型,实现对选择的图片进行分类。 17 18## 环境准备 19 20安装DevEco Studio,要求版本 >= 4.1,并更新SDK到API 11或以上。 21 22## 开发步骤 23 24本文以对相册的一张图片进行推理为例,提供使用MindSpore Lite实现图像分类的开发指导。 25 26### 选择模型 27 28本示例程序中使用的图像分类模型文件为[mobilenetv2.ms](https://download.mindspore.cn/model_zoo/official/lite/mobilenetv2_openimage_lite/1.5/mobilenetv2.ms),放置在entry/src/main/resources/rawfile工程目录下。 29 30如果开发者有其他图像分类的预训练模型,请参考[MindSpore Lite 模型转换](mindspore-lite-converter-guidelines.md)介绍,将原始模型转换成.ms格式。 31 32### 编写代码 33 34#### 图像输入和预处理 35 361. 此处以获取相册图片为例,调用[@ohos.file.picker](../../reference/apis-core-file-kit/js-apis-file-picker.md) 实现相册图片文件的选择。 37 38 ```ts 39 import { photoAccessHelper } from '@kit.MediaLibraryKit'; 40 import { BusinessError } from '@kit.BasicServicesKit'; 41 42 let uris: Array<string> = []; 43 44 // 创建图片文件选择实例 45 let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); 46 47 // 设置选择媒体文件类型为IMAGE,设置选择媒体文件的最大数目 48 photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; 49 photoSelectOptions.maxSelectNumber = 1; 50 51 // 创建图库选择器实例,调用select()接口拉起图库界面进行文件选择。文件选择成功后,返回photoSelectResult结果集。 52 let photoPicker = new photoAccessHelper.PhotoViewPicker(); 53 photoPicker.select(photoSelectOptions, async ( 54 err: BusinessError, photoSelectResult: photoAccessHelper.PhotoSelectResult) => { 55 if (err) { 56 console.error('MS_LITE_ERR: PhotoViewPicker.select failed with err: ' + JSON.stringify(err)); 57 return; 58 } 59 console.info('MS_LITE_LOG: PhotoViewPicker.select successfully, ' + 60 'photoSelectResult uri: ' + JSON.stringify(photoSelectResult)); 61 uris = photoSelectResult.photoUris; 62 console.info('MS_LITE_LOG: uri: ' + uris); 63 }) 64 ``` 65 662. 根据模型的输入尺寸,调用[@ohos.multimedia.image](../../reference/apis-image-kit/js-apis-image.md) (实现图片处理)、[@ohos.file.fs](../../reference/apis-core-file-kit/js-apis-file-fs.md) (实现基础文件操作) API对选择图片进行裁剪、获取图片buffer数据,并进行标准化处理。 67 68 ```ts 69 import { image } from '@kit.ImageKit'; 70 import { fileIo } from '@kit.CoreFileKit'; 71 72 let modelInputHeight: number = 224; 73 let modelInputWidth: number = 224; 74 75 // 使用fileIo.openSync接口,通过uri打开这个文件得到fd 76 let file = fileIo.openSync(this.uris[0], fileIo.OpenMode.READ_ONLY); 77 console.info('MS_LITE_LOG: file fd: ' + file.fd); 78 79 // 通过fd使用fileIo.readSync接口读取这个文件内的数据 80 let inputBuffer = new ArrayBuffer(4096000); 81 let readLen = fileIo.readSync(file.fd, inputBuffer); 82 console.info('MS_LITE_LOG: readSync data to file succeed and inputBuffer size is:' + readLen); 83 84 // 通过PixelMap预处理 85 let imageSource = image.createImageSource(file.fd); 86 imageSource.createPixelMap().then((pixelMap) => { 87 pixelMap.getImageInfo().then((info) => { 88 console.info('MS_LITE_LOG: info.width = ' + info.size.width); 89 console.info('MS_LITE_LOG: info.height = ' + info.size.height); 90 // 根据模型输入的尺寸,将图片裁剪为对应的size,获取图片buffer数据readBuffer 91 pixelMap.scale(256.0 / info.size.width, 256.0 / info.size.height).then(() => { 92 pixelMap.crop( 93 { x: 16, y: 16, size: { height: modelInputHeight, width: modelInputWidth } } 94 ).then(async () => { 95 let info = await pixelMap.getImageInfo(); 96 console.info('MS_LITE_LOG: crop info.width = ' + info.size.width); 97 console.info('MS_LITE_LOG: crop info.height = ' + info.size.height); 98 // 需要创建的像素buffer大小 99 let readBuffer = new ArrayBuffer(modelInputHeight * modelInputWidth * 4); 100 await pixelMap.readPixelsToBuffer(readBuffer); 101 console.info('MS_LITE_LOG: Succeeded in reading image pixel data, buffer: ' + 102 readBuffer.byteLength); 103 // 处理readBuffer,转换成float32格式,并进行标准化处理 104 const imageArr = new Uint8Array( 105 readBuffer.slice(0, modelInputHeight * modelInputWidth * 4)); 106 console.info('MS_LITE_LOG: imageArr length: ' + imageArr.length); 107 let means = [0.485, 0.456, 0.406]; 108 let stds = [0.229, 0.224, 0.225]; 109 let float32View = new Float32Array(modelInputHeight * modelInputWidth * 3); 110 let index = 0; 111 for (let i = 0; i < imageArr.length; i++) { 112 if ((i + 1) % 4 == 0) { 113 float32View[index] = (imageArr[i - 3] / 255.0 - means[0]) / stds[0]; // B 114 float32View[index+1] = (imageArr[i - 2] / 255.0 - means[1]) / stds[1]; // G 115 float32View[index+2] = (imageArr[i - 1] / 255.0 - means[2]) / stds[2]; // R 116 index += 3; 117 } 118 } 119 console.info('MS_LITE_LOG: float32View length: ' + float32View.length); 120 let printStr = 'float32View data:'; 121 for (let i = 0; i < 20; i++) { 122 printStr += ' ' + float32View[i]; 123 } 124 console.info('MS_LITE_LOG: float32View data: ' + printStr); 125 }) 126 }) 127 }); 128 }); 129 ``` 130 131#### 编写推理代码 132 133调用[MindSpore](../../reference/apis-mindspore-lite-kit/_mind_spore.md)实现端侧推理,推理代码流程如下。 134 1351. 引用对应的头文件 136 137 ```c++ 138 #include <iostream> 139 #include <sstream> 140 #include <stdlib.h> 141 #include <hilog/log.h> 142 #include <rawfile/raw_file_manager.h> 143 #include <mindspore/types.h> 144 #include <mindspore/model.h> 145 #include <mindspore/context.h> 146 #include <mindspore/status.h> 147 #include <mindspore/tensor.h> 148 #include "napi/native_api.h" 149 ``` 150 1512. 读取模型文件 152 153 ```c++ 154 #define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "[MSLiteNapi]", __VA_ARGS__)) 155 #define LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "[MSLiteNapi]", __VA_ARGS__)) 156 #define LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, "[MSLiteNapi]", __VA_ARGS__)) 157 #define LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "[MSLiteNapi]", __VA_ARGS__)) 158 159 void *ReadModelFile(NativeResourceManager *nativeResourceManager, const std::string &modelName, size_t *modelSize) { 160 auto rawFile = OH_ResourceManager_OpenRawFile(nativeResourceManager, modelName.c_str()); 161 if (rawFile == nullptr) { 162 LOGE("MS_LITE_ERR: Open model file failed"); 163 return nullptr; 164 } 165 long fileSize = OH_ResourceManager_GetRawFileSize(rawFile); 166 void *modelBuffer = malloc(fileSize); 167 if (modelBuffer == nullptr) { 168 LOGE("MS_LITE_ERR: OH_ResourceManager_ReadRawFile failed"); 169 } 170 int ret = OH_ResourceManager_ReadRawFile(rawFile, modelBuffer, fileSize); 171 if (ret == 0) { 172 LOGI("MS_LITE_LOG: OH_ResourceManager_ReadRawFile failed"); 173 OH_ResourceManager_CloseRawFile(rawFile); 174 return nullptr; 175 } 176 OH_ResourceManager_CloseRawFile(rawFile); 177 *modelSize = fileSize; 178 return modelBuffer; 179 } 180 ``` 181 1823. 创建上下文,设置线程数、设备类型等参数,并加载模型。 183 184 ```c++ 185 void DestroyModelBuffer(void **buffer) { 186 if (buffer == nullptr) { 187 return; 188 } 189 free(*buffer); 190 *buffer = nullptr; 191 } 192 193 OH_AI_ContextHandle CreateMSLiteContext(void *modelBuffer) { 194 // Set executing context for model. 195 auto context = OH_AI_ContextCreate(); 196 if (context == nullptr) { 197 DestroyModelBuffer(&modelBuffer); 198 LOGE("MS_LITE_ERR: Create MSLite context failed.\n"); 199 return nullptr; 200 } 201 auto cpu_device_info = OH_AI_DeviceInfoCreate(OH_AI_DEVICETYPE_CPU); 202 203 OH_AI_DeviceInfoSetEnableFP16(cpu_device_info, true); 204 OH_AI_ContextAddDeviceInfo(context, cpu_device_info); 205 206 LOGI("MS_LITE_LOG: Build MSLite context success.\n"); 207 return context; 208 } 209 210 OH_AI_ModelHandle CreateMSLiteModel(void *modelBuffer, size_t modelSize, OH_AI_ContextHandle context) { 211 // Create model 212 auto model = OH_AI_ModelCreate(); 213 if (model == nullptr) { 214 DestroyModelBuffer(&modelBuffer); 215 LOGE("MS_LITE_ERR: Allocate MSLite Model failed.\n"); 216 return nullptr; 217 } 218 219 // Build model object 220 auto build_ret = OH_AI_ModelBuild(model, modelBuffer, modelSize, OH_AI_MODELTYPE_MINDIR, context); 221 DestroyModelBuffer(&modelBuffer); 222 if (build_ret != OH_AI_STATUS_SUCCESS) { 223 OH_AI_ModelDestroy(&model); 224 LOGE("MS_LITE_ERR: Build MSLite model failed.\n"); 225 return nullptr; 226 } 227 LOGI("MS_LITE_LOG: Build MSLite model success.\n"); 228 return model; 229 } 230 ``` 231 2324. 设置模型输入数据,执行模型推理。 233 234 ```c++ 235 constexpr int K_NUM_PRINT_OF_OUT_DATA = 20; 236 237 // 设置模型输入数据 238 int FillInputTensor(OH_AI_TensorHandle input, std::vector<float> input_data) { 239 if (OH_AI_TensorGetDataType(input) == OH_AI_DATATYPE_NUMBERTYPE_FLOAT32) { 240 float *data = (float *)OH_AI_TensorGetMutableData(input); 241 for (size_t i = 0; i < OH_AI_TensorGetElementNum(input); i++) { 242 data[i] = input_data[i]; 243 } 244 return OH_AI_STATUS_SUCCESS; 245 } else { 246 return OH_AI_STATUS_LITE_ERROR; 247 } 248 } 249 250 // 执行模型推理 251 int RunMSLiteModel(OH_AI_ModelHandle model, std::vector<float> input_data) { 252 // Set input data for model. 253 auto inputs = OH_AI_ModelGetInputs(model); 254 255 auto ret = FillInputTensor(inputs.handle_list[0], input_data); 256 if (ret != OH_AI_STATUS_SUCCESS) { 257 LOGE("MS_LITE_ERR: RunMSLiteModel set input error.\n"); 258 return OH_AI_STATUS_LITE_ERROR; 259 } 260 // Get model output. 261 auto outputs = OH_AI_ModelGetOutputs(model); 262 // Predict model. 263 auto predict_ret = OH_AI_ModelPredict(model, inputs, &outputs, nullptr, nullptr); 264 if (predict_ret != OH_AI_STATUS_SUCCESS) { 265 LOGE("MS_LITE_ERR: MSLite Predict error.\n"); 266 return OH_AI_STATUS_LITE_ERROR; 267 } 268 LOGI("MS_LITE_LOG: Run MSLite model Predict success.\n"); 269 // Print output tensor data. 270 LOGI("MS_LITE_LOG: Get model outputs:\n"); 271 for (size_t i = 0; i < outputs.handle_num; i++) { 272 auto tensor = outputs.handle_list[i]; 273 LOGI("MS_LITE_LOG: - Tensor %{public}d name is: %{public}s.\n", static_cast<int>(i), 274 OH_AI_TensorGetName(tensor)); 275 LOGI("MS_LITE_LOG: - Tensor %{public}d size is: %{public}d.\n", static_cast<int>(i), 276 (int)OH_AI_TensorGetDataSize(tensor)); 277 LOGI("MS_LITE_LOG: - Tensor data is:\n"); 278 auto out_data = reinterpret_cast<const float *>(OH_AI_TensorGetData(tensor)); 279 std::stringstream outStr; 280 for (int i = 0; (i < OH_AI_TensorGetElementNum(tensor)) && (i <= K_NUM_PRINT_OF_OUT_DATA); i++) { 281 outStr << out_data[i] << " "; 282 } 283 LOGI("MS_LITE_LOG: %{public}s", outStr.str().c_str()); 284 } 285 return OH_AI_STATUS_SUCCESS; 286 } 287 ``` 288 2895. 调用以上方法,实现完整的模型推理流程。 290 291 ```c++ 292 static napi_value RunDemo(napi_env env, napi_callback_info info) { 293 LOGI("MS_LITE_LOG: Enter runDemo()"); 294 napi_value error_ret; 295 napi_create_int32(env, -1, &error_ret); 296 // 传入数据处理 297 size_t argc = 2; 298 napi_value argv[2] = {nullptr}; 299 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); 300 bool isArray = false; 301 napi_is_array(env, argv[0], &isArray); 302 uint32_t length = 0; 303 // 获取数组的长度 304 napi_get_array_length(env, argv[0], &length); 305 LOGI("MS_LITE_LOG: argv array length = %{public}d", length); 306 std::vector<float> input_data; 307 double param = 0; 308 for (int i = 0; i < length; i++) { 309 napi_value value; 310 napi_get_element(env, argv[0], i, &value); 311 napi_get_value_double(env, value, ¶m); 312 input_data.push_back(static_cast<float>(param)); 313 } 314 std::stringstream outstr; 315 for (int i = 0; i < K_NUM_PRINT_OF_OUT_DATA; i++) { 316 outstr << input_data[i] << " "; 317 } 318 LOGI("MS_LITE_LOG: input_data = %{public}s", outstr.str().c_str()); 319 // Read model file 320 const std::string modelName = "mobilenetv2.ms"; 321 LOGI("MS_LITE_LOG: Run model: %{public}s", modelName.c_str()); 322 size_t modelSize; 323 auto resourcesManager = OH_ResourceManager_InitNativeResourceManager(env, argv[1]); 324 auto modelBuffer = ReadModelFile(resourcesManager, modelName, &modelSize); 325 if (modelBuffer == nullptr) { 326 LOGE("MS_LITE_ERR: Read model failed"); 327 return error_ret; 328 } 329 LOGI("MS_LITE_LOG: Read model file success"); 330 331 auto context = CreateMSLiteContext(modelBuffer); 332 if (context == nullptr) { 333 LOGE("MS_LITE_ERR: MSLiteFwk Build context failed.\n"); 334 return error_ret; 335 } 336 auto model = CreateMSLiteModel(modelBuffer, modelSize, context); 337 if (model == nullptr) { 338 OH_AI_ContextDestroy(&context); 339 LOGE("MS_LITE_ERR: MSLiteFwk Build model failed.\n"); 340 return error_ret; 341 } 342 int ret = RunMSLiteModel(model, input_data); 343 if (ret != OH_AI_STATUS_SUCCESS) { 344 OH_AI_ModelDestroy(&model); 345 OH_AI_ContextDestroy(&context); 346 LOGE("MS_LITE_ERR: RunMSLiteModel failed.\n"); 347 return error_ret; 348 } 349 napi_value out_data; 350 napi_create_array(env, &out_data); 351 auto outputs = OH_AI_ModelGetOutputs(model); 352 OH_AI_TensorHandle output_0 = outputs.handle_list[0]; 353 float *output0Data = reinterpret_cast<float *>(OH_AI_TensorGetMutableData(output_0)); 354 for (size_t i = 0; i < OH_AI_TensorGetElementNum(output_0); i++) { 355 napi_value element; 356 napi_create_double(env, static_cast<double>(output0Data[i]), &element); 357 napi_set_element(env, out_data, i, element); 358 } 359 OH_AI_ModelDestroy(&model); 360 OH_AI_ContextDestroy(&context); 361 LOGI("MS_LITE_LOG: Exit runDemo()"); 362 return out_data; 363 } 364 ``` 365 3666. 编写CMake脚本,链接MindSpore Lite动态库。 367 368 ```c++ 369 # the minimum version of CMake. 370 cmake_minimum_required(VERSION 3.4.1) 371 project(MindSporeLiteCDemo) 372 373 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 374 375 if(DEFINED PACKAGE_FIND_FILE) 376 include(${PACKAGE_FIND_FILE}) 377 endif() 378 379 include_directories(${NATIVERENDER_ROOT_PATH} 380 ${NATIVERENDER_ROOT_PATH}/include) 381 382 add_library(entry SHARED mslite_napi.cpp) 383 target_link_libraries(entry PUBLIC mindspore_lite_ndk) 384 target_link_libraries(entry PUBLIC hilog_ndk.z) 385 target_link_libraries(entry PUBLIC rawfile.z) 386 target_link_libraries(entry PUBLIC ace_napi.z) 387 ``` 388 389#### 使用N-API将C++动态库封装成ArkTS模块 390 3911. 在 entry/src/main/cpp/types/libentry/Index.d.ts,定义ArkTS接口`runDemo()` 。内容如下: 392 393 ```ts 394 export const runDemo: (a: number[], b:Object) => Array<number>; 395 ``` 396 3972. 在 oh-package.json5 文件,将API与so相关联,成为一个完整的ArkTS模块: 398 399 ```json 400 { 401 "name": "libentry.so", 402 "types": "./Index.d.ts", 403 "version": "1.0.0", 404 "description": "MindSpore Lite inference module" 405 } 406 ``` 407 408#### 调用封装的ArkTS模块进行推理并输出结果 409 410在 entry/src/main/ets/pages/Index.ets 中,调用封装的ArkTS模块,最后对推理结果进行处理。 411 412```ts 413import msliteNapi from 'libentry.so' 414import { resourceManager } from '@kit.LocalizationKit'; 415 416let resMgr: resourceManager.ResourceManager = getContext().getApplicationContext().resourceManager; 417let max: number = 0; 418let maxIndex: number = 0; 419let maxArray: Array<number> = []; 420let maxIndexArray: Array<number> = []; 421 422// 调用c++的runDemo方法,完成图像输入和预处理后的buffer数据保存在float32View,具体可见上文图像输入和预处理中float32View的定义和处理。 423console.info('MS_LITE_LOG: *** Start MSLite Demo ***'); 424let output: Array<number> = msliteNapi.runDemo(Array.from(float32View), resMgr); 425// 取分类占比的最大值 426this.max = 0; 427this.maxIndex = 0; 428this.maxArray = []; 429this.maxIndexArray = []; 430let newArray = output.filter(value => value !== max); 431for (let n = 0; n < 5; n++) { 432 max = output[0]; 433 maxIndex = 0; 434 for (let m = 0; m < newArray.length; m++) { 435 if (newArray[m] > max) { 436 max = newArray[m]; 437 maxIndex = m; 438 } 439 } 440 maxArray.push(Math.round(this.max * 10000)); 441 maxIndexArray.push(this.maxIndex); 442 // filter函数数组过滤函数 443 newArray = newArray.filter(value => value !== max); 444} 445console.info('MS_LITE_LOG: max:' + this.maxArray); 446console.info('MS_LITE_LOG: maxIndex:' + this.maxIndexArray); 447console.info('MS_LITE_LOG: *** Finished MSLite Demo ***'); 448``` 449 450### 调测验证 451 4521. 在DevEco Studio中连接设备,点击Run entry,编译Hap,有如下显示: 453 454 ```shell 455 Launching com.samples.mindsporelitecdemo 456 $ hdc shell aa force-stop com.samples.mindsporelitecdemo 457 $ hdc shell mkdir data/local/tmp/xxx 458 $ hdc file send C:\Users\xxx\MindSporeLiteCDemo\entry\build\default\outputs\default\entry-default-signed.hap "data/local/tmp/xxx" 459 $ hdc shell bm install -p data/local/tmp/xxx 460 $ hdc shell rm -rf data/local/tmp/xxx 461 $ hdc shell aa start -a EntryAbility -b com.samples.mindsporelitecdemo 462 ``` 463 4642. 在设备屏幕点击photo按钮,选择图片,点击确定。设备屏幕显示所选图片的分类结果,在日志打印结果中,过滤关键字”MS_LITE“,可得到如下结果: 465 466 ```verilog 467 08-05 17:15:52.001 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: PhotoViewPicker.select successfully, photoSelectResult uri: {"photoUris":["file://media/Photo/13/IMG_1501955351_012/plant.jpg"]} 468 ... 469 08-05 17:15:52.627 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: crop info.width = 224 470 08-05 17:15:52.627 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: crop info.height = 224 471 08-05 17:15:52.628 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: Succeeded in reading image pixel data, buffer: 200704 472 08-05 17:15:52.971 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: float32View data: float32View data: 1.2385478019714355 1.308123230934143 1.4722440242767334 1.2385478019714355 1.308123230934143 1.4722440242767334 1.2385478019714355 1.308123230934143 1.4722440242767334 1.2385478019714355 1.308123230934143 1.4722440242767334 1.2385478019714355 1.308123230934143 1.4722440242767334 1.2385478019714355 1.308123230934143 1.4722440242767334 1.2385478019714355 1.308123230934143 473 08-05 17:15:52.971 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: *** Start MSLite Demo *** 474 08-05 17:15:53.454 4684-4684 A00000/[MSLiteNapi] pid-4684 I MS_LITE_LOG: Build MSLite model success. 475 08-05 17:15:53.753 4684-4684 A00000/[MSLiteNapi] pid-4684 I MS_LITE_LOG: Run MSLite model Predict success. 476 08-05 17:15:53.753 4684-4684 A00000/[MSLiteNapi] pid-4684 I MS_LITE_LOG: Get model outputs: 477 08-05 17:15:53.753 4684-4684 A00000/[MSLiteNapi] pid-4684 I MS_LITE_LOG: - Tensor 0 name is: Default/head-MobileNetV2Head/Sigmoid-op466. 478 08-05 17:15:53.753 4684-4684 A00000/[MSLiteNapi] pid-4684 I MS_LITE_LOG: - Tensor data is: 479 08-05 17:15:53.753 4684-4684 A00000/[MSLiteNapi] pid-4684 I MS_LITE_LOG: 3.43385e-06 1.40285e-05 9.11969e-07 4.91007e-05 9.50266e-07 3.94537e-07 0.0434676 3.97196e-05 0.00054832 0.000246202 1.576e-05 3.6494e-06 1.23553e-05 0.196977 5.3028e-05 3.29346e-05 4.90475e-07 1.66109e-06 7.03273e-06 8.83677e-07 3.1365e-06 480 08-05 17:15:53.781 4684-4684 A03d00/JSAPP pid-4684 W MS_LITE_WARN: output length = 500 ;value = 0.0000034338463592575863,0.000014028532859811094,9.119685273617506e-7,0.000049100715841632336,9.502661555416125e-7,3.945370394831116e-7,0.04346757382154465,0.00003971960904891603,0.0005483203567564487,0.00024620210751891136,0.000015759984307806008,0.0000036493988773145247,0.00001235533181898063,0.1969769448041916,0.000053027983085485175,0.000032934600312728435,4.904751449430478e-7,0.0000016610861166554969,0.000007032729172351537,8.836767619868624e-7 481 08-05 17:15:53.831 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: max:9497,7756,1970,435,46 482 08-05 17:15:53.831 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: maxIndex:323,46,13,6,349 483 08-05 17:15:53.831 4684-4684 A03d00/JSAPP pid-4684 I MS_LITE_LOG: *** Finished MSLite Demo *** 484 ``` 485 486 487### 效果示意 488 489在设备上,点击photo按钮,选择相册中的一张图片,点击确定。在图片下方显示此图片占比前4的分类信息。 490 491<img src="figures/stepc1.png" width="20%"/> <img src="figures/step2.png" width="20%"/> <img src="figures/step3.png" width="20%"/> <img src="figures/stepc4.png" width="20%"/> 492 493## 相关实例 494 495针对使用MindSpore Lite进行图像分类应用的开发,有以下相关实例可供参考: 496 497- [基于Native接口的MindSpore Lite应用开发(C/C++)(API11)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/DocsSample/ApplicationModels/MindSporeLiteCDemo) 498 499