【拥抱鸿蒙】HarmonyOS NEXT实现双路预览并识别文字

news2024/11/25 4:48:36

我们在许多其他平台看到过OCR功能的应用,那么HarmonyOS在这方面的支持如何呢?我们如何能快速使用这一能力呢?使用这一能力需要注意的点有哪些呢?就让我们一起来探究吧~

【开发环境】

  • 版本规则号:HarmonyOS NEXT
  • 版本类型:Developer Preview2
  • OpenHarmony API Version:11 Release
  • compileSdkVersion:4.1.0(11)
  • IDE:DevEco Studio 4.1.3.700(Mac)

实现目标

通过对Core Vision Kit的基础功能的实现,完成相册图片获取、OCR、相机预览,图片格式转换等功能,熟悉ArkTS的开发流程和细节,加深对HarmonyOS中各类基础库的理解。

名词解释

  • Core Vision Kit:基础视觉服务
  • Camera Kit:相机服务
  • Core File Kit:文件基础服务
  • OCR:Optical Character Recognition,通用文字识别或光学字符识别
  • URI: Uniform Resource Identifier,资源标识符,本文中URI指图片资源的访问路径

核心功能

本篇所涉及的核心功能就是通用文字识别(OCR)。

OCR是通过拍照、扫描等光学输入方式,把各种票据、卡证、表格、报刊、书籍等印刷品文字转化为图像信息,再利用文字识别技术将图像信息转化为计算机等设备可以使用的字符信息的技术。

首先,我们实现从相册选取一张图片,并识别图片上的文字的功能。这一功能的实现基于系统提供的Core Vision Kit中的OCR能力。

  1. 创建一个ImageOCRUtil类,用于封装OCR相关功能。 从CoreVisionKit中导入textRecognition模块,声明一个名为ImageOCRUtil的类,并创建其new()方法。
import { textRecognition } from '@kit.CoreVisionKit';

export class ImageOCRUtil {}

export default new ImageOCRUtil();
  1. ImageOCRUtil中实现图片中文字识别功能。 构建一个异步方法:async recognizeText(image: PixelMap | undefined, resultCallback: Function),其中PixelMap为图像像素类,用于读取或写入图像数据以及获取图像信息。目前pixelmap序列化大小最大128MB,超过会送显失败。大小计算方式为:宽 x 高 x 每像素占用字节数。
export class ImageOCRUtil {
  /**
   * 文字识别
   * 
   * @param image 图片源数据
   * @param resultCallback 结果返回
   * @returns
   */
  async recognizeText(image:  PixelMap | undefined, resultCallback: Function) {
    // 非空判断
    if (!image || image === undefined) {
      hilog.error(0x0000, 'OCR', 'the image is not existed');
      return;
    }

    let visionInfo: textRecognition.VisionInfo = {
      pixelMap: image
    };

    let textConfiguration: textRecognition.TextRecognitionConfiguration = {
      isDirectionDetectionSupported: false
    };

    textRecognition.recognizeText(visionInfo, textConfiguration, (error: BusinessError, data: textRecognition.TextRecognitionResult) => {
      // 识别成功,获取结果
      if (error.code == 0) {
        let recognitionRes = data.toString();
        // 将识别结果返回
        resultCallback(recognitionRes);
      }
    });
  }
}
  1. ImageOCRUtil中实现从相册获取图片URI功能。 这里需用到Core File Kit,可借助图片选择器获取图片的存储路径。
import { picker } from '@kit.CoreFileKit';

/**
  * 打开相册选择图片
  * @returns 异步返回图片URI
  */
openAlbum(): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      let photoPicker = new picker.PhotoViewPicker;
      photoPicker.select({
        MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE,
        maxSelectNumber: 1
      }).then((res: picker.PhotoSelectResult) => {
        resolve(res.photoUris[0]);
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, "OCR", `Failed to get photo uri, code: ${err.code}, message: ${err.message}`)
        resolve('')
      })
    })
}

UI与调用

为了验证图片识别的效果,我们可以搭建简单的UI,提供从相册获取图片 -> 文字识别 -> 显示识别结果这一流程的UI与交互。

