Android无障碍自动化结合opencv实现支付宝能量自动收集

Android无障碍服务可以操作元素,手势模拟,实现基本的控制。opencv可以进行图像识别。两者结合在一起即可实现支付宝能量自动收集。opencv用于识别能量,无障碍服务用于模拟手势,即点击能量。

当然这两者结合不单单只能实现这些,还能做很多自动化的程序,如芭芭农场自动施肥、蚂蚁庄园等等的自动化,甚至游戏的自动化也没问题。

下面简单介绍下核心的实现逻辑

核心步骤

  1. 准确识别多个能量球位置
  2. 准确点击能量球位置

opencv识别能量球

OpenCV是一个可用于开发实时的图像处理、计算机视觉以及模式识别可商用的开源库-opencv介绍

思路

使用opencv怎么识别能量球呢?

使用opencv的模板匹配。即,将能量球单独裁剪出来作为模板,再将其与屏幕图像进行匹配,筛选匹配分值最高的结果即获取能量球在屏幕中的位置。

实现

1. 项目集成opencv-android版
dependencies {
 implementation 'org.opencv:opencv:4.9.0'
}

最新版本可查看官方集成教程

2. 截取能量球图像作为模板
3. 截取屏幕图像
4. 使用opencv模板匹配获取所有能量球位置

opencv模板匹配api

Imgproc.matchTemplate(image, templ, result, method, mask)

参数解释:
image屏幕图像,即步骤3中截取的屏幕图像
templ模板图像,即步骤2中截图的能量球图像
result匹配结果容器,用于存储匹配的结果
mask掩膜,用于指定模板中哪些位置需要匹配,哪些不需要匹配

其中参数mask掩膜是匹配准确度的关键点

掩膜图像是根据模板生成的一张黑白图像,其中黑色为不需要匹配的区域

模板图像与生成的掩膜图像对比

模板图像 掩模图像

其中文字也是我们不需要匹配的,因为里面的文字会变化,所以中间加了一块黑色矩形用于指定匹配忽略区域

对于掩膜的创建方法这里不介绍了,所有代码都已经开放在我的自动化开源库Assists里,想直接看代码这里:https://github.com/ven-coder/Assists

参数准备好就可以进行匹配了,下面是完整代码(kotlin代码)

    /**
     * 模板匹配能量球
     */
    fun match() {
        try {
            val path = System.getProperty("user.dir") + "\\lib\\x64\\opencv_java490.dll"
            System.load(path)
            val temp = System.getProperty("user.dir") + "\\images\\temp.jpg"
            val image = System.getProperty("user.dir") + "\\images\\image.png"
            //模板图像
            val img = Imgcodecs.imread(image)
            //屏幕图像
            val templ = Imgcodecs.imread(temp)
            //掩膜图像
            val mask = createMask(templ)
            // 创建结果矩阵
            val resultCols: Int = img.cols() - templ.cols() + 1
            val resultRows: Int = img.rows() - templ.rows() + 1
            val result = Mat(resultRows, resultCols, CvType.CV_32FC1)
            // 进行模板匹配
            Imgproc.matchTemplate(img, templ, result, Imgproc.TM_CCORR_NORMED, mask)
            // 遍历结果矩阵,找到所有匹配超过阈值的位置
            val threshold = 0.98 // 阈值,根据实际情况调整
            var count = 0
            var countValue = 0
            for (y in 0 until result.rows()) {
                for (x in 0 until result.cols()) {
                    countValue++
                    val matchValue = result[y, x]
                    if (matchValue[0] >= threshold) {
                        count++
                        // 找到一个匹配位置
                        val matchLoc = Point(x.toDouble(), y.toDouble())
                        // 绘制矩形框
                        Imgproc.rectangle(img, matchLoc, Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), Scalar( 85.0, 85.0,205.0,), 2, Imgproc.LINE_AA, 0)
                    }
                }
            }
            // 显示结果
            Imgproc.resize(img, img, Size(img.cols() / 2.0, img.rows() / 2.0)) // 可选:调整显示大小
            HighGui.imshow("Matched Result: $count", img)
            HighGui.waitKey(0)
        } catch (e: Throwable) {
            e.printStackTrace()
        }
    }

    /**
     * 创建掩膜
     */
    fun createMask(source: Mat): Mat {
        // 转换为 HSV 颜色空间
        val hsvImage = Mat()
        Imgproc.cvtColor(source, hsvImage, Imgproc.COLOR_BGR2HSV)


        // 定义绿色的颜色范围
        val lowerGreen = Scalar(35.0, 100.0, 100.0)
        val upperGreen = Scalar(85.0, 255.0, 255.0)


        // 创建掩膜
        val mask = Mat()
        Core.inRange(hsvImage, lowerGreen, upperGreen, mask)

        // 忽略"27g"文字
        // 你可以使用形态学操作去掉文字部分,或者手动确定文字的位置并将其设置为黑色(0)。
        // 假设文字位于圆形中心,可以手动遮盖这个区域
        // Rect(中心位置x, 中心位置y, 宽度, 高度)
        val width = 80
        val height = 60
        val textRect = Rect(source.width() / 2 - width / 2, source.height() / 2 - height / 2, width, height) // 假设的"27g"文字位置和大小
        Imgproc.rectangle(mask, textRect, Scalar(0.0), -1)
        Imgproc.rectangle(mask, Rect((source.width() / 2 - width / 2) + 10, (source.height() / 2 - height / 2) + height, 40, 25), Scalar(255.0), -1)
        return mask
    }

匹配结果

点击能量球

准确得到能量球位置之后就好办了,使用我的开源库Assists开启无障碍服务后调用gestureClick(x: Float, y: Float)点击能量球位置即可

//it.x + temp3.width() / 2,坐标加上模板大小的一半即点击中间位置
Assists.gestureClick((it.x + temp3.width() / 2).toFloat(), (it.y + temp3.height() / 2).toFloat())

最终效果

以上所有代码都在我的开源库Assists示例里了,需要的自取即可。

觉得有帮助顺便可以start一下,满足以下一下老夫虚荣心憋

相关推荐
命运之手1 小时前
【Android】自定义换肤框架01之皮肤包制作
android·skin·skinner·换肤框架·不重启换肤
练习本1 小时前
android perfetto使用技巧梳理
android
GitLqr2 小时前
Android - 云游戏本地悬浮输入框实现
android·开源·jitpack
周周的Unity小屋2 小时前
Unity实现安卓App预览图片、Pdf文件和视频的一种解决方案
android·unity·pdf·游戏引擎·webview·3dwebview
单丽尔4 小时前
Gemini for China 大更新,现已上架 Android APP!
android
JerryHe6 小时前
Android Camera API发展历程
android·数码相机·camera·camera api
Synaric7 小时前
Android与Java后端联调RSA加密的注意事项
android·java·开发语言
程序员老刘·8 小时前
如何评价Flutter?
android·flutter·ios
JoyceMill9 小时前
Android 图像效果的奥秘
android
想要打 Acm 的小周同学呀10 小时前
ThreadLocal学习
android·java·学习