Skip to main content

数字识别

一梦江湖案例

原文链接

image-20220315145808562

然后将数字都提取出来做成模板:

import cv2

# 读取图片并转为灰度图
gray = cv2.imread('0123456789.png', cv2.IMREAD_GRAYSCALE)
# 二值化,cv2.THRESH_OTSU指定由函数判断阈值
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
# 寻找轮廓
contours = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
# 将符合要求的轮廓收集并从左到右排序
result = []
for cnt in contours:
[x,y,w,h] = cv2.boundingRect(cnt)
if 22 > h > 18:
result.append([x,y,w,h])
result.sort(key=lambda x:x[0])
# 按轮廓截取数字并缩放到14×20大小,导出
count = 0
for x, y, w, h in result:
# 在灰度图中画出轮廓
cv2.rectangle(gray, (x,y),(x+w,y+h),(0,0,255),1)
res = cv2.resize(thresh[y:y+h, x:x+w], (14, 20))
cv2.imwrite(f'nums/{count}.png', res)
count += 1
# 保存轮廓图
cv2.imwrite("draw_contours.png", gray)

image-20220315145843538

需要注意一点,在寻找轮廓时,要利用轮廓的高度将0、6、8、9的内部轮廓过滤掉。这样模板就制作好了。

数字识别测试

为了方便测试,我们可以对游戏画面实时截图,然后在窗口中显示出来,就可以得到类似监控一样的效果,并可以实时的显示识别结果,对游戏画面的处理和提取模板时一致,先转灰度图,再二值化,接着提取符合要求的轮廓,然后按照轮廓提取子图,和数字模板0-9比对,找到最相似的模板就可以得到对应的数字了:

from ctypes import windll, byref, c_ubyte
from ctypes.wintypes import RECT, HWND
import numpy as np
import time

# 窗口操作请看https://zhuanlan.zhihu.com/p/363599118
from window import resize_client
# 截图操作请看https://zhuanlan.zhihu.com/p/361569101
from capture import capture

if __name__ == "__main__":
import cv2
handle = windll.user32.FindWindowW(None, "一梦江湖")
resize_client(handle, 1334, 750)
# 加载数字模板
temps = []
for i in range(10):
temps.append(cv2.imread(f'nums/{i}.png', cv2.IMREAD_GRAYSCALE))

# 按下任意键退出识别
while cv2.waitKey(delay=100) == -1:
im = capture(handle)
im = im[157:655, 355:1148]
# 提取指定画面中的数字轮廓
gray = cv2.cvtColor(im, cv2.COLOR_BGRA2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)
contours = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
result = []
for cnt in contours:
[x,y,w,h] = cv2.boundingRect(cnt)
# 按照高度筛选
if 22 > h > 18:
result.append([x,y,w,h])

result.sort(key=lambda x:x[0])

for x, y, w, h in result:
# 在画面中标记识别的结果
cv2.rectangle(im, (x,y),(x+w,y+h),(0,0,255),1)
digit = cv2.resize(thresh[y:y+h, x:x+w], (14, 20))
res = []
for i, t in enumerate(temps):
score = cv2.matchTemplate(digit, t, cv2.TM_CCORR_NORMED)
res.append((i, score[0]))
res.sort(key=lambda x:x[1])
cv2.putText(im, str(f"{res[-1][0]}"), (x, y+35), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0))
cv2.imshow('Digits OCR Test', im)

来看下匹配的效果:

GIF 2022-3-15 15-00-07

可以看到,这个方法在识别商品的价格上,不管是速度还是成功率都有较好的表现。实际应用中可以缩小识别轮廓的范围,减少误差。

以上就是本篇的所有内容,到此基本将所有的关键技术点都实现了,这个系列文章也将暂告一段落了,如果有其他想要交流的技术点,可以私信或者评论告诉我~