在当前移动应用安全机制日益严密的环境下,基于 Android 无障碍服务(Accessibility Service)的传统自动化脚本已面临严峻挑战。由于无障碍技术依赖系统级接口读取界面控件信息并模拟点击,其行为特征(如高频调用、无真实触摸轨迹、控件树遍历等)极易被金融、社交、游戏等高风控类 App 的反作弊系统识别,进而触发验证、限流甚至账号封禁。为突破这一限制,越来越多的智能辅助方案转向视觉驱动的自动化技术,其中图色识别(Color/Template Matching)与 YOLOv8 目标检测模型的结合成为规避风控的有效路径。

图色技术通过截取屏幕图像,利用像素颜色或模板匹配定位按钮、图标、文字等关键元素,无需依赖应用内部控件结构,从根本上绕开了对无障碍权限的依赖。而 YOLOv8 作为当前先进的轻量化目标检测模型,可在移动端高效运行,实现对复杂界面中特定 UI 元素(如“领取奖励”按钮、“滑块验证”区域)的高精度实时识别。二者结合后,自动化系统仅需“看图操作”:先截图,再通过图色或 YOLOv8 定位目标坐标,最后通过底层输入方式(如 USB HID 或蓝牙 HID)模拟真实点击或滑动。整个过程完全在操作系统图形层完成,不触碰应用逻辑层,行为模式与真人操作高度一致,极大提升了隐蔽性。

此外,该方案具备良好的跨平台兼容性——无论目标 App 是否混淆、是否动态加载界面,只要视觉元素可辨识,即可被定位。同时,配合随机延迟、仿人滑动轨迹等策略,可进一步降低被行为分析模型识别的风险。尽管图色对界面变化敏感、YOLOv8 需要训练数据,但冰狐等智能辅助平台已内置预训练模型和可视化标注工具,大幅降低了使用门槛。综上,基于图色与 YOLOv8 的视觉自动化不仅有效规避了无障碍风控,更代表了未来“无感知、高仿真”智能操作的发展方向。

用户可通过 图色匹配(findColor / findImage) 与 YOLOv8 目标检测 实现不依赖无障碍服务的视觉驱动自动化脚本。该方案通过截屏 + 图像识别定位目标元素,再结合底层输入(如 USB HID 或蓝牙 HID)执行操作,有效规避风控。

一、准备工作

  1. 启用调试模式
    • 在冰狐 App 中开启【调试模式】(路径:设置 → 调试模式),以允许截图和图像识别。
  2. 准备资源文件
    • 将用于图色匹配的小图标模板(如 PNG 格式按钮截图)或 YOLOv8 模型文件.onnx 格式)上传至冰狐项目资源目录。
  3. 选择执行模块
    • 若需高隐蔽性,建议配合 usb 或 ble 模块执行点击(避免使用 click() 等无障碍函数)。

二、核心 API 说明

▶ 图色识别(基于 OpenCV)
// 查找单个颜色点(RGB,支持容差)
var point = findColor(r, g, b, [x1, y1, x2, y2], tolerance);

// 查找图像模板(返回中心坐标)
var rect = findImage("template.png", threshold, [x1, y1, x2, y2]);
▶ YOLOv8 目标检测
// 加载 ONNX 模型(首次调用自动缓存)
yolo.loadModel("my_model.onnx");

// 执行检测(返回对象数组:{class: "button", confidence: 0.92, x, y, w, h})
var results = yolo.detect("screen", confidenceThreshold);

注:"screen" 表示对当前屏幕截图进行检测;也可传入本地图片路径。

三、完整示例:使用图色 + USB HID 自动点击“领取”按钮

function main() {
    // 1. 连接 USB HID 设备(确保已 OTG 连接)
    if (usb.connect() !== 1) {
        toast("USB HID 连接失败");
        return;
    }
    usbInit(rsScreenWidth, rsScreenHeight); // 初始化分辨率

    // 2. 截图并查找“领取”按钮(使用模板图)
    var btn = findImage("lingqu_btn.png", 0.85, [0, 0, rsScreenWidth, rsScreenHeight]);
    
    if (btn) {
        console.log("找到按钮,坐标:", btn.x, btn.y);
        // 3. 使用 USB HID 点击(非无障碍!)
        usb.click(btn.x, btn.y);
        sleep(1000);
        toast("已点击领取按钮");
    } else {
        // 4. 若模板匹配失败,尝试用 YOLOv8 检测
        tryYoloFallback();
    }
}

