diff --git a/apkbuilder/src/main/java/com/stardust/autojs/apkbuilder/ApkUnpackUtil.java b/apkbuilder/src/main/java/com/stardust/autojs/apkbuilder/ApkUnpackUtil.java index ab7c6eca..b8cfb905 100755 --- a/apkbuilder/src/main/java/com/stardust/autojs/apkbuilder/ApkUnpackUtil.java +++ b/apkbuilder/src/main/java/com/stardust/autojs/apkbuilder/ApkUnpackUtil.java @@ -35,10 +35,19 @@ public class ApkUnpackUtil { if (!e.isDirectory() && !TextUtils.isEmpty(name)) { File file = new File(mWorkspacePath, name); System.out.println(file); - if (file.getParentFile() != null && file.getParentFile().mkdirs()) { + File parentFile = file.getParentFile(); + if (parentFile != null && (parentFile.exists() || parentFile.mkdirs())) { try (FileOutputStream fos = new FileOutputStream(file)) { + System.out.println(file.getName() + " has been written"); StreamUtils.write(zis, fos); } + } else { + System.out.println(file.getName() + " can not write"); + System.out.println("is parent null? " + (parentFile != null)); + if (file.getParentFile() != null) { + System.out.println("is parent exists? " + parentFile.exists()); + System.out.println("can parent mkdirs? " + parentFile.mkdirs()); + } } } else { System.out.println("file or empty:" + name); diff --git a/app/src/main/java/org/autojs/autojs/build/ApkBuilder.java b/app/src/main/java/org/autojs/autojs/build/ApkBuilder.java index 3bc51860..0f307f8e 100644 --- a/app/src/main/java/org/autojs/autojs/build/ApkBuilder.java +++ b/app/src/main/java/org/autojs/autojs/build/ApkBuilder.java @@ -76,6 +76,7 @@ public class ApkBuilder { Boolean usePaddleOcr = false; Boolean useMlKitOcr = false; Boolean useTessTwo = false; + Boolean useOnnx = false; Set enabledPermission = new HashSet<>(); public static AppConfig fromProjectConfig(String projectDir, ProjectConfig projectConfig) { @@ -187,6 +188,14 @@ public class ApkBuilder { this.useTessTwo = useTessTwo; } + public Boolean getUseOnnx() { + return useOnnx; + } + + public void setUseOnnx(Boolean useOnnx) { + this.useOnnx = useOnnx; + } + public Set getEnabledPermission() { return enabledPermission; } @@ -237,6 +246,9 @@ public class ApkBuilder { if (!mAppConfig.useTessTwo) { removeSoList.addAll(Arrays.asList("libtess.so", "liblept.so", "libjpgt.so", "libpngt.so")); } + if (!mAppConfig.useOnnx) { + removeSoList.addAll(Arrays.asList("libonnxruntime.so", "libonnxruntime4j_jni.so")); + } if (!mAppConfig.useOpenCv) { removeSoList.add("libopencv_java4.so"); } diff --git a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java index 31041d92..785eff50 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java @@ -108,6 +108,9 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa @ViewById(R.id.use_tess_two) CheckBox mUseTessTwo; + @ViewById(R.id.use_onnx_runtime) + CheckBox mUseOnnx; + @ViewById(R.id.recycler_view) RecyclerView recyclerView; @@ -202,6 +205,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa "https://i.autojs.org/autojs/plugin/%d.apk", ApkBuilderPluginHelper.getSuitablePluginVersion())); } + @SuppressLint("StringFormatInvalid") private void setupWithSourceFile(ScriptFile file) { String dir = file.getParent(); if (dir.startsWith(getFilesDir().getPath())) { @@ -349,6 +353,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa appConfig.setUsePaddleOcr(mUsePaddleOcr.isChecked()); appConfig.setUseMlKitOcr(mUseMlKitOcr.isChecked()); appConfig.setUseTessTwo(mUseTessTwo.isChecked()); + appConfig.setUseOnnx(mUseOnnx.isChecked()); Set enabledPermission = new HashSet<>(); for (Option option : options) { if (option.isSelected()) { @@ -387,6 +392,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa Log.e(LOG_TAG, "Build failed", error); } + @SuppressLint("StringFormatInvalid") private void onBuildSuccessful(File outApk) { mProgressDialog.dismiss(); mProgressDialog = null; diff --git a/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java index 85421f24..8e66b551 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java @@ -195,7 +195,7 @@ public class ProjectConfigActivity extends BaseActivity { @Click(R.id.icon) void selectIcon() { ShortcutIconSelectActivity_.intent(this) - .flags(Intent.FLAG_ACTIVITY_NEW_TASK) + .flags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) .startForResult(REQUEST_CODE); } @@ -249,6 +249,7 @@ public class ProjectConfigActivity extends BaseActivity { @SuppressLint("CheckResult") @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (resultCode != RESULT_OK) { return; } diff --git a/app/src/main/res/layout/activity_build.xml b/app/src/main/res/layout/activity_build.xml index b9345551..6e16a40d 100644 --- a/app/src/main/res/layout/activity_build.xml +++ b/app/src/main/res/layout/activity_build.xml @@ -280,6 +280,13 @@ android:checked="true" android:text="@string/text_use_ml_kit_ocr" /> + + 使用OpenCV 使用PaddleOCR 使用ML-KitOCR + 使用OnnxRuntime 使用TessTwoOCR 描述至少要 %d 个字. diff --git a/autojs-aar/opencv/build.gradle b/autojs-aar/opencv/build.gradle index fb472df0..794804c8 100644 --- a/autojs-aar/opencv/build.gradle +++ b/autojs-aar/opencv/build.gradle @@ -1,2 +1,2 @@ configurations.maybeCreate("default") -artifacts.add("default", file('opencv-4.5.5.aar')) \ No newline at end of file +artifacts.add("default", file('opencv-4.8.0.aar')) \ No newline at end of file diff --git a/autojs-aar/opencv/opencv-4.5.5.aar b/autojs-aar/opencv/opencv-4.8.0.aar similarity index 68% rename from autojs-aar/opencv/opencv-4.5.5.aar rename to autojs-aar/opencv/opencv-4.8.0.aar index 269b419f..86a2549b 100644 Binary files a/autojs-aar/opencv/opencv-4.5.5.aar and b/autojs-aar/opencv/opencv-4.8.0.aar differ diff --git a/autojs-aar/paddleocr/build.gradle b/autojs-aar/paddleocr/build.gradle index 3e147e05..fb1f1038 100644 --- a/autojs-aar/paddleocr/build.gradle +++ b/autojs-aar/paddleocr/build.gradle @@ -50,11 +50,11 @@ def archives = [ // ], ] /** - * 下载opencv4.5.5源码包 默认为4.2.0 和 AutoJS中的版本不匹配会产生冲突 这里进行修改 + * 下载opencv4.8.0源码包 默认为4.2.0 和 AutoJS中的版本不匹配会产生冲突 这里进行修改 */ def zipArchives = [ [ - 'src' : 'https://github.com/opencv/opencv/releases/download/4.5.5/opencv-4.5.5-android-sdk.zip', + 'src' : 'https://github.com/opencv/opencv/releases/download/4.8.0/opencv-4.8.0-android-sdk.zip', 'dest': 'OpenCV' ] ] diff --git a/autojs/build.gradle b/autojs/build.gradle index 7203e0eb..82883805 100644 --- a/autojs/build.gradle +++ b/autojs/build.gradle @@ -76,6 +76,7 @@ dependencies { api project(path: ':common') api project(path: ':automator') implementation 'com.rmtheis:tess-two:9.1.0' - implementation 'com.google.mlkit:text-recognition-chinese:16.0.0-beta6' + implementation 'com.google.mlkit:text-recognition-chinese:16.0.0' + implementation 'com.microsoft.onnxruntime:onnxruntime-android:1.15.1' } diff --git a/autojs/src/main/java/com/stardust/autojs/onnx/YoloV8Predictor.java b/autojs/src/main/java/com/stardust/autojs/onnx/YoloV8Predictor.java new file mode 100644 index 00000000..28695972 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/onnx/YoloV8Predictor.java @@ -0,0 +1,306 @@ +package com.stardust.autojs.onnx; + +import android.os.Build; +import android.util.Log; + +import com.google.gson.Gson; +import com.stardust.autojs.onnx.domain.DetectResult; +import com.stardust.autojs.onnx.domain.Detection; +import com.stardust.autojs.onnx.util.Letterbox; + +import org.opencv.android.OpenCVLoader; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import ai.onnxruntime.OnnxTensor; +import ai.onnxruntime.OrtEnvironment; +import ai.onnxruntime.OrtException; +import ai.onnxruntime.OrtSession; +import ai.onnxruntime.providers.NNAPIFlags; +import androidx.annotation.RequiresApi; + +/** + * @author TonyJiangWJ + * @since 2023/8/20 + */ +@RequiresApi(api = Build.VERSION_CODES.N) +public class YoloV8Predictor { + private static final String TAG = "YoloV8Predictor"; + + static { + OpenCVLoader.initDebug(); + } + + private final String modelPath; + + private float confThreshold = 0.35F; + private float nmsThreshold = 0.55F; + private boolean tryNpu; + private Size shapeSize = new Size(640, 640); + private Letterbox letterbox; + + private List labels = new ArrayList<>(); + private List apiFlags = Arrays.asList("CPU_DISABLED"); + + public YoloV8Predictor(String modelPath) { + this.modelPath = modelPath; + } + + public YoloV8Predictor(String modelPath, float confThreshold, float nmsThreshold) { + this.modelPath = modelPath; + this.confThreshold = confThreshold; + this.nmsThreshold = nmsThreshold; + } + + public void setConfThreshold(float confThreshold) { + this.confThreshold = confThreshold; + } + + public void setNmsThreshold(float nmsThreshold) { + this.nmsThreshold = nmsThreshold; + } + + public void setLabels(List labels) { + this.labels = labels; + } + + public void setShapeSize(double width, double height) { + this.shapeSize = new Size(width, height); + } + + public void setTryNpu(boolean tryNpu) { + this.tryNpu = tryNpu; + } + + public void setApiFlags(List apiFlags) { + this.apiFlags = apiFlags; + } + + private OrtSession session; + private OrtEnvironment environment; + + private void prepareSession() throws OrtException { + if (environment != null) { + return; + } + // 加载ONNX模型 + environment = OrtEnvironment.getEnvironment(); + OrtSession.SessionOptions sessionOptions = new OrtSession.SessionOptions(); + addNNApiProvider(sessionOptions); + + session = environment.createSession(modelPath, sessionOptions); + // 输出基本信息 + session.getInputInfo().keySet().forEach(x -> { + try { + System.out.println("input name = " + x); + System.out.println(session.getInputInfo().get(x).getInfo().toString()); + } catch (OrtException e) { + throw new RuntimeException(e); + } + }); + } + + private void addNNApiProvider(OrtSession.SessionOptions sessionOptions) { + if (!tryNpu) { + return; + } + try { + List flags = new ArrayList<>(); + if (apiFlags.contains("USE_FP16")) { + flags.add(NNAPIFlags.USE_FP16); + } + if (apiFlags.contains("USE_NCHW")) { + flags.add(NNAPIFlags.USE_NCHW); + } + if (apiFlags.contains("CPU_ONLY")) { + flags.add(NNAPIFlags.CPU_ONLY); + } + if (apiFlags.contains("CPU_DISABLED")) { + flags.add(NNAPIFlags.CPU_DISABLED); + } + Log.d(TAG, "addNNApiProvider: 当前启用nnapiFlags:" + new Gson().toJson(apiFlags)); + sessionOptions.addNnapi(EnumSet.copyOf(flags)); + Log.d(TAG, "prepareSession: 启用nnapi成功"); + } catch (Exception e) { + Log.e(TAG, "prepareSession: 无法启用nnapi"); + } + } + + private HashMap preprocessImage(Mat img) throws OrtException { + + // 读取 image + Mat image = img.clone(); + // 将四通道转换为三通道 + if (image.channels() == 4) { + Imgproc.cvtColor(image, image, Imgproc.COLOR_RGBA2BGR); + } + Log.d(TAG, "preprocessImage: image's channels: " + image.channels()); + Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2RGB); + // 更改 image 尺寸 + letterbox = new Letterbox(); + letterbox.setNewShape(this.shapeSize); + image = letterbox.letterbox(image); + + int rows = letterbox.getHeight(); + int cols = letterbox.getWidth(); + int channels = image.channels(); + + // 将Mat对象的像素值赋值给Float[]对象 + float[] pixels = new float[channels * rows * cols]; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + double[] pixel = image.get(j, i); + for (int k = 0; k < channels; k++) { + // 这样设置相当于同时做了image.transpose((2, 0, 1))操作 + pixels[rows * cols * k + j * cols + i] = (float) pixel[k] / 255.0f; + } + } + } + image.release(); + // 创建OnnxTensor对象 + long[] shape = {1L, (long) channels, (long) rows, (long) cols}; + OnnxTensor tensor = OnnxTensor.createTensor(environment, FloatBuffer.wrap(pixels), shape); + HashMap stringOnnxTensorHashMap = new HashMap<>(); + stringOnnxTensorHashMap.put(session.getInputInfo().keySet().iterator().next(), tensor); + return stringOnnxTensorHashMap; + } + + private List postProcessOutput(OrtSession.Result output) throws OrtException { + float[][] outputData = ((float[][][]) output.get(0).getValue())[0]; + + outputData = transposeMatrix(outputData); + Map> class2Bbox = new HashMap<>(); + + for (float[] bbox : outputData) { + float[] conditionalProbabilities = Arrays.copyOfRange(bbox, 4, outputData.length); + int label = argmax(conditionalProbabilities); + float conf = conditionalProbabilities[label]; + if (conf < confThreshold) { + continue; + } + + bbox[4] = conf; + + // xywh to (x1, y1, x2, y2) + xywh2xyxy(bbox); + + // skip invalid predictions + if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) { + continue; + } + + class2Bbox.putIfAbsent(label, new ArrayList<>()); + class2Bbox.get(label).add(bbox); + } + + List detections = new ArrayList<>(); + for (Map.Entry> entry : class2Bbox.entrySet()) { + int label = entry.getKey(); + List bboxes = entry.getValue(); + bboxes = nonMaxSuppression(bboxes, nmsThreshold); + for (float[] bbox : bboxes) { + String labelString = ""; + if (labels.size() - 1 < label) { + labelString = String.valueOf(label); + } else { + labelString = labels.get(label); + } + detections.add(new Detection(labelString, entry.getKey(), Arrays.copyOfRange(bbox, 0, 4), bbox[4])); + } + } + return detections; + } + + public List predictYolo(String imagePath) throws OrtException { + return predictYolo(Imgcodecs.imread(imagePath)); + } + + public List predictYolo(Mat image) throws OrtException { + prepareSession(); + long start_time = System.currentTimeMillis(); + // 运行推理 + OrtSession.Result output = session.run(preprocessImage(image)); + List detections = postProcessOutput(output); + System.out.printf("time:%d ms.\n", (System.currentTimeMillis() - start_time)); + return detections.stream().map(detection -> new DetectResult(detection, letterbox)) + .collect(Collectors.toList()); + } + + public static void xywh2xyxy(float[] bbox) { + float x = bbox[0]; + float y = bbox[1]; + float w = bbox[2]; + float h = bbox[3]; + + bbox[0] = x - w * 0.5f; + bbox[1] = y - h * 0.5f; + bbox[2] = x + w * 0.5f; + bbox[3] = y + h * 0.5f; + } + + public static float[][] transposeMatrix(float[][] m) { + float[][] temp = new float[m[0].length][m.length]; + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[0].length; j++) { + temp[j][i] = m[i][j]; + } + } + return temp; + } + + public static List nonMaxSuppression(List bboxes, float iouThreshold) { + + List bestBboxes = new ArrayList<>(); + + bboxes.sort(Comparator.comparing(a -> a[4])); + + while (!bboxes.isEmpty()) { + float[] bestBbox = bboxes.remove(bboxes.size() - 1); + bestBboxes.add(bestBbox); + bboxes = bboxes.stream().filter(a -> computeIOU(a, bestBbox) < iouThreshold).collect(Collectors.toList()); + } + + return bestBboxes; + } + + public static float computeIOU(float[] box1, float[] box2) { + + float area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]); + float area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]); + + float left = Math.max(box1[0], box2[0]); + float top = Math.max(box1[1], box2[1]); + float right = Math.min(box1[2], box2[2]); + float bottom = Math.min(box1[3], box2[3]); + + float interArea = Math.max(right - left, 0) * Math.max(bottom - top, 0); + float unionArea = area1 + area2 - interArea; + return Math.max(interArea / unionArea, 1e-8f); + + } + + //返回最大值的索引 + public static int argmax(float[] a) { + float re = -Float.MAX_VALUE; + int arg = -1; + for (int i = 0; i < a.length; i++) { + if (a[i] >= re) { + re = a[i]; + arg = i; + } + } + return arg; + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/onnx/domain/DetectResult.java b/autojs/src/main/java/com/stardust/autojs/onnx/domain/DetectResult.java new file mode 100644 index 00000000..c50c2b66 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/onnx/domain/DetectResult.java @@ -0,0 +1,89 @@ +package com.stardust.autojs.onnx.domain; + +import com.stardust.autojs.onnx.util.Letterbox; + +/** + * @author TonyJiangWJ + * @since 2023/8/20 + */ +public class DetectResult { + + private String label; + private Integer clsId; + private double left; + private double top; + private double right; + private double bottom; + private float confidence; + + public DetectResult() { + } + + public DetectResult(Detection detection, Letterbox letterbox) { + this.label = detection.label; + this.confidence = detection.confidence; + double dw = letterbox.getDw(); + double dh = letterbox.getDh(); + double ratio = letterbox.getRatio(); + left = (detection.getBbox()[0] - dw) / ratio; + right = (detection.getBbox()[2] - dw) / ratio; + top = (detection.getBbox()[1] - dh) / ratio; + bottom = (detection.getBbox()[3] - dh) / ratio; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Integer getClsId() { + return clsId; + } + + public void setClsId(Integer clsId) { + this.clsId = clsId; + } + + public double getLeft() { + return left; + } + + public void setLeft(double left) { + this.left = left; + } + + public double getTop() { + return top; + } + + public void setTop(double top) { + this.top = top; + } + + public double getRight() { + return right; + } + + public void setRight(double right) { + this.right = right; + } + + public double getBottom() { + return bottom; + } + + public void setBottom(double bottom) { + this.bottom = bottom; + } + + public float getConfidence() { + return confidence; + } + + public void setConfidence(float confidence) { + this.confidence = confidence; + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/onnx/domain/Detection.java b/autojs/src/main/java/com/stardust/autojs/onnx/domain/Detection.java new file mode 100644 index 00000000..468d5824 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/onnx/domain/Detection.java @@ -0,0 +1,62 @@ +package com.stardust.autojs.onnx.domain; + +/** + * @author TonyJiangWJ + * @since 2023/8/20 + */ +public class Detection { + public String label; + + private Integer clsId; + + public float[] bbox; + + public float confidence; + + + public Detection(String label,Integer clsId, float[] bbox, float confidence){ + this.clsId = clsId; + this.label = label; + this.bbox = bbox; + this.confidence = confidence; + } + + public Detection(){ + + } + + public Integer getClsId() { + return clsId; + } + + public void setClsId(Integer clsId) { + this.clsId = clsId; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public float[] getBbox() { + return bbox; + } + + public void setBbox(float[] bbox) { + } + + + @Override + public String toString() { + return " label="+label + + " \t clsId="+clsId + + " \t x0="+bbox[0] + + " \t y0="+bbox[1] + + " \t x1="+bbox[2] + + " \t y1="+bbox[3] + + " \t score="+confidence; + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/onnx/util/Letterbox.java b/autojs/src/main/java/com/stardust/autojs/onnx/util/Letterbox.java new file mode 100644 index 00000000..bc60d0cf --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/onnx/util/Letterbox.java @@ -0,0 +1,87 @@ +package com.stardust.autojs.onnx.util; + +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +/** + * @author TonyJiangWJ + * @since 2023/8/20 + */ +public class Letterbox { + + private Size newShape = new Size(640, 640); + private final double[] color = new double[]{114, 114, 114}; + private final Boolean auto = false; + private final Boolean scaleUp = true; + private Integer stride = 32; + + private double ratio; + private double dw; + private double dh; + + public double getRatio() { + return ratio; + } + + public double getDw() { + return dw; + } + + public Integer getWidth() { + return (int) this.newShape.width; + } + + public Integer getHeight() { + return (int) this.newShape.height; + } + + public double getDh() { + return dh; + } + + public void setNewShape(Size newShape) { + this.newShape = newShape; + } + + public void setStride(Integer stride) { + this.stride = stride; + } + + public Mat letterbox(Mat im) { // 调整图像大小和填充图像,使满足步长约束,并记录参数 + + // 当前形状 [height, width] + int[] shape = {im.rows(), im.cols()}; + // Scale ratio (new / old) + double r = Math.min(this.newShape.height / shape[0], this.newShape.width / shape[1]); + // 仅缩小,不扩大(一且为了mAP) + if (!this.scaleUp) { + r = Math.min(r, 1.0); + } + // Compute padding + Size newUnpad = new Size(Math.round(shape[1] * r), Math.round(shape[0] * r)); + // wh 填充 + double dw = this.newShape.width - newUnpad.width, dh = this.newShape.height - newUnpad.height; + // 最小矩形 + if (this.auto) { + dw = dw % this.stride; + dh = dh % this.stride; + } + // 填充的时候两边都填充一半,使图像居于中心 + dw /= 2; + dh /= 2; + // resize + if (shape[1] != newUnpad.width || shape[0] != newUnpad.height) { + Imgproc.resize(im, im, newUnpad, 0, 0, Imgproc.INTER_LINEAR); + } + int top = (int) Math.round(dh - 0.1), bottom = (int) Math.round(dh + 0.1); + int left = (int) Math.round(dw - 0.1), right = (int) Math.round(dw + 0.1); + // 将图像填充为正方形 + Core.copyMakeBorder(im, im, top, bottom, left, right, Core.BORDER_CONSTANT, new org.opencv.core.Scalar(this.color)); + this.ratio = r; + this.dh = dh; + this.dw = dw; + return im; + } +} \ No newline at end of file diff --git a/inrt/build.gradle b/inrt/build.gradle index 06f15541..030d51e2 100644 --- a/inrt/build.gradle +++ b/inrt/build.gradle @@ -15,6 +15,7 @@ android { signingConfigs { if (file(homePath + '/auto-js-t-pkcs12.jks').exists()) { + signSupport = true release { storeFile file(homePath + '/auto-js-t-pkcs12.jks') storePassword storePasswd