Table of Contents
- 4.1. Detection objects
- Button
- Button.appear_on(self, image, threshold=10)
- Button.match(self, image, offset=30, threshold=0.85)
- Button.button
- Button.load_color(self, image)
- 添加一个 Button
- ButtonGrid
- ButtonGrid.buttons(self)
- Button.getitem(self, item)
- Template
- Template.match(self, image, similarity=0.85)
- Template.match_result(self, image)
- Template.match_multi(self, image, similarity=0.85)
- 添加用于敌人识别的模板图片
- 添加识别动态敌人的 GIF 模板图片
- 游戏内容识别
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
4.1. Detection objects
这里介绍 Alas 中用于识别的类。
Button
在 Alas 中,用于识别的图片称为 Assets,经过 button_extract 提取后,得到 Button 对象。
Button.appear_on(self, image, threshold=10)
判断在图片上是否出现当前 button,使用平均颜色识别。
Button.match(self, image, offset=30, threshold=0.85)
判断在图片上是否出现当前 button,使用模板匹配识别。offset 表示搜索的范围,为整数时上下搜索,为 tuple 时在四周搜索。匹配成功后会设置 _button_offset
,表示识别的区域相对原始区域的偏移,将影响未来的点击区域。
首次调用时,会重新读取 assets 文件并缓存。
Button.button
产生一个在点击区域内的随机点。原始的点击区域加上 _button_offset
得到当前的点击范围。碧蓝航线曾经有过 确认
和 取消
按钮的上下抖动,_button_offset
可以自动处理这个问题。
Button.load_color(self, image)
重新从截图中载入 button 的颜色和图像。只在伏击空袭的识别中使用。
添加一个 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
ButtonGrid
生成 Button 的二维阵列。
origin
是最左上角 button 的坐标,delta
是每个 button 移动的距离,button_shape
是每个 button 的大小,grid_shape
是网格的大小。
ButtonGrid.buttons(self)
将网格展平为 list。
Button.getitem(self, item)
获取某个位置的 Button。
Template
模板图片。Template 需要以 TEMPLATE_
开头,不需要像处理 Button 一样处理,直接裁切即可,但同样需要运行 button_extract。首次调用时,会重新读取 assets 文件并缓存。
Template 可以是 GIF 图片,GIF 中的每一帧都会用于匹配。
Template.match(self, image, similarity=0.85)
模板匹配。
Template.match_result(self, image)
模板匹配。返回相似度和最相似点。
Template.match_multi(self, image, similarity=0.85)
多点模板匹配,自动合并相邻的点。返回一个 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
添加识别动态敌人的 GIF 模板图片
在活动永夜幻光(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}
。
游戏内容识别
self.appear(self, button, offset=0, interval=0, threshold=None)
判断 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 秒,能避免连击。
self.appear_then_click()
判断 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
。
image_color_count(self, button, color, threshold=221, count=50)
判断颜色相似的像素的个数。有时候土办法会意外地好用。
其他方法
wait_until_appear()
wait_until_appear_then_click()
wait_until_disappear()
wait_until_stable()
已经较少使用。
注意:不要把 wait_until_stable()
当作 sleep()
使用,建议优化逻辑以减少对等待的依赖。
Getting Started
- Installation [EN]
- Installation [CN]
- Installation With Docker [EN]
- Emulator Support [CN]
- FAQ [EN/CN]
- FAQ [JP]
- Troubleshooting [EN]
- Another Installation guide
- Research Filter String [EN]
- Research Filter String [CN]
- Reward Shop Filter String [EN/CN]
- Onepush Configuration [EN]
- Onepush Configuration [CN]
Development
- Perspective [CN]
- Perspective [EN]
- Debug perspective [CN]
- Debug perspective [EN]
- Item Statistics [EN]
- 1. Start
- 2.1. Debugging
- 2.2. Multi-server support
- 3.1. Utils
- 3.2. Decorators
- 3.3. Log
- 3.4. Exception
- 4.1. Detection objects
- 4.2. UI control
- 4.3. OCR
- 4.4. State loop
- 5.1. Local Map
- 5.2. Create globe Map
- 5.3. Globe Map
- 6.1. GUI Option
MISC