用python-OpenCV做一个魔方墙找茬 (3D视眼训练)

4 篇文章 0 订阅
4 篇文章 0 订阅

前言

相信有些朋友接触过魔方墙找茬这类游戏,在两边对照的众多颜色块中找到其中一个不同的颜色块,有些人会用来训练3D视眼,那么就来做一个魔方墙找茬的程序吧。

本次采用OpenCV来制作,用到了基础库NumPy。


先演示一下5X5方块的效果
在这里插入图片描述


程序实现思路

在这里插入图片描述

让我们一起来看看详细的过程。


制作颜色画布

在这里插入图片描述
这样的色块图是如何制作出来的呢?
首先,先定义单个小方块的宽度和数量:

self.squareWidth = 20  # 小方块的宽度
self.squareCount = 15  # 一行或一列小方块的数量

创建一个纯黑色背景

# 尺寸为450X200大小的背景
self.img = np.zeros((200, 450, 3), dtype='uint8')

看一下self.img的值
在这里插入图片描述

然后创建单个颜色块,这里只采用了了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窗口上也会显示的新的画布了。


总结

总结一下创建颜色画布的思路:

  1. 创建黑色背景
  2. 创建小的方块并把小方块贴到背景上
  3. 循环创建,铺满左边的画布
  4. 把左边的颜色块全部拷贝到右边的画布上
  5. 随机创建一个要更改的坐标位置
  6. 再创建一个小方块并贴到那个位置上

完整代码

完整代码可以点击下载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 是<enter>键的键值
                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  一行或一列小方块的数量

最后再展示一下整体的效果:
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值