鸿蒙简易OCR文字识别系统开发指南 原创

进修的泡芙
发布于 2025-6-22 16:31
浏览
0收藏

鸿蒙简易OCR文字识别系统开发指南

一、系统架构设计

基于HarmonyOS的OCR能力和分布式技术,构建跨设备文字识别系统:
图像采集层:从相机或相册获取图片

OCR处理层:识别图片中的文字内容

结果展示层:显示识别结果并支持编辑

跨端同步层:多设备间同步识别结果

!https://example.com/harmony-ocr-system-arch.png

二、核心代码实现
OCR服务封装

// OCRService.ets
import ocr from ‘@ohos.ai.ocr’;
import distributedData from ‘@ohos.distributedData’;
import { OCRParams, OCRResult } from ‘./OCRTypes’;

class OCRService {
private static instance: OCRService = null;
private ocrEngine: ocr.OCREngine;
private dataManager: distributedData.DataManager;
private resultListeners: OCRResultListener[] = [];

private constructor() {
this.initOCREngine();
this.initDataManager();
public static getInstance(): OCRService {

if (!OCRService.instance) {
  OCRService.instance = new OCRService();

return OCRService.instance;

private initOCREngine(): void {

try {
  const context = getContext() as common.Context;
  this.ocrEngine = ocr.createOCREngine(context);

catch (err) {

  console.error('初始化OCR引擎失败:', JSON.stringify(err));

}

private initDataManager(): void {
this.dataManager = distributedData.createDataManager({
bundleName: ‘com.example.ocr’,
area: distributedData.Area.GLOBAL,
isEncrypted: true
});

this.dataManager.registerDataListener('ocr_result_sync', (data) => {
  this.handleSyncData(data);
});

public async requestPermissions(): Promise<boolean> {

try {
  const permissions = [
    'ohos.permission.USE_AI',
    'ohos.permission.READ_MEDIA',
    'ohos.permission.CAMERA',
    'ohos.permission.DISTRIBUTED_DATASYNC'
  ];
  
  const result = await abilityAccessCtrl.requestPermissionsFromUser(
    getContext(), 
    permissions
  );
  
  return result.grantedPermissions.length === permissions.length;

catch (err) {

  console.error('请求权限失败:', JSON.stringify(err));
  return false;

}

public async recognizeText(params: OCRParams): Promise<OCRResult> {
try {
const result = await this.ocrEngine.recognize({
image: params.image,
language: params.language || ‘zh’,
options: {
detectDirection: true,
detectLanguage: true
});

  const ocrResult: OCRResult = {
    id: Date.now().toString(),
    text: result.text,
    language: result.language,
    confidence: result.confidence,
    timestamp: Date.now(),
    imageUri: params.imageUri
  };
  
  // 同步识别结果
  this.syncResult(ocrResult);
  
  return ocrResult;

catch (err) {

  console.error('文字识别失败:', JSON.stringify(err));
  throw err;

}

private syncResult(result: OCRResult): void {
this.dataManager.syncData(‘ocr_result_sync’, {
type: ‘ocr_result’,
result: result,
timestamp: Date.now()
});
private handleSyncData(data: any): void {

if (!data || data.type !== 'ocr_result') return;

this.notifyResultListeners(data.result);

private notifyResultListeners(result: OCRResult): void {

this.resultListeners.forEach(listener => {
  listener.onOCRResult(result);
});

public addResultListener(listener: OCRResultListener): void {

if (!this.resultListeners.includes(listener)) {
  this.resultListeners.push(listener);

}

public removeResultListener(listener: OCRResultListener): void {
this.resultListeners = this.resultListeners.filter(l => l !== listener);
}

interface OCRResultListener {
onOCRResult(result: OCRResult): void;
export const ocrService = OCRService.getInstance();

图像处理服务

// ImageService.ets
import picker from ‘@ohos.file.picker’;
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;
import { OCRParams } from ‘./OCRTypes’;

class ImageService {
private static instance: ImageService = null;
private cameraManager: camera.CameraManager;

private constructor() {
this.initCamera();
public static getInstance(): ImageService {

if (!ImageService.instance) {
  ImageService.instance = new ImageService();

return ImageService.instance;

private initCamera(): void {

try {
  const context = getContext() as common.Context;
  this.cameraManager = camera.getCameraManager(context);

catch (err) {

  console.error('初始化相机失败:', JSON.stringify(err));

}

public async takePhoto(): Promise<OCRParams> {
try {
const cameraOutputCapability = this.cameraManager.getSupportedOutputCapability();
const photoProfiles = cameraOutputCapability.photoProfiles;

  if (photoProfiles.length === 0) {
    throw new Error('设备不支持拍照');

const photoProfile = photoProfiles[0];

  const photoOutput = this.cameraManager.createPhotoOutput(photoProfile);
  
  const photo = await photoOutput.capture();
  const imageBuffer = await photo.getComponent(image.ComponentType.JPEG);
  
  return {
    image: imageBuffer.byteArray,
    imageUri: photo.uri
  };

catch (err) {

  console.error('拍照失败:', JSON.stringify(err));
  throw err;

}

public async pickImage(): Promise<OCRParams> {
try {
const photoSelectOptions = new picker.PhotoSelectOptions();
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;

  const photoPicker = new picker.PhotoViewPicker();
  const result = await photoPicker.select(photoSelectOptions);
  
  if (result.photoUris.length === 0) {
    throw new Error('未选择图片');

const imageUri = result.photoUris[0];

  const imageFile = await fileIo.open(imageUri, fileIo.OpenMode.READ_ONLY);
  const imageBuffer = await fileIo.read(fileIo.fd, { length: 0 });
  await fileIo.close(fileIo.fd);
  
  return {
    image: imageBuffer.buffer,
    imageUri: imageUri
  };

catch (err) {

  console.error('选择图片失败:', JSON.stringify(err));
  throw err;

}

export const imageService = ImageService.getInstance();

主界面实现

// MainScreen.ets
import { ocrService } from ‘./OCRService’;
import { imageService } from ‘./ImageService’;
import { OCRResult } from ‘./OCRTypes’;

@Component
export struct MainScreen {
@State hasPermission: boolean = false;
@State isProcessing: boolean = false;
@State currentResult: OCRResult | null = null;
@State processingProgress: number = 0;
@State showImage: boolean = false;

build() {
Column() {
// 标题栏
Row() {
Text(‘简易OCR文字识别’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)

    Button(this.hasPermission ? '识别' : '授权')
      .width(80)
      .onClick(() => {
        if (this.hasPermission) {
          this.showActionMenu();

else {

          this.requestPermissions();

})

.padding(10)

  .width('100%')
  
  // 处理状态
  if (this.isProcessing) {
    Column() {
      Text('正在识别文字...')
        .fontSize(16)
        .margin({ bottom: 10 })
      
      Progress({
        value: this.processingProgress,
        total: 100,
        type: ProgressType.Ring
      })
      .width(100)
      .height(100)

.width(‘100%’)

    .margin({ top: 50 })

// 识别结果

  if (this.currentResult && !this.isProcessing) {
    Column() {
      if (this.showImage) {
        Image(this.currentResult.imageUri)
          .width('90%')
          .height(200)
          .objectFit(ImageFit.Contain)
          .margin({ bottom: 20 })
          .onClick(() => {
            this.showImage = false;
          })

Scroll() {

        Text(this.currentResult.text)
          .fontSize(16)
          .textAlign(TextAlign.Start)
          .width('100%')
          .padding(10)

.height(300)

      .border({ width: 1, color: '#E0E0E0' })
      .borderRadius(8)
      .margin({ bottom: 20 })
      
      Row() {
        Button('查看原图')
          .width(120)
          .height(50)
          .fontSize(18)
          .onClick(() => {
            this.showImage = true;
          })
        
        Button('重新识别')
          .width(120)
          .height(50)
          .fontSize(18)
          .margin({ left: 20 })
          .onClick(() => {
            this.showActionMenu();
          })

}

    .width('100%')
    .margin({ top: 30 })

else if (!this.hasPermission) {

    Column() {
      Text('需要权限')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      Text('请点击右上角"授权"按钮,允许应用访问相机和AI功能')
        .fontSize(16)
        .fontColor('#666666')

.padding(20)

    .width('90%')
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
    .margin({ top: 50 })

else if (!this.currentResult && !this.isProcessing) {

    Column() {
      Text('点击"识别"按钮开始')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      Text('从相册选择图片或拍照识别文字内容')
        .fontSize(16)
        .fontColor('#666666')

.padding(20)

    .width('90%')
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
    .margin({ top: 50 })

}

.width('100%')
.height('100%')
.padding(20)
.onAppear(() => {
  this.checkPermissions();
  ocrService.addResultListener({
    onOCRResult: (result) => {
      this.handleOCRResult(result);

});

})
.onDisappear(() => {
  ocrService.removeResultListener({
    onOCRResult: (result) => {
      this.handleOCRResult(result);

});

})

private async checkPermissions(): Promise<void> {

try {
  const permissions = [
    'ohos.permission.USE_AI',
    'ohos.permission.READ_MEDIA',
    'ohos.permission.CAMERA',
    'ohos.permission.DISTRIBUTED_DATASYNC'
  ];
  
  const result = await abilityAccessCtrl.verifyPermissions(
    getContext(),
    permissions
  );
  
  this.hasPermission = result.every(perm => perm.granted);

catch (err) {

  console.error('检查权限失败:', JSON.stringify(err));
  this.hasPermission = false;

}

private async requestPermissions(): Promise<void> {
this.hasPermission = await ocrService.requestPermissions();

if (!this.hasPermission) {
  prompt.showToast({ message: '授权失败,无法使用OCR功能' });

}

private showActionMenu(): void {
ActionMenu.show({
title: ‘选择图片来源’,
buttons: [
value: ‘camera’,

      action: () => this.recognizeFromCamera()
    },

value: ‘gallery’,

      action: () => this.recognizeFromGallery()

]

});

private async recognizeFromCamera(): Promise<void> {

try {
  this.isProcessing = true;
  this.processingProgress = 0;
  
  // 模拟处理进度
  const progressInterval = setInterval(() => {
    if (this.processingProgress < 40) {
      this.processingProgress += 10;

}, 300);

  const imageParams = await imageService.takePhoto();
  
  clearInterval(progressInterval);
  this.processingProgress = 50;
  
  const result = await ocrService.recognizeText({
    image: imageParams.image,
    imageUri: imageParams.imageUri
  });
  
  this.currentResult = result;

catch (err) {

  console.error('拍照识别失败:', JSON.stringify(err));
  prompt.showToast({ message: '拍照识别失败,请重试' });

finally {

  this.isProcessing = false;
  this.processingProgress = 100;

}

private async recognizeFromGallery(): Promise<void> {
try {
this.isProcessing = true;
this.processingProgress = 0;

  // 模拟处理进度
  const progressInterval = setInterval(() => {
    if (this.processingProgress < 40) {
      this.processingProgress += 10;

}, 300);

  const imageParams = await imageService.pickImage();
  
  clearInterval(progressInterval);
  this.processingProgress = 50;
  
  const result = await ocrService.recognizeText({
    image: imageParams.image,
    imageUri: imageParams.imageUri
  });
  
  this.currentResult = result;

catch (err) {

  console.error('图片识别失败:', JSON.stringify(err));
  prompt.showToast({ message: '图片识别失败,请重试' });

finally {

  this.isProcessing = false;
  this.processingProgress = 100;

}

private handleOCRResult(result: OCRResult): void {
this.currentResult = result;
this.isProcessing = false;
this.processingProgress = 100;
}

类型定义

// OCRTypes.ets
export interface OCRParams {
image: ArrayBuffer;
imageUri: string;
language?: string;
export interface OCRResult {

id: string;
text: string;
language: string;
confidence: number;
timestamp: number;
imageUri: string;

三、项目配置与权限
权限配置

// module.json5
“module”: {

"requestPermissions": [

“name”: “ohos.permission.USE_AI”,

    "reason": "使用OCR识别文字"
  },

“name”: “ohos.permission.READ_MEDIA”,

    "reason": "访问相册图片"
  },

“name”: “ohos.permission.CAMERA”,

    "reason": "拍照识别文字"
  },

“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,

    "reason": "同步识别结果"

],

"abilities": [

“name”: “MainAbility”,

    "type": "page",
    "visible": true
  },

“name”: “CameraAbility”,

    "type": "service",
    "backgroundModes": ["camera"]

]

}

四、总结与扩展

本OCR文字识别系统实现了以下核心功能:
多源图像输入:支持拍照和相册选择两种方式获取图片

高精度文字识别:利用鸿蒙AI能力识别多种语言的文字

结果可视化:清晰展示识别结果和原图对比

跨设备同步:多设备间实时同步识别结果

扩展方向:
多语言支持:扩展更多语言的识别能力

表格识别:识别图片中的表格并转换为结构化数据

手写体识别:支持手写文字的识别

实时识别:相机预览时实时识别文字

结果导出:将识别结果导出为TXT、PDF等格式

历史记录:保存识别历史便于回溯

通过HarmonyOS的OCR能力和分布式技术,我们构建了一个简单易用但功能强大的文字识别工具,帮助用户快速从图片中提取文字内容,并在多设备间无缝同步工作成果。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