【HarmonyOS AI】 通用文字识别详解

【HarmonyOS AI】 通用文字识别详解

一、通用文字识别是什么?

通用文字识别,也就是 OCR(Optical Character Recognition,光学字符识别),用来把图片中的文字转换成应用可以处理的文本。例如,一张书页照片经过识别后,里面的内容就能被复制到笔记里;一张包含快递单号的截图,也可以提取出单号,用于查询物流。

在 HarmonyOS 中,Core Vision Kit(基础视觉服务)提供了端侧通用文字识别能力。开发者把图片交给系统服务,就可以获取识别到的文字及其位置,无需自行训练和部署 OCR 模型。识别过程在设备本地执行,图片不必为了这一步处理上传到云端。

文字识别,处理的是移动应用里另一个常见需求:把拍下来、存下来的文字,变成可以继续使用的内容。

二、它能做什么?

1、 提取图片里的文字

输入可以来自相机、图库,也可以是应用拿到的截图或扫描图片。识别后得到的字符,可以直接展示在文本框里,供用户修改、复制或保存。

比如,用户收到一张会议通知截图,应用可以提取其中的时间、地址等文字,再让用户选择需要的部分。OCR 负责把图片读成文字,具体哪些内容要填进日历或表单,由应用继续处理。

2、返回文字位置,用于选取和高亮

除了文字内容,识别结果还包含文字区域的位置和外框信息。开发者可以据此在原图上标出文字,做出"点击某一行""选择某一段"的交互。

如果页面上的图片经过缩放或旋转,绘制文字框时也需要做相应的坐标换算。后面的 Demo 先实现整张图片的文字提取,不涉及框选交互。

3、处理多种语言的印刷体文字

能力覆盖简体中文、繁体中文、英文、日文和韩文,适用于书籍、报刊、文档等印刷体文字,也支持一定范围内的文本倾斜、拍摄倾斜及复杂光照和背景场景。

本期介绍的主要规格如下,选取测试图片时可以先按这张表检查。

项目 支持范围或要求
图片格式 JPEG、JPG、PNG
语言 简体中文、繁体中文、英文、日文、韩文
字体 文档印刷体
文本长度 不超过 10000 字符
分辨率 720p 以上
图片高度 100 像素 < 高度 < 15210 像素
图片宽度 100 像素 < 宽度 < 10000 像素
图片比例 高宽比建议在 10:1 以下,接近手机屏幕比例为宜
角度 文本与拍摄角度夹角在 ±30° 范围内

规格依据华为开发者联盟本期能力介绍。印刷体支持不能直接推及任意手写字;拍摄时也应尽量保持清晰,避免反光遮挡文字。

端侧处理给这类功能带来的直接好处,是省去 OCR 请求中的图片上传和结果回传。应用也可以让原图留在本地。实际识别耗时仍与图片大小和设备有关;如果后续接入在线翻译、搜索或同步,相关数据传输需要由应用另行安排。

三、实际移动开发中,哪些场景需要它?

1、表单录入:从图片中取出需要填写的内容

报销、物流、客户资料登记等应用,经常需要用户对着图片填写一串信息。比如,物流查询页要求输入运单号,而用户手里只有一张快递单照片;报销页面需要填写票据上的日期和金额。

可以在输入框旁边增加"从图片提取"入口:选择图片,识别文字,再由用户选取或确认要填入的内容。这类功能尤其适合减少长编号的手动录入。

开发时要留意,通用 OCR 返回的是文字,金额、日期、单号的筛选和校验仍需业务代码处理。识别出票据文字,并不代表已经完成了票据字段结构化。

2、笔记与阅读:把纸面内容摘录到应用里

读书笔记、学习工具和知识管理应用,可以把 OCR 放在"新增笔记"或"导入内容"的流程中。

用户拍下一页书,提取文字,删掉不需要的段落,再保存到笔记。原本需要逐字输入的内容,变成了核对和编辑。对于学习外语的应用,也可以先提取句子,再接到查词或翻译功能。

这个场景适合先做一个很小的版本:一张图、一个结果编辑框、一个保存入口。本文的 Demo 就从这一流程的前半段开始。

3、 图片搜索:按图片里的文字查找资料

截图收藏、资料归档和企业文档应用里,文件名经常不能反映图片内容。用户记得截图里有一个项目名称,却不记得图片存在哪里。

一种接入方式是在用户导入图片时执行 OCR,将文字结果与图片标识一起保存,再用这些文字建立搜索索引。用户输入关键词后,应用查找索引并返回对应图片。

这里的检索和索引由应用实现,OCR 提供可供搜索的文本。若要批量处理相册,还需要单独设计授权、任务调度和资源管理,不能把单图 Demo 直接放进无限循环。

4、拍照翻译:先识别,再把文字交给翻译服务

旅行、语言学习和跨境购物应用,常会遇到菜单、招牌、商品说明等图片。用户想知道其中某段外文的意思,通常不方便手动输入。

应用可以先识别原文,让用户确认或选择目标段落,再调用翻译服务。图片上的文字位置还可以用于标记原文,帮助用户对照。

