This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

+20
View File
@@ -0,0 +1,20 @@
## 1.0.82025-08-17
* 兼容鸿蒙原生
## 1.0.72025-02-14
* 增加对web的支持,web使用时需要网络加载模型数据
## 1.0.62024-12-18
* ios,安卓添加本地路径识别函数localFilePathImageBuilder,可自己循环批量处理.
## 1.0.52024-11-01
* 修复ios可能的兼容问题
## 1.0.42024-10-31
* ios没对齐安卓,失败不会返回回调.
## 1.0.32024-10-27
升级了调用方式,使得安卓,ios用同样的方式调用,不再区别,统一使用callback,并且在回调中携带回了坐标,以便让大家通过坐标计算识别比例或者绘制位置
.并且文本块统一为行返回(之前是文本块返回,但在源始数据中还是块和坐标)
## 1.0.22024-09-24
* 各个函数追回了个参数languagestring|null,可以是zh,ja,两种语言中文和日文识别。
## 1.0.12024-05-04
* 更新支持IOS端,需要IOS12.0(含)+
## 1.0.02024-04-10
* ocr文本识别,ai模型识别,离线识别。
* tmui4.0种子用户,可免费赠送源码,无需购买
+97
View File
@@ -0,0 +1,97 @@
{
"id": "x-ocr-s",
"displayName": "Ocr文本识别ai模型离线本机识别无需服务端 支持ios,andriod,鸿蒙Next安卓",
"version": "1.0.8",
"description": "支持中文,日语,韩语,英语识别,支持本地,远程图片,相机相机选取识别文本",
"keywords": [
"ocr,文本识别,tmui4.0,uts"
],
"repository": "",
"engines": {
"HBuilderX": "^3.99",
"uni-app": "",
"uni-app-x": "^4.75"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "99.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "369986563"
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "本地相机/相册选取权限,需要征得同意。"
},
"npmurl": "",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "√",
"aliyun": "√",
"alipay": "√"
},
"client": {
"uni-app": {
"vue": {
"vue2": "-",
"vue3": "-"
},
"web": {
"safari": "-",
"chrome": "-"
},
"app": {
"vue": "-",
"nvue": "-",
"android": "-",
"ios": "-",
"harmony": "-"
},
"mp": {
"weixin": "-",
"alipay": "-",
"toutiao": "-",
"baidu": "-",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "-",
"lark": "-"
},
"quickapp": {
"huawei": "-",
"union": "-"
}
},
"uni-app-x": {
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "x"
}
}
}
}
}
}
+156
View File
@@ -0,0 +1,156 @@
# x-ocr-s
### 开发文档
[TMUI4.0文档](https://xui.tmui.design/)
[TMUI4.0组件库](https://ext.dcloud.net.cn/plugin?id=16369)
**如果需要查看效果请[下载TMUI4.0组件库应用demo](https://ext.dcloud.net.cn/plugin?id=16369),导航到原生插件栏目体验。**
**本插件会让你的安卓应用增加大约8.7mb体积**
**本插件会让你的IOS应用增加大约38mb(估算)体积**
**web端需要网络加载模型文件,因为web无法离线,也不是调用api就是本地模型加载一次后,就不需要网络了**
### UniApp 适配的版本
[oc离线识别uniapp版本](https://ext.dcloud.net.cn/plugin?name=tm-ocr)
### 功能
支持以下特殊的ocr文本识别。**离线识别,不需要联网**
注意:鸿蒙使用本插件时第二参数语言类型不会起作用,它会自适应识别语言。
- 支持中文
- 支持英文
- 支持韩文
- 支持日文
### 兼容性
| Harmony | IOS | Android | WEB | 小程序 |
| --- | --- | --- | --- | --- |
| 支持 | 支持 | 支持 | 支持 | x |
### 说明
这是tmui4.0|XUI的原生插件附赠插件。种子用户可免于购买。
非种子用户:
普通授权:99元
源码授权:299元
### 使用
如果是安卓请务必打自定义基座,如果ios:你在mac环境下配置好了环境无需打包本地编译,如果win开发ios需要打包基座。
安卓:
如果你需要32位系统和模拟器x86上运行,需要自行配置打包cpu 支持类型,配置abis
需要自定义基座运行,且安卓5.0+(含5.0)以上支持
Ios:
同样按照官方文档配置所需CPU类型,默认是arm64
WEB:
请复制插件目录中的static目录到低的根目录static目录中即可。
### API说明
插件提供了三个主要的API函数用于OCR文本识别:
#### 1. chooseImageBuilder
从相册或相机选择图片进行文字识别
```ts
chooseImageBuilder(
callback: (str: string[], source: string[]) => void,
langs: string|null
)
```
参数说明:
- callback:识别结果回调函数
- str: 识别出的文本数组
- source: 包含文本位置信息的数组,每个元素为JSON字符串,格式为:`{boundingBox:[left,top,width,height], text:string}`
- langs:识别语言,可选值:
- 'zh':中文识别
- 'ja':日文识别
- null:默认中文zh
#### 2. downloadUrlImageBuilder
下载网络图片进行文字识别
```ts
downloadUrlImageBuilder(
url: string,
callback: (str: string[], source: string[]) => void,
langs: string|null
)
```
参数说明:
- url:网络图片地址
- callback:识别结果回调函数,参数同chooseImageBuilder
- langs:识别语言,可选值同chooseImageBuilder
#### 3. localFilePathImageBuilder
识别本地图片文件
```ts
localFilePathImageBuilder(
pathfile: string,
callback: (str: string[], source: string[]) => void,
langs: string|null
)
```
参数说明:
- pathfile:本地图片文件路径
- callback:识别结果回调函数,参数同chooseImageBuilder
- langs:识别语言,可选值同chooseImageBuilder
### 使用示例
```ts
import {chooseImageBuilder,downloadUrlImageBuilder,localFilePathImageBuilder} from "@/uni_modules/x-ocr-s"
// 从相册/相机选择图片识别
chooseImageBuilder((txt:string[], sour:string[])=>{
// txt为识别出的文本数组
// sour为包含位置信息的JSON字符串数组,需要JSON.parse转换
sour.forEach(item => {
const info = JSON.parse(item)
console.log('文本:', info.text)
console.log('位置:', info.boundingBox)
})
}, 'zh')
// 识别网络图片
downloadUrlImageBuilder(
'https://example.com/test.jpg',
(txt:string[], sour:string[])=>{
console.log('识别文本:', txt)
console.log('详细信息:', sour)
},
'ja' // 日文识别
)
// 识别本地图片
localFilePathImageBuilder(
'/static/test.png',
(txt:string[], sour:string[])=>{
console.log('识别文本:', txt)
console.log('详细信息:', sour)
},
'zh' // 中文识别
)
```
### 注意事项
1. 识别置信度默认设置为0.50,只返回置信度大于等于0.50的识别结果
2. 回调函数中的source参数需要通过JSON.parse解析才能获取位置信息
3. 建议图片清晰度较高,避免模糊、反光等影响识别效果
4. Web端首次使用需要下载模型文件,请确保网络环境良好
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="io.dcloud.uni_modules.xVibrateS">
<uses-permission android:name="android.permission.VIBRATE" />
<!-- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> -->
</manifest>
@@ -0,0 +1,11 @@
{
"minSdkVersion": "20",
"dependencies":[
"com.google.mlkit:text-recognition:16.0.0",
"com.google.mlkit:text-recognition-chinese:16.0.0",
"com.google.mlkit:text-recognition-devanagari:16.0.0",
"com.google.mlkit:text-recognition-japanese:16.0.0",
"com.google.mlkit:text-recognition-korean:16.0.0"
],
"abis": ["armeabi-v7a", "arm64-v8a"]
}
@@ -0,0 +1,260 @@
import Text from "com.google.mlkit.vision.text.Text"
import TextRecognition from "com.google.mlkit.vision.text.TextRecognition"
import TextRecognizer from "com.google.mlkit.vision.text.TextRecognizer"
import TextRecognizerOptionsInterface from "com.google.mlkit.vision.text.TextRecognizerOptionsInterface"
import ChineseTextRecognizerOptions from "com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions"
import JapaneseTextRecognizerOptions from "com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions"
import Context from 'android.content.Context'
import IOException from 'java.io.IOException'
import InputStream from 'java.io.InputStream'
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import Bitmap from "android.graphics.Bitmap"
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
import InputImage from "com.google.mlkit.vision.common.InputImage"
import Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
import ByteBuffer from 'java.nio.ByteBuffer'
import ImageFormat from 'android.graphics.ImageFormat'
import Vibrator from "android.os.Vibrator"
import ContentValues from "android.content.ContentValues"
import ContentUris from "android.content.ContentUris"
import JSONObject from "org.json.JSONObject"
import TimeZone from "java.util.TimeZone"
import Cursor from "android.database.Cursor"
import MediaStore from "android.provider.MediaStore"
import SimpleDateFormat from "java.text.SimpleDateFormat"
import Locale from "java.util.Locale"
import MediaScannerConnection from "android.media.MediaScannerConnection"
type callType = (list:any)=> void;
/**
* 震动
* @param {number} duriation 震动时间单位ms
*/
export function vibrator(duriation : number) : boolean {
try {
const context = UTSAndroid.getAppContext() as Context
let vb = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
if (vb!.hasVibrator()) {
vb!.vibrate(duriation.toLong());
} else {
return false
}
} catch (e) {
console.error(e)
}
return false
}
/**
* 从相册/相机中选取图片进行识别
*/
export function chooseImageBuilder(language:string|null) : Promise<string[]> {
// 置信度
let zxd = 0.50
let local = language == null?'zh':language!
return new Promise((res, rej) => {
uni.chooseImage({
count: 1,
success(evt : ChooseImageSuccess) {
uni.showLoading({ title: '...', mask: true })
if (evt.tempFilePaths.length > 0) {
const imgs = evt.tempFilePaths[0] as string;
try {
const decodedPath = Uri.decode(imgs).substring(7)
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let opts = new ChineseTextRecognizerOptions().Builder()
// if(local == 'ja'){
// opts = new JapaneseTextRecognizerOptions().Builder()
// }
const recognizer = TextRecognition.getClient(opts.build()) as TextRecognizer;
recognizer.process(image!)
.addOnSuccessListener((TextList) => {
let str = [] as string[]
for (block in TextList.textBlocks) {
for (line in block.lines) {
// let lineText = line.text
// let lineCornerPoints = line.cornerPoints
// let lineFrame = line.boundingBox
for (element in line.elements) {
if (element.getConfidence() >= zxd) {
let elementText = element.text as string;
// let elementCornerPoints = element.cornerPoints
// let elementFrame = element.boundingBox
str.push(elementText as string)
}
}
}
}
uni.hideLoading()
vibrator(100)
res(str as string[])
})
.addOnFailureListener((e) => {
uni.hideLoading()
rej(["识别失败"] as string[])
})
} catch (e : IOException) {
uni.hideLoading()
rej(["图片选取失败"] as string[])
}
}
},
fail() {
rej(["图片选择失败"] as string[])
}
})
})
}
/**
* 提供一个远程图片地址进行识别
*/
export function downloadUrlImageBuilder(url : string,language:string|null) : Promise<string[]> {
// 置信度
let zxd = 0.50
let local = language == null?'zh':language!
uni.showLoading({ title: '...', mask: true })
return new Promise((res, rej) => {
uni.downloadFile({
url,
success(evt) {
const contentResolver = UTSAndroid.getAppContext()!.contentResolver
let decodedPath = evt.tempFilePath
if (evt.tempFilePath.indexOf('file://') > -1) {
decodedPath = Uri.decode(evt.tempFilePath).substring(7)
}
try {
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
console.log(typeof ChineseTextRecognizerOptions)
let opts = new ChineseTextRecognizerOptions().Builder()
// if(local == 'ja'){
// opts = new JapaneseTextRecognizerOptions().Builder()
// }
const recognizer = TextRecognition.getClient(opts.build()) as TextRecognizer;
if (image != null) {
const result = recognizer.process(image!)
.addOnSuccessListener((TextList) => {
let str = [] as string[]
for (block in TextList.textBlocks) {
for (line in block.lines) {
// let lineText = line.text
// let lineCornerPoints = line.cornerPoints
// let lineFrame = line.boundingBox
for (element in line.elements) {
if (element.getConfidence() >= zxd) {
let elementText = element.text as string;
// let elementCornerPoints = element.cornerPoints
// let elementFrame = element.boundingBox
str.push(elementText as string)
}
}
}
}
uni.hideLoading()
vibrator(100)
res(str as string[])
})
.addOnFailureListener((e) => {
uni.hideLoading()
rej(["识别失败"] as string[])
})
} else {
uni.hideLoading()
rej(["图片下载失败"])
}
} catch (e : IOException) {
uni.hideLoading()
rej(["图片下载失败"])
}
},
fail() {
uni.hideLoading()
rej(["图片下载失败"])
}
})
})
}
/**
* 给定图片路径进行识别
*/
export function localFilePathImageBuilder(pathfile : string,language:string|null) : Promise<string[]> {
// 置信度
let zxd = 0.50
let local = language == null?'zh':language!
uni.showLoading({ title: '...', mask: true })
return new Promise((res, rej) => {
try {
let decodedPath = pathfile
if (pathfile.indexOf('file://') > -1) {
decodedPath = Uri.decode(pathfile).substring(7)
}
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let opts = new ChineseTextRecognizerOptions().Builder()
// if(local == 'ja'){
// opts = new JapaneseTextRecognizerOptions().Builder()
// }
const recognizer = TextRecognition.getClient(opts.build()) as TextRecognizer;
if (image != null) {
const result = recognizer.process(image!)
.addOnSuccessListener((TextList) => {
let str = [] as string[]
for (block in TextList.textBlocks) {
for (line in block.lines) {
// let lineText = line.text
// let lineCornerPoints = line.cornerPoints
// let lineFrame = line.boundingBox
for (element in line.elements) {
if (element.getConfidence() >= zxd) {
let elementText = element.text as string;
// let elementCornerPoints = element.cornerPoints
// let elementFrame = element.boundingBox
str.push(elementText as string)
}
}
}
}
uni.hideLoading()
vibrator(100)
res(str as string[])
})
.addOnFailureListener((e) => {
uni.hideLoading()
rej(["识别失败"] as string[])
})
} else {
uni.hideLoading()
rej(["图片地址解析失败"])
}
} catch (e : IOException) {
uni.hideLoading()
rej(["图片地址解析失败"])
}
})
}
@@ -0,0 +1,258 @@
import Text from "com.google.mlkit.vision.text.Text"
import TextRecognition from "com.google.mlkit.vision.text.TextRecognition"
import TextRecognizer from "com.google.mlkit.vision.text.TextRecognizer"
import TextRecognizerOptionsInterface from "com.google.mlkit.vision.text.TextRecognizerOptionsInterface"
import ChineseTextRecognizerOptions from "com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions"
import JapaneseTextRecognizerOptions from "com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions"
import Context from 'android.content.Context'
import IOException from 'java.io.IOException'
import InputStream from 'java.io.InputStream'
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import Bitmap from "android.graphics.Bitmap"
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
import InputImage from "com.google.mlkit.vision.common.InputImage"
import Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
import ByteBuffer from 'java.nio.ByteBuffer'
import ImageFormat from 'android.graphics.ImageFormat'
import Vibrator from "android.os.Vibrator"
import ContentValues from "android.content.ContentValues"
import ContentUris from "android.content.ContentUris"
import JSONObject from "org.json.JSONObject"
import TimeZone from "java.util.TimeZone"
import Cursor from "android.database.Cursor"
import MediaStore from "android.provider.MediaStore"
import SimpleDateFormat from "java.text.SimpleDateFormat"
import Locale from "java.util.Locale"
import MediaScannerConnection from "android.media.MediaScannerConnection"
import Rect from 'android.graphics.Rect';
type callType = (list:any)=> void;
/**
* 震动
* @param {number} duriation 震动时间单位ms
*/
export function vibrator(duriation : number) : boolean {
try {
const context = UTSAndroid.getAppContext() as Context
let vb = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
if (vb!.hasVibrator()) {
vb!.vibrate(duriation.toLong());
} else {
return false
}
} catch (e) {
console.error(e)
}
return false
}
/**
* 从相册/相机中选取图片进行识别
* @param callback {(string[],any[])=>{}} 回调参数:第一个是识别的文本数组,第二个是源数据{boundingBox:[x,y,width,height],text:elementText}[]字符串,需要JSON.pare,携带了坐标等详细资料
* @param language {string} 需要识别的语言 zh中文,ja日文
*/
export function chooseImageBuilder(callback:(str:string[],source:string[])=>void,langs:string|null){
// 置信度
let zxd = 0.50
uni.chooseImage({
count: 1,
success(evt : ChooseImageSuccess) {
uni.showLoading({ title: '...', mask: true })
if (evt.tempFilePaths.length > 0) {
const imgs = evt.tempFilePaths[0] as string;
try {
const decodedPath = UTSAndroid.convert2AbsFullPath(imgs)
const file = new File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let recognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build()) as TextRecognizer;
if(langs=='ja'){
recognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build()) as TextRecognizer;
}
recognizer.process(image!)
.addOnSuccessListener((TextList) => {
let str = [] as string[]
let sour = [] as string[]
for (block in TextList.textBlocks) {
for (line in block.lines) {
// let lineText = line.text
// let lineCornerPoints = line.cornerPoints
// let lineFrame = line.getBoundingBox
let texts = ''
for (element in line.elements) {
if (element.getConfidence() >= zxd) {
let elementText = element.text as string;
let elementFrame = element.getBoundingBox()
let rect = elementFrame as Rect
sour.push(JSON.stringify({boundingBox:[rect.left,rect.top,rect.width(),rect.height()],text:elementText} as UTSJSONObject))
texts+=elementText
}
}
if(texts!=''){
str.push(texts)
}
}
}
uni.hideLoading()
vibrator(100)
callback(str as string[],sour as string[])
})
.addOnFailureListener((e) => {
callback([] as string[],[] as string[])
})
} catch (e : IOException) {
callback([] as string[],[] as string[])
}
}
},
fail() {
callback([] as string[],[] as string[])
}
})
}
/**
* 从相册/相机中选取图片进行识别
* @param url {string} 图片地址.
* @param callback {(string[],any[])=>{}} 回调参数:第一个是识别的文本数组,第二个是源数据{boundingBox:[x,y,width,height],text:elementText}[]字符串,需要JSON.pare,携带了坐标等详细资料
* @param language {string} 需要识别的语言 zh中文,ja日文
*/
export function downloadUrlImageBuilder(url : string,callback:(str:string[],source:string[])=>void,langs:string|null) {
// 置信度
let zxd = 0.50
uni.downloadFile({
url,
success(evt) {
const contentResolver = UTSAndroid.getAppContext()!.contentResolver
let decodedPath = UTSAndroid.convert2AbsFullPath(evt.tempFilePath)
try {
const file = new File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let recognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build()) as TextRecognizer;
if(langs=='ja'){
recognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build()) as TextRecognizer;
}
if (image != null) {
const result = recognizer.process(image!)
.addOnSuccessListener((TextList) => {
let str = [] as string[]
let sour = [] as string[]
for (block in TextList.textBlocks) {
for (line in block.lines) {
let texts = ''
for (element in line.elements) {
if (element.getConfidence() >= zxd) {
let elementText = element.text as string;
let elementFrame = element.getBoundingBox()
let rect = elementFrame as Rect
sour.push(JSON.stringify({boundingBox:[rect.left,rect.top,rect.width(),rect.height()],text:elementText} as UTSJSONObject))
texts+=elementText
}
}
if(texts!=''){
str.push(texts)
}
}
}
uni.hideLoading()
vibrator(100)
callback(str as string[],sour)
})
.addOnFailureListener((e) => {
callback([] as string[],[] as string[])
})
} else {
callback([] as string[],[] as string[])
}
} catch (e : IOException) {
callback([] as string[],[] as string[])
}
},
fail() {
callback([] as string[],[] as string[])
}
})
}
/**
* 从相册/相机中选取图片进行识别
* @param pathfile {string} 图片地址.
* @param callback {(string[],any[])=>{}} 回调参数:第一个是识别的文本数组,第二个是源数据{boundingBox:[x,y,width,height],text:elementText}[]字符串,需要JSON.pare,携带了坐标等详细资料
* @param language {string} 需要识别的语言 zh中文,ja日文
*/
export function localFilePathImageBuilder(pathfile : string,callback:(str:string[],source:string[])=>void,langs:string|null){
// 置信度
let zxd = 0.50
try {
let decodedPath = UTSAndroid.convert2AbsFullPath(pathfile)
const file = new File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let recognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build()) as TextRecognizer;
if(langs=='ja'){
recognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build()) as TextRecognizer;
}
if (image != null) {
const result = recognizer.process(image!)
.addOnSuccessListener((TextList) => {
let str = [] as string[]
let sour = [] as string[]
for (block in TextList.textBlocks) {
for (line in block.lines) {
let texts = ''
for (element in line.elements) {
if (element.getConfidence() >= zxd) {
let elementText = element.text as string;
let elementFrame = element.getBoundingBox()
let rect = elementFrame as Rect
sour.push(JSON.stringify({boundingBox:[rect.left,rect.top,rect.width(),rect.height()],text:elementText} as UTSJSONObject))
texts+=elementText
}
}
if(texts!=''){
str.push(texts)
}
}
}
callback(str as string[],sour)
})
.addOnFailureListener((e) => {
callback([] as string[],[] as string[])
})
} else {
callback([] as string[],[] as string[])
}
} catch (e : IOException) {
callback([] as string[],[] as string[])
}
}
@@ -0,0 +1,5 @@
{
"dependencies": {
"x_ocr_s": "./lib/x_ocr_s.har"
}
}
@@ -0,0 +1,36 @@
import { x_ocrDecode } from "x_ocr_s"
export function chooseImageBuilder(callback : (str : string[], source : string[]) => void, langs : string | null) {
uni.chooseImage({
count: 1,
success(evt : ChooseImageSuccess) {
uni.showLoading({ title: '', mask: true })
if (evt.tempFilePaths.length > 0) {
const imgs = UTSHarmony.convert2AbsFullPath(evt.tempFilePaths[0] as string);
x_ocrDecode(imgs, (arg1 : string[], arg2 : string[]) => {
callback(arg1, arg2)
uni.hideLoading()
})
}
},
fail() {
callback([] as string[], [] as string[])
}
})
}
export function downloadUrlImageBuilder(url : string,callback:(str:string[],source:string[])=>void,langs:string|null) {
uni.downloadFile({
url,
success(evt) {
x_ocrDecode(UTSHarmony.convert2AbsFullPath(evt.tempFilePath), callback)
},
fail() {
callback([] as string[],[] as string[])
}
})
}
export function localFilePathImageBuilder(pathfile : string,callback:(str:string[],source:string[])=>void,langs:string|null){
x_ocrDecode(UTSHarmony.convert2AbsFullPath(pathfile), callback)
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict>
</plist>
@@ -0,0 +1,17 @@
{
"deploymentTarget": "12",
"dependencies-pods": [
{
"name": "GoogleMLKit/TextRecognition",
"version": "3.2.0"
},
{
"name": "GoogleMLKit/TextRecognitionChinese",
"version": "3.2.0"
},
{
"name": "GoogleMLKit/TextRecognitionJapanese",
"version": "3.2.0"
}
]
}
@@ -0,0 +1,222 @@
import {ChineseTextRecognizerOptions,JapaneseTextRecognizerOptions,TextRecognizer,VisionImage,TextBlock,Text} from "MLKit"
import * as UIKit from "UIKit"
import { UIAlertController , UIAlertAction , UITextField,UIImage } from "UIKit"
import { UTSiOS } from "DCloudUTSFoundation"
import {String} from "Swift"
import { PHPhotoLibrary } from "Photos"
import { CGPoint , CGRect } from 'CoreFoundation';
/**
* 从相册/相机中选取图片进行识别
* @param callback {(string[],string[])=>{}} 回调参数:第一个是识别的文本数组,第二个是源数据{boundingBox:[x,y,width,height],text:elementText}[]字符串,需要JSON.pare,携带了坐标等详细资料
* @param language {string} 需要识别的语言 zh中文,ja日文
*/
export function chooseImageBuilder(callback:(str:string[],source:string[])=>void,langs:string|null){
// 置信度
let zxd = 0.50
let local = 'zh'
if(langs=='ja'){
local = 'ja'
}
uni.chooseImage({
count: 1,
success:(evt : ChooseImageSuccess)=> {
uni.showLoading({title:'...',mask:true})
let chineseOptions = new ChineseTextRecognizerOptions()
let chineseTextRecognizer = TextRecognizer.textRecognizer(options=chineseOptions)
if(local == 'ja'){
chineseTextRecognizer = TextRecognizer.textRecognizer(options=new JapaneseTextRecognizerOptions())
}
if (evt.tempFilePaths.length > 0) {
let imgs = evt.tempFilePaths[0] as string;
try {
let realImgPath = UTSiOS.convert2AbsFullPath(imgs)
let img = new UIImage(contentsOfFile = realImgPath);
if(img!=null){
let image = new VisionImage(image = img!);
chineseTextRecognizer.process(image as VisionImage,completion=(TextList,error)=>{
if(error!=null||TextList==null){
uni.hideLoading()
uni.showToast({title:"失败",icon:'none'})
return;
}
let str = [] as string[]
let sour = [] as string[]
for(block in TextList!.blocks){
let lines = block.lines
if(lines!=null){
for(line in lines) {
let elements = line.elements
if(elements!=null){
let texts = ''
for(element in elements) {
let elementText = element.text;
let rect = element.frame as CGRect
let elementFrame = [rect.origin.x,rect.origin.y,rect.size.width,rect.size.height] as number[]
sour.push(JSON.stringify({boundingBox:elementFrame,text:elementText})!)
texts+=elementText
}
if(texts!=''){
str.push(texts)
}
}
}
}
}
uni.hideLoading()
callback(str as string[],sour)
})
}
} catch (e) {
console.warn("图片选择失败")
uni.hideLoading()
callback([] as string[],[] as string[])
}
}
},
fail() {
console.warn("图片选择失败")
uni.showToast({title:"图片选择失败",icon:'none'})
callback([] as string[],[] as string[])
}
})
}
/**
* 从相册/相机中选取图片进行识别
* @param url {string} 图片地址.
* @param callback {(string[],string[])=>{}} 回调参数:第一个是识别的文本数组,第二个是源数据{boundingBox:[x,y,width,height],text:elementText}[]字符串,需要JSON.pare,携带了坐标等详细资料
* @param language {string} 需要识别的语言 zh中文,ja日文
*/
export function downloadUrlImageBuilder(url : string,callback:(str:string[],source:string[])=>void,langs:string|null){
uni.showLoading({title:'...',mask:true})
let local = langs == null?'zh':(langs! as string)
uni.downloadFile({
url,
success:(evt)=> {
let chineseOptions = new ChineseTextRecognizerOptions()
let chineseTextRecognizer = TextRecognizer.textRecognizer(options=chineseOptions)
if(local == 'ja'){
chineseTextRecognizer = TextRecognizer.textRecognizer(options=new JapaneseTextRecognizerOptions())
}
let imgs = evt.tempFilePath as string;
try {
let realImgPath = UTSiOS.convert2AbsFullPath(imgs)
let img = new UIImage(contentsOfFile = realImgPath);
if(img!=null){
let image = new VisionImage(image = img!);
chineseTextRecognizer.process(image as VisionImage,completion=(TextList,error)=>{
if(error!=null||TextList==null){
uni.hideLoading()
uni.showToast({title:"失败",icon:'none'})
return;
}
let str = [] as string[]
let sour = [] as string[]
for(block in TextList!.blocks){
let lines = block.lines
if(lines!=null){
for(line in lines) {
let elements = line.elements
if(elements!=null){
let texts = ''
for(element in elements) {
let elementText = element.text;
let rect = element.frame as CGRect
let elementFrame = [rect.origin.x,rect.origin.y,rect.size.width,rect.size.height] as number[]
sour.push(JSON.stringify({boundingBox:elementFrame,text:elementText})!)
texts+=elementText
}
if(texts!=''){
str.push(texts)
}
}
}
}
}
uni.hideLoading()
callback(str as string[],sour)
})
}
} catch (e) {
console.warn("下载失败")
uni.hideLoading()
uni.showToast({title:"下载失败",icon:'none'})
callback([] as string[],[] as string[])
}
},
fail() {
uni.hideLoading()
uni.showToast({title:"下载失败",icon:'none'})
callback([] as string[],[] as string[])
}
})
}
export function localFilePathImageBuilder(pathfile : string,callback:(str:string[],source:string[])=>void,langs:string|null){
let local = langs == null?'zh':(langs! as string)
let chineseOptions = new ChineseTextRecognizerOptions()
let chineseTextRecognizer = TextRecognizer.textRecognizer(options=chineseOptions)
if(local == 'ja'){
chineseTextRecognizer = TextRecognizer.textRecognizer(options=new JapaneseTextRecognizerOptions())
}
let realImgPath = UTSiOS.convert2AbsFullPath(pathfile)
let img = new UIImage(contentsOfFile = realImgPath);
if(img!=null){
let image = new VisionImage(image = img!);
chineseTextRecognizer.process(image as VisionImage,completion=(TextList,error)=>{
if(error!=null||TextList==null){
callback([] as string[],[] as string[])
return;
}
let str = [] as string[]
let sour = [] as string[]
for(block in TextList!.blocks){
let lines = block.lines
if(lines!=null){
for(line in lines) {
let elements = line.elements
if(elements!=null){
let texts = ''
for(element in elements) {
let elementText = element.text;
let rect = element.frame as CGRect
let elementFrame = [rect.origin.x,rect.origin.y,rect.size.width,rect.size.height] as number[]
sour.push(JSON.stringify({boundingBox:elementFrame,text:elementText})!)
texts+=elementText
}
if(texts!=''){
str.push(texts)
}
}
}
}
}
callback(str as string[],sour)
})
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>读取您的相册图片,用来识别图片中的文字功能,如果拒绝将无法使用相关识别功能.</string>
</dict>
</plist>
+103
View File
@@ -0,0 +1,103 @@
async function loadScript(url) {
return new Promise(res=>{
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// 当脚本加载完成后,执行回调
script.onload = function() {
res()
};
// 处理旧版浏览器的onreadystatechange事件
script.onreadystatechange = function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
script.onload();
}
};
// 将脚本添加到head中,开始加载
document.head.appendChild(script);
})
}
async function loadScripts(scripts) {
await (async function loadNextScript(i) {
if (i < scripts.length) {
await loadScript(scripts[i]);
await loadNextScript(i + 1)
}
})(0);
}
let ocr = window?.paddlejs?.ocr??null;
let jsFiles = [
'/static/tmui4xLibs/lib/ocrLib.js'
];
const decoderText =async (imgpath:string,callback)=>{
if(!window?.paddlejs){
await loadScripts(jsFiles)
ocr = paddlejs.ocr
await ocr.init()
}
let img = new Image()
img.src = imgpath
img.onload = async function(){
const canvas = document.createElement('canvas')
canvas.width = 800;
canvas.height = 800
const res = await ocr.recognize(img, { canvas });
let texts = res.text
let bounds = []
for(let i=0;i<res.points.length;i++){
let item = res.points[i]
bounds.push({
x:item[0][0],
y:item[0][1],
width:item[1][0]-item[0][0],
height:item[3][1]+item[0][1]
})
}
callback(texts,JSON.stringify(bounds))
}
}
export async function chooseImageBuilder(callback : (str : string[], source : string[]) => void, langs : string | null) {
uni.chooseImage({
count: 1,
success:async (evt : ChooseImageSuccess)=> {
uni.showLoading({title:'...',mask:true})
if (evt.tempFilePaths.length > 0) {
let imgpath = evt.tempFilePaths[0] as string;
try {
await decoderText(imgpath,callback)
} catch (error) {
console.error(error)
callback([] as string[],[] as string[])
}
}
uni.hideLoading()
},
fail() {
console.warn("图片选择失败")
callback([] as string[],[] as string[])
}
})
}
export async function downloadUrlImageBuilder(url : string, callback : (str : string[], source : string[]) => void, langs : string | null) {
uni.showLoading({title:'...',mask:true})
try {
await decoderText(url,callback)
} catch (error) {
console.error(error)
callback([] as string[],[] as string[])
}
uni.hideLoading()
}
export async function localFilePathImageBuilder(pathfile : string, callback : (str : string[], source : string[]) => void, langs : string | null) {
downloadUrlImageBuilder(pathfile,callback)
}