用 OpenCV 做的拼图小游戏,用 上下左右按键 实现方块的移动
图片、难度 都可以自选
示例:
使用 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]
直接创建一个 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()
# 初始化
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)
试试
整体代码
''' # 拼图
输入图片路径、分割难度,通过上下左右按键移动方块
'''
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())
有问题欢迎指出 ~