这时 OCR 负责读取文字,翻译由后续能力完成。把两步拆开后,原文也能单独复制、编辑,识别错字时不必重新走完整流程。

四、Demo:从图库选一张图片,提取并编辑文字

下面做一个单页面示例,完成这些操作:

  1. 点击按钮,打开系统图片选择器。
  2. 选择一张图片,在页面上预览。
  3. 自动调用通用文字识别,展示识别结果。
  4. 用户可以直接修改文本,也可以长按选择、复制。

示例代码单独保存为同目录下的 Index.ets,也完整列在下面。

1、准备工程

在 DevEco Studio 中创建 Stage 模型的 Empty Ability 工程,使用支持 Core Vision Kit 的 HarmonyOS SDK,将页面代码放到 entry/src/main/ets/pages/Index.ets

准备一张符合前面规格的 JPG 或 PNG 图片,放入测试设备图库;先用一张清晰的印刷体文档图片跑通流程。目标系统版本和设备支持范围按 textRecognition API 参考核对,并在支持该能力的真机上验证。

选图使用系统 PhotoViewPicker,应用只读取用户本次选中的图片。本示例的处理过程是选图、解码和本地识别,没有服务端接口配置。

2、完整页面代码

typescript 复制代码
import { textRecognition } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
import { fileIo } from '@kit.CoreFileKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  @State private previewUri: string = '';
  @State private resultText: string = '';
  @State private statusText: string = '请选择一张包含印刷体文字的图片';
  @State private busy: boolean = false;
  private pageActive: boolean = true;

  aboutToAppear(): void {
    this.pageActive = true;
  }

  aboutToDisappear(): void {
    // 已经开始的任务继续执行清理,但不再更新已退出的页面。
    this.pageActive = false;
  }

  private async loadPixelMap(uri: string): Promise<image.PixelMap> {
    const file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
    try {
      const source = image.createImageSource(file.fd);
      try {
        const info = await source.getImageInfo();
        const width = info.size.width;
        const height = info.size.height;
        if (width <= 100 || width >= 10000 ||
          height <= 100 || height >= 15210) {
          throw new Error('图片尺寸超出识别范围,请裁剪或缩放后重试');
        }
        return await source.createPixelMap();
      } finally {
        await source.release();
      }
    } finally {
      await fileIo.close(file);
    }
  }

  private async recognize(pixelMap: image.PixelMap): Promise<string> {
    await textRecognition.init();

    try {
      const visionInfo: textRecognition.VisionInfo = {
        pixelMap: pixelMap
      };
      const configuration: textRecognition.TextRecognitionConfiguration = {
        isDirectionDetectionSupported: false
      };
      const result = await textRecognition.recognizeText(
        visionInfo, configuration
      );
      return result.value;
    } finally {
      await textRecognition.release();
    }
  }

  private async selectAndRecognize(): Promise<void> {
    if (this.busy) {
      return;
    }
    this.busy = true;

    try {
      const picker = new photoAccessHelper.PhotoViewPicker();
      const selection = await picker.select({
        MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
        maxSelectNumber: 1
      });
      if (!this.pageActive || selection.photoUris.length === 0) {
        return;
      }

      const uri = selection.photoUris[0];
      this.previewUri = uri;
      this.resultText = '';
      this.statusText = '正在识别,请稍候...';

      const pixelMap = await this.loadPixelMap(uri);
      try {
        if (!this.pageActive) {
          return;
        }
        const text = await this.recognize(pixelMap);
        if (this.pageActive) {
          this.resultText = text;
          this.statusText = text.trim().length > 0
            ? '识别完成,可以编辑结果或长按复制'
            : '未识别到文字,请换一张更清晰的图片';
        }
      } finally {
        await pixelMap.release();
      }
    } catch (error) {
      if (this.pageActive) {
        const err = error as BusinessError;
        this.statusText = `处理失败:${err.message}`;
      }
    } finally {
      if (this.pageActive) {
        this.busy = false;
      }
    }
  }

  build() {
    Scroll() {
      Column({ space: 16 }) {
        Text('图片文字提取')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)

        if (this.previewUri.length > 0) {
          Image(this.previewUri)
            .width('100%')
            .height(220)
            .objectFit(ImageFit.Contain)
            .backgroundColor('#F3F4F6')
        }

        Button(this.busy ? '处理中...' : '从图库选图并识别')
          .width('100%')
          .enabled(!this.busy)
          .onClick(() => {
            this.selectAndRecognize();
          })

        Text(this.statusText)
          .width('100%')
          .fontSize(14)
          .fontColor('#666666')

        TextArea({
          text: this.resultText,
          placeholder: '识别结果会显示在这里'
        })
          .width('100%')
          .height(260)
          .fontSize(16)
          .copyOption(CopyOptions.LocalDevice)
          .onChange((value: string) => {
            this.resultText = value;
          })
      }
      .width('100%')
      .padding(20)
    }
    .width('100%')
    .height('100%')
  }
}

