相信有些朋友接触过魔方墙找茬这类游戏,在两边对照的众多颜色块中找到其中一个不同的颜色块,有些人会用来训练3D视眼,那么就来做一个魔方墙找茬的程序吧。
本次采用OpenCV来制作,用到了基础库NumPy。
让我们一起来看看详细的过程。
这样的色块图是如何制作出来的呢?
首先,先定义单个小方块的宽度和数量:
self.squareWidth = 20 # 小方块的宽度
self.squareCount = 15 # 一行或一列小方块的数量
创建一个纯黑色背景
# 尺寸为450X200大小的背景
self.img = np.zeros((200, 450, 3), dtype='uint8')
然后创建单个颜色块,这里只采用了了7种颜色,分别是红、绿、蓝、黄、粉、白、青,这7种颜色刚好对应RGB三个颜色通道分别是0或255。
color = [np.random.choice([0, 255]) for i in range(3)] # 随机创建一种颜色
colorImg = np.array([[color for _ in range(self.squareWidth)] for __ in range(self.squareWidth)],
dtype='uint8') # 创建20X20像素大小的颜色块
看一下变量color的值,[0, 255, 0] 代表绿色的RGB值,变量colorImg里面存储的就是有20X20个color,呈现出来的就是一个宽为20像素的绿色小方块。
如此就创建好了一个小方块。
ps:上面创建小方块采用 for 循环的方式,还有第二种创建小方块的方法。
color = np.random.choice([0,255],size=3)
w = self.squareWidth
colorImg = np.array(np.tile(color,w**2).reshape(w,w,3),dtype='uint8')
np.tile(color,w**2)是将color数组进行重复 w**2 次,再通过reshape把数组改成三维。
这种方式比上面那种 for 循环来创建会稍微快一点。
好,我们继续。
把创建好的方块贴到之前创建的黑色背景图上:
self.img[0:self.squareWidth, 0:self.squareWidth] = colorImg
我们要创建15X15个方块的画布,只需要进行循环创建小方块即可,行和列都循环15次(双层循环),经过循环,左边的画布就创建好了。
for i in range(0, self.height, self.squareWidth):
for j in range(0, self.height, self.squareWidth):
# self.height 是整个画布的高度
pass
同样,我们把左边的画布贴到黑色背景图的右边,这样左右两边对称的画布就形成了,注意中间留出一些位置来把两边区分开。
# self.height 是整个画布的高度
# self.interval 左右两边画布之间的间距
self.img[0: self.height, self.height + self.interval: self.height * 2 + self.interval] = \
self.img[0: self.height, 0: self.height]
两边对称的画布创建好之后,我们再创建一个新的不同颜色的小方块去覆盖掉画布上其中一个位置。这样就有一个小方块颜色不一样了。
要覆盖哪个位置呢?这个位置是随机的,那么先创建一个随机位置。
# 在15X15个色块上随机产生一个位置坐标
self.rPlace = (np.random.randint(self.squareCount),np.random.randint(self.squareCount))
在这个位置贴上一个新的小方块,注意要进行判断会不会跟原来位置上的色块相同,如果相同的话就要重新创建一个,不然就达不到魔方墙找不同的效果了。
color = [np.random.choice([0, 255]) for i in range(3)] # 随机创建一种颜色
while (np.array(color,'uint8') == self.img[self.rPlace[0]*self.squareWidth, self.rPlace[1] * self.squareWidth]).all():
# 判断跟原位置上的颜色块是否相同颜色,如果相同要重新创建
color = [np.random.choice([0, 255]) for i in range(3)]
colorImg = np.array([[color for _ in range(self.squareWidth)] for __ in range(self.squareWidth)],
dtype='uint8') # 创建20X20像素大小的颜色块
# 将创建好的新的不同颜色的小方块贴入原图上去
self.img[self.rPlace[0] * self.squareWidth: (self.rPlace[0] + 1) * self.squareWidth,
self.rPlace[1] * self.squareWidth: (self.rPlace[1] + 1) * self.squareWidth] = colorImg
这样我们的画布就做好了
看,箭头所指的小方块是不是不一样的颜色呢,嘻嘻。
接下来要进行鼠标点击事件的处理,当找到不同颜色块的位置后,鼠标点击一下该位置,就会再重复上面的步骤,做出一张新的魔方墙画布来。
调用OpenCV的鼠标事件函数:cv.setMouseCallback('img', self.mouse_main)
参数解释: ‘img’ 是指定要接收’img’这个GUI窗口的事件,self.mouse_main是自己定义的回调函数
回调函数代码如下:
def mouse_main(self, event, x, y, flag, param):
# 调用OpenCV的鼠标处理事件函数,介绍一下用到的参数,event:事件类型,x和y是鼠标位置
if event == cv.EVENT_LBUTTONDOWN: # 鼠标左键点击事件
ix = self.rPlace[1] * self.squareWidth # ix和iy是被改变的小方块的坐标位置
iy = self.rPlace[0] * self.squareWidth
if (ix < x < ix + self.squareWidth ) and (iy < y < iy + self.squareWidth):
# 如果鼠标点击的位置正确,那么就创建新的颜色画布
self.creat_canvas()
主要的流程就是这样啦,我们只需要在主程序上一直循环显示创建的颜色画布就好,如果鼠标点击了正确位置,就创建新的颜色画布,GUI窗口上也会显示的新的画布了。
总结一下创建颜色画布的思路:
完整代码可以点击下载https://download.csdn.net/download/FujLiny/13089510
或直接拷贝:
# @Author: FujLiny
# --------versions 1-1, Update time:2020/10/28--------
#
import cv2 as cv
import numpy as np
class Eyes(object):
def __init__(self, squarePixel=20,squareCount=3):
if squareCount * squarePixel < 60:
raise ValueError('尺寸太小,请增加方块数量或方块宽度')
self.interval = int(squarePixel*squareCount/4) if squarePixel*squareCount/4 >= 60 else 60
self.height = squarePixel * squareCount
self.width = 2 * self.height + self.interval
if self.width > 1920:
raise ValueError('尺寸超过屏幕大小,请减小方块数量或方块宽度')
self.squareWidth = squarePixel
self.squareCount = squareCount
self.rPlace = None
self.img = np.zeros((self.height, self.width, 3), dtype='uint8')
self.blackColor = np.array([0,0,0],'uint8')
cv.namedWindow('img')
def creat_canvas(self):
for i in range(0, self.height, self.squareWidth):
for j in range(0, self.height, self.squareWidth):
color = np.random.choice([0,255],size=(3,))
while (color == self.blackColor).all():
color = np.random.choice([0,255],size=(3,))
colorImg = np.array(
[[color for _ in range(self.squareWidth)] for __ in range(self.squareWidth)],
dtype='uint8')
self.img[i: i + self.squareWidth, j: j + self.squareWidth] = colorImg
self.img[0: self.height, self.height + self.interval: self.height * 2 + self.interval] = \
self.img[0: self.height, 0: self.height]
self.rPlace = (np.random.randint(self.squareCount),
np.random.randint(self.squareCount))
color = np.random.choice([0,255],size=(3,))
while (np.array(color,'uint8') ==
self.img[self.rPlace[0]*self.squareWidth, self.rPlace[1] * self.squareWidth]).all():
color = np.random.choice([0,255],size=(3,))
colorImg = np.array(
[[color for _ in range(self.squareWidth)] for __ in range(self.squareWidth)],
dtype='uint8')
self.img[self.rPlace[0] * self.squareWidth: (self.rPlace[0] + 1) * self.squareWidth,
self.rPlace[1] * self.squareWidth: (self.rPlace[1] + 1) * self.squareWidth] = colorImg
def mouse_main(self, event, x, y, flag, param):
if event == cv.EVENT_LBUTTONDOWN:
ix = self.rPlace[1] * self.squareWidth
iy = self.rPlace[0] * self.squareWidth
i2 = self.height + self.interval
if (ix < x < ix + self.squareWidth or ix + i2 < x < ix + i2 + self.squareWidth) \
and (iy < y < iy + self.squareWidth):
self.creat_canvas()
def eye_main(self):
self.creat_canvas()
cv.setMouseCallback('img', self.mouse_main)
while True:
cv.imshow('img', self.img)
if cv.waitKey(20) == 13: # 13 是键的键值
break
print('正确位置:{}行{}列'.format(self.rPlace[0]+1, self.rPlace[1]+1))
cv.waitKey(0)
cv.destroyAllWindows()
if __name__ == '__main__':
e = Eyes(squarePixel=15, squareCount=20)
e.eye_main()
# squarePixel 单个小方块的像素宽度
# squareCount 一行或一列小方块的数量