博客
王佳欣博客链接:https://www.cnblogs.com/wjiaxin/p/13782491.html
邹薇博客链接:https://www.cnblogs.com/RoseZou/p/13796964.html
王佳欣Github链接(原型设计):https://github.com/wjx0110/designlast
邹薇Github链接 (保存AI部分以及游戏的代码):https://github.com/RoseZ18/ZW
具体分工如下 | |
---|---|
原型设计 | 王佳欣 |
游戏模块 | 邹薇 |
AI与原型设计的实现 | 邹薇 |
博客的编写 | 王佳欣 |
一. 原型设计
[1.1]结对作业的设计说明
原型设计链接:https://modao.cc/app/3462eef755c5933319fe98d3d29c4541b6f77793?simulator_type=device&sticky
我们的原型设计是以微信小程序为例制作而成的,用墨刀APP来实现原型设计,我们主要有登录界面,游戏界面,音乐模块界面,排行榜界面,更换图片界面,自动拼图界面等。起初我们只有开始界面和游戏界面,游戏功能较少,界面不美观。后来经过修改又增加了其他界面。我们的游戏主要有以下几个特点;(1)玩家在玩游戏过程中可以一边听音乐一遍玩,增添了拼图的乐趣,同时用户也可以开始游戏界面关闭音乐,用户可以根据自己的喜好来选择。(2)游戏开始时,我们会为玩家提供游戏的目标图案,便于玩家了解原图,拼图更便捷,若此图案不是玩家心仪的,可选择更换图片。(3)若玩家在拼图过程中遇到了困难,可以选择重玩游戏或是自动拼图,根据玩家的选择会跳至不同的界面继续游戏。(4)游戏过程中,会记录玩家的游戏用时和移动步数,在游戏结束后会根据二者来计算出玩家得分,并记录在排行榜上。(5)游戏结束之后,会根据游戏的结果加载出不同音乐,若玩家胜利,则会弹出'Congratulations'的庆祝字样。
[1.2]结对照片展示。
[1.3]遇到的困难及解决方法
困难1:在游戏音乐加载模块,最初这一模块只是设想,后来实践时发现,在游戏结束后,音乐加载出现延迟现象。后来经过反复检查代码,修改代码,发现我们加载在音乐模块的代码放错了位置。
困难2:最初更换图片模块一直闪,程序无响应,后来经过修改图片会出现更换两次的情况,在更换图片多次以后,会有图片更换不了,异常的情况。后来我们通过引用参数的方法,用参数来传递图片。
困难3:重玩游戏模块,在多次点击重玩游戏模块后,会出现bug。(这个改了很久,最后还是没有改出来......)
二.AI与原型设计实现
1.代码实现思路:
原型设计实现部分:
利用python中的pygame模块来实现图形界面,切割图像扣掉最后一块并在一定步骤内移动空白块使它与周围图块随机交换(保证最终图像有解),设置点击鼠标和键盘响应移动白块的事件,刷新游戏界面来实现移动图片,利用重新调用游戏这个部分的函数来实现重新开始游戏,此外我们还引入了音乐模块,在游戏过程中会有音乐伴随,游戏结束后能够输出胜利的提醒。玩家能在游戏开始界面设置音乐的开启或者关闭(静音或播放功能),增加了更换图片的模块,在游戏开始之前玩家可以根据自己的喜好来选择不同的图片进行拼图。
AI大比拼部分:
大体思路是先对请求得到的乱图进行图像识别并匹配原图,我们将请求到的图片进行切割,分成九份图片,并保存到一个文件夹(testtu)里,计算九份图片的熵,根据得到的熵选择识别度较高的一张小图对原图进行匹配(这样是为了避免选到的小图是全白或者全黑或者白色部分较少的导致辨识度不高而匹配错误),找到原图后,将原图进行切割并保存到一个文件夹(yuantu)里,然后用乱图得到的九张图一一和原图切割得到的九张图进行对比,并用列表记录下乱图中的每个部分在原图中的位置,匹配不到的则为白块用0来表示它,这个部分用函数来做然后返回两个列表,一个是待恢复的列表,一个是目标列表。复原图片这部分的核心算法是用双向BFS,利用求逆序数判断拼图是否有解,若无解则交换相邻位置的两个数再进行双向BFS,最后用列表保存操作序列。
[1.1]网络接口的使用
利用python自带的requests模块进行post请求题目和提交答案
请求题目的代码(放在request.py)
url="http://47.102.118.1:8089/api/challenge/start/e08f1739-e7fe-4139-97ea-6c99bb7ededd"
body = { "teamid": 41,
"token": "38f216cd-c9fb-47ac-8075-c37f650c6892"}
headers = {'content-type': "application/json"}
response = requests.request("post",url,headers=headers,json=body)
user_dic=response.json()
data = user_dic['data']
img=data['img']#获取图像编码
stepre=data['step']
uuid=user_dic['uuid']
swap = data['swap']#获取交换位置
image_data = base64.b64decode(img)#解码图片
提交答案的代码(放在AIsuanf.py的输出结果的部分)
headers = {'content-type': "application/json"}
anserurl = "http://47.102.118.1:8089/api/challenge/submit"
answer = {
"uuid": uuid,
"teamid": 41,
"token": "38f216cd-c9fb-47ac-8075-c37f650c6892",
"answer": {
"operations": arr,#复原算法结果
"swap": swap#由请求题目那边得来的
}
}
response = requests.request("post", anserurl, headers=headers, json=answer)
print(response.text)#输出接口返回的数据
[1.2]代码组织与内部实现设计(类图)
AI部分
请求图片在request.py里面
图像识别在tupip.py里面
算法复原和提交答案在AIsuanf.py里面
AI部分使用方法:从github下载AI这个文件夹然后在request.py和AIsuanf.py中修改一下token,最后运行AIsunaf.py就可以了
[1.3]说明算法的关键与关键实现部分流程图
我们算法的关键是图像的识别!!!
[1.4]贴出你认为重要的/有价值的代码片段,并解释
原型实现(小游戏)部分:
以下是打乱拼图的代码展示
在指定交换步骤内将白块随机与周围的图片进行交换确保生成的拼图
# 随机生成游戏盘面
def newgameboard():
board = []
for i in range(CELLNUMS):
board.append(i)#[0,1,2,3,4,5,6,7,8]
blackcell = CELLNUMS - 1
board[blackcell] = -1#扣掉最后一张图
for i in range(MAXRANDTIME):
direction = random.randint(0, 3)
if (direction == 0):
blackcell = moveleft(board, blackcell)
elif (direction == 1):
blackcell = moveright(board, blackcell)
elif (direction == 2):
blackcell = moveup(board, blackcell)
elif (direction == 3):
blackcell = movedown(board, blackcell)
return board, blackcell
AI部分:
图像识别部分代码展示
以下是进行小图匹配小图(已经匹配到原图并切割原图的情况下)的算法
# 计算直方图
def hist_similar(lh, rh):
assert len(lh) == len(rh)
hist = sum(1 - (0 if l == r else float(abs(l - r)) / max(l, r)) for l, r in zip(lh, rh)) / len(lh)
return hist
# 计算相似度
def calc_similar(li, ri):
calc_sim = hist_similar(li.histogram(), ri.histogram())
return calc_sim
复原算法部分代码展示
def do_with(cache, cache2, far, l, r, l2, r2):#操作棋盘(将状态加入列表)
flag = 0
t = cache[l]
# 得到0所在的行列数x,y
pos = getPos(t)
x, y = divmod(pos, 3)
# 四个方向,四种新状态
newpos = []
if y < 2:
newpos.append(pos + 1)
# print('右')
if y > 0:
newpos.append(pos - 1)
# print('左')
if x < 2:
newpos.append(pos + 3)
#print('下')
if x > 0:
newpos.append(pos - 3)
#print('上')
for ipos in newpos:
tt = cache[l][:]
tt[ipos] = cache[l][pos]
tt[pos] = cache[l][ipos]
# 如果新状态没在cache中就加入cache,队列尾巴r+1
if tt not in cache:
cache.append(tt)
far.append(l)
r += 1
# 如果新状态在逆向的cache2中找到,那么新状态就是接口状态
# 找到接口状态就可以直接打印了,返回接口状态的下标res,赋值给flag
res = result(tt, cache2, l2, r2)
if res != -1:
flag = res
l += 1
return cache, cache2, far, l, r, l2, r2, flag
[1.5]性能分析与改进
在图像识别部分乱图匹配原图成功率低,改变匹配原图算法和方法
#找原图
model_image ='testtu/' + str(x) + '.jpg'
y=-1
for i in range(1,37):
search_image='yuantuku/' + str(i) + '.jpg'
threshold = 0.999
flag=model_match(search_image, model_image, threshold)
if flag==1:
y=i
break
print("识别到第"+str(y)+"张图")
if y!=-1:
print("匹配成功")
#切割原图片并保存到文件夹yuantu
st = 'yuantu/'
file_name = 'yuantuku/'+str(y)+'.jpg'
image = Image.open(file_name)
image_list = cut_image(image)
save_images(st, image_list)
#匹配题目图片中切割后的九张图分别在原图中的什么位置
arr=[]#保存题目分割成的九张图片在原图的位置
arr1=[]#记录原图的每个部分是否被匹配到即判断哪一张图被扣掉
global ar
for i in range(0,10):
arr1.append(0)
for i in range(1,10):
image1 = Image.open('testtu/'+str(i)+'.jpg')
image1 = make_regalur_image(image1)
flag=0#是否匹配到图(找白色图片)
for j in range(1,10):
image2 = Image.open('yuantu/'+str(j)+'.jpg')
image2 = make_regalur_image(image2)
calc_sim = calc_similar(image1, image2)#计算相似度
if calc_sim==1:
flag=1
arr.append(j)
arr1[j-1]=1
break
if flag==0:#白色块用0表示
arr.append(0)
#判断哪一块图被扣掉并生成目标序列
target=[]
for i in range(9):
if(arr1[i]!=0):
target.append(i+1)
else:
target.append(0)
else:
print("匹配失败")
[1.6]描述你改进的思路
本来是直接用乱图去匹配原图,结果发现匹配成功率太低了,然后就改变思路,先将请求到的乱图切割成九份,拿出其中的一份去匹配,那么现在问题来了,我们应该拿哪一块去匹配?由于很多图像中含有黑色图片,导致图片的辨识度不高,白色和黑色肯定不能用来匹配原图。所以我就决定选出其中一张辨识度较高的去匹配(利用图像的熵,纯色的图像熵为0),然后再判断乱图中的每个部分在原图中的位置即我们之后要操作的待恢复列表,我将前面匹配得到的原图切割成九份保存到另一文件夹,接下来进行小图匹配小图,我先是用了平均哈希值算法但是匹配效果不好而且区分不开白色和黑色,经过多次尝试,我最终利用直方图来计算图片相似度(解决了前面的黑色与白色区分不了的问题)。
在还原拼图那部分普通的BFS速度较慢,看了有些文章里面讲到了双向BFS速度会比平常的BFS快,然后就改用双向BFS作为AI还原部分的核心算法。
[1.7]展示性能分析图和程序中消耗最大的函数
[1.8]展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
测试还原算法,因为在该算法在找到复原路径后会执行sys.exit(0)退出程序
import unittest
from AIsuanf import *
board=[3,4,0,6,1,9,8,7,5]
end = [1,0,3,4,5,6,7,8,9]
class MyclassTest(unittest.TestCase):
def test_something(self):
main(board, end)
if __name__ == '__main__':
unittest.main()
2.贴出Github的代码签入记录,合理记录commit信息。
[2.2.3]遇到的代码模块异常或结对困难及解决方法。
- 问题描述
问题1:在AI复原算法部分和游戏点击鼠标事件部分都会出现列表越界,然后游戏切换图片部分会出现多次切换图片屏闪的情况,AI复原部分无解时程序会一直运行无法停止
问题2:AI的图像的识别部分,我们最初的算法只能用一张分割的1/9图片在一个完整的图片中匹配出它的位置,并不能完全将整个图像匹配出来,而且匹配度很低。 - 解决尝试
清除已经遍历过的列表,改变更换图片语句的位置,在进行复原算法前判断是否有解,无解则输出无解提示并不做复原算法
我们引入图像熵,选择熵值较高的图像进行匹配,找到原图后切割原图,接下来利用并直方图计算图像相似度匹配出题目给的图像的每一部分分别在原图中的位置,判断出扣掉的那一部分图的位置。 - 是否解决
列表越界问题没有解决,更换图片屏闪问题已解决
图像识别已解决 - 有何收获
学习到了一些有关图像识别的知识,将一些知识能够综合运用起来,有时候换一个思路你会有更开阔的眼界
[2.2.4]评价你的队友。
U1S1,我觉得队友很有耐心,心态好,在我觉得AI算法做不出来的时候安慰和鼓励我没给我压力。刚开始分下作业后,我们分析讨论了一番之后,决定用pygame做游戏部分和确定了各自的分工了以后,就开始各自学习python。我们也会在遇到困难时相互讨论鼓励对方。总之她是一个很棒的队友。
[2.2.5]提供此次结对作业的PSP和学习进度条(每周追加)
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 100 | 100 | 15 | 15 | 学习了python的基本语言,pygame的图形界面的设置,原型设计的工具的运用,游戏界面大体框架已形成 |
2 | 153 | 253 | 20 | 35 | 完成界面按钮图标的设计,初建原型设计模板,切换界面的设计完成,载入音乐,移动步数等模块完成,AI与原型设计的实现 |
3 | 149 | 402 | 30 | 65 | 学习图像识别相关知识,并完成图像到目标图像的识别,并成功匹配乱图中每块在原图中的位置 |
4 | 225 | 675 | 20 | 85 | 复原算法的实现,网络接口的使用,撰写博客,改进代码,上传github文件 |
PSP2.1 | Personal Software Process Stages | 预估耗时(小时) | 实际耗时(小时) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 45 | 85 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 10 | 35 |
· Design Spec | · 生成设计文档 | 1 | 1 |
· Design Review | · 设计复审 | 0.5 | 0.5 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 0.5 | 0.5 |
· Design | · 具体设计 | 1 | 1 |
· Coding | · 具体编码 | 20 | 23 |
· Code Review | · 代码复审 | 1 | 2 |
· Test | · 测试(自我测试,修改代码,提交修改 | 1 | 3 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 0.5 | 0.5 |
· Size Measurement | · 计算工作量 | 0.5 | 0.5 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 3 | 4 |
· 合计 | 83 | 156 |