前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用OpenCV实现猜词游戏

用OpenCV实现猜词游戏

作者头像
小白学视觉
发布2019-07-08 16:24:39
6610
发布2019-07-08 16:24:39
举报

小伙伴们是不是在用OpenCV来处理图像处理的相关任务,从来没有想过还可以通过OpenCV设计一款游戏,今天小白将为各位小伙伴们介绍如何通过OpenCV创建一个猜词的小游戏。为了增加趣味性,我们给小游戏起了一个比较具有故事性的名字“刽子手游戏(Hangman)”,我们先来看一下该游戏的视频。

这是一个猜电影名字的游戏,会在屏幕下方显示电影的单词数目以及每个单词的字母个数,我们需要猜电影名字中含有的字母,如果猜测错误,右侧的刽子手处就会依次出现人头、身体、手和脚等,当猜错6次之后,刽子手就会行动,游戏就输了。但是为了增加获胜几率,在出现错误的时候会给出关于电影的部分提示,当把电影名字全部猜出后,我们就取得了胜利。

接下来将介绍如何实现这个有趣的小游戏,我们将本小游戏实现分成4个子功能模块,其构成如下图所示。

数据处理

为了确保游戏不会很快耗尽电影和线索,我们选择TMDb数据集(电影数据库),数据处理步骤包括删除却是值,仅保留所需的内容-发行年份、演员表、导演、电影关键字和标语。同时为了减少数据集大小,只保留标题不超过20个字符且标语不超过30的电影。

电影和线索选择

首先我们从CSV文件加载数据集并以字典格式存储它

代码语言:javascript
复制
def read_from_csv(csv_f):
   with open(csv_f,'r') as f:
    movie_data = {}
     for line in f.readlines():
      line_split = line.strip().split(",")
      year = line_split[-1].split("|")
      keywords = line_split[-2].split("|")
      tagline = line_split[-3].split("|")
      director = line_split[-4].split("|")
      cast = line_split[-5].split("|")
      movie = line_split[0].upper()
      movie_data[movie] = [year,keywords,tagline,director,cast]
   return movie_data

请注意,我们使用以下字典格式存储电影和电影详细信息。

代码语言:javascript
复制
movie_title:[year,list of keywords, tagline, director, list of cast]

接下来,我们将从电影列表中获取一部随机电影并获取该电影的信息(年份,关键字,标语,导演和演员)

代码语言:javascript
复制
def get_movie_info(movies_data):
  movies_list = list(movies_data.keys())
  movie = np.random.choice(movies_list,1)[0].upper()
  movie_info = movies_data[movie]
   return movie,movie_info

我们将使用另一个函数select_hints来选择任意3个随机提示。如果关键字或强制转换作为提示存在,我们将从它们中随机选择一个元素

代码语言:javascript
复制
def select_hints(movie_info):
 # We will randomly select 3 types of
 # hints to display
 hints_index = list(np.random.choice(5,3,replace=False))
 hints = []
 hints_labels = ["Release Year","Keyword","Tagline","Director","Cast"]
 labels = []
 for hint_index in hints_index:
 hint = np.random.choice(movie_info[hint_index],1)[0].upper()
 hints.append(hint)
 labels.append(hints_labels[hint_index].upper())
 return hints,labels

编程显示

加载Hangman Canvas

首先,我们显示下面的的hangman模板

代码语言:javascript
复制
def get_canvas(canvas_file):
  img = cv2.imread(canvas_file,1)
   return img

获得角色尺寸

接下来,这里有一个棘手的部分。我们想要显示空白框,以便用户知道电影标题的长度。考虑到角色的宽度和高度,我们将使它更有趣和更通用。我们将使用该标题中所有字符的最大高度和宽度。我们将使用函数get_char_coords来计算这些框的坐标。

代码语言:javascript
复制
def get_char_coords(movie):
  x_coord = 100
  y_coord = 400
 
  char_ws = []
  char_hs = []
 
   for i in movie:
    char_width, char_height = cv2.getTextSize(i,\
      cv2.FONT_HERSHEY_SIMPLEX,1,2)[0]
    char_ws.append(char_width)
    char_hs.append(char_height)
 
  max_char_h = max(char_hs)
   max_char_w = max(char_ws)
 
  char_rects = []
 
   for i in range(len(char_ws)):
    rect_coord = [(x_coord,y_coord-max_char_h),\
      (x_coord+max_char_w,y_coord)]
    char_rects.append(rect_coord)
    x_coord = x_coord + max_char_w
   return char_rects

画空白框

一旦我们有这些坐标,我们将使用它们来绘制框。

代码语言:javascript
复制
def draw_blank_rects(movie,char_rects,img):
   for i in range(len(char_rects)):
    top_left, bottom_right = char_rects[i]
     if not movie[i].isalpha() or \
       ord(movie[i]) < 65 or \
       ord(movie[i]) > 122 or \
       (ord(movie[i]) > 90 and \
       ord(movie[i]) < 97):
      cv2.putText(img,movie[i],(top_left[0],bottom_right[1]),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),2)
       continue
    cv2.rectangle(img,top_left,\bottom_right,(0,0,255),thickness=1,lineType = cv2.LINE_8)
  return img

