更新记录

1.0.0(2026-11-12)

初次发版


平台兼容性

uni-app x(4.71)

Chrome Safari Android iOS iOS插件版本 鸿蒙 微信小程序
× × 5.0 16 1.0.0 × ×

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × √ ×

erha-ocr 本地离线 OCR 识别

纯本地离线 OCR 识别插件。不需要服务器、不需要 API Key、图片不出设备。

支持通用文字识别、身份证正反面识别、银行卡识别。

支持平台:App-Android / App-iOS

最低版本:HBuilderX 4.71+ / uni-app x 4.71+

iOS 买家必读

iOS 端基于 Apple 系统 Vision 框架,插件本身不需额外授权。但要把 App 装到 iPhone (真机调试或上架)必须有 Apple 开发者账号(99 美元/年),这是 Apple 的签名要求, 与插件无关。没有开发者账号的,iOS 端无法运行。


为什么选本地识别

市面上多数 OCR 插件要么要你申请云服务密钥、按次付费,要么要把图片传到第三方服务器。

处理身份证、银行卡这类敏感信息的业务(金融、政务、实名认证)有一条硬性约束:证件照片不允许上传到第三方。云方案在合规评审阶段就可能被否。

本插件全部识别在设备本地完成:

云端方案 本插件
服务器 需要,按次计费 不需要
API Key 必须申请 不需要
图片流向 上传到服务商 不出设备
调用次数 有额度限制 无限
金融/政务合规 需评估 可直接用

技术实现

平台 方案 包体积影响
Android Google ML Kit 文字识别(中文) 约 +5-10MB
iOS Apple Vision 框架 几乎为 0(系统自带)

Android 需要把中文模型打包进 APK,所以包体积会增加几 MB。iOS 用的是系统框架,不占包体积,且走 NPU 加速,单张图通常几十毫秒。


安装

把 uni_modules/erha-ocr 整个目录放进你项目的 uni_modules/ 下。

不需要在 manifest.json 里勾选任何模块,ML Kit 依赖由插件自带的 config.json 声明,云打包会自动处理。

Android 权限

"permissions": [
  "<uses-permission android:name=\"android.permission.CAMERA\"/>"
]

iOS 权限

"privacyDescription": {
  "NSCameraUsageDescription": "需要使用相机拍摄证件以进行识别",
  "NSPhotoLibraryUsageDescription": "需要访问相册以选择图片进行识别"
}

iOS 需要 Apple 开发者账号

再强调一次,因为这是最常见的售后问题:

场景 是否需要 Apple 开发者账号(99 美元/年)
打 iOS 自定义基座 / 真机调试 需要
上架 App Store 需要
只做 Android 不需要
H5 / 小程序 不支持本插件

免费 Apple ID 只能用于模拟器和 7 天有效期的个人签名,不能用于正式分发。 如果项目只面向 Android,可以忽略这一节。

这笔费用是 Apple 收取的,与插件无关,也无法绕过。


快速开始

<template>
  <view>
    <button @click="doRecognize">识别身份证</button>
  </view>
</template>

<script setup>
  import {
    initOcr,
    recognizeIdCard,
    type RecognizeIdCardResult,
    type OcrFail
  } from '@/uni_modules/erha-ocr'

  onMounted(() => {
    initOcr({
      success: () => console.log('OCR 就绪'),
      fail: (err : OcrFail) => console.log(err.errMsg)
    })
  })

  function doRecognize() {
    // 先用相机拍照或从相册选图,拿到本地路径
    uni.chooseImage({
      count: 1,
      success: (res) => {
        recognizeIdCard({
          filePath: res.tempFilePaths[0],
          side: 'front',              // front 人像面 / back 国徽面
          success: (r : RecognizeIdCardResult) => {
            const info = r.front
            if (info != null) {
              console.log('姓名:', info.name)
              console.log('身份证号:', info.idNumber)
              console.log('校验通过:', r.valid)
            }
          },
          fail: (err : OcrFail) => {
            uni.showToast({ title: err.errMsg, icon: 'none' })
          }
        })
      }
    })
  }