3、代码里的几个关键点

图片 URI 要先转换为 PixelMap 图片选择器返回的是 URI;代码通过 fileIo.open() 打开文件,再用 image.createImageSource() 解码。传给识别接口的是 PixelMap,不是文件路径字符串。

真正发起识别的是 recognizeText() 输入放在 VisionInfo.pixelMap 中,识别后的完整文本通过 result.value 获取。isDirectionDetectionSupported 用于配置朝向检测,示例沿用官方指南的 false 设置。接口调用方式可对照官方开发指南

这个 Demo 按单次任务管理资源。 每次识别前初始化服务,识别结束后释放;文件、图片源和 PixelMap 也分别在使用结束后清理。这样的写法便于看清完整流程。如果业务需要连续识别多张图片,可以在一个识别会话中复用服务,并在全部任务结束后释放。

选图和识别期间禁止重复点击。 busy 控制按钮状态;取消选图、未识别到文字和调用失败分别处理。页面退出后,任务会继续走资源清理,但不再更新页面上的结果。

4、怎么验证这个 Demo?

运行后先选择一张清晰的文字图片。正常流程中,页面会显示原图和"正在识别"的提示,随后把识别内容填入文本框。可以手动修改一处文字,并长按选中内容进行复制。

再试三种情况:打开图库后取消选择;选择一张没有文字的图片;开始识别后退出页面。分别检查按钮能否恢复、空结果是否有提示,以及退出过程中是否出现异常。

这里提供的是完整页面示例,识别调用流程依据官方开发指南整理,未在本次整理中进行 DevEco Studio 编译和真机运行,识别效果及耗时需要在目标设备上验证。

跑通这个页面后,接到笔记应用时,可以把文本框内容保存为笔记;接到表单页面时,可以把用户确认的文字填入对应字段。先把一个具体录入流程做顺,再扩展拍照、文字框选或批量处理。

5、参考资料

6、AI提示词,用于开发HarmonyOS通用文字识别

bash 复制代码
你是一名熟悉 HarmonyOS、ArkTS 和 ArkUI 的移动应用开发工程师。请基于 Core Vision Kit 的通用文字识别能力,开发一个"图片文字提取"页面。

功能要求:
1. 点击"选择图片",通过系统 PhotoViewPicker 从图库选择一张图片,并在页面预览。
2. 将图片解码为 PixelMap,调用 textRecognition 完成端侧文字识别。
3. 将识别结果展示在可编辑文本框中,支持选择、复制,方便用户提取截图、书页和文档中的文字。
4. 识别期间显示处理状态并禁止重复提交;正确处理取消选图、图片读取失败、未识别到文字和识别异常。
5. 正确管理识别服务、文件句柄、ImageSource 和 PixelMap 的生命周期,避免内存泄漏及页面退出后的无效更新。

技术要求:
使用 ArkTS、ArkUI 和 Stage 模型,优先适配当前工程的 SDK。识别调用使用 @kit.CoreVisionKit 的 textRecognition,选图使用系统图片选择器。OCR 在设备本地执行,不接入云端识别接口。界面简洁,包含图片预览、操作按钮、状态提示和结果编辑区。

请先检查现有工程结构,并根据华为官方文档核对当前 SDK 的接口、设备支持范围及权限要求,不要编造 API。随后直接完成代码和必要配置,提供完整 Index.ets 及涉及的其他文件,不要只给伪代码或零散片段。最后说明运行步骤,并验证正常识别、取消选图、空结果、连续操作和退出页面等情况;无法实际编译或真机验证的部分请明确标注。
相关推荐
哈__1 小时前
Flutter 3.44.9 + OpenHarmony7:home_widget 三方库桌面服务卡片(FormKit)的应用
flutter·华为·harmonyos
贾伟康1 小时前
【句匠|08】HarmonyOS ArkTS 句库搜索实战:支持关键词、分类和无结果反馈
harmonyos·arkts·分类筛选·学习应用·搜索功能
Sunny_G2 小时前
从 DevEco Code 到 Claude Code:一次工具链切换的完整决策
harmonyos
Kevin Coding2 小时前
鸿蒙 emitter/EventHub 没有 Sticky 粘性事件?手写一个轻量级 EmitterManager 解决
前端·华为·前端框架·移动开发·harmonyos
ChinaDragonDreamer2 小时前
HarmonyOS:Web使用Dsbridge与JavaScript完成交互
harmonyos·鸿蒙
贾伟康3 小时前
【句匠|10】HarmonyOS ArkTS 分类句库实战:复用列表结构并保持导航参数类型安全
harmonyos·arkts·router·分类导航·题库列表
体毛旺盛的猿4 小时前
HarmonyOS开发面试题
前端·华为·harmonyos
贾伟康4 小时前
【口算王|16】HarmonyOS ArkTS 多设备布局实战:适配手机、平板和 PC/2in1 的窗口变化
harmonyos·arkts·arkui·响应式布局·多设备适配
ChinaDragon13 小时前
HarmonyOS:6.0 新增和增强特性
harmonyos