关于Template matching错误:可能是更新后文字清晰度改变,以及某些template matching的threshold设置过高
在提问之前...
- [x] 我已经搜索了现有的 issues
- [x] 我在提问题之前至少花费了 5 分钟来思考和准备
- [x] 我已经阅读了文档中的 常见问题(FAQ)
- [x] 这个问题出现了至少三次,不是偶发的
- [ ] 我使用 OAS 的 dev 分支
已解决
我描述的第一个问题是因为我没有注意到主题下方有多个轮换,只取消勾选了依始之卷下方的几个主题。如此一来偏移量的问题就解决了。
然而,解决阴阳寮按钮识别不到后,发现其他许多按钮也存在识别问题。所以依然烦请作者花一点时间阅读以下内容。
比如,作者设置的从探索进入结界突破界面时,识别的threshold为0.8: ··· I_EXPLORATION_GOTO_REALM_RAID = RuleImage(roi_front=(248,636,67,48), roi_back=(229,612,102,87), threshold=0.8, method="Template matching", file="./tasks/GameUi/page/page_exploration_goto_realm_raid.png") ··· 然而我本地使用以下描述的方法debug后,发现matchTemplate返回的最大匹配度为matchTemplate为0.7539175152778625,被认为按钮不存在,从而导致界面切换失败。
再比如,我在式神录界面识别“预设按钮”失败。以下是mat和source:
尽管两者看上去确实非常相似,但事实上由于字体的细微差别,导致像素发生了非常细微的偏移,最终我得到的匹配度仅有0.12435638904571533,远小于作者预设的0.8
这里顺便提供一下新的预设按钮。如果字体更新不是我本地的问题,则应该将tasks\Component\SwitchSoul\ss下的ss_soul_preset.png替换成
类似的,\tasks\GameUi\page中的page_exploration_goto_area_boss threshold设置过高。我调成0.3才能通过匹配
描述你的问题
首先感谢作者制作出这样的脚本!我之前并未知道OAS的存在,自己写了一个双号自动魂十-突破的脚本。今天偶然发现了这一工具,才知道已有前人栽树。
我尝试在本地运行了这一工具,发现在我本地大量的Page switch(如page_main -> page_guild)无法完成。
之前我误以为是由于截图的rb channels错误交换,导致opencv的pattern match没有成功。但是我仔细debug后,发现应该是作者预设的偏移量和我本地不符合,导致template matching失败。以下我是debug的过程: 在tasks\GameUi\page下,找到page_main_goto_guild.png,得知其size为54乘50 在match实际发生的image.py中加入breakpoint, 记录下mat为50乘54时,我们使用的source和mat文件:
def match(self, image: np.array, threshold: float = None) -> bool:
"""
:param threshold:
:param image:
:return:
"""
if threshold is None:
threshold = self.threshold
if not self.is_template_match:
return self.sift_match(image)
# raise Exception(f"unknown method {self.method}")
source = self.corp(image)
mat = self.image
if mat is None or mat.shape[0] == 0 or mat.shape[1] == 0:
logger.error(f"Template image is invalid: {mat.shape}") #检测模板尺寸,不合法则不进行匹配,避免两次截图画面完全相同造成模板不合法
return True # 如果模板图像无效,直接返回 True
res = cv2.matchTemplate(source, mat, cv2.TM_CCOEFF_NORMED)
if mat.shape[0] == 54 and mat.shape[1] == 50:
cv2.namedWindow('imgSrceen0') # 命名窗口
cv2.imshow("imgSrceen0", image) # 显示
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.namedWindow('imgSrceen') # 命名窗口
cv2.imshow("imgSrceen", source) # 显示
logger.info(os.getcwd())
cv2.imwrite("test_source.png", source)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.namedWindow('imgSrceen2') # 命名窗口
cv2.imshow("imgSrceen2", mat) # 显示
cv2.imwrite("test_mat.png", mat)
cv2.waitKey(0)
cv2.destroyAllWindows()
exit()
我的test_source.png显示为:
而test_mat.png(即为作者预设的page_main_goto_guild.png r/b转化的版本)为:
可以得见,由截屏image使用crop后source = self.corp(image)得到的source,偏移量和预设的pattern不符,因此导致template matching失败,我们无法从庭院进入阴阳寮。 crop中的参数(预期upper left point所在位置540 611)是作者hardcode的:
··· I_MAIN_GOTO_GUILD = RuleImage(roi_front=(540,611,50,54), roi_back=(540,611,50,54), threshold=0.7, method="Template matching", file="./tasks/GameUi/page/page_main_goto_guild.png") ··· 既然其他人都未在此处遇到问题,猜想应该是我的某些本地设置有问题,导致庭院下方按钮整体右移,从而使crop得到不符合预期的结果?但我模拟器分辨率已经设置为1280*720,庭院皮肤也调成默认的了。我想不出有什么其他原因,导致我的菜单整体会偏右?
理论上与屏幕分辨率也无关。实际上我也测试过。我的本地分辨率为28801800. 尝试将其调整为符合1280720的16:9的分辨率,发现依然有偏移。
以下为我之前误以为的原因,请作者无视
我尝试在本地运行了这一工具,发现在我本地大量的Page switch(如page_main -> page_guild)无法完成。简单定位了问题所在后,我怀疑是进行screenshot后,将截屏错误的转换成了红蓝相反的版本(while the pattern images under tasks/GameUi/page use the correct r/b channels) 才导致template matching失败。(因为我之前写脚本时也遇到了相似的问题)
检查一下,GUILD的图片属于RuleImage类,使用的match函数是https://github.com/runhey/OnmyojiAutoScript/blob/master/module/atom/image.py#L137. 我使用的截屏工具是windows_background, 定位到https://github.com/runhey/OnmyojiAutoScript/blob/master/module/device/method/window.py#L64. 最终发现这一行可疑的代码:
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_BGR2RGB)
我前天也曾经在这个转换上卡了半天,导致pattern无法识别,印象非常深刻,所以大概知道问题。我本地的dev环境下,如果使用以上code截图后,会发现红蓝反转。而如果使用 cv2.COLOR_BGRA2BGR, 则红蓝显示是正确的。
因此我想问一下作者,是否在某些测试环境中遇到过相同的问题?即cv2.COLOR_BGR2RGB不符合预期?
作为比较,以下是我之前写的一个脚本中截屏和match的部分:
def match_pattern_and_return_points(feature_img_name, threshold=0.5, show=False):
# Correctly handle Chinese file paths
file_path = f"pattern/{feature_img_name}.png"
# Read file in binary mode and decode
with open(file_path, "rb") as f:
image_bytes = f.read()
image_array = np.frombuffer(image_bytes, dtype=np.uint8)
# Decode the image using OpenCV
feature_img = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
# Check if the image loaded successfully
if feature_img is None:
raise ValueError("Feature image not found! Check the file path.")
# Capture the screen using mss
with mss.mss() as sct:
screenshot = sct.grab(sct.monitors[1]) # Capture full screen
screen = np.array(screenshot) # Convert to NumPy array
screen = cv2.cvtColor(screen, cv2.COLOR_BGRA2BGR)
# Perform template matching
result = cv2.matchTemplate(screen, feature_img, cv2.TM_CCOEFF_NORMED)
此处使用cv2.COLOR_BGRA2BGR才能得到正确结果。有趣的是,chatgpt给我的初版代码,使用的是和作者完全相同的 cv2.COLOR_BGR2RGB, 然而在我本地却是错误的。
(请教了一下chatgpt, 它认为应该与opencv的版本无关。不过gpt认为这一mss的版本变动可能会导致r/b channels swapped. 然而,作者您在window.py中使用的截屏函数是win32gui自带的)
以下为chatgpt对话
我有以下code:
def screenshot_window_background(self):
"""
后台截屏
:return:
"""
widthScreen, heightScreen = self.screenshot_size
# 返回句柄窗口的设备环境,覆盖整个窗口,包括非客户区,标题栏,菜单,边框
hwndDc = GetWindowDC(self.screenshot_handle_num)
# 创建设备描述表
mfcDc = CreateDCFromHandle(hwndDc)
# 创建内存设备描述表
saveDc = mfcDc.CreateCompatibleDC()
# 创建位图对象准备保存图片
saveBitMap = CreateBitmap()
# 为bitmap开辟存储空间
saveBitMap.CreateCompatibleBitmap(mfcDc, widthScreen, heightScreen)
# 将截图保存到saveBitMap中
saveDc.SelectObject(saveBitMap)
# 保存bitmap到内存设备描述表
saveDc.BitBlt((0, 0), (widthScreen, heightScreen), mfcDc, (0, 0), SRCCOPY)
# 保存图像
signedIntsArray = saveBitMap.GetBitmapBits(True)
imgSrceen = frombuffer(signedIntsArray, dtype='uint8')
imgSrceen.shape = (heightScreen, widthScreen, 4)
# 这点很重要 在alas中图片以np.ndarray(RGB)的顺序存储。但是opencv是以BGR
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_BGR2RGB)
# # 测试显示截图图片
cv2.namedWindow('imgSrceen') # 命名窗口
cv2.imshow("imgSrceen", imgSrceen) # 显示
cv2.waitKey(0)
cv2.destroyAllWindows()
原有的code显示出的图片蓝红相反。我发现在使用cv2.COLOR_BGRA2BGR后,图片颜色就正确了。请问这是否跟opencv的版本有关?
✅ Why cv2.COLOR_BGRA2BGR Fixes the Color Issue in Your Code?
Yes, the issue you encountered is not directly related to the OpenCV version, but rather how Windows GDI (Graphics Device Interface) handles color formats when capturing screenshots.
🔹 Why Is Red and Blue Swapped Initially?
1️⃣ GDI Captures Screenshots in BGRA Format
Your screenshot is obtained using:
signedIntsArray = saveBitMap.GetBitmapBits(True)
imgSrceen = frombuffer(signedIntsArray, dtype='uint8')
imgSrceen.shape = (heightScreen, widthScreen, 4)
- Windows GDI captures images in BGRA format (Blue, Green, Red, Alpha).
- The array structure you get from
GetBitmapBits(True)is likely in BGRA order. - However, OpenCV expects BGR or RGB, causing colors to appear swapped when displaying with
cv2.imshow().
🔹 How cv2.COLOR_BGRA2BGR Fixes It
When you apply:
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_BGRA2BGR)
- It removes the alpha channel.
- It keeps BGR ordering, ensuring OpenCV displays the image correctly.
Alternatively, if you prefer RGB format:
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_BGRA2RGB)
This will ensure correct color representation in libraries like PIL, which expects RGB.
🔹 Does This Depend on OpenCV Version?
No, this issue is not related to OpenCV version but instead depends on:
- How GDI stores pixel data (BGRA format).
- How OpenCV expects the data (BGR or RGB format).
✅ The same behavior will occur in all OpenCV versions unless Windows changes its screenshot color format.
✅ Final Fix for Correct Color Representation
Replace this line:
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_BGR2RGB)
With:
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_BGRA2BGR) # Removes alpha & corrects colors
🚀 Now your screenshot colors will always be correct in OpenCV! 🎯🔥
如何复现
既然这一code在大部分人的环境下能跑通,怀疑是我本地某些环境/package version有异,导致rgb/bgr反转。
预期行为
No response
相关 Logs
截图
使用作者原先配置的RGB2BGR,将module/device/method/window.py下的代码uncomment, 显示出的图像是红蓝反转的。
imgSrceen = cv2.cvtColor(imgSrceen, cv2.COLOR_RGB2BGR)
# imgSrceen = cv2.resize(imgSrceen, (win_size[0], win_size[1]))
# 很奇怪的
# # 测试显示截图图片
cv2.namedWindow('imgSrceen') # 命名窗口
cv2.imshow("imgSrceen", imgSrceen) # 显示
cv2.waitKey(0)
cv2.destroyAllWindows()
如若改成BGRA2BGR, 则红蓝正确。
还有别的吗?
No response
- 换截屏方式,一个一个试试
- 看游戏设置https://runhey.github.io/OnmyojiAutoScript-website/docs/user-manual/installation#2%E4%BF%AE%E6%94%B9%E6%B8%B8%E6%88%8F%E8%AE%BE%E7%BD%AE
3.模拟器设置
4. 换模拟器
最近我也遇到了这个问题, 应该是字体不一致引起的,其他大部分图片识别都是可以用的。但是几乎没有人反应这个问题,我想知道是不是 模拟器中的 阴阳师 没有更新到最新的版本。
最好 新增一个将字体类型 图片匹配 全部切换为 OCR 文字识别的任务, 降低因字体更新而引发问题的可能性
最近我也遇到了这个问题, 应该是字体不一致引起的,其他大部分图片识别都是可以用的。但是几乎没有人反应这个问题,我想知道是不是 模拟器中的 阴阳师 没有更新到最新的版本。 最好 新增一个将字体类型 图片匹配 全部切换为 OCR 文字识别的任务, 降低因字体更新而引发问题的可能性
我也有这个问题,刚更新游戏客户端就卡探索界面,识别不了,我看是字体问题
https://github.com/runhey/OnmyojiAutoScript/tree/dev_new_font
先开了一个分支来修这个问题
1 mumu模拟器的VULKAN模式和directx模式可能会导致截图位置有变化 尽量选择Vulkan模式 2 颜色问题是因为 CV2 默认的彩色图像的颜色为BGR格式 但是保存的图像PNG的默认显示的是RGB模式 3 建议修改模板匹配类为抽象类 提供基础的模板匹配适用于所有task 我们可以采用不同是类实现对不同task的特化