</script>

注意:必须从插件根路径 @/uni_modules/erha-ocr 导入,不要引用 utssdk 内部文件。


API

initOcr(options)

初始化识别引擎。Android 会加载模型,建议在页面 onMounted 或 App onLaunch 调用一次。

参数 类型 说明
success (res: string) => void 初始化成功
fail (err: OcrFail) => void 初始化失败

recognizeText(options)

通用文字识别,返回全部文本。

参数 类型 说明
filePath string 图片本地路径
success (res: OcrTextResult) => void 识别成功
fail (err: OcrFail) => void 识别失败
// OcrTextResult
{
  fullText : string    // 整段文本,按行用 \n 连接
  lines : OcrLine[]    // 逐行结果(当前版本为空数组,用 fullText 即可)
  width : number       // 图片宽度
  height : number      // 图片高度
}

recognizeIdCard(options)

身份证识别。

参数 类型 说明
filePath string 图片本地路径
side string 'front' 人像面 / 'back' 国徽面
success (res: RecognizeIdCardResult) => void 识别成功
fail (err: OcrFail) => void 识别失败
// RecognizeIdCardResult
{
  front : IdCardFront | null   // 人像面字段
  back : IdCardBack | null     // 国徽面字段
  rawText : string             // 原始识别文本,用于排查
  valid : boolean              // 是否通过校验
}

// IdCardFront
{
  name : string        // 姓名
  gender : string      // 性别:男 / 女
  ethnicity : string   // 民族,如「汉」
  birthday : string    // 出生,格式 1990-05-12
  address : string     // 住址(已合并跨行)
  idNumber : string    // 18 位身份证号
}

// IdCardBack
{
  issuingAuthority : string  // 签发机关
  validFrom : string         // 有效期起,格式 2015-01-01
  validTo : string           // 有效期止,格式 2035-01-01 或「长期」
}

recognizeBankCard(options)

银行卡识别。

// RecognizeBankCardResult
{
  card : BankCard | null
  rawText : string
  valid : boolean
}

// BankCard
{
  cardNumber : string  // 卡号(已去除空格)
  cardType : string    // 借记卡 / 信用卡
  bankName : string    // 发卡行,如「工商银行」,未匹配时为空串
}

releaseOcr()

释放资源。页面卸载时可选调用,不调也不影响使用。


工具函数

插件还导出了几个纯逻辑工具函数,可以直接用:

import {
  isValidIdNumber,
  isValidBankCard,
  formatCardNumber,
  maskCardNumber
} from '@/uni_modules/erha-ocr/utssdk/utils/parser.uts'

isValidIdNumber('110105199005123012')  // true,18 位身份证校验位校验
isValidBankCard('6222021234567896')    // true,Luhn 校验
formatCardNumber('6222021234567896')   // '6222 0212 3456 7896'
maskCardNumber('6222021234567896')     // '**** **** **** 7896'

识别准确率是怎么保证的

单纯把 OCR 文本吐出来没用,用户要的是「姓名:张三」这样的结构化字段。本插件在解析层做了这些处理:

身份证号校验位

采用 GB 11643-1999 标准的校验位算法。18 位身份证号最后一位是前 17 位算出来的校验码,OCR 认错任何一位都会导致校验失败。这是最有效的一道防线。

关键字定位 + 位置兜底

身份证排版固定,正常情况下靠「姓名」「住址」等关键字定位。但当 OCR 把关键字认丢时,改用位置策略:出生日期下面、身份证号上面就是住址。

血缘兜底

性别和出生日期可从身份证号第 17 位和第 7-14 位反推,OCR 文本认错时用这个兜底。

住址跨行合并

身份证住址通常占 2-3 行,按行序拼接,遇到身份证号行停止。

银行卡 Luhn 校验

银行卡号有 Luhn 校验,同样能拦截大部分误识别。另外做了卡号分段合并(6222 0212 3456 7896)和跨行拼接处理。

实测数据

