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一下,满足以下一下老夫虚荣心憋

相关推荐
阿巴斯甜17 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker18 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952719 小时前
Andorid Google 登录接入文档
android
黄林晴20 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android