实时读取屏幕识别指定颜色

本文介绍了一种利用OpenCV、PIL和pyautogui库解决GBA游戏中特定关卡的方法。通过识别屏幕上的特定颜色区域,当颜色达到预设阈值时自动按下空格键。首先获取目标颜色的HSV颜色区间,然后在游戏运行时实时捕获屏幕,对比颜色数量判断是否达到触发条件,最终实现自动操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

在GBA测评中有一个关卡是解锁(如下图所示),需要在转盘到指定区间时按下空格键,后期会越来快,难度比较高,因此想要提出一种能够识别屏幕、当转盘到达指定区间时自动按空格的方法。

GBA-解锁游戏

思路

首先想到的思路就是通过颜色来进行区分,明显看到当轮盘转到指定区间会出现一种不同的颜色。如果能够识别这种颜色大概的数量,当超过一定阈值时就可以认为处于正确状态(即轮盘转到指定位置)。具体流程如下:

  1. 截取正确状态下特殊的颜色,识别该特殊颜色。
  2. 截取正确状态与错误状态图片,分别识别两种状态下该颜色的数量,计算得到一个特殊颜色数量的阈值,当大于该阈值时就认为处于正确状态。
  3. 捕获屏幕中的当前图片。
  4. 识别当前图片中特殊的数量,与阈值做比较,判断是否属于正确状态。

方法

  • cv2提供有方法可以进行指定颜色提取,参照这篇文章使用OpenCV和Python检测特定颜色,关键代码如下:

    import cv2
    # 读取图片
    cv2img = cv2.imread(true_case_dir)
    # 格式转换
    hsv = cv2.cvtColor(cv2img, cv2.COLOR_BGR2HSV)
    # 颜色区间选择
    mask = cv2.inRange(hsv, lower_range, upper_range)
    # 通过按位与筛选得到指定颜色部分的图像
    res = cv2.bitwise_and(cv2img, cv2img, mask=mask)
    
  • ImageGrab提供有方法可以进行屏幕捕获,参照这篇文章用python进行屏幕截图,只用两行代码搞定,关键代码如下:

    from PIL import ImageGrab
    img = ImageGrab.grab()
    
  • pyautogui提供有方法可以进行模拟键入空格,关键代码如下

    import pyautogui as pg
    pg.press('space')
    
  • tips

结果

使用目标颜色进行遮罩,可以得到以下结果。
true case
true mask
true result
false case

false mask
false result

代码

from PIL import ImageGrab
import pyautogui as pg
import numpy as np
import cv2
import time

def get_color_range(target_color_dir):
    """
    获取指定图片的HSV颜色区间
    """
    target_img = cv2.imread(target_color_dir)
    target_color_hsv = cv2.cvtColor(target_img, cv2.COLOR_BGR2HSV)
    target_color_hsv = target_color_hsv.reshape(-1, 3)
    lower_range = np.min(target_color_hsv, axis=0)
    upper_range = np.max(target_color_hsv, axis=0)
    return lower_range, upper_range

def case_test(true_case_dir, false_case_dir, target_color_dir):
    """
    根据正确、错误样本、目标颜色获取颜色区间以及目标区域大小
    """
    lower_range, upper_range = get_color_range(target_color_dir)
    # good case
    print("========good case=======")
    cv2img = cv2.imread(true_case_dir)
    hsv = cv2.cvtColor(cv2img, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower_range, upper_range)
    max_area = np.sum(mask)
    res = cv2.bitwise_and(cv2img, cv2img, mask=mask)
    cv2.imwrite(true_case_dir[:-4]+"-result.png", res)
    cv2.imwrite(true_case_dir[:-4]+"-mask.png", mask)
    print("good case area is %d" % max_area)
    # bad case
    print("========bad case=======")
    cv2img = cv2.imread(false_case_dir)
    hsv = cv2.cvtColor(cv2img, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower_range, upper_range)
    min_area = np.sum(mask)
    res = cv2.bitwise_and(cv2img, cv2img, mask=mask)
    cv2.imwrite(false_case_dir[:-4]+"-result.png", res)
    cv2.imwrite(false_case_dir[:-4]+"-mask.png", mask)
    print("bad case area is %d" % min_area)
    # compute area limit
    limit_area = min_area + int((max_area-min_area)/10)
    return lower_range, upper_range, limit_area

# 封装成一个主方法
if __name__=="__main__":
    # 获取目标颜色范围及目标区域大小
    lower_range, upper_range, limit_area = case_test("file/2-true.png", "file/2-false.png", "file/2-target_color.png")
    # 程序休眠5s此时切换到游戏视图
    time.sleep(5)
    # 开始循环处理
    time_limit = 20
    flag = True
    while flag:
        # 截取屏幕图像
        img = ImageGrab.grab()
        hsv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2HSV)
        mask = cv2.inRange(hsv, lower_range, upper_range)
        area = np.sum(mask)
        # print(area)
        if area > limit_area:
            pg.press('space')
            print('space')
            time_limit -= 1
            if time_limit%5 == 0:
                print("该关卡结束, 下一个!")
                time.sleep(5)
            else:
                print("第%d位密码已解锁" % (5 - (time_limit)%5))
                time.sleep(2)
            if time_limit == 0:
                flag = False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值