Index页面中,UI相关的代码如下:

import { image } from '@kit.ImageKit'
import { hilog } from '@kit.PerformanceAnalysisKit';
import ImageOCRUtil from '../common/utils/ImageOCRUtil';
import CommonUtils from '../common/utils/CommonUtils';
import { fileIo } from '@kit.CoreFileKit';

@Entry
@Component
struct Index {
  private imageSource: image.ImageSource | undefined = undefined;
  @State selectedImage: PixelMap | undefined = undefined;
  @State dataValues: string = '';

  build() {
    Column() {
      // 选中的图片
      Image(this.selectedImage)
        .objectFit(ImageFit.Fill)
        .height('60%')

      // 识别的内容
      Text(this.dataValues)
        .copyOption(CopyOptions.LocalDevice)
        .height('15%')
        .width('60%')
        .margin(10)

      // 选择图片按钮
      Button('选择图片')
        .type(ButtonType.Capsule)
        .fontColor(Color.White)
        .width('80%')
        .margin(10)
        .onClick(() => {
          this.selectImage();
        })

      Button('开始识别')
        .type(ButtonType.Capsule)
        .fontColor(Color.White)
        .alignSelf(ItemAlign.Center)
        .width('80%')
        .margin(10)
        .onClick(() => {
            // 点击“开始识别”
          });
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  private async selectImage() {
    let uri = await ImageOCRUtil.openAlbum();
    if (uri === undefined) {
      hilog.error(0x0000, 'OCR', 'Failed to get the uri of photo.')
      return;
    }

    this.loadImage(uri);
  }

  loadImage(path: string) {
    setTimeout(async () => {
      let fileSource = await fileIo.open(path, fileIo.OpenMode.READ_ONLY);
      this.imageSource = image.createImageSource(fileSource.fd);
      this.selectedImage = await this.imageSource.createPixelMap();
    })
  }

}

在“开始识别”的按钮的点击事件中,我们调用ImageOCRUtilrecognizeText,并在其回调中显示识别结果。 并对imageSourceselectedImage进行release()释放内存空间。

ImageOCRUtil.recognizeText(this.selectedImage, (content: string) => {
  if (!CommonUtils.isEmpty(content)) {
    this.dataValues = content;
  }
  
  // 释放内存空间
  this.imageSource?.release();
  this.selectedImage?.release();
});

其实现效果如下所示:

双路预览

为了对文字识别这一功能进行扩展,我们可以结合相机的双路预览功能实时获取图片帧,并对图片帧进行文字识别。

我们创建一个XComponentPage的页面,添加一个相机预览视图。

  1. 获取ImageReceiver组件的SurfaceId。
async getImageReceiverSurfaceId(receiver: image.ImageReceiver): Promise<string | undefined> {
    let ImageReceiverSurfaceId: string | undefined = undefined;
    if (receiver !== undefined) {
      console.info('receiver is not undefined');
      let ImageReceiverSurfaceId: string = await receiver.getReceivingSurfaceId();
      console.info(`ImageReceived id: ${ImageReceiverSurfaceId}`);
    } else {
      console.error('createImageReceiver failed');
    }
    return ImageReceiverSurfaceId;
  }
  1. 创建XComponent组件Surface。
XComponent({
        // 组件的唯一标识
        id: 'LOXComponent',
        // surface:EGL/OpenGLES和媒体数据写入  component:开发者定制绘制内容
        type: XComponentType.SURFACE,
        // 应用Native层编译输出动态库名称,仅XComponent类型为"surface"时有效
        libraryname: 'SingleXComponent',
        // 给组件绑定一个控制器,通过控制器调用组件方法,仅XComponent类型为"surface"时有效
        controller: this.mXComponentController
      })// 插件加载完成时回调事件
        .onLoad(() => {
          // 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置
          // 预览流与录像输出流的分辨率的宽高比要保持一致
          this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 1920, surfaceHeight: 1080 });
          // 获取Surface ID
          this.xComponentSurfaceId = this.mXComponentController.getXComponentSurfaceId();
        })// 插件卸载完成时回调事件
        .onDestroy(() => {

        })
        .width("100%")
        .height(display.getDefaultDisplaySync().width * 9 / 16)
  1. 实现双路预览。