// YOLOv8 备用方案
function tryYoloFallback() {
    yolo.loadModel("reward_button_yolov8s.onnx"); // 预训练模型
    var detects = yolo.detect("screen", 0.7);
    
    for (var i = 0; i < detects.length; i++) {
        if (detects[i].class === "claim_button") {
            var cx = detects[i].x + detects[i].w / 2;
            var cy = detects[i].y + detects[i].h / 2;
            usb.click(cx, cy);
            console.log("YOLOv8 定位成功,点击:", cx, cy);
            return;
        }
    }
    toast("未找到目标按钮");
}

// USB HID 分辨率初始化(必须!)
function usbInit(w, h) {
    usb.send([0x0E].concat(int2bytes(w, 2), int2bytes(h, 2)));
    sleep(3000);
}

四、关键注意事项

  1. 模板图要求

    • lingqu_btn.png 应为透明背景或高对比度的小图(建议 50×50 像素内);
    • 存放于脚本同目录的 res/ 文件夹中,或通过冰狐 Web 端上传。
  2. YOLOv8 模型训练

    • 需使用冰狐提供的标注工具标注数百张界面截图;
    • 导出为 ONNX 格式(支持 YOLOv8n/s/m);
    • 类别名需与代码中 detects[i].class 一致(如 "claim_button")。
  3. 性能优化

    • 限制检测区域(如只扫描屏幕下半部分)可提升速度;
    • 设置合理阈值(图色 threshold=0.85,YOLO confidence=0.7)平衡准确率与误检。
  4. 风控规避优势

    • 全程未调用 AccessibilityService
    • 输入由 USB HID 模拟,行为不可被应用层监控;
    • 适用于抖音、拼多多、银行 App 等强风控场景。

五、扩展建议

  • 结合 ocr() 识别文字内容,实现“点击包含‘立即到账’的按钮”;
  • 使用 findMultiColors() 匹配多点颜色特征,提高抗干扰能力;
  • 添加随机偏移(click(x+rand(-10,10), y+rand(-10,10)))模拟真人误差。

六、实际例子

使用yolov8实现的番茄小说自动化脚本

function startup(){
    home();

    // //需删除
    // if (findView('txt:冰狐智能辅助@android.widget.TextView', {flag:'find_all', maxStep:10}).length){
    //     scroll(null, 'left', {type: 1, distance: 0.95, beforeWait: 1000});
    //     scroll(null, 'left', {type: 1, distance: 0.95, beforeWait: 1000});
    // }

    if (findView('txt:精品应用@cn:android.widget.TextView', {flag:'find_all'}).length){
        gestureClick(323, 473);
        if (findView('txt:番茄畅听@cn:android.widget.TextView', {flag:'find_all'}).length){
            gestureClick(277, 1605);
        }
    }
}

//处理福利中心页面逻辑
function zhuanqian_page(){
    sleep(6000);

    var yolo =  new YoloV8();
    var temp = yolo.init('yolo_train/best-sim', ['chacha']);
    console.log('yolo初始化结果:' + temp);
    var ret = yolo.detect();  // 会自动截屏,然后检测截图,识别目标
    console.log('检测结果:' + ret);
    if (ret && ret.length > 0) {
        console.log('检测到chacha');
        gestureClick(r[0].left + r[0].width / 2, r[0].top + r[0].height / 2);
    }

    //在首页点击赚钱进入福利中心页面
    if (findView('id:com.xs.fm:id/dpg', {flag:'find_all'}).length){
        var ret = findView('id:com.xs.fm:id/dpg', {flag:'find_all'});
        var result = extractBounds(ret.views); 
        if (result) {
            gestureClick(result.left_num, result.top_num);

            //福利中心
            fuli_center();
        }
    } 

    

    // kanju();

    return true;
}

function cbFunc() {
    console.log('time is coming');
}

