1# 使用MindSpore Lite实现图像分类(ArkTS) 2 3## 场景说明 4 5开发者可以使用[@ohos.ai.mindSporeLite](../../reference/apis-mindspore-lite-kit/js-apis-mindSporeLite.md),在UI代码中集成MindSpore Lite能力,快速部署AI算法,进行AI模型推理,实现图像分类的应用。 6 7图像分类可实现对图像中物体的识别,在医学影像分析、自动驾驶、电子商务、人脸识别等有广泛的应用。 8 9## 基本概念 10 11在进行开发前,请先了解以下概念。 12 13**张量**:它与数组和矩阵非常相似,是MindSpore Lite网络运算中的基本数据结构。 14 15**Float16推理模式**: Float16又称半精度,它使用16比特表示一个数。Float16推理模式表示推理的时候用半精度进行推理。 16 17## 接口说明 18 19这里给出MindSpore Lite推理的通用开发流程中涉及的一些接口,具体请见下列表格。更多接口及详细内容,请见[@ohos.ai.mindSporeLite (推理能力)](../../reference/apis-mindspore-lite-kit/js-apis-mindSporeLite.md)。 20 21| 接口名 | 描述 | 22| ------------------------------------------------------------ | ---------------- | 23| loadModelFromFile(model: string, context?: Context): Promise<Model> | 从路径加载模型。 | 24| getInputs(): MSTensor[] | 获取模型的输入。 | 25| predict(inputs: MSTensor[]): Promise<MSTensor[]> | 推理模型。 | 26| getData(): ArrayBuffer | 获取张量的数据。 | 27| setData(inputArray: ArrayBuffer): void | 设置张量的数据。 | 28 29## 开发流程 30 311. 选择图像分类模型。 322. 在端侧使用MindSpore Lite推理模型,实现对选择的图片进行分类。 33 34## 环境准备 35 36安装DevEco Studio,要求版本 >= 4.1,并更新SDK到API 11或以上。 37 38## 开发步骤 39 40本文以对相册的一张图片进行推理为例,提供使用MindSpore Lite实现图像分类的开发指导。 41 42### 选择模型 43 44本示例程序中使用的图像分类模型文件为[mobilenetv2.ms](https://download.mindspore.cn/model_zoo/official/lite/mobilenetv2_openimage_lite/1.5/mobilenetv2.ms),放置在entry/src/main/resources/rawfile工程目录下。 45 46如果开发者有其他图像分类的预训练模型,请参考[MindSpore Lite 模型转换](mindspore-lite-converter-guidelines.md)介绍,将原始模型转换成.ms格式。 47 48### 编写代码 49 50#### 图像输入和预处理 51 521. 此处以获取相册图片为例,调用[@ohos.file.picker](../../reference/apis-core-file-kit/js-apis-file-picker.md) 实现相册图片文件的选择。 53 54 ```ts 55 import { photoAccessHelper } from '@kit.MediaLibraryKit'; 56 import { BusinessError } from '@kit.BasicServicesKit'; 57 58 let uris: Array<string> = []; 59 60 // 创建图片文件选择实例 61 let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); 62 63 // 设置选择媒体文件类型为IMAGE,设置选择媒体文件的最大数目 64 photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; 65 photoSelectOptions.maxSelectNumber = 1; 66 67 // 创建图库选择器实例,调用select()接口拉起图库界面进行文件选择。文件选择成功后,返回photoSelectResult结果集。 68 let photoPicker = new photoAccessHelper.PhotoViewPicker(); 69 photoPicker.select(photoSelectOptions, async ( 70 err: BusinessError, photoSelectResult: photoAccessHelper.PhotoSelectResult) => { 71 if (err) { 72 console.error('MS_LITE_ERR: PhotoViewPicker.select failed with err: ' + JSON.stringify(err)); 73 return; 74 } 75 console.info('MS_LITE_LOG: PhotoViewPicker.select successfully, ' + 76 'photoSelectResult uri: ' + JSON.stringify(photoSelectResult)); 77 uris = photoSelectResult.photoUris; 78 console.info('MS_LITE_LOG: uri: ' + uris); 79 }) 80 ``` 81 822. 根据模型的输入尺寸,调用[@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数据,并进行标准化处理。 83 84 ```ts 85 import { image } from '@kit.ImageKit'; 86 import { fileIo } from '@kit.CoreFileKit'; 87 88 let modelInputHeight: number = 224; 89 let modelInputWidth: number = 224; 90 91 // 使用fileIo.openSync接口,通过uri打开这个文件得到fd 92 let file = fileIo.openSync(this.uris[0], fileIo.OpenMode.READ_ONLY); 93 console.info('MS_LITE_LOG: file fd: ' + file.fd); 94 95 // 通过fd使用fileIo.readSync接口读取这个文件内的数据 96 let inputBuffer = new ArrayBuffer(4096000); 97 let readLen = fileIo.readSync(file.fd, inputBuffer); 98 console.info('MS_LITE_LOG: readSync data to file succeed and inputBuffer size is:' + readLen); 99 100 // 通过PixelMap预处理 101 let imageSource = image.createImageSource(file.fd); 102 imageSource.createPixelMap().then((pixelMap) => { 103 pixelMap.getImageInfo().then((info) => { 104 console.info('MS_LITE_LOG: info.width = ' + info.size.width); 105 console.info('MS_LITE_LOG: info.height = ' + info.size.height); 106 // 根据模型输入的尺寸,将图片裁剪为对应的size,获取图片buffer数据readBuffer 107 pixelMap.scale(256.0 / info.size.width, 256.0 / info.size.height).then(() => { 108 pixelMap.crop( 109 { x: 16, y: 16, size: { height: modelInputHeight, width: modelInputWidth } } 110 ).then(async () => { 111 let info = await pixelMap.getImageInfo(); 112 console.info('MS_LITE_LOG: crop info.width = ' + info.size.width); 113 console.info('MS_LITE_LOG: crop info.height = ' + info.size.height); 114 // 需要创建的像素buffer大小 115 let readBuffer = new ArrayBuffer(modelInputHeight * modelInputWidth * 4); 116 await pixelMap.readPixelsToBuffer(readBuffer); 117 console.info('MS_LITE_LOG: Succeeded in reading image pixel data, buffer: ' + 118 readBuffer.byteLength); 119 // 处理readBuffer,转换成float32格式,并进行标准化处理 120 const imageArr = new Uint8Array( 121 readBuffer.slice(0, modelInputHeight * modelInputWidth * 4)); 122 console.info('MS_LITE_LOG: imageArr length: ' + imageArr.length); 123 let means = [0.485, 0.456, 0.406]; 124 let stds = [0.229, 0.224, 0.225]; 125 let float32View = new Float32Array(modelInputHeight * modelInputWidth * 3); 126 let index = 0; 127 for (let i = 0; i < imageArr.length; i++) { 128 if ((i + 1) % 4 == 0) { 129 float32View[index] = (imageArr[i - 3] / 255.0 - means[0]) / stds[0]; // B 130 float32View[index+1] = (imageArr[i - 2] / 255.0 - means[1]) / stds[1]; // G 131 float32View[index+2] = (imageArr[i - 1] / 255.0 - means[2]) / stds[2]; // R 132 index += 3; 133 } 134 } 135 console.info('MS_LITE_LOG: float32View length: ' + float32View.length); 136 let printStr = 'float32View data:'; 137 for (let i = 0; i < 20; i++) { 138 printStr += ' ' + float32View[i]; 139 } 140 console.info('MS_LITE_LOG: float32View data: ' + printStr); 141 }) 142 }) 143 }); 144 }); 145 ``` 146 147#### 编写推理代码 148 1491. 工程默认设备定义的能力集可能不包含MindSporeLite。需在DevEco Studio工程的entry/src/main目录下,手动创建syscap.json文件,内容如下: 150 151 ```json 152 { 153 "devices": { 154 "general": [ 155 // 需跟module.json5中deviceTypes保持一致。 156 "default" 157 ] 158 }, 159 "development": { 160 "addedSysCaps": [ 161 "SystemCapability.AI.MindSporeLite" 162 ] 163 } 164 } 165 ``` 166 1672. 调用[@ohos.ai.mindSporeLite](../../reference/apis-mindspore-lite-kit/js-apis-mindSporeLite.md)实现端侧推理。具体开发过程及细节如下: 168 169 1. 创建上下文,设置线程数、设备类型等参数。 170 2. 加载模型。本文从内存加载模型。 171 3. 加载数据。模型执行之前需要先获取输入,再向输入的张量中填充数据。 172 4. 执行推理。使用predict接口进行模型推理。 173 174 ```ts 175 // model.ets 176 import { mindSporeLite } from '@kit.MindSporeLiteKit' 177 178 export default async function modelPredict( 179 modelBuffer: ArrayBuffer, inputsBuffer: ArrayBuffer[]): Promise<mindSporeLite.MSTensor[]> { 180 181 // 1.创建上下文,设置线程数、设备类型等参数。 182 let context: mindSporeLite.Context = {}; 183 context.target = ['cpu']; 184 context.cpu = {} 185 context.cpu.threadNum = 2; 186 context.cpu.threadAffinityMode = 1; 187 context.cpu.precisionMode = 'enforce_fp32'; 188 189 // 2.从内存加载模型。 190 let msLiteModel: mindSporeLite.Model = await mindSporeLite.loadModelFromBuffer(modelBuffer, context); 191 192 // 3.设置输入数据。 193 let modelInputs: mindSporeLite.MSTensor[] = msLiteModel.getInputs(); 194 for (let i = 0; i < inputsBuffer.length; i++) { 195 let inputBuffer = inputsBuffer[i]; 196 if (inputBuffer != null) { 197 modelInputs[i].setData(inputBuffer as ArrayBuffer); 198 } 199 } 200 201 // 4.执行推理。 202 console.info('=========MS_LITE_LOG: MS_LITE predict start====='); 203 let modelOutputs: mindSporeLite.MSTensor[] = await msLiteModel.predict(modelInputs); 204 return modelOutputs; 205 } 206 ``` 207 208#### 进行推理并输出结果 209 210加载模型文件,调用推理函数,对相册选择的图片进行推理,并对推理结果进行处理。 211 212```ts 213import modelPredict from './model'; 214import { resourceManager } from '@kit.LocalizationKit' 215 216let modelName: string = 'mobilenetv2.ms'; 217let max: number = 0; 218let maxIndex: number = 0; 219let maxArray: Array<number> = []; 220let maxIndexArray: Array<number> = []; 221 222// 完成图像输入和预处理后的buffer数据保存在float32View,具体可见上文图像输入和预处理中float32View的定义和处理。 223let inputs: ArrayBuffer[] = [float32View.buffer]; 224let resMgr: resourceManager.ResourceManager = getContext().getApplicationContext().resourceManager; 225resMgr.getRawFileContent(modelName).then(modelBuffer => { 226 // predict 227 modelPredict(modelBuffer.buffer.slice(0), inputs).then(outputs => { 228 console.info('=========MS_LITE_LOG: MS_LITE predict success====='); 229 // 结果打印 230 for (let i = 0; i < outputs.length; i++) { 231 let out = new Float32Array(outputs[i].getData()); 232 let printStr = outputs[i].name + ':'; 233 for (let j = 0; j < out.length; j++) { 234 printStr += out[j].toString() + ','; 235 } 236 console.info('MS_LITE_LOG: ' + printStr); 237 // 取分类占比的最大值 238 this.max = 0; 239 this.maxIndex = 0; 240 this.maxArray = []; 241 this.maxIndexArray = []; 242 let newArray = out.filter(value => value !== max) 243 for (let n = 0; n < 5; n++) { 244 max = out[0]; 245 maxIndex = 0; 246 for (let m = 0; m < newArray.length; m++) { 247 if (newArray[m] > max) { 248 max = newArray[m]; 249 maxIndex = m; 250 } 251 } 252 maxArray.push(Math.round(max * 10000)) 253 maxIndexArray.push(maxIndex) 254 // filter函数,数组过滤函数 255 newArray = newArray.filter(value => value !== max) 256 } 257 console.info('MS_LITE_LOG: max:' + maxArray); 258 console.info('MS_LITE_LOG: maxIndex:' + maxIndexArray); 259 } 260 console.info('=========MS_LITE_LOG END========='); 261 }) 262}) 263``` 264 265### 调测验证 266 2671. 在DevEco Studio中连接设备,点击Run entry,编译Hap,有如下显示: 268 269 ```shell 270 Launching com.samples.mindsporelitearktsdemo 271 $ hdc shell aa force-stop com.samples.mindsporelitearktsdemo 272 $ hdc shell mkdir data/local/tmp/xxx 273 $ hdc file send C:\Users\xxx\MindSporeLiteArkTSDemo\entry\build\default\outputs\default\entry-default-signed.hap "data/local/tmp/xxx" 274 $ hdc shell bm install -p data/local/tmp/xxx 275 $ hdc shell rm -rf data/local/tmp/xxx 276 $ hdc shell aa start -a EntryAbility -b com.samples.mindsporelitearktsdemo 277 ``` 278 2792. 在设备屏幕点击photo按钮,选择图片,点击确定。设备屏幕显示所选图片的分类结果,在日志打印结果中,过滤关键字”MS_LITE“,可得到如下结果: 280 281 ```verilog 282 08-06 03:24:33.743 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: PhotoViewPicker.select successfully, photoSelectResult uri: {"photoUris":["file://media/Photo/13/IMG_1501955351_012/plant.jpg"]} 283 08-06 03:24:33.795 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: readSync data to file succeed and inputBuffer size is:32824 284 08-06 03:24:34.147 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: crop info.width = 224 285 08-06 03:24:34.147 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: crop info.height = 224 286 08-06 03:24:34.160 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: Succeeded in reading image pixel data, buffer: 200704 287 08-06 03:24:34.970 22547-22547 A03d00/JSAPP com.sampl...liteark+ I =========MS_LITE_LOG: MS_LITE predict start===== 288 08-06 03:24:35.432 22547-22547 A03d00/JSAPP com.sampl...liteark+ I =========MS_LITE_LOG: MS_LITE predict success===== 289 08-06 03:24:35.447 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: Default/head-MobileNetV2Head/Sigmoid-op466:0.0000034338463592575863,0.000014028532859811094,9.119685273617506e-7,0.000049100715841632336,9.502661555416125e-7,3.945370394831116e-7,0.04346757382154465,0.00003971960904891603... 290 08-06 03:24:35.499 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: max:9497,7756,1970,435,46 291 08-06 03:24:35.499 22547-22547 A03d00/JSAPP com.sampl...liteark+ I MS_LITE_LOG: maxIndex:323,46,13,6,349 292 08-06 03:24:35.499 22547-22547 A03d00/JSAPP com.sampl...liteark+ I =========MS_LITE_LOG END========= 293 ``` 294 295### 效果示意 296 297在设备上,点击photo按钮,选择相册中的一张图片,点击确定。在图片下方显示此图片占比前4的分类信息。 298 299<img src="figures/step1.png" width="20%"/> <img src="figures/step2.png" width="20%"/> <img src="figures/step3.png" width="20%"/> <img src="figures/step4.png" width="20%"/> 300 301## 相关实例 302 303针对使用MindSpore Lite进行图像分类应用的开发,有以下相关实例可供参考: 304 305- [基于ArkTS接口的MindSpore Lite应用开发(ArkTS)(API11)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/DocsSample/ApplicationModels/MindSporeLiteArkTSDemo) 306 307<!--RP1--><!--RP1End-->