diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index e4bd2992..00a356b8 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -1,7 +1,7 @@ { "$data": { "v6.7.0": { - "released_date": "2025/11/02", + "released_date": "2025/11/19", "feature": [ "cvt 模块, 用于数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))", "fmt 模块, 用于数据格式化 (参阅 项目文档 > [格式化](https://docs.autojs6.com/#/fmt))", @@ -46,6 +46,8 @@ "images 部分方法使用区域参数时 1 被误解析为百分比的问题", "images 部分相关方法出现异常时 oneShot 标记功能失效的问题 _[`issue #372`](http://issues.autojs6.com/372)_", "images 部分相关方法可能引发内存泄露的问题 _[`issue #372`](http://issues.autojs6.com/372)_", + "ocr 部分重载方法可能无法正常使用的问题", + "ocr.detect 方法获得的结果可能与 ocr.mode 不匹配的问题", "Android 10 UiObject#child 方法可能出现 ArrayIndexOutOfBoundsException 异常的问题 _[`issue #416`](http://issues.autojs6.com/416)_", "运行项目时 project.json 配置参数无法正常解析的问题", "项目配置文件中构建版本号或构建时间出现较大数字时可能导致应用崩溃的问题", diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/OcrRapid.kt b/app/src/main/java/org/autojs/autojs/runtime/api/OcrRapid.kt index 22f7047c..619c2854 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/OcrRapid.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/OcrRapid.kt @@ -36,7 +36,7 @@ class OcrRapid { // 创建 OcrResult 对象 OcrResult( - label = block.text, + text = block.text, confidence = block.boxScore, bounds = rect, ) diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt b/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt index e9585c37..08b8f3f7 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt @@ -8,11 +8,14 @@ import kotlin.math.abs /** * Represents a result from Optical Character Recognition (OCR) processing. * - * @property label The recognized text label. + * @property text The recognized text. * @property confidence The confidence score of the recognition, ranging from 0 (least confident) to 1 (most confident). * @property bounds The bounding box coordinates of the recognized text region. */ -class OcrResult(@JvmField val label: String, @JvmField val confidence: Float, @JvmField val bounds: Rect) : Comparable { +class OcrResult(@JvmField val text: String, @JvmField val confidence: Float, @JvmField val bounds: Rect) : Comparable { + + @JvmField + val label: String = text override fun compareTo(other: OcrResult): Int { // 上下差距小于二分之一的高度 判定为同一行 @@ -27,6 +30,6 @@ class OcrResult(@JvmField val label: String, @JvmField val confidence: Float, @J override fun toString() = "${OcrResult::class.java.simpleName}@${Integer.toHexString(hashCode())}" + - "{label=$label, confidence=$confidence, bounds=$bounds}" + "{text=$text, confidence=$confidence, bounds=$bounds}" } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/Ocr.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/Ocr.kt index 7232c48f..53b47246 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/Ocr.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/Ocr.kt @@ -7,23 +7,27 @@ import org.autojs.autojs.extension.AnyExtensions.isJsString import org.autojs.autojs.extension.AnyExtensions.jsBrief import org.autojs.autojs.extension.AnyExtensions.jsSpecies import org.autojs.autojs.extension.ArrayExtensions.toNativeArray -import org.autojs.autojs.extension.ScriptableExtensions.prop +import org.autojs.autojs.extension.FlexibleArray.Companion.component1 +import org.autojs.autojs.extension.FlexibleArray.Companion.component2 +import org.autojs.autojs.extension.FlexibleArray.Companion.component3 import org.autojs.autojs.extension.ScriptableExtensions.defineProp +import org.autojs.autojs.extension.ScriptableExtensions.prop +import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire import org.autojs.autojs.runtime.ScriptRuntime +import org.autojs.autojs.runtime.api.OcrResult import org.autojs.autojs.runtime.api.augment.Augmentable import org.autojs.autojs.runtime.api.augment.Invokable -import org.autojs.autojs.runtime.api.augment.images.Images import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException -import org.autojs.autojs.runtime.exception.ShouldNeverHappenException -import org.autojs.autojs.util.RhinoUtils import org.autojs.autojs.util.RhinoUtils.UNDEFINED -import org.autojs.autojs.util.RhinoUtils.newNativeArray +import org.autojs.autojs.util.RhinoUtils.coerceString import org.autojs.autojs.util.RhinoUtils.newNativeObject import org.mozilla.javascript.NativeArray import org.mozilla.javascript.NativeObject +import org.mozilla.javascript.ScriptableObject import org.mozilla.javascript.Undefined import java.util.function.Consumer import java.util.function.Supplier +import kotlin.reflect.full.declaredMemberProperties import android.graphics.Rect as AndroidRect import org.autojs.autojs.runtime.api.augment.images.Images as AugmentableImages import org.opencv.core.Rect as OpencvRect @@ -46,187 +50,25 @@ class Ocr(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) companion object { - enum class OcrMode(val value: String) { - MLKIT("mlkit"), - PADDLE("paddle"), - RAPID("rapid"), - UNKNOWN("unknown") - } - @JvmStatic @RhinoRuntimeFunctionInterface fun tap(scriptRuntime: ScriptRuntime, args: Array): Undefined = ensureArgumentsOnlyOne(args) { mode -> - scriptRuntime.ocr.mode = when (mode) { - scriptRuntime.augmentedOcrMLKit -> OcrMode.MLKIT - scriptRuntime.augmentedOcrPaddle -> OcrMode.PADDLE - scriptRuntime.augmentedOcrRapid -> OcrMode.RAPID - else -> when (RhinoUtils.coerceStringLowercase(mode, "")) { - OcrMode.MLKIT.value -> OcrMode.MLKIT - OcrMode.PADDLE.value -> OcrMode.PADDLE - OcrMode.RAPID.value -> OcrMode.RAPID - else -> null - } - } ?: OcrMode.MLKIT.also { throw WrappedIllegalArgumentException("Unknown mode ${mode.jsSpecies()} for ocr.tap") } + scriptRuntime.ocr.mode = mode.toOcrModeOrNull(scriptRuntime) ?: OcrMode.MLKIT.also { + throw WrappedIllegalArgumentException("Unknown mode ${mode.jsSpecies()} for ocr.tap") + } return@ensureArgumentsOnlyOne UNDEFINED } @JvmStatic @RhinoRuntimeFunctionInterface fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> - val (arg0, arg1, arg2) = argList - - when { - arg0.isJsString() -> { - - // @Signature - // recognizeText(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; - // recognizeText(imgPath: string, region: OmniRegion): string[]; - - val imgPath = arg0 - val img = AugmentableImages.read(scriptRuntime, arrayOf(imgPath)) ?: throw WrappedIllegalArgumentException( - "Invalid image of path \"$imgPath\" for ocr.recognizeText(img, options?)", - ) - - // @Overload - // recognizeText(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; - // recognizeText(img: ImageWrapper, region: OmniRegion): string[]; - recognizeText(scriptRuntime, arrayOf(img.oneShot(), arg1, arg2)) - } - arg0 !is ImageWrapper -> { - // @Signature - // recognizeText(options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; - // recognizeText(region: OmniRegion): string[]; - - // @Overload - // recognizeText(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; - // recognizeText(img: ImageWrapper, region: OmniRegion): string[]; - - val capt = AugmentableImages.captureScreen(scriptRuntime, emptyArray()) - recognizeText(scriptRuntime, arrayOf(capt, arg0, arg1)) - } - shouldTakenAsRegion(arg1) -> { - - // @Signature recognizeText(img: ImageWrapper, region: OmniRegion): string[]; - - val region = arg1 - val options = newNativeObject().also { o -> - o.defineProp("region", region) - } - - // @Overload recognizeText(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): string[]; - recognizeText(scriptRuntime, arrayOf(arg0, options)) - } - else -> { - - // @Signature recognizeText(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; - - val img = arg0 - val opt = arg1 as? NativeObject ?: newNativeObject() - when (val region = opt.prop("region")) { - null -> newNativeArray() - else -> { - var shouldShoot = false - val (image, options) = when { - region.isJsNullish() -> img to opt - else -> { - val clip = AugmentableImages.clip(scriptRuntime, arrayOf(img, region)) - shouldShoot = true - clip.oneShot() to opt - } - } - val results = when (opt.prop("mode").takeIf { it is OcrMode } ?: scriptRuntime.ocr.mode) { - OcrMode.MLKIT -> OcrMLKit.recognizeTextInternal(scriptRuntime, image, options) - OcrMode.PADDLE -> OcrPaddle.recognizeTextInternal(scriptRuntime, image, options) - OcrMode.RAPID -> OcrRapid.recognizeTextInternal(scriptRuntime, image, options) - else -> throw WrappedIllegalArgumentException("Cannot call ocr.recognizeText with an unknown mode") - } - if (shouldShoot) img.shoot() - results.toNativeArray() - } - } - } - } + recognizeTextWith(scriptRuntime, null, argList) } @JvmStatic @RhinoRuntimeFunctionInterface fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> - val (arg0, arg1, arg2) = argList - - when { - arg0.isJsString() -> { - - // @Signature - // detect(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; - // detect(imgPath: string, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; - - val imgPath = arg0 - val img = AugmentableImages.read(scriptRuntime, arrayOf(imgPath)) ?: throw WrappedIllegalArgumentException( - "Invalid image of path \"$imgPath\" for ocr.detect(img, options?)", - ) - - // @Overload - // detect(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; - // detect(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; - detect(scriptRuntime, arrayOf(img.oneShot(), arg1, arg2)) - } - arg0 !is ImageWrapper -> { - // @Signature - // detect(options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; - // detect(region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; - - // @Overload - // detect(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; - // detect(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; - - val capt = AugmentableImages.captureScreen(scriptRuntime, emptyArray()) - detect(scriptRuntime, arrayOf(capt, arg0, arg1)) - } - shouldTakenAsRegion(arg1) -> { - - // @Signature detect(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; - - val region = arg1 - val options = newNativeObject().also { o -> - o.defineProp("region", region) - } - - // @Overload detect(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; - detect(scriptRuntime, arrayOf(arg0, options)) - } - else -> { - - // @Signature detect(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; - - val img = arg0 - val opt = arg1 as? NativeObject ?: newNativeObject() - when (val region = opt.prop("region")) { - null -> newNativeArray() - else -> { - var shouldShoot = false - val (image, options) = when { - region.isJsNullish() -> img to opt - else -> { - val clip = AugmentableImages.clip(scriptRuntime, arrayOf(img, region)) - shouldShoot = true - clip.oneShot() to opt - } - } - val results = when (scriptRuntime.ocr.mode) { - OcrMode.MLKIT -> OcrMLKit.detectInternal(scriptRuntime, image, options) - OcrMode.PADDLE -> OcrPaddle.detectInternal(scriptRuntime, image, options) - OcrMode.RAPID -> OcrRapid.detectInternal(scriptRuntime, image, options) - else -> throw WrappedIllegalArgumentException("Cannot call ocr.detect with an unknown mode") - }.also { - val rect = Images.buildRegion(scriptRuntime, arrayOf(img, region)) - it.forEach { result -> result.bounds.offset(rect.x, rect.y) } - } - if (shouldShoot) img.shoot() - results.toNativeArray() - } - } - } - } + detectWith(scriptRuntime, null, argList) } @Suppress("EnumValuesSoftDeprecate") @@ -246,58 +88,131 @@ class Ocr(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) summary(scriptRuntime, args) } - fun commonRecognizeText(scriptRuntime: ScriptRuntime, mode: OcrMode, vararg args: Any?): NativeArray = ensureArgumentsAtMost(args, 2) { argList -> - commonInvoker(scriptRuntime, mode, ::recognizeText, *argList) + internal fun recognizeTextWith(scriptRuntime: ScriptRuntime, overrideMode: OcrMode?, unwrappedArgs: Array): NativeArray { + return dispatchOcrWith(scriptRuntime, ::recognizeText.name, unwrappedArgs, overrideMode).toNativeArray() } - fun commonDetect(scriptRuntime: ScriptRuntime, mode: OcrMode, vararg args: Any?): NativeArray = ensureArgumentsAtMost(args, 2) { argList -> - commonInvoker(scriptRuntime, mode, ::detect, *argList) + internal fun detectWith(scriptRuntime: ScriptRuntime, overrideMode: OcrMode?, unwrappedArgs: Array): NativeArray { + return dispatchOcrWith(scriptRuntime, ::detect.name, unwrappedArgs, overrideMode) { results, rect -> + results.apply { forEach { it.bounds.offset(rect.x, rect.y) } } + }.toNativeArray() } - private fun commonInvoker( + @Suppress("UNCHECKED_CAST") + internal fun dispatchOcrWith( scriptRuntime: ScriptRuntime, - mode: OcrMode, - invoker: (ScriptRuntime, Array) -> NativeArray, - vararg args: Any?, - ): NativeArray = ensureArgumentsAtMost(args, 2) { argList -> - val (arg0, arg1) = argList - when (argList.size) { - 2 -> when { - shouldTakenAsRegion(arg1) -> newNativeObject().let { options -> - options.defineProp("region", arg1) - options.defineProp("mode", mode) - invoker(scriptRuntime, arrayOf(arg0, options)) - } - arg1 is NativeObject -> arg1.let { options -> - options.defineProp("mode", mode) - invoker(scriptRuntime, arrayOf(arg0, options)) - } - else -> throw WrappedIllegalArgumentException("Unknown argument[1] ${arg1.jsBrief()} for ${mode.value}") + funcName: String, + unwrappedArgs: Array, + overrideMode: OcrMode? = null, + resultsHandler: ((results: List, rect: OpencvRect) -> List)? = null, + ): List { + val (arg0, arg1, arg2) = unwrappedArgs + + return when { + arg0.isJsString() -> { + + // @Signature + // funcName(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; + // funcName(imgPath: string, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; + + val imgPath = arg0 + val img = AugmentableImages.read(scriptRuntime, arrayOf(imgPath)) ?: throw WrappedIllegalArgumentException( + "Invalid image of path \"$imgPath\" for ocr.$funcName(img, options?)", + ) + + // @Overload + // funcName(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; + // funcName(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; + dispatchOcrWith(scriptRuntime, funcName, arrayOf(img.oneShot(), arg1, arg2)) } - 1 -> when { - arg0 is NativeObject -> invoker(scriptRuntime, arrayOf(arg0.also { options -> - options.defineProp("mode", mode) - })) - arg0 is ImageWrapper -> invoker(scriptRuntime, arrayOf(arg0, newNativeObject().also { options -> - options.defineProp("mode", mode) - })) - shouldTakenAsRegion(arg0) -> newNativeObject().let { options -> - options.defineProp("region", arg0) - options.defineProp("mode", mode) - invoker(scriptRuntime, arrayOf(arg0, options)) + arg0 !is ImageWrapper -> { + // @Signature + // funcName(options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; + // funcName(region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; + + // @Overload + // funcName(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; + // funcName(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; + + val capt = AugmentableImages.captureScreen(scriptRuntime, emptyArray()) + dispatchOcrWith(scriptRuntime, funcName, arrayOf(capt, arg0, arg1)) + } + shouldTakenAsRegion(arg1) -> { + + // @Signature funcName(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[]; + + val region = arg1 + val options = newNativeObject().also { o -> + o.defineProp("region", region) } - else -> throw WrappedIllegalArgumentException("Unknown argument[0] ${arg0.jsBrief()} for ${mode.value}") + + // @Overload funcName(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; + dispatchOcrWith(scriptRuntime, funcName, arrayOf(arg0, options)) } - 0 -> newNativeObject().let { options -> - options.defineProp("mode", mode) - invoker(scriptRuntime, arrayOf(options)) + else -> { + + // @Signature funcName(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; + + val img = arg0 + val opt = arg1 as? NativeObject ?: newNativeObject() + val region = opt.prop("region") ?: return emptyList() + + var shouldShoot = false + val (image, options) = when { + region.isJsNullish() -> img to opt + else -> { + val clip = AugmentableImages.clip(scriptRuntime, arrayOf(img, region)) + shouldShoot = true + clip.oneShot() to opt + } + } + val optMode = overrideMode ?: opt.inquire("mode", scriptRuntime.ocr.mode) + val ocrMode = optMode.toOcrModeOrNull(scriptRuntime) + ?: throw WrappedIllegalArgumentException("Cannot call ocr.$funcName with an unknown mode: ${optMode.jsBrief()}") + val prop = OcrMode::class.declaredMemberProperties.firstOrNull { it.name.contentEquals(funcName, true) } + ?: throw WrappedIllegalArgumentException("Cannot find ocr dispatcher property for $funcName") + val fn = prop.get(ocrMode) as? (ScriptRuntime, ImageWrapper, NativeObject) -> List + ?: throw WrappedIllegalArgumentException("Dispatcher type mismatch for $funcName") + var results = fn.invoke(scriptRuntime, image, options) + resultsHandler?.let { + val rect = AugmentableImages.buildRegion(scriptRuntime, arrayOf(img, region)) + results = resultsHandler(results, rect) + } + if (shouldShoot) img.shoot() + results } - else -> throw ShouldNeverHappenException() } } private fun shouldTakenAsRegion(o: Any?) = o is OpencvRect || o is AndroidRect || o is NativeArray + private fun Any?.toOcrModeOrNull(scriptRuntime: ScriptRuntime? = null) = when (this) { + is ScriptableObject -> when (this) { + scriptRuntime?.augmentedOcrMLKit -> OcrMode.MLKIT + scriptRuntime?.augmentedOcrPaddle -> OcrMode.PADDLE + scriptRuntime?.augmentedOcrRapid -> OcrMode.RAPID + else -> null + } + else -> { + val modeValue = coerceString(this, "") + OcrMode.entries.find { it.value.contentEquals(modeValue, true) } + } + } + + @Suppress("unused") + private fun nullImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject) = emptyList() + + enum class OcrMode( + val value: String, + val recognizeText: ((ScriptRuntime, ImageWrapper, NativeObject) -> List)?, + val detect: ((ScriptRuntime, ImageWrapper, NativeObject) -> List)?, + ) { + MLKIT("mlkit", OcrMLKit::recognizeTextImpl, OcrMLKit::detectImpl), + PADDLE("paddle", OcrPaddle::recognizeTextImpl, OcrPaddle::detectImpl), + RAPID("rapid", OcrRapid::recognizeTextImpl, OcrRapid::detectImpl), + UNKNOWN("unknown", ::nullImpl, ::nullImpl); + } + } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrMLKit.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrMLKit.kt index cf631049..0f783a25 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrMLKit.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrMLKit.kt @@ -22,24 +22,25 @@ class OcrMLKit(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRun override fun invoke(vararg args: Any?): NativeArray = recognizeText(scriptRuntime, args) + @Suppress("unused") companion object { @JvmStatic @RhinoRuntimeFunctionInterface - fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { - Ocr.commonRecognizeText(scriptRuntime, OcrMode.MLKIT, *it) + fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> + Ocr.recognizeTextWith(scriptRuntime, OcrMode.MLKIT, argList) } @JvmStatic @RhinoRuntimeFunctionInterface - fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { - Ocr.commonDetect(scriptRuntime, OcrMode.MLKIT, *it) + fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> + Ocr.detectWith(scriptRuntime, OcrMode.MLKIT, argList) } // @Hint by SuperMonster003 on Nov 1, 2024. // ! Reserved param `options`. // ! zh-CN: 预留参数 `options`. - fun recognizeTextInternal(scriptRuntime: ScriptRuntime, image: ImageWrapper, @Suppress("UNUSED_PARAMETER") options: NativeObject): List { + fun recognizeTextImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { ApkBuilder.Libs.MLKIT_OCR.ensureLibFiles(OcrMode.MLKIT.value) return scriptRuntime.ocrMLKit.recognizeText(image) } @@ -47,7 +48,7 @@ class OcrMLKit(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRun // @Hint by SuperMonster003 on Nov 1, 2024. // ! Reserved param `options`. // ! zh-CN: 预留参数 `options`. - fun detectInternal(scriptRuntime: ScriptRuntime, image: ImageWrapper, @Suppress("UNUSED_PARAMETER") options: NativeObject): List { + fun detectImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { ApkBuilder.Libs.MLKIT_OCR.ensureLibFiles(OcrMode.MLKIT.value) return scriptRuntime.ocrMLKit.detect(image) } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrPaddle.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrPaddle.kt index b7730dab..a84e0117 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrPaddle.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrPaddle.kt @@ -1,19 +1,23 @@ package org.autojs.autojs.runtime.api.augment.ocr +import kotlinx.coroutines.runBlocking import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface import org.autojs.autojs.apkbuilder.ApkBuilder import org.autojs.autojs.core.image.ImageWrapper +import org.autojs.autojs.core.plugin.ocr.OcrPluginHost import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire import org.autojs.autojs.runtime.ScriptRuntime import org.autojs.autojs.runtime.api.OcrResult import org.autojs.autojs.runtime.api.augment.Augmentable import org.autojs.autojs.runtime.api.augment.Invokable import org.autojs.autojs.runtime.api.augment.ocr.Ocr.Companion.OcrMode +import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException import org.autojs.autojs.util.RhinoUtils.coerceBoolean import org.autojs.autojs.util.RhinoUtils.coerceIntNumber import org.mozilla.javascript.NativeArray import org.mozilla.javascript.NativeObject +@Suppress("unused") class OcrPaddle(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime), Invokable { override val key = OcrMode.PADDLE.value @@ -29,40 +33,55 @@ class OcrPaddle(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRu private const val DEFAULT_CPU_THREAD_NUM = 4 private const val DEFAULT_USE_SLIM = true + private const val DEFAULT_USE_OPENCL = false @JvmStatic @RhinoRuntimeFunctionInterface - fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { - Ocr.commonRecognizeText(scriptRuntime, OcrMode.PADDLE, *it) + fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> + Ocr.recognizeTextWith(scriptRuntime, OcrMode.PADDLE, argList) } @JvmStatic @RhinoRuntimeFunctionInterface - fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { - Ocr.commonDetect(scriptRuntime, OcrMode.PADDLE, *it) + fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> + Ocr.detectWith(scriptRuntime, OcrMode.PADDLE, argList) } - fun recognizeTextInternal(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { - ApkBuilder.Libs.PADDLE_OCR.ensureLibFiles(OcrMode.PADDLE.value) - val (cpuThreadNum, useSlim) = getOcrOptions(options) - return scriptRuntime.ocrPaddle.recognizeText(image, cpuThreadNum, useSlim) + fun recognizeTextImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { + return performOcr(scriptRuntime, image, options).map { + it.text + } } - fun detectInternal(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { + fun detectImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { + return performOcr(scriptRuntime, image, options).map { + OcrResult(it.text, it.confidence, it.bounds) + } + } + + private fun performOcr(scriptRuntime: ScriptRuntime, image: ImageWrapper, options: NativeObject): List { ApkBuilder.Libs.PADDLE_OCR.ensureLibFiles(OcrMode.PADDLE.value) - val (cpuThreadNum, useSlim) = getOcrOptions(options) - return scriptRuntime.ocrPaddle.detect(image, cpuThreadNum, useSlim).map { result -> - OcrResult(result.label, result.confidence, result.bounds) + val (cpuThreadNum, useSlim, useOpenCL) = getOcrOptions(options) + val ocrOptions = org.autojs.plugin.ocr.OcrOptions().apply { + this.threads = cpuThreadNum + this.useSlim = useSlim + this.useOpenCL = useOpenCL + } + return runBlocking(scriptRuntime.coroutineContext) { + val target = OcrPluginHost.select(globalContext) + ?: throw WrappedIllegalArgumentException("No Paddle OCR plugin matched") + OcrPluginHost.detect(globalContext, target, image.bitmap, ocrOptions) } } private fun getOcrOptions(options: NativeObject): OcrOptions { val cpuThreadNum = options.inquire("cpuThreadNum", ::coerceIntNumber, DEFAULT_CPU_THREAD_NUM) val useSlim = options.inquire("useSlim", ::coerceBoolean, DEFAULT_USE_SLIM) - return OcrOptions(cpuThreadNum, useSlim) + val useOpenCL = options.inquire("useOpenCL", ::coerceBoolean, DEFAULT_USE_OPENCL) + return OcrOptions(cpuThreadNum, useSlim, useOpenCL) } - data class OcrOptions(val cpuThreadNum: Int, val useSlim: Boolean) + private data class OcrOptions(val cpuThreadNum: Int, val useSlim: Boolean, val useOpenCL: Boolean) } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrRapid.kt b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrRapid.kt index 7bf70496..536d9529 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrRapid.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/augment/ocr/OcrRapid.kt @@ -26,20 +26,20 @@ class OcrRapid(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRun @JvmStatic @RhinoRuntimeFunctionInterface - fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { - Ocr.commonRecognizeText(scriptRuntime, OcrMode.RAPID, *it) + fun recognizeText(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> + Ocr.recognizeTextWith(scriptRuntime, OcrMode.RAPID, argList) } @JvmStatic @RhinoRuntimeFunctionInterface - fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { - Ocr.commonDetect(scriptRuntime, OcrMode.RAPID, *it) + fun detect(scriptRuntime: ScriptRuntime, args: Array): NativeArray = ensureArgumentsAtMost(args, 3) { argList -> + Ocr.detectWith(scriptRuntime, OcrMode.RAPID, argList) } // @Hint by SuperMonster003 on Nov 1, 2024. // ! Reserved param `options`. // ! zh-CN: 预留参数 `options`. - fun recognizeTextInternal(scriptRuntime: ScriptRuntime, image: ImageWrapper, @Suppress("UNUSED_PARAMETER") options: NativeObject): List { + fun recognizeTextImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, @Suppress("UNUSED_PARAMETER") options: NativeObject): List { ApkBuilder.Libs.RAPID_OCR.ensureLibFiles(OcrMode.RAPID.value) return scriptRuntime.ocrRapid.recognizeText(image) } @@ -47,7 +47,7 @@ class OcrRapid(private val scriptRuntime: ScriptRuntime) : Augmentable(scriptRun // @Hint by SuperMonster003 on Nov 1, 2024. // ! Reserved param `options`. // ! zh-CN: 预留参数 `options`. - fun detectInternal(scriptRuntime: ScriptRuntime, image: ImageWrapper, @Suppress("UNUSED_PARAMETER") options: NativeObject): List { + fun detectImpl(scriptRuntime: ScriptRuntime, image: ImageWrapper, @Suppress("UNUSED_PARAMETER") options: NativeObject): List { ApkBuilder.Libs.RAPID_OCR.ensureLibFiles(OcrMode.RAPID.value) return scriptRuntime.ocrRapid.detect(image) }