//提取动态UI的x,y坐标及width和height
function extractBounds(jsonData) {
    var width_num = 0, height_num = 0, left_num = 0, top_num = 0;

    //将 JSON 数据转换为字符串
    var jsonString = JSON.stringify(jsonData);
    console.log('JSON 字符串:' + jsonString); // 打印出完整的 JSON 字符串

    //利用正则表达式获取到字符串中第一个括号的起止位置
    var boundsRegex1 = new RegExp('\\(([^)]+)\\)', 'g');
    console.log('正则表达式一: ' + boundsRegex1);
    //使用 exec 方法逐步提取捕获组
    var match1 = boundsRegex1.exec(jsonString);

    if (match1) {
        console.log('匹配到的字段:', match1[0]); // 输出 

        var str = match1[0];
        var regex = new RegExp("\\((\\d+),\\s*(\\d+)");
        var strMatch = regex.exec(str);

        if (strMatch) {
            width_num = parseInt(strMatch[1]);  // 第一个数字
            height_num = parseInt(strMatch[2]);  // 第二个数字

            console.log("width: " + width_num);  // 870
            console.log("height: " + height_num);  // 1464
        } else {
            console.log("未找到匹配的数字");
        }    
    } else {
        console.log("未找到 boundsInScreen 字段");
        return null;
    }

    var startIndex = match1.index; // 匹配开始位置
    var endIndex = startIndex + match1[0].length; // 匹配结束位置
    console.log(`End index: ${endIndex}`);

    //利用获取到的起止位置将第一个括号及以前的内容剔除
    var temp = jsonString.substring(endIndex + 2); 
    var newString = JSON.stringify(temp);
    console.log('newString: ' + newString);

    //再用一次正则表达式获取到第二个括号中的内容
    var boundsRegex2 = new RegExp('\\(([^)]+)\\)', 'g');
    console.log('正则表达式二: ' + boundsRegex2);
    //使用 exec 方法逐步提取捕获组
    var match2 = boundsRegex2.exec(newString);

    if (match2) {
        console.log('匹配到的字段:', match2[0]); // 输出 

        var str = match2[0];
        var regex = new RegExp("\\((\\d+),\\s*(\\d+)");
        var strMatch = regex.exec(str);

        if (strMatch) {
            left_num = parseInt(strMatch[1]);  // 第一个数字
            top_num = parseInt(strMatch[2]);  // 第二个数字

            console.log("left: " + left_num);  // 870
            console.log("top: " + top_num);  // 1464
        } else {
            console.log("未找到匹配的数字");
        }    
    } else {
        console.log("未找到 boundsInScreen 字段");
        return null;
    }

    return { width_num: width_num, height_num: height_num, left_num: left_num, top_num: top_num };
}   

