diff --git a/README.en.md b/README.en.md index c21bbb7b4c2cb1b8bcadd70c68621d5838304d33..669cba81e6e84cff5f0587a1993793473908cd60 100644 --- a/README.en.md +++ b/README.en.md @@ -1,22 +1,22 @@ # Photo Text Recognition -### Overview +## Overview This sample shows how to use the APIs provided by @ohos.multimedia.camera (camera management) and textRecognition (text recognition) to recognize and extract text in images. -### Preview +## Preview | Home page | Text recognition dialog | | ------------------------------------ | ------------------------------------- | | ![](screenshots/device/index_EN.png) | ![](screenshots/device/dialog_EN.png) | -### How to Use +## How to Use 1. Tap the round button in the lower part of the page. A dialog is displayed, showing the text extracted from the image. 2. Tap a blank area except the dialog to close the dialog and return to the home screen. -### Project Directory +## Project Directory ``` @@ -24,9 +24,9 @@ This sample shows how to use the APIs provided by @ohos.multimedia.camera (camer │ ├──common/constant │ │ └──CommonConstants.ets // Common constants │ ├──common/utils -│ │ └──DeviceScreen.ets // Utility used to calculate the screen size -│ │ └──Logger.ets // Logging utility -│ │ └──PermissionUtils.ets // Utility used to obtain permissions +│ │ ├──DeviceScreen.ets // Utility used to calculate the screen size +│ │ ├──Logger.ets // Logging utility +│ │ ├──PermissionUtils.ets // Utility used to obtain permissions │ │ └──Camera.ets // Utility used to manage the camera │ ├──entryability │ │ └──EntryAbility.ets // Entry ability @@ -35,20 +35,19 @@ This sample shows how to use the APIs provided by @ohos.multimedia.camera (camer │ └──view │ └──CustomDialogView.ets // Dialog for displaying the text extracted from the image └──entry/src/main/resources // Static resources of the app - ``` -### How to Implement +## How to Implement * The APIs for AI text recognition are encapsulated in **CameraModel**. For details about the source code, see [Camera.ets](entry/src/main/ets/common/utils/Camera.ets). * Camera module: encapsulates the APIs for initializing and releasing the camera. * On the Index page, a click event triggers camera shooting. After the photo output stream is obtained, use @hms.ai.ocr.textRecognition to extract the text from the photo. -### Required Permissions +## Required Permissions **ohos.permission.CAMERA**: allows an app to use the camera. -### Constraints +## Constraints 1. The sample is only supported on Huawei phones with standard systems. diff --git a/README.md b/README.md index d6cd195acffd43eb1c048370fea27c9d2d42c6bd..b712cf8d3727541090987c3321308bf963e85503 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # 基于基础视觉服务及相机实现拍照识别文字功能 -### 介绍 +## 介绍 本示例通过使用@ohos.multimedia.camera (相机管理)和textRecognition(文字识别)接口实现拍照后识别提取照片内文字的功能。帮助开发者了解开发文字识别工具类应用时,如何将照片输入流与文字识别接口关联,从而实现文字识别的功能。 -### 效果预览 +## 效果预览 | 主页 | 文字识别弹窗 | |-----------------------------------|------------------------------------| @@ -16,17 +16,16 @@ 2.点击除了弹窗外的空白区域,弹窗关闭,返回主页。 -### 工程目录 +## 工程目录 ``` - ├──entry/src/main/ets/ │ ├──common/constant │ │ └──CommonConstants.ets // 公共常量类 │ ├──common/utils -│ │ └──DeviceScreen.ets // 屏幕尺寸计算工具 -│ │ └──Logger.ets // 日志工具 -│ │ └──PermissionUtils.ets // 权限获取工具 +│ │ ├──DeviceScreen.ets // 屏幕尺寸计算工具 +│ │ ├──Logger.ets // 日志工具 +│ │ ├──PermissionUtils.ets // 权限获取工具 │ │ └──Camera.ets // 相机管理工具 │ ├──entryability │ │ └──EntryAbility.ets // 程序入口类 @@ -35,19 +34,18 @@ │ └──view │ └──CustomDialogView.ets // 识别弹窗页面 └──entry/src/main/resources // 应用静态资源目录 - ``` -### 具体实现 +## 具体实现 * 本实例完成AI文字识别的功能模块主要封装在CameraModel,源码参考:[Camera.ets](entry/src/main/ets/common/utils/Camera.ets) 。 * 相机模块:在Camera中封装了相机初始化、相机释放。 * 在Index页面通过点击事件触发相机拍摄,在获取到照片输出流后通过@hms.ai.ocr.textRecognition文字识别接口进行识别。 -### 相关权限 +## 相关权限 获取相机权限:ohos.permission.CAMERA。 -### 约束与限制 +## 约束与限制 1.本示例仅支持标准系统上运行,支持设备:华为手机。 diff --git a/entry/src/main/ets/common/utils/Camera.ets b/entry/src/main/ets/common/utils/Camera.ets index d9e771f8eb6b0c28cda7e19e550177350b4946e0..652bd7665e688a80feaf7548fd54165864ddd9c2 100644 --- a/entry/src/main/ets/common/utils/Camera.ets +++ b/entry/src/main/ets/common/utils/Camera.ets @@ -12,60 +12,68 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { camera } from '@kit.CameraKit'; -import { common } from '@kit.AbilityKit'; -import { image } from '@kit.ImageKit'; import { textRecognition } from '@kit.CoreVisionKit'; +import { image } from '@kit.ImageKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; import Logger from './Logger'; import CommonConstants from '../constants/CommonConstants'; const TAG: string = '[CameraModel]'; -const context: common.UIAbilityContext = AppStorage.get("context") as common.UIAbilityContext; + export default class Camera { private cameraMgr: camera.CameraManager | undefined = undefined; private cameraDevice: camera.CameraDevice | undefined = undefined; private capability: camera.CameraOutputCapability | undefined = undefined; private cameraInput: camera.CameraInput | undefined = undefined; - public previewOutput: camera.PreviewOutput | undefined = undefined; private receiver: image.ImageReceiver | undefined = undefined; private photoOutput: camera.PhotoOutput | undefined = undefined; + public previewOutput: camera.PreviewOutput | undefined = undefined; public captureSession: camera.PhotoSession | undefined = undefined; public result: string = ''; - private imgReceive: Function | undefined = undefined; async initCamera(surfaceId: string): Promise { - this.cameraMgr = camera.getCameraManager(context); - let cameraArray = this.getCameraDevices(this.cameraMgr); - this.cameraDevice = cameraArray[CommonConstants.INPUT_DEVICE_INDEX]; - this.cameraInput = this.getCameraInput(this.cameraDevice, this.cameraMgr) as camera.CameraInput; - await this.cameraInput.open(); - this.capability = this.cameraMgr.getSupportedOutputCapability(this.cameraDevice, camera.SceneMode.NORMAL_PHOTO); - - this.previewOutput = this.getPreviewOutput(this.cameraMgr, this.capability, surfaceId) as camera.PreviewOutput; - this.photoOutput = this.getPhotoOutput(this.cameraMgr, this.capability) as camera.PhotoOutput; - - this.photoOutput.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => { - let imageObj = photo.main; - imageObj.getComponent(image.ComponentType.JPEG,async (errCode: BusinessError, component: image.Component)=> { - if (errCode || component === undefined) { - return; - } - let buffer: ArrayBuffer; - buffer = component.byteBuffer - this.result = await this.recognizeImage(buffer); + try { + const context: common.UIAbilityContext = AppStorage.get('context') as common.UIAbilityContext; + this.cameraMgr = camera.getCameraManager(context); + let cameraArray = this.getCameraDevices(this.cameraMgr); + this.cameraDevice = cameraArray[CommonConstants.INPUT_DEVICE_INDEX]; + this.cameraInput = this.getCameraInput(this.cameraDevice, this.cameraMgr) as camera.CameraInput; + await this.cameraInput.open(); + this.capability = this.cameraMgr.getSupportedOutputCapability(this.cameraDevice, camera.SceneMode.NORMAL_PHOTO); + + this.previewOutput = this.getPreviewOutput(this.cameraMgr, this.capability, surfaceId) as camera.PreviewOutput; + this.photoOutput = this.getPhotoOutput(this.cameraMgr, this.capability) as camera.PhotoOutput; + + this.photoOutput.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => { + let imageObj = photo.main; + imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => { + if (errCode || component === undefined) { + return; + } + let buffer: ArrayBuffer; + buffer = component.byteBuffer + this.result = await this.recognizeImage(buffer); + }) }) - }) - // Session Init - this.captureSession = this.getCaptureSession(this.cameraMgr) as camera.PhotoSession; - this.beginConfig(this.captureSession); - this.startSession(this.captureSession, this.cameraInput, this.previewOutput, this.photoOutput); + // Session Init + this.captureSession = this.getCaptureSession(this.cameraMgr) as camera.PhotoSession; + this.beginConfig(this.captureSession); + this.startSession(this.captureSession, this.cameraInput, this.previewOutput, this.photoOutput); + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, 'Camera', `initCamera failed. code=${err.code}, message=${err.message}`); + } } async takePicture() { this.result = ''; - this.photoOutput!.capture(); + this.photoOutput!.capture().catch((error: BusinessError) => { + hilog.error(0x0000, 'Camera', `capture failed. code=${error.code}, message=${error.message}`); + }); } async recognizeImage(buffer: ArrayBuffer): Promise { @@ -78,46 +86,56 @@ export default class Camera { isDirectionDetectionSupported: true }; let recognitionString: string = ''; - if (canIUse('SystemCapability.AI.OCR.TextRecognition')) { - await textRecognition.recognizeText(visionInfo, textConfiguration).then((TextRecognitionResult) => { - if (TextRecognitionResult.value === '') { - recognitionString = context.resourceManager.getStringSync($r('app.string.unrecognizable').id); - } else { - recognitionString = TextRecognitionResult.value; - } - }) - pixelMapInstance.release(); - imageResource.release(); - } else { - recognitionString = context.resourceManager.getStringSync($r('app.string.Device_not_support').id); - Logger.error(TAG, `device not support`); + const context: common.UIAbilityContext = AppStorage.get('context') as common.UIAbilityContext; + try { + if (canIUse('SystemCapability.AI.OCR.TextRecognition')) { + await textRecognition.recognizeText(visionInfo, textConfiguration).then((TextRecognitionResult) => { + if (TextRecognitionResult.value === '') { + recognitionString = context.resourceManager.getStringSync($r('app.string.unrecognizable').id); + } else { + recognitionString = TextRecognitionResult.value; + } + }) + pixelMapInstance.release(); + imageResource.release(); + } else { + recognitionString = context.resourceManager.getStringSync($r('app.string.Device_not_support').id); + Logger.error(TAG, `device not support`); + } + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, 'Camera', `recognizeImage failed. code=${err.code}, message=${err.message}`); } return recognitionString; } async releaseCamera(): Promise { - if (this.cameraInput) { - await this.cameraInput.close(); - Logger.info(TAG, 'cameraInput release'); - } - if (this.previewOutput) { - await this.previewOutput.release(); - Logger.info(TAG, 'previewOutput release'); - } - if (this.receiver) { - await this.receiver.release(); - Logger.info(TAG, 'receiver release'); - } - if (this.photoOutput) { - await this.photoOutput.release(); - Logger.info(TAG, 'photoOutput release'); - } - if (this.captureSession) { - await this.captureSession.release(); - Logger.info(TAG, 'captureSession release'); - this.captureSession = undefined; + try { + if (this.cameraInput) { + await this.cameraInput.close(); + Logger.info(TAG, 'cameraInput release'); + } + if (this.previewOutput) { + await this.previewOutput.release(); + Logger.info(TAG, 'previewOutput release'); + } + if (this.receiver) { + await this.receiver.release(); + Logger.info(TAG, 'receiver release'); + } + if (this.photoOutput) { + await this.photoOutput.release(); + Logger.info(TAG, 'photoOutput release'); + } + if (this.captureSession) { + await this.captureSession.release(); + Logger.info(TAG, 'captureSession release'); + this.captureSession = undefined; + } + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, TAG, `releaseCamera failed. code=${err.code}, message=${err.message}`); } - this.imgReceive = undefined; } getCameraDevices(cameraManager: camera.CameraManager): Array { @@ -130,17 +148,29 @@ export default class Camera { } } - getCameraInput(cameraDevice: camera.CameraDevice, cameraManager: camera.CameraManager): camera.CameraInput | undefined { + getCameraInput(cameraDevice: camera.CameraDevice, + cameraManager: camera.CameraManager): camera.CameraInput | undefined { let cameraInput: camera.CameraInput | undefined = undefined; - cameraInput = cameraManager.createCameraInput(cameraDevice); + try { + cameraInput = cameraManager.createCameraInput(cameraDevice); + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, TAG, `createCameraInput failed. code=${err.code}, message=${err.message}`); + } return cameraInput; } getPreviewOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability, - surfaceId: string): camera.PreviewOutput | undefined { + surfaceId: string): camera.PreviewOutput | undefined { let previewProfilesArray: Array = cameraOutputCapability.previewProfiles; let previewOutput: camera.PreviewOutput | undefined = undefined; - previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[CommonConstants.OUTPUT_DEVICE_INDEX], surfaceId); + try { + previewOutput = + cameraManager.createPreviewOutput(previewProfilesArray[CommonConstants.OUTPUT_DEVICE_INDEX], surfaceId); + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, TAG, `createPreviewOutput failed. code=${err.code}, message=${err.message}`); + } return previewOutput; } @@ -153,7 +183,8 @@ export default class Camera { return photoSurfaceId; } - getPhotoOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability): camera.PhotoOutput | undefined { + getPhotoOutput(cameraManager: camera.CameraManager, + cameraOutputCapability: camera.CameraOutputCapability): camera.PhotoOutput | undefined { let photoProfilesArray: Array = cameraOutputCapability.photoProfiles; Logger.info(TAG, JSON.stringify(photoProfilesArray)); if (!photoProfilesArray) { @@ -163,7 +194,8 @@ export default class Camera { try { photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[CommonConstants.OUTPUT_DEVICE_INDEX]); } catch (error) { - Logger.error(TAG, `Failed to createPhotoOutput. error: ${JSON.stringify(error as BusinessError)}`); + let err = error as BusinessError; + Logger.error(TAG, `Failed to createPhotoOutput. code: ${err.code}, message=${err.message}`); } return photoOutput; } @@ -173,7 +205,8 @@ export default class Camera { try { captureSession = cameraManager.createSession(1) as camera.PhotoSession; } catch (error) { - Logger.error(TAG, `Failed to create the CaptureSession instance. error: ${JSON.stringify(error as BusinessError)}`); + let err = error as BusinessError; + Logger.error(TAG, `Failed to create the CaptureSession instance. code: ${err.code}, message=$err.message}`); } return captureSession; } @@ -183,25 +216,22 @@ export default class Camera { captureSession.beginConfig(); Logger.info(TAG, 'captureSession beginConfig') } catch (error) { - Logger.error(TAG, `Failed to beginConfig. error: ${JSON.stringify(error as BusinessError)}`); + let err = error as BusinessError; + Logger.error(TAG, `Failed to beginConfig. code: ${err.code}, message=${err.message}`); } } async startSession(captureSession: camera.PhotoSession, cameraInput: camera.CameraInput, previewOutput: camera.PreviewOutput, photoOutput: camera.PhotoOutput): Promise { - captureSession.addInput(cameraInput); - captureSession.addOutput(previewOutput); - captureSession.addOutput(photoOutput); - await captureSession.commitConfig().then(() => { - Logger.info(TAG, 'Promise returned to captureSession the session start success.') - }).catch((err: BusinessError) => { - Logger.info(TAG, 'captureSession error') - Logger.info(TAG, JSON.stringify(err)) - }); - await captureSession.start().then(() => { - Logger.info(TAG, 'Promise returned to indicate the session start success.') - }).catch((err: BusinessError) => { - Logger.info(TAG, JSON.stringify(err)) - }) + try { + captureSession.addInput(cameraInput); + captureSession.addOutput(previewOutput); + captureSession.addOutput(photoOutput); + await captureSession.commitConfig(); + await captureSession.start(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `Failed to startSession. code: ${err.code}, message=${err.message}`); + } } } \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/DeviceScreen.ets b/entry/src/main/ets/common/utils/DeviceScreen.ets index 1020952e71dbced83e1aae13c7dcb17bb3433a66..46a2f64feadd533c02bb4f2a0a95c803024d5c65 100644 --- a/entry/src/main/ets/common/utils/DeviceScreen.ets +++ b/entry/src/main/ets/common/utils/DeviceScreen.ets @@ -13,19 +13,37 @@ * limitations under the License. */ import { display } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; export class DeviceScreen { public static getDeviceHeight(): number { - let displayObject = display.getDefaultDisplaySync(); - let screenPixelHeight = displayObject.height; - let screenDensityDPI = displayObject.densityDPI; - return screenPixelHeight * (160 / screenDensityDPI); + let height: number = 0; + try { + let displayObject = display.getDefaultDisplaySync(); + let screenPixelHeight = displayObject.height; + let screenDensityDPI = displayObject.densityDPI; + height = screenPixelHeight * (160 / screenDensityDPI); + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, 'DeviceScreen', 'Failed to get the height. Code: %{public}d, message: %{public}s', err.code, + err.message); + } + return height; } public static getDeviceWidth(): number { - let displayObject = display.getDefaultDisplaySync(); - let screenPixelWidth = displayObject.width; - let screenDensityDPI = displayObject.densityDPI; - return screenPixelWidth * (160 / screenDensityDPI); + let width: number = 0; + try { + let displayObject = display.getDefaultDisplaySync(); + let screenPixelWidth = displayObject.width; + let screenDensityDPI = displayObject.densityDPI; + width = screenPixelWidth * (160 / screenDensityDPI); + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, 'DeviceScreen', 'Failed to get the width. Code: %{public}d, message: %{public}s', err.code, + err.message); + } + return width; } } \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/PermissionUtils.ets b/entry/src/main/ets/common/utils/PermissionUtils.ets index b181247f583c5f9ba92573614be803aa67d1abd8..b40d7c8900bc353bebf09c275470de2605fd734b 100644 --- a/entry/src/main/ets/common/utils/PermissionUtils.ets +++ b/entry/src/main/ets/common/utils/PermissionUtils.ets @@ -14,6 +14,7 @@ */ import { abilityAccessCtrl, PermissionRequestResult,Permissions,bundleManager,common} from '@kit.AbilityKit'; import Logger from './Logger'; +import { BusinessError } from '@kit.BasicServicesKit'; const TAG: string = '[Permission]'; @@ -21,7 +22,7 @@ const PERMISSIONS: Array = [ 'ohos.permission.CAMERA' ]; -const context: common.UIAbilityContext = AppStorage.get("context") as common.UIAbilityContext; +const context: common.UIAbilityContext = AppStorage.get('context') as common.UIAbilityContext; export default async function grantPermission(): Promise { try { @@ -57,7 +58,8 @@ export default async function grantPermission(): Promise { Logger.info(TAG, 'grantPermission success'); return true; } catch (error) { - Logger.error(TAG, 'grantPermission fail'); + let err = error as BusinessError; + Logger.error(TAG, `grantPermission fail. code=${err.code}, message=${err.message}`); return false; } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index aa7bcb44d1e25701089147837dc28313bf72c6cf..9cd6de49ed9f1968d273dae83b90a25c26280044 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { BusinessError } from '@kit.BasicServicesKit'; import Logger from '../common/utils/Logger' import grantPermission from '../common/utils/PermissionUtils' @@ -19,6 +20,7 @@ import { DeviceScreen } from '../common/utils/DeviceScreen' import Camera from '../common/utils/Camera' import { CustomDialogExample } from '../view/CustomDialogView' import CommonConstants from '../common/constants/CommonConstants'; +import { hilog } from '@kit.PerformanceAnalysisKit'; const TAG: string = '[IndexPage]'; @@ -41,17 +43,17 @@ struct Index { } } - async aboutToAppear() { - await grantPermission().then(async () => { + aboutToAppear() { + grantPermission().then(() => { this.XComponentinit(); }).catch((err: BusinessError) => { - Logger.info(TAG, `grantPermission faild ${JSON.stringify(err.code)}`); + Logger.error(TAG, `grantPermission faild ${JSON.stringify(err.code)}`); }) } async aboutToDisappear() { await this.camera.releaseCamera(); - this.dialogController.close() + this.dialogController.close(); } onPageShow() { @@ -72,8 +74,13 @@ struct Index { await this.camera.initCamera(this.surfaceId); } - async refresh() { - this.camera.captureSession!.start(); + refresh() { + try { + this.camera.captureSession!.start(); + } catch (error) { + let err = error as BusinessError; + hilog.error(0x0000, 'Index', 'Failed to refresh. Code: %{public}d, message: %{public}s', err.code, err.message); + } } dialogController: CustomDialogController = new CustomDialogController({ @@ -128,8 +135,8 @@ struct Index { radius: $r('app.float.button_border_radius') }) } - .onClick(async () => { - await this.camera.takePicture() + .onClick(() => { + this.camera.takePicture(); }) .backgroundColor(Color.Black) .width($r('app.float.button_border_size')) diff --git a/entry/src/main/ets/view/CustomDialogView.ets b/entry/src/main/ets/view/CustomDialogView.ets index eca38a72443e1f3efef0cdae1e03cc85ed7a4a03..e060dfd55fee6c8895bd8307e6417f9fa81eec4f 100644 --- a/entry/src/main/ets/view/CustomDialogView.ets +++ b/entry/src/main/ets/view/CustomDialogView.ets @@ -16,7 +16,7 @@ import CommonConstants from '../common/constants/CommonConstants'; @CustomDialog export struct CustomDialogExample { - text: string = ""; + text: string = ''; cancel: () => void = () => { }; private scroller: Scroller = new Scroller(); diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json index b16ec3e7686aa1f3d18fbf752f8926ba044cff50..422380253fac2799d4377e82961cb1aa263bfd79 100644 --- a/entry/src/main/resources/base/element/float.json +++ b/entry/src/main/resources/base/element/float.json @@ -1,9 +1,5 @@ { "float": [ - { - "name": "module_desc", - "value": "module description" - }, { "name": "top_height", "value": "30vp"