Python OpenCV,拼图小游戏

Python,拼图小游戏

简介

用 OpenCV 做的拼图小游戏,用 上下左右按键 实现方块的移动
图片、难度 都可以自选
示例:
Python OpenCV,拼图小游戏_第1张图片
Python OpenCV,拼图小游戏_第2张图片

流程

  1. 输入图片、参数,预处理
  2. 建立 class,处理移动图片
  3. 移动图片

1. 预处理

使用 numpy 和 opencv 库,输入图片和难度:

import numpy as np
import cv2

# 先输入图片
file= input('请先输入图片路径(不能含有中文):\n')
img = cv2.imread(file)

# 再输入难度
n_level = input('请输入难度(分割次数):\n')
n_level = int(n_level)

如果图片过大,将其放缩到合适的尺寸(限制到屏幕尺寸)

# 图片过大,放缩调整
k_shape = max(img.shape[0]/800, img.shape[1]/1500)  # 限制最大尺寸,大约是屏幕尺寸
if k_shape>1:
    img = cv2.resize(img,(round(img.shape[1]//k_shape), round(img.shape[0]//k_shape)))
    print('图片过大,已缩放\n')

M, N = img.shape[:2]
M, N = n_level*(M//n_level), n_level*(N//n_level)
img = img[0:M, 0:N]

2. 创建图片移动机制

直接创建一个 class:

# 移动图片
class Imoving():
    ''' # 可移动的图像
    函数:
    1. 移动:self.move(direction) -> None
    2. 展示:self.show() -> image
    '''
    def __init__(self, image:np.ndarray, level:int=3,
                    flag:int=0, color:list=[200, 200, 200]) -> None:
        ''' ## 创建可移动图像
        `image`: 图像矩阵
        `level`: 切分等级,如 3
        `flag` : 移动方块的位置,位于左上角:`0`,右下角:`-1`
        `color`: 方块颜色,RGB
        '''
        self.image = image  # 图像,constant
        self.level = level  # 等级,constant
        self.flag = flag % level**2 # 标志点,constant
        self.color = color  # 方块颜色,RGB,constant
        self.steps = [image.shape[0]//level, image.shape[1]//level] # 每个方块的长宽,constant

        # 下面两个是变量:
        self.positions = np.resize(np.arange(0, level**2, 1), [level, level])    # 创建矩阵,记录每一块的位置信息
        p0 = np.where(self.positions == self.flag)
        self.p0 = [p0[0][0], p0[1][0]]  # 初始点


    # 图片移动
    def move(self, direction:str) -> None:
        ''' ## 移动图像
        `input`: `direction`为移动方向,可选:`'up'`, `'down'`, `'left'`, `'right'`
        '''
        mv = {
            'up'    : [-1, 0],
            'down'  : [1, 0],
            'left'  : [0, -1],
            'right' : [0, 1]
        }[direction]

        # 移动
        p0 = [self.p0[0]+ mv[0], self.p0[1]+ mv[1]]
        if min(p0)>=0 and max(p0)<self.level:
            # 更新
            self.positions[self.p0[0], self.p0[1]] = self.positions[p0[0], p0[1]]
            self.positions[p0[0], p0[1]] = self.flag
            self.p0 = p0


    # 图片显示
    def show(self) -> np.ndarray:
        ''' ## 显示图片
        `return`: new image to show
        '''
        # 创建新图片
        img_show = np.ones(self.image.shape, dtype=np.uint8)
        for i in range(3):
            img_show[:, :, i] = img_show[:, :, i]*self.color[2-i]   # BGR转RGB
        
        # 新图片重新赋值
        for k in range(self.level**2):
            i, j = k // self.level, k % self.level # 矩阵对应位置
            if self.positions[i,j] != self.flag:
                I, J = self.positions[i,j] // self.level, self.positions[i,j] % self.level  # 图片对应位置
                img_show[(i*self.steps[0]):((i+1)*self.steps[0]), (j*self.steps[1]):((j+1)*self.steps[1])] = \
                    self.image[(I*self.steps[0]):((I+1)*self.steps[0]), (J*self.steps[1]):((J+1)*self.steps[1])]
        
        return img_show

这一块还挺繁琐的,总之就是,创建了一个 class:Imoving
用法很简单:
创建: Imv = Imoving(img, 3)
移动: Imv.move('up')
展示: img_show = Imv.show()

3. 开始游戏,移动方块

# 初始化
Imv = Imoving(image=img, level=n_level, flag=-1, color=[102, 204, 255])
positions = Imv.positions.copy()
cv2.imshow('Jigsaw', Imv.show())

# 打乱
directions = ['up', 'down', 'left', 'right']
for i in range(1000):
    Imv.move(np.random.choice(directions))


# 上下左右按键的值
keys = {
    2490368: 'up',
    2621440: 'down',
    2424832: 'left',
    2555904: 'right'
}

# 开始
while True:
    key = cv2.waitKeyEx(0)  # 获取按键信息
    if key < 0:             # 关闭窗口
        break
    if key not in keys:     # 其他
        continue

    print(keys[key])
    Imv.move(keys[key]) # 更新
    cv2.imshow('Jigsaw', Imv.show())    # 展示

    if (positions == Imv.positions).all():
        print('成功!')
        break

while cv2.waitKey(0)>0:
    cv2.imshow('Jigsaw', Imv.show())

先显示图片,等待第一次按下按键之后,显示打乱后的图片,之后就可以开玩了。
可能这里的 按键数值 每个人的电脑不太一样?那样可以 print(key) 试试

然而我真不擅长这游戏……
Python OpenCV,拼图小游戏_第3张图片

整体代码

''' # 拼图
输入图片路径、分割难度,通过上下左右按键移动方块
'''

import numpy as np
import cv2

# 先输入图片
file= input('请先输入图片路径(不能含有中文):\n')
img = cv2.imread(file)

# 再输入难度
n_level = input('请输入难度(分割次数):\n')
n_level = int(n_level)


# 图片过大,放缩调整
k_shape = max(img.shape[0]/800, img.shape[1]/1500)  # 限制最大尺寸,大约是屏幕尺寸
if k_shape>1:
    img = cv2.resize(img,(round(img.shape[1]//k_shape), round(img.shape[0]//k_shape)))
    print('图片过大,已缩放\n')

M, N = img.shape[:2]
M, N = n_level*(M//n_level), n_level*(N//n_level)
img = img[0:M, 0:N]


# 移动图片
class Imoving():
    ''' # 可移动的图像
    函数:
    1. 移动:self.move(direction) -> None
    2. 展示:self.show() -> image
    '''
    def __init__(self, image:np.ndarray, level:int=3,
                    flag:int=0, color:list=[200, 200, 200]) -> None:
        ''' ## 创建可移动图像
        `image`: 图像矩阵
        `level`: 切分等级,如 3
        `flag` : 移动方块的位置,位于左上角:`0`,右下角:`-1`
        `color`: 方块颜色,RGB
        '''
        self.image = image  # 图像,constant
        self.level = level  # 等级,constant
        self.flag = flag % level**2 # 标志点,constant
        self.color = color  # 方块颜色,RGB,constant
        self.steps = [image.shape[0]//level, image.shape[1]//level] # 每个方块的长宽,constant

        # 下面两个是变量:
        self.positions = np.resize(np.arange(0, level**2, 1), [level, level])    # 创建矩阵,记录每一块的位置信息
        p0 = np.where(self.positions == self.flag)
        self.p0 = [p0[0][0], p0[1][0]]  # 初始点


    # 图片移动
    def move(self, direction:str) -> None:
        ''' ## 移动图像
        `input`: `direction`为移动方向,可选:`'up'`, `'down'`, `'left'`, `'right'`
        '''
        mv = {
            'up'    : [-1, 0],
            'down'  : [1, 0],
            'left'  : [0, -1],
            'right' : [0, 1]
        }[direction]

        # 移动
        p0 = [self.p0[0]+ mv[0], self.p0[1]+ mv[1]]
        if min(p0)>=0 and max(p0)<self.level:
            # 更新
            self.positions[self.p0[0], self.p0[1]] = self.positions[p0[0], p0[1]]
            self.positions[p0[0], p0[1]] = self.flag
            self.p0 = p0


    # 图片显示
    def show(self) -> np.ndarray:
        ''' ## 显示图片
        `return`: new image to show
        '''
        # 创建新图片
        img_show = np.ones(self.image.shape, dtype=np.uint8)
        for i in range(3):
            img_show[:, :, i] = img_show[:, :, i]*self.color[2-i]   # BGR转RGB
        
        # 新图片重新赋值
        for k in range(self.level**2):
            i, j = k // self.level, k % self.level # 矩阵对应位置
            if self.positions[i,j] != self.flag:
                I, J = self.positions[i,j] // self.level, self.positions[i,j] % self.level  # 图片对应位置
                img_show[(i*self.steps[0]):((i+1)*self.steps[0]), (j*self.steps[1]):((j+1)*self.steps[1])] = \
                    self.image[(I*self.steps[0]):((I+1)*self.steps[0]), (J*self.steps[1]):((J+1)*self.steps[1])]
        
        return img_show


# 初始化
Imv = Imoving(image=img, level=n_level, flag=-1, color=[102, 204, 255])
positions = Imv.positions.copy()
cv2.imshow('Jigsaw', Imv.show())

# 打乱
directions = ['up', 'down', 'left', 'right']
for i in range(1000):
    Imv.move(np.random.choice(directions))


# 上下左右按键的值
keys = {
    2490368: 'up',
    2621440: 'down',
    2424832: 'left',
    2555904: 'right'
}

# 开始
while True:
    key = cv2.waitKeyEx(0)  # 获取按键信息
    if key < 0:             # 关闭窗口
        break
    if key not in keys:     # 其他
        continue

    print(keys[key])
    Imv.move(keys[key]) # 更新
    cv2.imshow('Jigsaw', Imv.show())    # 展示

    if (positions == Imv.positions).all():
        print('成功!')
        break


while cv2.waitKey(0)>0:
    cv2.imshow('Jigsaw', Imv.show())

有问题欢迎指出 ~

你可能感兴趣的:(python,opencv)