显示提示

要显示的提示取决于不正确尝试的次数。如果没有不正确的尝试,我们将不会显示任何提示。如果一次尝试不正确,我们将显示第一个提示。类似地,对于少于4次不正确的尝试,我们将显示第二个提示,最后,对于少于7个不正确的尝试,我们将显示第三个提示。

代码语言:javascript
复制
def draw_hint(img,hints,labels,incorrect_attempts):
  x,y = 20,30
   if incorrect_attempts == 0:
     return img
   elif incorrect_attempts <= 1:
    index = 0
   elif incorrect_attempts <= 3:
    index = 1
   elif incorrect_attempts <= 6:
    index = 2
  cv2.putText(img,"HINT: {}".format(labels[index]),(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.6,(255,0,255),1)
  cv2.putText(img,"{}".format(hints[index]),(x,y+30),cv2.FONT_HERSHEY_SIMPLEX,0.6,(255,0,255),1)
   return img

显示正确或不正确的尝试

如果电影标题中出现猜测的字母,需要进行提示。

代码语言:javascript
复制
def draw_wrong(img,incorrect_attempts):
  cv2.putText(img,"WRONG {}/6".format(incorrect_attempts+1),(380,40),\
  cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),2)
   return img
 
def draw_right(img):
  cv2.putText(img,"RIGHT",(380,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,255,0),2)
   return img

显示游戏输赢

如果游戏赢了或输了,同样需要进行提示

