|
最近儿子迷上了军棋,军棋的乐趣在于对阵过程通过死亡的棋子获取信息,随时调整自己的策略和布局。而想体验原汁原味,必然需要第三个人来当裁判。可裁判融入不了游戏,只能陪衬,获取不了游戏乐趣,基本上没人喜欢干这个,那怎么办呢?
到网上一搜,有个电子裁判的军棋,把棋子放在判定区,自然能分出胜负,也不会暴露双方的棋。东西倒是不错,解决了裁判问题,可一看价格,200多。而且棋盘固定不好收拾,棋子都是带芯的,不能替换。另外,小朋友的热度也就三分钟,估计过了几天又不玩了,这玩意买回来大概率沾灰。

商品截图
那有什么其他办法么?我学过一点python,玩过一些AI识别的应用,能不能用程序搞个裁判出来呢?那就试试看,说干就干。
首先要总结一下军棋裁判要做的事情,简单来说就三个步骤:输入、判断、输出。
输入:通过某种方法,让程序获取双方的棋子。
判断:判断双方棋子大小。
输出:将判断结果展示出来。
那我们先进行第一步:该如何输入信息,获取到双方的棋子呢?答案是通过OCR的文本识别,找出图片上的文字。我用的PaddleOCR,是一个基于百度飞桨的OCR工具库。具体介绍如下:
PaddleOCR目前有两个分支,一个是PP-Structure,识别图表样式,识别图标中键与值的关系,并对表格布局进行恢复;另个是PP-OCR,各种复杂场景图片中的文字识别,用PP-OCR就行了。快速安装开始请参考:PaddleOCR/quickstart.md at release/2.6 · PaddlePaddle/PaddleOCR (github.com)
先照些军棋的图片作为输入

改下官方代码,测试下效果
from paddleocr import PaddleOCR, draw_ocr
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
img_path = './1.jpg'
result = ocr.ocr(img_path, cls=True)
result = result[0]
print(result)
# 打印的结果
# [[[[97.0, 48.0], [124.0, 23.0], [164.0, 72.0], [137.0, 97.0]], ('团长', 0.9352545738220215)], [[[36.0, 144.0], [96.0, 161.0], [86.0, 200.0], [26.0, 183.0]], ('连长', 0.9903252124786377)]]这样不是很直观,把官方的展示代码加进去。
from PIL import Image
image = Image.open(img_path).convert('RGB')
boxes = [line[0] for line in result]
txts = [line[1][0] for line in result]
scores = [line[1][1] for line in result]
im_show = draw_ocr(image, boxes, txts, scores, font_path='./fonts/simfang.ttf')
im_show = Image.fromarray(im_show)
im_show.save('result.jpg')

通过OCR识别出的文字和置信度
PP-OCR的识别效果还是很不错的,置信度都在90%以上,推理结果的框点坐标也有,我们可以用推理结果在输出上做很多事情。
获取双方棋子的这个步骤,感觉好像完成了,但还远远不够。原因是输入的方式不对,这照片怎么给模型啊?还有谁来拍照的问题?可以让电子裁判自己“看”棋。利用电脑的摄像头,实时获取信息,对战双方只需要手持各自的棋子,对着摄像头即可。
import cv2
cap = cv2.VideoCapture(0) # 设置0是默认的摄像头
while True:
ret, frame = cap.read() # 实时获取摄像头的图像帧
cv2.imshow('frame', frame) # 实时显示图像帧
c = cv2.waitKey(1)
if c == ord('q'):
break
cap.release() 这个摄像头的代码,就解决了我们输入和输出的问题。摄像头实时捕获图像,当获取到含有两个军棋棋子的图像时,进行逻辑判断后,将结果反馈在图像中,在通过cv2.imshow方法实时展示出来。试试看!

军棋OCR
https://www.zhihu.com/video/1577668080129511424
获取双方的棋子后,就可以进行逻辑判断了。
class JunQi:
level = ['军旗', '工兵', '排长', '连长', '营长', '团长', '旅长', '师长', '军长', '司令']
other = ['地雷', '炸弹']
WIN = 1
LOSE = 2
SAME = 3
def check(self, grade):
if grade in self.level + self.other:
return True
return False
def battle(self, left, right):
if left == '炸弹' or right == '炸弹': # 炸弹逻辑,遇到任何都平
return self.SAME
# 地雷逻辑
if left == '地雷':
if right != '工兵':
return self.SAME
else:
return self.LOSE
if right == '地雷':
if left != '工兵':
return self.SAME
else:
return self.WIN
# level列表,由小到大
l_index = self.level.index(left)
r_index = self.level.index(right)
if l_index > r_index:
return self.WIN
if l_index == r_index:
return self.SAME
if l_index < r_index:
return self.LOSE试试输出的效果呢,最简单的方式就是在赢的棋子打个勾勾,输的棋子上打个xx,如果打平就画个=。

https://www.zhihu.com/video/1577670076324618240
可是这样双方可以还是可以看到对方的棋子,很好解决,把图像搞成黑屏或者模糊屏,然后打上结果标签就行了。另外,笔记本电脑的影像是拍摄视角,看到影像是正常视角,你看到的图像与你实际图像的方向是相反的。当黑屏时,左边的棋子实际在图像右边,右边棋子实际在左边,这样不是很合理。只需通过cv2的一个镜像命令,就可以完美解决。最后的效果如下:

https://www.zhihu.com/video/1577671992827990016
大功告成,可以愉快地和小朋友军棋大战了。
ocr.py
4.4K
· 百度网盘 |
|