拍照识别文字识别不准?华为 Core Vision Kit 让你三步搞定
前两天有个朋友找我吐槽,说他们公司要做个发票识别功能,用了第三方的 OCR 服务,结果识别准确率感人,尤其是那种拍摄角度有点偏的图片,基本就是瞎猜。我问他为啥不用 HarmonyOS 自带的文字识别能力,他一脸懵逼:"还有这玩意儿?"说实话,HarmonyOS 的 Core Vision Kit 提供的通用文字识别能力,在很多场景下比第三方服务更靠谱。而且它直接集成在系统里,不用额外申请 API
拍照识别文字识别不准?华为 Core Vision Kit 让你三步搞定
前两天有个朋友找我吐槽,说他们公司要做个发票识别功能,用了第三方的 OCR 服务,结果识别准确率感人,尤其是那种拍摄角度有点偏的图片,基本就是瞎猜。我问他为啥不用 HarmonyOS 自带的文字识别能力,他一脸懵逼:“还有这玩意儿?”
说实话,HarmonyOS 的 Core Vision Kit 提供的通用文字识别能力,在很多场景下比第三方服务更靠谱。而且它直接集成在系统里,不用额外申请 API Key,也不用担心网络延迟问题。
今天我就把这套能力从原理到实战给你盘一遍,顺便把踩过的坑都列出来,让你少走弯路。
一、这玩意儿能干啥?
Core Vision Kit 的文字识别能力,说白了就是把图片里的文字提取出来。听起来简单,但实际场景里要考虑的东西还挺多:
- 支持多种来源的图片:相机拍照、图库选择、甚至是网络下载的图片
- 能处理复杂场景:文本倾斜、拍摄角度偏移、光照不均匀、背景复杂这些情况都能应付
- 提供位置信息:不光给你文字内容,还能告诉你每个字、每行、每段在图片上的坐标位置
- 支持多语言:简体中文、英文、日文、韩文、繁体中文这五种语言都能识别
适用场景也很多,比如:
- 扫描文档、票据、卡证这些印刷品
- 街景招牌识别
- 翻译应用里的文字提取
- 搜索应用里的文字搜索
二、使用前先搞清楚这些限制
别急着上手写代码,有些限制你得先心里有数,不然到时候踩坑了都不知道为啥。
图片格式要求
- 只支持 JPEG、JPG、PNG 这三种格式
- 图片质量建议 720p 以上
- 尺寸范围:100px < 高度 < 15210px,100px < 宽度 < 10000px
- 高宽比例建议 10:1 以下,接近手机屏幕比例最好
语言和文本限制
- 支持的语言就那五种,其他语言识别不了
- 文本长度不超过 10000 字符
- 只能识别印刷体,手写体识别能力有限
拍摄角度要求
- 拍摄角度与文本平面的夹角要小于 30 度
- 超过这个角度,识别准确率会直线下降
设备限制
- 当前不支持模拟器,只能在真机上测试
- 支持的设备:Phone、PC/2in1、Tablet
三、核心 API 速览
HarmonyOS 文字识别的 API 设计还挺清晰的,主要就这几个关键类:
VisionInfo
这是待识别的入参,目前只支持 PixelMap 类型的视觉信息,而且颜色格式必须是 RGBA_8888。
import { image } from '@kit.ImageKit';
const visionInfo: textRecognition.VisionInfo = {
pixelMap: pixelMap // 待识别的图片 PixelMap
};
TextRecognitionConfiguration
配置项,主要用来控制是否开启朝向检测。
const config: textRecognition.TextRecognitionConfiguration = {
isDirectionDetectionSupported: true // 是否支持文字朝向检测,默认 true
};
这里有个小技巧:如果你确定图片是正向的,可以把这个设为 false,能提升性能。但如果你不确定,还是保持 true 比较稳妥。
TextRecognitionResult
这是识别结果的返回值,层级结构是这样的:
TextRecognitionResult (识别结果)
└── TextBlock[] (文本块/段落)
└── TextLine[] (文本行)
└── TextWord[] (单词)
└── PixelPoint[] (外框坐标点)
每个层级都包含文本内容和外框坐标信息,你可以根据需要选择合适的层级来处理数据。
四、实战:从零实现文字识别功能
下面我给你一个完整的实战案例,从图片选择到文字识别,再到结果展示,全流程走一遍。
1. 配置页面布局
先搞个简单的 UI,一个按钮触发图片选择,一个文本框展示识别结果。
// Index.ets
@Entry
@Component
struct Index {
@State recognizedText: string = '识别结果将显示在这里';
build() {
Column() {
Button('选择图片并识别')
.onClick(() => {
this.selectAndRecognizeImage();
})
.margin({ top: 20 })
Text(this.recognizedText)
.fontSize(16)
.margin({ top: 20 })
.padding(10)
.border({ width: 1, color: Color.Gray })
.width('90%')
}
.width('100%')
.height('100%')
}
}
2. 实现图片选择和识别逻辑
核心代码来了,这里涉及几个关键步骤:
import { textRecognition } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
import { picker } from '@kit.CoreFileKit';
import { fileIo as fs } from '@kit.CoreFileKit';
async selectAndRecognizeImage() {
try {
// 步骤1:拉起图库选择图片
const photoSelectOptions = new picker.PhotoSelectOptions();
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
const photoPicker = new picker.PhotoViewPicker();
const photoSelectResult = await photoPicker.select(photoSelectOptions);
if (photoSelectResult.photoUris.length === 0) {
this.recognizedText = '未选择图片';
return;
}
// 步骤2:将图片转换为 PixelMap
const imageSource = image.createImageSource(photoSelectResult.photoUris[0]);
const pixelMap = await imageSource.createPixelMap();
// 步骤3:实例化 VisionInfo
const visionInfo: textRecognition.VisionInfo = {
pixelMap: pixelMap
};
// 步骤4:配置识别参数
const config: textRecognition.TextRecognitionConfiguration = {
isDirectionDetectionSupported: true
};
// 步骤5:调用识别接口
const result = await textRecognition.recognizeText(visionInfo, config);
// 步骤6:处理识别结果
if (result && result.length > 0) {
let fullText = '';
result.forEach((block: textRecognition.TextBlock) => {
block.lines.forEach((line: textRecognition.TextLine) => {
fullText += line.value + '\n';
});
});
this.recognizedText = fullText;
} else {
this.recognizedText = '未识别到文字';
}
// 步骤7:释放资源
pixelMap.release();
} catch (error) {
this.recognizedText = `识别失败: ${JSON.stringify(error)}`;
}
}
3. 完整代码整合
把上面的代码整合到一起,就是一个完整的文字识别功能了。
// Index.ets
import { textRecognition } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
import { picker } from '@kit.CoreFileKit';
@Entry
@Component
struct Index {
@State recognizedText: string = '点击下方按钮选择图片进行文字识别';
async selectAndRecognizeImage() {
try {
// 拉起图库选择图片
const photoSelectOptions = new picker.PhotoSelectOptions();
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
const photoPicker = new picker.PhotoViewPicker();
const photoSelectResult = await photoPicker.select(photoSelectOptions);
if (photoSelectResult.photoUris.length === 0) {
this.recognizedText = '未选择图片';
return;
}
// 将图片转换为 PixelMap
const imageSource = image.createImageSource(photoSelectResult.photoUris[0]);
const pixelMap = await imageSource.createPixelMap();
// 实例化 VisionInfo
const visionInfo: textRecognition.VisionInfo = {
pixelMap: pixelMap
};
// 配置识别参数
const config: textRecognition.TextRecognitionConfiguration = {
isDirectionDetectionSupported: true
};
// 调用识别接口
const result = await textRecognition.recognizeText(visionInfo, config);
// 处理识别结果
if (result && result.length > 0) {
let fullText = '';
result.forEach((block: textRecognition.TextBlock) => {
block.lines.forEach((line: textRecognition.TextLine) => {
fullText += line.value + '\n';
});
});
this.recognizedText = fullText;
} else {
this.recognizedText = '未识别到文字';
}
// 释放资源
pixelMap.release();
} catch (error) {
this.recognizedText = `识别失败: ${JSON.stringify(error)}`;
}
}
build() {
Column() {
Button('选择图片并识别')
.onClick(() => {
this.selectAndRecognizeImage();
})
.margin({ top: 20 })
Text(this.recognizedText)
.fontSize(16)
.margin({ top: 20 })
.padding(10)
.border({ width: 1, color: Color.Gray })
.width('90%')
}
.width('100%')
.height('100%')
}
}
五、实战中遇到的坑
上面的代码看起来挺简单,但实际用起来还是会遇到一些问题。我把踩过的坑给你列出来,希望能帮你省点时间。
坑1:图片格式不对
有时候你从图库选的图片,格式可能不是 JPEG/JPG/PNG,或者颜色格式不是 RGBA_8888,这时候就会报错。
解决方法:在创建 PixelMap 之前,先检查图片格式,如果不支持就提示用户重新选择。
// 检查图片格式
const imageInfo = await imageSource.getImageInfo();
if (imageInfo.size.width < 100 || imageInfo.size.height < 100) {
this.recognizedText = '图片尺寸过小,请选择 100px 以上的图片';
return;
}
坑2:拍摄角度太大
如果你选的图片拍摄角度超过 30 度,识别准确率会明显下降。这时候可以在 UI 上加个提示,引导用户重新拍摄。
// 在识别结果为空时提示
if (!result || result.length === 0) {
this.recognizedText = '未识别到文字,可能原因:\n1. 图片中没有文字\n2. 拍摄角度过大(建议小于30度)\n3. 光照不足或背景复杂';
}
坑3:内存泄漏
PixelMap 是个需要手动释放的资源,如果你忘了调用 pixelMap.release(),会导致内存泄漏。特别是在循环处理多张图片时,这个问题会更明显。
建议在 try-catch-finally 里统一处理资源释放:
let pixelMap: image.PixelMap | null = null;
try {
// ... 识别逻辑
} catch (error) {
// ... 错误处理
} finally {
if (pixelMap) {
pixelMap.release();
}
}
坑4:模拟器不支持
这个最坑,我在模拟器上测试了半天,一直报错,后来才发现官方文档明确说了"该能力当前不支持模拟器"。
所以记得用真机测试,别在模拟器上浪费时间。
六、进阶玩法:获取文字位置信息
除了文字内容,你还能获取每个字、每行、每段在图片上的坐标位置。这个功能在做文字标注、文字高亮等场景特别有用。
// 获取文字位置信息
if (result && result.length > 0) {
result.forEach((block: textRecognition.TextBlock, blockIndex: number) => {
console.log(`段落 ${blockIndex + 1}: ${block.value}`);
block.lines.forEach((line: textRecognition.TextLine, lineIndex: number) => {
console.log(` 行 ${lineIndex + 1}: ${line.value}`);
// 获取行的外框坐标
const corners = line.cornerPoints;
console.log(` 坐标: (${corners[0].x}, ${corners[0].y}) -> (${corners[2].x}, ${corners[2].y})`);
line.words.forEach((word: textRecognition.TextWord, wordIndex: number) => {
console.log(` 单词 ${wordIndex + 1}: ${word.value}`);
});
});
});
}
七、性能优化建议
如果你的应用需要频繁调用文字识别功能,可以考虑下面这些优化点:
1. 关闭朝向检测
如果你确定图片都是正向的,可以把 isDirectionDetectionSupported 设为 false,能提升识别速度。
const config: textRecognition.TextRecognitionConfiguration = {
isDirectionDetectionSupported: false // 已知图片正向,关闭朝向检测
};
2. 图片预处理
在识别前对图片进行预处理,比如调整大小、增强对比度、去噪等,能提升识别准确率。
// 调整图片大小
const targetWidth = 1920;
const targetHeight = 1080;
const scaledPixelMap = await pixelMap.scale(targetWidth, targetHeight);
3. 批量处理
如果有多张图片需要识别,可以考虑批量处理,减少重复的初始化开销。
八、总结
HarmonyOS 的文字识别能力,说实话,在大多数场景下已经够用了。特别是对于印刷体识别,准确率还挺高的。而且它是系统级能力,不用依赖第三方服务,也不用担心隐私问题。
但也有一些局限性,比如手写体识别能力有限,不支持模拟器测试,这些你在做技术选型时得考虑清楚。
如果你在做发票识别、文档扫描、翻译应用这类功能,HarmonyOS 的文字识别能力值得试试。至少比用第三方 OCR 服务省不少事。
有问题的话,欢迎在评论区交流,我看到了都会回复。
更多推荐




所有评论(0)