import camera from '@ohos.multimedia.camera';


async createDualChannelPreview(cameraManager: camera.CameraManager, XComponentSurfaceId: string, receiver: image.ImageReceiver): Promise<void> {
    // 获取支持的相机设备对象
    let camerasDevices: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();

    // 获取支持的模式类型
    let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(camerasDevices[0]);
    let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
    if (!isSupportPhotoMode) {
      console.error('photo mode not support');
      return;
    }

    // 获取profile对象
    let profiles: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(camerasDevices[0], camera.SceneMode.NORMAL_PHOTO); // 获取对应相机设备profiles
    let previewProfiles: Array<camera.Profile> = profiles.previewProfiles;

    // 预览流1
    let previewProfilesObj: camera.Profile = previewProfiles[0];

    // 预览流2
    let previewProfilesObj2: camera.Profile = previewProfiles[0];

    // 创建 预览流1 输出对象
    let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj, XComponentSurfaceId);

    // 创建 预览流2 输出对象
    let imageReceiverSurfaceId: string = await receiver.getReceivingSurfaceId();
    let previewOutput2: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj2, imageReceiverSurfaceId);

    // 创建cameraInput对象
    let cameraInput: camera.CameraInput = cameraManager.createCameraInput(camerasDevices[0]);

    // 打开相机
    await cameraInput.open();

    // 会话流程
    let photoSession: camera.PhotoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;

    // 开始配置会话
    photoSession.beginConfig();

    // 把CameraInput加入到会话
    photoSession.addInput(cameraInput);

    // 把 预览流1 加入到会话
    photoSession.addOutput(previewOutput);

    // 把 预览流2 加入到会话
    photoSession.addOutput(previewOutput2);

    // 提交配置信息
    await photoSession.commitConfig();

    // 会话开始
    await photoSession.start();
  }
  1. 通过ImageReceiver实时获取预览图像。
  onImageArrival(receiver: image.ImageReceiver): void {
    receiver.on('imageArrival', () => {
      receiver.readNextImage((err: BusinessError, nextImage: image.Image) => {
        if (err || nextImage === undefined) {
          console.error('readNextImage failed');
          return;
        }
        nextImage.getComponent(image.ComponentType.JPEG, async (err: BusinessError, imgComponent: image.Component) => {
          if (err || imgComponent === undefined) {
            console.error('getComponent failed');
          }
          if (imgComponent && imgComponent.byteBuffer as ArrayBuffer) {
            let imageArrayBuffer = imgComponent.byteBuffer as ArrayBuffer;
            console.log("得到图片数据:" + JSON.stringify(imageArrayBuffer))
            console.log("图片数据长度:" + imageArrayBuffer.byteLength)
            
            // do something
          } else {
            console.error('byteBuffer is null');
          }
          nextImage.release();
        })
      })
    })
  }

最后,我们对预览返回进行文字识别。预览返回的结果imageArrayBuffer的类型为ArrayBuffer,我们需要将其转换为PixelMap类,然后再调用recognizeText()识别。

// 转换图片格式为PixelMap,并识别其中的文字

            let imageSource: image.ImageSource = image.createImageSource(imageArrayBuffer);
            try {
              let option: image.DecodingOptions = {
                desiredSize: { width: 1920, height: 1080 }
              }
              let imagePm = await imageSource.createPixelMap(option);
              ImageOCRUtil.recognizeText(imagePm, (res: string) => {
                console.info(res);
              });

            } catch (err) {
              console.error(JSON.stringify(err))
            }

完整代码见 -> hosgo-vision

拥抱鸿蒙,拥抱未来,选择远方,风雨兼程。

最后

如果你想成为一名鸿蒙开发者,以下这些资料将是十分优质且有价值,让你的鸿蒙开发之路事半功倍!相对于网上那些碎片化的知识内容,这份学习资料的知识点更加系统化,更容易理解和记忆。