//福利中心
function fuli_center() {
    if (findView('txt:看视频领10次奖励(0/10)@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length) {
        console.log('看视频领10次奖励');
        gestureClick(88, 478);
    }

    sleep(2000);
    var yolo =  new YoloV8();
    var status = yolo.init('yolo_train/best-sim', ['chacha']);
    console.log('yolo初始化结果:' + status);
    var ret = yolo.detect();  // 会自动截屏,然后检测截图,识别目标
    console.log('检测结果:' + ret);
    if (ret && ret.length > 0) {
        gestureClick(ret[0].left + ret[0].width / 2, ret[0].top + ret[0].height / 2);
    }

    // if (findView('txt:金币收益@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length) {
    //     scroll(null, 'up', {type: 1, distance: 0.5});
    // }

    //每日挑战
    // if (findView('txt:去完成@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length){
    //     var ret = findView('txt:去完成@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'});
    //     var result = extractBounds(ret.views); 
    //     if (result) {
    //         gestureClick(result.left_num, result.top_num);

    //         sleep(2000);
    //         gestureClick(846, 951);
    //     }
    // }

    // //天天抽奖赢金币
    // if (findView('txt:天天抽奖赢金币@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length) {
    //     var ret = findView('txt:去抽奖@cn:com.lynx.tasm.behavior.ui.LynxFlattenUI', {flag:'find_all'});
    //     var result = extractBounds(ret.views); 
    //     if (result) {
    //         gestureClick(result.firstNum, result.secondNum);
    //     }
    // }

//     //预约领金币
//     if (findView('txt:预约领金币@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length) {
//         var ret = findView('txt:去领取@cn:com.lynx.tasm.behavior.ui.LynxFlattenUI', {flag:'find_all'});
//         var result = extractBounds(ret.views); 
//         if (result) {
//             gestureClick(result.firstNum, result.secondNum);
//         }
//     }

    //时长赚金币
    // if (findView('txt:时长赚金币@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length) {
    //     var sc_txt_ret = findView('txt:时长赚金币@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'});
    //     var sc_txt_result = extractBounds(sc_txt_ret.views);
    //     if (sc_txt_result) {
    //         //gestureClick(sc_txt_result.left_num, sc_txt_result.top_num);

    //         //
    //     }
    //     if (findView('txt:去加倍@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length) {
    //         var jb_txt_ret = findView('txt:去加倍@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'});
    //         var jb_txt_result = extractBounds(jb_txt_ret.views);
    //         if (jb_txt_result) {
    //             console.log('left:' + jb_txt_result.left_num);
    //             console.log('top:' + jb_txt_result.top_num);
    //             gestureClick(jb_txt_result.left_num - 200, jb_txt_result.top_num);

    //             while (1) {
    //                 if (findView('txt:领取成功', {flag:'find_all'}).length) {
    //                     console.log('领取成功');

    //                     var yolo =  new YoloV8();
    //                     var status = yolo.init('yolo_train/best-sim', ['chacha']);
    //                     console.log('yolo初始化结果:' + status);
    //                     var ret = yolo.detect();  // 会自动截屏,然后检测截图,识别目标
    //                     console.log('检测结果:' + ret);
    //                     if (ret && ret.length > 0) {
    //                         console.log('检测到chacha');
    //                         console.log('left:' + ret[0].left);
    //                         console.log('top:' + ret[0].top);
    //                         gestureClick(ret[0].left + ret[0].width / 2, ret[0].top + ret[0].height / 2);

    //                         if (findView('txt:坚持退出|txt:退出|txt:直接退出', {flag:'find_all'}).length) {
    //                             var tc_txt_ret = findView('txt:坚持退出|txt:退出|txt:直接退出', {flag:'find_all'});
    //                             var tc_txt_result = extractBounds(tc_txt_ret.views);
    //                             if (tc_txt_result) {
    //                                 console.log('退出');
    //                                 console.log('left:' + tc_txt_result.left_num);
    //                                 console.log('top:' + tc_txt_result.top_num);
    //                                 gestureClick(tc_txt_result.left_num, tc_txt_result.top_num);

    //                                 break;
    //                             }
    //                         }
    //                         break;
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }

    gesture([500, 1750, 500, 500]);

    // if (findView('txt:签到@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all', beforeWait: 2000}).length){
    //     var qd_txt_ret = findView('txt:签到@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'});
    //     var qd_txt_result = extractBounds(qd_txt_ret.views);
    //     if (qd_txt_result) {
    //         console.log('签到');
    //         console.log('left:' + qd_txt_result.left_num);
    //         console.log('top:' + qd_txt_result.top_num);
    //         gestureClick(qd_txt_result.left_num, qd_txt_result.top_num);

    //         if (findView('txt:立即签到@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all', beforeWait: 2000}).length) {
    //             var ljqd_txt_ret = findView('txt:立即签到@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'});
    //             var ljqd_txt_result = extractBounds(ljqd_txt_ret.views);
    //             if (ljqd_txt_result) {
    //                 console.log('签到成功');
    //                 console.log('left:' + ljqd_txt_result.left_num);
    //                 console.log('top:' + ljqd_txt_result.top_num);
    //                 gestureClick(ljqd_txt_result.left_num + ljqd_txt_result.width_num / 2, ljqd_txt_result.top_num + ljqd_txt_result.height_num / 2);
    //             }
    //         }
    //     }
    // }

    if (findView('txt:今日签到可领@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'}).length == 0) {
        gesture([500, 1750, 500, 300]);

        if (findView('txt:阅读赚金币@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all', beforeWait: 3000}).length) {
            var yd_txt_ret = findView('txt:阅读赚金币@cn:com.lynx.tasm.behavior.ui.text.FlattenUIText', {flag:'find_all'});
            var yd_txt_result = extractBounds(yd_txt_ret.views);
            if (yd_txt_result) {
                console.log('签到成功');
                console.log('left:' + yd_txt_result.left_num);
                console.log('top:' + yd_txt_result.top_num);
                gestureClick(yd_txt_result.left_num, yd_txt_result.top_num);

                if (findView('txt:看书@cn:android.widget.TextView', {flag:'find_all', beforeWait: 2000}).length) {
                    gestureClick(500, 581);
                }
            }
        }
    }

    
    
}

function main() {
    requestScreenShot();  //请求截屏权限

    startup();  //启动番茄小说

    zhuanqian_page();  //开始番茄小说任务的逻辑

}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