用 4 组模拟 ML Kit 输出的文本测试(含关键字完整、关键字缺失、日期格式变体、完全无关键字四种情况),字段命中率 95.8%(23/24)。未命中的那个是日期用纯空格分隔的极端情况。


常见问题

Q:能离线用吗?

能。Android 的模型随 APK 打包,iOS 用系统框架,首次使用不需要联网下载。

Q:Android 包体积增加多少?

约 5-10MB,取决于 ABI 数量。如果只需要单 ABI(如仅 arm64-v8a),增量更小。iOS 几乎不增加。

Q:H5 / 小程序能用吗?

不能。Vision 和 ML Kit 都是 App 端原生能力。H5 端调用会返回错误码 2001 并提示不支持。如果在 H5 有需求,建议做端判断后降级为手动输入。

Q:识别不准怎么办?

先看 rawText 里 OCR 到底认出了什么。常见原因:

  • 图片模糊或反光 → 提示用户重拍
  • 证件倾斜 → 拍照时提示四角对齐
  • 光照不足 → 用组件的闪光灯

Q:为什么 valid 是 false?

valid 表示关键字段校验通过。身份证看 18 位校验位,银行卡看 Luhn。如果不通过但字段看起来是对的,可能是 OCR 认错了某一位,建议重拍。

Q:支持护照、驾驶证、营业执照吗?

当前版本只做了身份证和银行卡的字段解析。通用文字识别(recognizeText)可以识别任何文字,但要结构化成护照/营业执照字段,需要自己解析 rawText。

Q:能连续识别、自动追焦吗?

当前版本是拍照识别(takePhoto + 识别)。实时流式识别(对着证件自动识别)需要逐帧处理,当前未实现。


已知限制

  1. 只支持 App-Android / App-iOS,H5 和小程序不可用
  2. Android 端包体积会增加 5-10MB
  3. OcrTextResult.lines 当前为空数组,多语言/多段落定位需求请用 fullText
  4. 鸿蒙未适配

必须重新打自定义基座

本插件包含原生代码(Kotlin + Swift 混编),标准的 HBuilderX 基座里没有这些原生类。

如果直接运行,会报类似错误:

error: undefined class: UTSSDKModulesErhaOcrIndexSwift, ...
插件不存在或者编译错误 ... Windows端请重新打自定义基座

这不是插件坏了,是基座里还没有你的原生代码。 正确做法:

  1. 菜单:发行 → 原生 App-云打包
  2. 勾选「打自定义调试基座」
  3. 打包完成后,运行 → 运行到手机或模拟器 → 选择「自定义基座」

Android 修改原生代码(.kt)后必须重新打基座;iOS 同理。改 index.uts 或 parser.uts 这类纯 UTS 逻辑,热更新即可,不用重打。

iOS 本地编译需要 Xcode 26 及以上

如果你在 Mac 上「运行到 iOS 模拟器」(本地编译插件,不走云打包),Xcode 版本必须 26+(Swift 6.2+)。

Xcode 16.x(Swift 6.0/6.1)会报:

error: missing argument label 'dictionary:' in call
at uni_modules/erha-ocr/utssdk/app-ios/index.uts

原因:HBuilderX 4.7+ 的 iOS UTS 运行时是按 Swift 6.2+ 编译的,其中 UTSJSONObject 的部分初始化器和下标访问带 #if compiler(>=5.3) && $NonescapableTypes 条件编译。旧版 Swift 读不到这些符号,而 UTS 转译器生成的代码依赖它们。

这条与插件本身无关,是工具链版本问题。 两种解决办法:

  1. 升级到 Xcode 26+(推荐,一次到位)
  2. 不本地编译,直接用云打包(不受本机 Xcode 影响)

我们用云打包验证过 Android 和 iOS 双端真机运行正常。若你只在 Windows 上开发, 完全不需要碰这个问题 —— 云打包直接在云端编译。


更新日志

见 changelog.md

License

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

相机、相册

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据。所有识别在设备本地完成,图片与识别结果均不上传任何服务器

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无