代码语言:javascript
复制
def draw_lost(img):
  cv2.putText(img,"YOU LOST",(380,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
   return img
 
def draw_won(img):
  cv2.putText(img,"YOU WON",(380,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,255,0),2)
   return img

显示无效的移动和字符重用

现在,我们还有两个案例需要考虑。如果输入了无效字符怎么办?这可以是数字或非字母数字字符。第二,如果用户输入之前已输入的角色,该怎么办

代码语言:javascript
复制
def draw_invalid(img):
  cv2.putText(img,"INVALID INPUT",(300,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
   return img
 
def draw_reuse(img):
  cv2.putText(img,"ALREADY USED",(300,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
   return img

显示使用的字符

现在,为了让用户更容易知道已经使用了哪些字符,我们也将显示它们。我们将在这里再添一个。我们将显示以红色输入的有效字符,以便用户可以看到他们输入的字符。

代码语言:javascript
复制
def draw_used_chars(img,chars_entered,letter):
  cv2.putText(img,"Letters used:",(300,80),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,0),1)
  y = 120
  x = 350
  count = 0
   for i in chars_entered:
     if count == 10:
      x += 50
      y = 120
     if i==letter:
      cv2.putText(img,i,(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),1)
     else:
      cv2.putText(img,i,(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,0),1)
    y += 20
    count += 1
   return img

显示刽子手身体

接下来轮到编写函数来绘制刽子手的不同身体部位 - 面部,背部,左右手臂,左右腿。将绘制什么身体部位取决于不正确尝试的次数

代码语言:javascript
复制
def draw_hangman(img,num_tries):
  if num_tries==1:
    return draw_circle(img)
  elif num_tries==2:
    return draw_back(img)
  elif num_tries==3:
    return draw_left_hand(img)
  elif num_tries==4:
    return draw_right_hand(img)
  elif num_tries==5:
    return draw_left_leg(img)
  elif num_tries==6:
    return draw_right_leg(img)
  else:
    return img

def draw_circle(img):
    cv2.circle(img,(190,160),40,(0,0,0),thickness=2,lineType=cv2.LINE_AA)
  return img

def draw_back(img):
    cv2.line(img,(190,200),(190,320),(0,0,0),thickness=2,lineType=cv2.LINE_AA)
  return img

def draw_left_hand(img):
    cv2.line(img,(190,240),(130,200),(0,0,0),thickness=2,lineType=cv2.LINE_AA)
  return img

def draw_right_hand(img):
  cv2.line(img,(190,240),(250,200),(0,0,0),thickness=2,lineType=cv2.LINE_AA)
  return img

def draw_left_leg(img):
  cv2.line(img,(190,320),(130,360),(0,0,0),thickness=2,lineType=cv2.LINE_AA)
  return img

def draw_right_leg(img):
  cv2.line(img,(190,320),(250,360),(0,0,0),thickness=2,lineType=cv2.LINE_AA)
  return img

显示字符出现次数

我们还希望显示在电影标题中输入的所有字母(如果它出现在电影标题中)。

代码语言:javascript
复制
def displayLetter(img,letter,movie,char_rects):
   for i in range(len(movie)):
     if movie[i]==letter:
      top_left, bottom_right = char_rects[i]
      cv2.putText(img, movie[i],(top_left[0],bottom_right[1]),cv2.FONT_HERSHEY_SIMPLEX,1,(255,0,0),2)
   return img

显示电影标题

最后,我们想在游戏结束后揭示正确的电影片名。

代码语言:javascript
复制
def revealMovie(movie,img,char_rects):
 #img = cv2.imread(canvas_file,1)
   for i in range(len(movie)):
    top_left, bottom_right = char_rects[i]
    cv2.putText(img,movie[i],(top_left[0],bottom_right[1]),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)
   return img

编程游戏

现在我们已经涵盖了所有功能,让我们看看我们将如何在游戏中使用它们。我们将从读取CSV文件中的数据并获取随机电影开始。

代码语言:javascript
复制
import cv2
import numpy as np
from utils import *
 
movie_csv = "movies-list-short.csv"
canvas = "blank-canvas.png"
 
movies_data = read_from_csv(movie_csv)
 
movie, movie_info = get_movie_info(movies_data)

我们还将选择之前讨论的3个随机提示。

代码语言:javascript
复制
hints,labels = select_hints(movie_info)

现在,让我们从空白的Hangman画布开始,零尝试不正确。

代码语言:javascript
复制
char_rects = get_char_coords(movie)
 
img = draw_blank_rects(movie,char_rects,img)
 
cv2.namedWindow("Hangman", cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty("Hangman",cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN)
 
cv2.imshow("Hangman",img)
 
chars_entered = []
 
incorrect_attempts = 0
 
img_copy = img.copy()

接下来,我们将对该部分进行编码,以便从用户那里获取输入并显示输入的字母。我们还需要显示尝试是正确还是错误,或者是否无效或已经使用过。如果用户用完了尝试,循环将中断。

我们通过以下方式实现上述目标。

  1. 创建当前图像的副本。这是为了确保我们不会覆盖诸如错误,正确等字样或提示。
  2. 接下来,根据不正确的尝试次数,我们将在图像上显示提示。
  3. 如果用户已经用完了所有的生命,我们将显示您丢失并且循环将中断。
  4. 如果用户设法猜出电影的所有字符,我们将显示你WON并打破循环。
  5. 要检查用户输入的字符是否有效,我们将检查字符是否位于az或AZ之间。如果移动无效,我们将显示相应的消息 - INVALID MOVE,游戏将继续。
  6. 将检查用户输入的有效字符以查看它之前是否已被使用过,在这种情况下将显示相应的消息并且游戏将继续。
  7. 请注意,在最后两个步骤中,不会更改不正确的尝试次数。
  8. 如果输入的字符是新字符,我们将首先将其附加到所用字符列表中,然后检查它是否出现在电影标题中,在这种情况下,我们将显示CORRECT并显示电影中所有出现的字符。
  9. 如果在电影标题中找不到该字符,我们将显示错误并增加错误尝试次数。
  10. 最后,一旦游戏获胜或失败,我们将揭示正确的电影标题。
代码语言:javascript
复制
while 1:
  img = img_copy.copy()
  img = draw_hint(img,hints,labels,incorrect_attempts)
   if incorrect_attempts >= 6:
    img = draw_lost(img)
     break
   elif check_all_chars_found(movie, chars_entered):
    img = draw_won(img)
     break
   else:
    letter = cv2.waitKey(0) &amp; 0xFF
     if letter < 65 or letter > 122 or (letter > 90 and letter < 97):
      img = draw_invalid(img)
      cv2.imshow("Hangman",img)
       continue
     else:
      letter = chr(letter).upper()
     if letter in chars_entered:
      img = draw_reuse(img)
      img = draw_used_chars(img,chars_entered,letter)
      cv2.imshow("Hangman",img)
       continue
     else:
      chars_entered.append(letter)
       if letter in movie:
        img = draw_right(img)
        img = displayLetter(img,letter,movie,char_rects)
        img_copy = displayLetter(img_copy,letter,movie,char_rects)
       else:
        img = draw_wrong(img,incorrect_attempts)
         incorrect_attempts += 1
  img = draw_used_chars(img,chars_entered,letter)
  img = draw_hangman(img,incorrect_attempts)
  img_copy = draw_used_chars(img_copy,chars_entered,letter)
  img_copy = draw_hangman(img_copy,incorrect_attempts)
  cv2.imshow("Hangman",img)
 
img = revealMovie(movie,img,char_rects)
cv2.imshow("Hangman",img)
cv2.waitKey(0)
 
cv2.destroyAllWindows()

最后,让我们借助下面显示的图像快速总结一下Hangman游戏的不同部分。

往期文章一览

1、人脸识别中的活体检测算法综述

2、CVPR2019:PizzaGAN通过深度学习制作披萨

3、漫话:如何给女朋友解释为什么计算机只认识0和1?

4、10个不得不知的Python图像处理工具,非常全了!

5、OpenCV4.0实现人脸识别

6、基于内容的图像检索技术综述-传统经典方法

7、为什么不建议你入门计算机视觉

8、机器视觉检测系统中这些参数你都知道么?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小白学视觉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
人脸识别
腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档