4.1. Detection objects - LmeSzinc/AzurLaneAutoScript GitHub Wiki
这里介绍 Alas 中用于识别的类。
在 Alas 中,用于识别的图片称为 Assets,经过 button_extract 提取后,得到 Button 对象。
判断在图片上是否出现当前 button,使用平均颜色识别。
判断在图片上是否出现当前 button,使用模板匹配识别。offset 表示搜索的范围,为整数时上下搜索,为 tuple 时在四周搜索。匹配成功后会设置 _button_offset
,表示识别的区域相对原始区域的偏移,将影响未来的点击区域。
首次调用时,会重新读取 assets 文件并缓存。
产生一个在点击区域内的随机点。原始的点击区域加上 _button_offset
得到当前的点击范围。碧蓝航线曾经有过 确认
和 取消
按钮的上下抖动,_button_offset
可以自动处理这个问题。
重新从截图中载入 button 的颜色和图像。只在伏击空袭的识别中使用。
Button 对象按模块保存于 ./asset 目录下,按钮定义于每个模块的 asset.py 文件中。比如 BATTLE_PREPARATION 的 Assets 图片是这样的:
在 assets.py 中,它是这样的:
BATTLE_PREPARATION = Button(area=(1043, 607, 1241, 667), color=(234, 179, 97), button=(1043, 607, 1241, 667), file='./assets/combat/BATTLE_PREPARATION.png')
area 是识别的区域,color 是平均颜色,button 是出现后的点击区域,file 是 assets 文件的位置。
注意,所有的 asset.py 都是由 button_extract 生成的,不要手动去修改它。
假设我们希望添加一个 确定
按钮,它出现于潜艇信号扫描时。
-
截图,使用模拟器自带的截图工具截图,并保证分辨率是 1280x720
-
将图片复制到 ./asset 下相应的目录中,更改文件名,比如
SEARCH_CONFIRM.png
-
拖动至 Photoshop 中打开
下面以 Photoshop CS6 为例。当然,你也可以使用别的软件来进行下面的操作,能把剩下的区域涂黑就行。使用 PS 只是因为它有录制动作的功能,会更快一些。
-
使用选区工具框选按钮区域
-
播放动作
直接点击 播放动作
的按钮,自动执行图片处理的操作。处理完的图片是这样的:
在第一次操作时,需要按照以下步骤添加动作。
在菜单栏的 窗口
中,点击 动作
,弹出动作窗口。
在添加动作之前,最好备份当前图片,因为接下来需要记录的操作是不可逆的。
- 在动作窗口中,点击新建动作的图标,按照自己的喜好命名,比如
button_image
。点击记录
,注意灰色的圆圈变红了,这表示动作录制开始了。 - 在图片区域单击鼠标右键,点击
选择反向
。 - 在菜单栏的
编辑
中,点击填充
。 - 在弹出的填充选项窗口中,填充内容使用
黑色
,填充模式选择正常
,不透明度选择100
,点击确定
。 - 在菜单栏的
文件
中,点击保存
。 - 在菜单栏的
文件
中,点击关闭。
- 在动作窗口中,单击停止录制的图标,此时动作录制停止。
录制完成后,会得到动作如下,以后就可以直接使用,不需要手动操作了。
-
(可选) 添加属性覆盖图片
一个按钮具有三个属性:
- area,按钮识别的区域。
- color,按钮的颜色。
- button,按钮出现后的点击区域。
假如添在同一目录下放置图片文件
SEARCH_CONFIRM.BUTTON.png
,并按照刚才描述的方法处理图片。那么这张图片的button
属性将覆盖SEARCH_CONFIRM.png
的button
属性。这是一个非常有用的特性,因为脚本通常需要判断截图中出现的元素,然后点击按钮,需要判断的地方和需要点击的地方可能不出于同一位置。
-
运行 button_extract
python -m dev_tools.button_extract
生成 Button 的二维阵列。
origin
是最左上角 button 的坐标,delta
是每个 button 移动的距离,button_shape
是每个 button 的大小,grid_shape
是网格的大小。
将网格展平为 list。
获取某个位置的 Button。
模板图片。Template 需要以 TEMPLATE_
开头,不需要像处理 Button 一样处理,直接裁切即可,但同样需要运行 button_extract。首次调用时,会重新读取 assets 文件并缓存。
Template 可以是 GIF 图片,GIF 中的每一帧都会用于匹配。
模板匹配。
模板匹配。返回相似度和最相似点。
多点模板匹配,自动合并相邻的点。返回一个 list 的 Button 对象。
首先,我们不能直接裁切截图来制作模板图片,因为地图中的物体是有透视的,而模板匹配是对图片的缩放敏感的。我们需要使用 dev_tools/relative_crop.py
来获取图片。其中的 get_relative_image
可以根据透视裁剪出相对位置的图片,并放大到固定的大小。
下图展示了 self.get_relative_image((-1, -1, 1, 0), output_shape=(120, 60))
的裁切区域。
-
编辑 ./dev_tools/relative_crop.py,粘贴地图文件中的设置
class Config: """ Paste the config of map file here """ pass
-
修改保存目录和截图文件路径
# Folder to save temp images folder = './screenshots/temp/' # Put Screenshot here file = './screenshots/TEMPLATE_AMBUSH_EVADE_FAILED.png'
-
运行 relative_crop
python -m dev_tools.relative_crop
-
在保存目录里找到对应格子的图片,在图片中裁切出需要的模板。将模板图片放置于
assets/<server>/template
目录下,文件名需以TEMPLATE_
开头。 -
运行 button_extract
在活动永夜幻光(event_20200723_cn)中,游戏里的精英敌人身上会覆盖一层动态的黑色烟雾,影响常规模板匹配,因此添加了对 GIF 模板的支持。原理是先大量截图并裁切,再去重,将剩下的图片储存为 GIF,用 GIF 中的帧当作多个模板去匹配一个敌人。即便没有黑雾,使用多模板匹配,也能提高识别正确率。
-
进入关卡,找到精英敌人 可能需要反复进入撤退来刷出特定的精英。
-
编辑 ./dev_tools/relative_record.py,粘贴地图文件中的设置
class Config: """ Paste the config of map file here """ pass
-
修改设置
NODE 是当前视角下的格子,不是在整个地图中的格子。
Arguments: CONFIG: ini config file to load. FOLDER: Folder to save. NAME: Siren name, images will save in <FOLDER>/<NAME> NODE: Node in local map view, that you are going to crop.
-
运行 ./dev_tools/relative_record.py
这会执行 300 次截图,然后使用
get_relative_image
函数裁切出特定格子中的塞壬图像。得到第一张截图时,会弹出裁切的预览,需要人工确认是否裁切到了正确的格子。如果裁到别的格子上,要停止运行,修改 NODE,再重新运行。 -
运行 ./dev_tools/relative_record_gif.py
暴力搜索帧数最少的 gif 模板,生成在
{FOLDER}/{NAME}_gif
。然后手动挑选帧数较少且能反应主要特征的 gif。如果有符合要求的,跳转至第 9 步,否则继续第 6 步,改用人工优化。 -
观察精英敌人
用 PhotoShop 打开
<FOLDER>/<NAME>
文件夹下的第一张图片,在游戏中观察精英敌人的上下呼吸运动。通常小人的身体是上下运动的,四肢是旋转的。模板匹配对缩放和旋转的识别很差,所以需要找到小人身上的不旋转的区域来裁切出模板,这个区域称为 AREA。AREA 选择时的一些经验:- 不要选择小人的面部(容易误判)或眼睛(小人会眨眼睛)。
- 不要包含小人身后的海面(海面颜色会变化)。
- AREA 的长宽都应大于 15 像素(减少误判)。
- 可以接受部分图像是旋转的。
例如在塞壬航母的中,选择了身体作为模板。(左:300 张截图中的前 30 张,右:提取的 GIF 模板)
-
编辑 ./dev_tools/relative_record_gif.py,修改设置
Arguments: FOLDER: Save folder from relative_record. NAME: Siren name from relative_record. Save gif file to <FOLDER>/TEMPLATE_SIREN_<NAME>.gif AREA: Area to crop, such as (32, 32, 54, 52). Choose an area where things don't rotate too much. THRESHOLD: If the similarity between a template and existing templates greater than THRESHOLD, this template will be dropped. Threshold in real detection is 0.85, for higher accuracy, threshold here should higher than 0.85.
FOLDER 和 NAME 从 relative_crop 中导入,一般不需要修改。AREA 是刚才观察得到的裁切区域。THRESHOLD 是去重时的相似度,实际匹配的相似度是 0.85,去重时的相似度应该高一些,以提高识别率。
-
运行 ./dev_tools/relative_record_gif.py 运行时会打印哪一张截图产生了新的模板,在 300 张截图中得到的模板数量应小于 5。产生的模板数量越少越好,过多的模板会拖慢识别速度,也意味着裁切区域不对。此时需要重复第 5 步至第 7 步,人工优化 AREA。
-
使用新的模板 将新的 GIF 模板复制到
./assets/<server>/template
文件夹中,然后运行 button_extract。在地图文件的设置中开启塞壬识别,并设置模板名称。MAP_HAS_SIREN = True MAP_SIREN_TEMPLATE = ['U101', 'U73', 'U552']
U101
是第 3 步中的 NAME,表示使用TEMPLATE_U101.gif
或TEMPLATE_U101.png
。它是大小写敏感的,如果在运行时找不到模板文件,会提示Enemy detection template not found: {name}
。
判断 button 是否出现在画面中
-
offset
button 的偏移量。假设
button.area=(100, 200, 300, 400)
,offset=(30, 20)
,那么 Alas 将在(100-30, 200-20, 300+30, 400+30)
的区域内搜索按钮。offset=None 时,使用平均颜色识别(
Button.appear_on()
)设置 offset 后,使用模板匹配识别(
Template.match()
) -
interval
按钮出现间隔。按钮出现后的若干秒内,对这个按钮的识别返回 False。一般设置 2 或 3 秒,能避免连击。
判断 button 是否出现在画面中,出现了就点击。
appear_then_click()
的本质是:
if self.appear(button):
self.device.click(button)
它们的连续调用会带来一个非常方便的特性:在设置了 offset 的情况下,即便游戏内的按钮相对 asset 图片有所移动,Alas 也能点击被移动后的按钮。这会降低 assets 维护成本,避免把时间浪费在应对游戏 UI 的微调上。它的原理是,上面已经提到过的 Button.match()
在匹配成功时会设置 _button_offset
,而 self.device.click()
也会使用 _button_offset
。
判断颜色相似的像素的个数。有时候土办法会意外地好用。
wait_until_appear()
wait_until_appear_then_click()
wait_until_disappear()
wait_until_stable()
已经较少使用。
注意:不要把 wait_until_stable()
当作 sleep()
使用,建议优化逻辑以减少对等待的依赖。