鸿蒙Next全套VIP学习资料←点击领取!(安全链接,放心点击

包含了:【OpenHarmony多媒体技术、Stage模型、ArkUI多端部署、分布式应用开发、音频、视频、WebGL、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战】等技术知识点。

1.鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

2.大厂面试必问面试题

3.鸿蒙南向开发技术

4.鸿蒙APP开发必备

 5.HarmonyOS Next 最新全套视频教程

 6.鸿蒙生态应用开发白皮书V2.0PDF

获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1833078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

研发团队的「技术债」如何进行量化管理?

我共事过的每个团队都会讨论技术债。有些团队知道如何管理它&#xff0c;也有些团队因此崩溃瘫痪&#xff0c;甚至有一家公司因为技术债务没有得到解决而宣告失败。 什么是技术债务&#xff1f; 「债务」这个比喻非常恰当。最早提出「技术债务 Technical Debt」比喻的工程师 W…

干货 | 使用 Navicat BI 解锁数据的力量

商业智能&#xff08;BI&#xff09;是一种将数据转化为可执行洞察的实践&#xff0c;能够帮助业务领导者提升整体业绩。这个过程中最重要的一个阶段是数据探索和可视化阶段&#xff0c;它涉及通过报告将数据组织并转化为有意义的信息。为了让数据更易于理解&#xff0c;BI 专业…

转型AI产品经理(11):“损失规避”如何应用在Chatbot产品中

损失规避是行为经济学和心理学中的一个重要概念&#xff0c;它揭示了人们在面对潜在的收益和损失时&#xff0c;表现出对损失的强烈偏好避免&#xff0c;相比于获得同等价值的利益&#xff0c;人们对损失的感受更为强烈。它主要有以下特征&#xff1a; 1、不对称性 损失规避体…

HTML中的资源提示关键词

渲染阻塞问题 之前在学习浏览器的渲染原理的时候我们就知道&#xff1a;因为浏览器一次只能开启一个渲染主线程&#xff0c;所以当浏览器解析到script标签时会停止DOM树的构建&#xff0c;转而去执行script&#xff0c;如果script中引用的是外部脚本&#xff0c;则浏览器会先从…

房间预订系统怎么做

在这个日新月异的时代&#xff0c;旅游已经成为了许多人生活中不可或缺的一部分。然而&#xff0c;在规划一场完美的旅行时&#xff0c;房间预订往往是一个让人头疼的问题。今天&#xff0c;我要为大家揭秘一款颠覆传统的房间预订系统——它不仅仅是一个预订工具&#xff0c;更…

来自红队大佬的经验之谈---命令执行过滤绕过-Windows篇

感谢来自老流氓大佬的投稿&#xff0c;本次文章介绍的是在windows环境下&#xff0c;过滤的“点”和“空格”等符号&#xff0c;导致在写入webshell时会受限。以下是针对该目标的绕过记录。 首先是命令执行和过滤验证&#xff0c;如下&#xff1a;​ 执行dir命令&#xff0c;…

APP Android

APP Android 安卓源生应用程序 APP IOS-CSDN博客 05.04 06:11Testing

【Qt】chartView设置橡皮筋效果(RubberBand)

1. 效果 2. 代码 QChartView* chartView new QChartView();chartView->setRubberBand(QChartView::RectangleRubberBand);

基于深度学习的鸟类检测识别系统【python源码+Pyqt5界面+数据集+训练代码 MX_003期】

简介&#xff1a; 基于深度学习的鸟类检测识别系统在当今世界中具有广泛的应用前景。系统不仅可以帮助生态学家和保护人员监测和保护鸟类种群&#xff0c;还能在农业管理、城市生态监测以及科学研究领域发挥重要作用。通过自动化的图像识别技术&#xff0c;可以实现对鸟类种类、…

Mac M3 Pro 部署Flink-1.16.3

目录 1、下载安装包 2、解压及配置 3、启动&测试 4、测试FlinkSQL读取hive数据 以上是mac硬件配置 1、下载安装包 官网&#xff1a;Downloads | Apache Flink 网盘&#xff1a; Flink 安装包 https://pan.baidu.com/s/1IN62_T5JUrnYUycYMwsQqQ?pwdgk4e Flink 已…

Comfyui|AnimateDiff生成动画基础使用方法

今天分享一个在Comfyui中使Amimatediff生成动画视频的小教程。与WebUI相比&#xff0c;ComfyUI在生成图片的速度更快&#xff0c;可控性更强&#xff0c;且所需的显存更小。 ComfyUI采用基于节点连接的工作流程&#xff0c;对于使用过Blender、C4D等三维软件的朋友来说应该会对…

PHP邮箱服务器搭建与配置教程?如何使用?

PHP邮箱服务器搭建的步骤&#xff1f;服务器搭建的注意事项&#xff1f; 在当今的数字化时代&#xff0c;电子邮件仍然是沟通和业务处理的重要工具之一。通过PHP搭建和配置一个邮箱服务器&#xff0c;您可以实现自主掌控邮件系统&#xff0c;确保数据的安全性和隐私性。AokSen…

微信酒店预订系统怎么做

随着科技的飞速发展&#xff0c;我们的生活正被智能化、数字化所包围。在旅行这一美好体验中&#xff0c;如何使预订过程更加简单、高效&#xff0c;成为了众多旅游爱好者关注的焦点。今天&#xff0c;我要为大家介绍一款引领行业潮流的微信酒店预订系统&#xff0c;让您的旅行…

无忧易售ERP:引领电商管理新纪元,一键EAN生成,让商品流通无忧

在瞬息万变的电商蓝海中&#xff0c;高效与精准成为企业制胜的关键。为了帮助广大电商卖家们在激烈的市场竞争中脱颖而出&#xff0c;无忧易售ERP支持Allegro、OZON、OnBuy、Walmart平台免费EAN生成工具及一键填充功能。 如何使用无忧易售erp快速生成EAN码呢&#xff0c;接下来…

水滴式粉碎机:粉碎干性物料的理想选择

在工业生产中&#xff0c;干性物料的粉碎是一个重要的环节&#xff0c;其对于提升产品质量、优化生产流程和提高生产效率具有显著的影响。近年来&#xff0c;水滴式粉碎机因其粉碎原理和工作性能&#xff0c;逐渐在干性物料粉碎领域崭露头角&#xff0c;成为众多企业的理想选择…

有关健身的俄语表达,柯桥零基础俄语培训

фитнес 健身 тренер 教练 абонемент 会员卡 аэробика 有氧运动 анаэробика 无氧运动 плавание 游泳 пробежка / бег трусцой 慢跑 беговая дорожка 跑步机 йога 瑜伽 коври…

大量用户中招,远控木马已经潜伏各类在线会议平台

从 2023 年 12 月开始&#xff0c;研究人员发现有攻击者创建虚假 Skype、Google Meet 和 Zoom 网站来进行恶意软件传播。攻击者为安卓用户投递 SpyNote 远控木马&#xff0c;为 Windows 用户投递 NjRAT 和 DCRAT 远控木马。 攻击行动概述 攻击者在单个 IP 地址上部署了所有的虚…

震撼科技界的GPT-4o发布首日即遭“越狱破防”

前言 本文主要解读分析OpenAI最新推出的大型模型GPT-4o可能存在的越狱风险。 5 月14 日凌晨的科技圈再一次被OpenAI轰动&#xff0c;其发布的最新大模型GPT-4o&#xff0c;能力横跨语音、文本和视觉&#xff0c;这一成果无疑再次巩固了OpenAI在人工智能领域的领先地位。 然而…

缓冲区设置

缓冲区设计 一、简介 在网络通讯中&#xff0c;用户态缓冲区和内核态缓冲区的大小设定对于优化网络性能和确保数据传输可靠性至关重要。下图是网路通讯的内核缓冲区使用情况&#xff1a; 数据的读写都需要进行系统调用&#xff0c;从用户态切换到内核态去接收数据&#xff0…

《市场瞭望》期刊简介及投稿要求

《市场瞭望》是一本由福建日报社&#xff08;福建日报报业集团&#xff09;主办的经济类杂志&#xff0c;创刊于1994年&#xff0c;半月刊&#xff0c;A4纸大小&#xff0c;内文80页&#xff0c;铜版纸全彩印刷&#xff0c;国内外公开发行。 该杂志的邮发代号为--国际刊号ISSN…