python pillow 把图片切成拼图块

Posted zbbzb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python pillow 把图片切成拼图块相关的知识,希望对你有一定的参考价值。

契机:

【Unity】实现拖拽功能并制作一个拼图游戏

当我看到这个视频里的素材, 很馋, 但是原作者也没有发素材文件, 只能自己弄素材了, 3D就直接pass了, 不熟和学习成本过高, 那就2D的来, 目的是脚本文件可以批量把照片分隔成拼图块

分解需求

最简易的矩形ROI分割(剪裁),其本质是多维数组(矩阵)的切片

1. 分隔成n*m个方块
2. 拿出一个方块, 获取左右方块 之间的中心圆心坐标, 还有上下方块 之间的中心圆心坐标, 然后补满这个圆就可以获得拼图的凸出, 消减这个圆可以获得拼图的凹入

3. 增加圆心偏移
也就是让凸出和凹进更加好看点

4. 随机凹凸

先了解下怎么切圆

python3切割圆形图片
看完这篇文章可知如何生成圆形图片
新建一个图像b, 然后把a中符合这个圆的点复制b中, 输出图像b, 就生成了圆形图像
如何符合圆, 要用到初中的知识点, 点到圆心的距离 <= 半径

分隔成n*m个方块

import os
from PIL import Image

# 分割几行几列, 二维数组保存
def SplitImages(img_path, row, col):
    path_name = os.path.dirname(img_path)
    img = Image.open(img_path).convert("RGBA")
    imgSize = img.size

    splitW = int(imgSize[0]/col)
    splitL = int(imgSize[1]/row)

    pimg = img.load()

    imbList = []
    for i in range(row):
        rowList = []
        l = (i + 1) * splitL
        for j in range(col):
            w = (j + 1) * splitW
            imb = Image.new('RGBA', (splitW, splitL),(255,255,255,0))
            pimb = imb.load()

            for k in range(j * splitW, w):
                for z in range(i * splitL, l):
                    pimb[k - splitW * j, z - i * splitL] = pimg[k,z]
            dirPath = path_name + "/" + str(i*10 + j) + ".png"
            # imb.save(dirPath)
            rowList.append(imb)
        imbList.append(rowList)

    return imbList

简单版

不考虑随机凹凸, 不考虑圆心偏移, 凹凸都是固定的, 从第一个开始向右凸出, 向下凸出

def SplitCircle(imbList, imgPath):
    path_name = os.path.dirname(imgPath)
    img = Image.open(imgPath).convert("RGBA")
    imgSize = img.size

    col = len(imbList[0])
    row = len(imbList)

    if col == 1 and row == 1:
        return 

    splitW = int(imgSize[0]/col)
    splitL = int(imgSize[1]/row)

    minV = min(splitW, splitL)
    r_d = int(minV / 4) # 要计算 两个不能比 l 长 并且加上 offset 也不能超过 l
    #r_offset = int(minV / 8)

    #pSplitW = splitW + (r_d + r_offset) * 2
    #pSplitL = splitL + (r_d + r_offset) * 2

    pimg = img.load()

    pointList = []
    for i in range(row):
        colPointList = []
        for j in range(col):
            colPoint = []
            rowPoint = []
            if j != col - 1:
                colPoint = [splitW * (j + 1), int(splitL/2) + i * splitL]
            if i != row - 1:
                rowPoint = [int(splitW / 2) + j * splitW, splitL * (i + 1)]
            colPointList.append({'colPoint': colPoint, 'rowPoint': rowPoint})
        pointList.append(colPointList)

    for i in range(row):
        for j in range(col):
            imbImg = imbList[i][j]
            new_img = imbImg

            if j != col - 1 and i != row - 1:
                l = imbImg.size[1]
                w = imbImg.size[0]
   
                l = l + r_d
                w = w + r_d

                new_img = Resize(imbImg, w, l)
            elif j == col - 1 and i != row - 1:
                new_img = Resize(imbImg, splitW, splitL + r_d)
            elif j != col - 1 and i == row - 1:
                new_img = Resize(imbImg, splitW + r_d, splitL)

            new_img_imb = new_img.load()

            if j != col - 1:
                    new_next_img = imbList[i][j + 1]
                    new_next_img_imb = new_next_img.load()
                    # 左右
                    for k in range((j + 1) * splitW, (j + 1) * splitW + r_d):
                        for z in range(i * splitL, (i + 1) * splitL):
                            r_w = pointList[i][j]['colPoint'][0]
                            r_l = pointList[i][j]['colPoint'][1]
                            r = ((pow(abs(k - r_w),2) + pow(abs(z - r_l),2))) ** 0.5
                            if r < r_d:
                                new_img_imb[k - j * splitW , z - i * splitL] = pimg[k, z]
                                new_next_img_imb[k - (j + 1) * splitW, z - i * splitL] = (255,255,255,0)

                    imbList[i][j + 1] = new_next_img      

            if i!= row - 1:
                    new_down_img = imbList[i + 1][j]
                    new_down_img_imb = new_down_img.load()
                    # 上下
                    for k in range(j * splitW, (j + 1) * splitW):
                        for z in range((i + 1) * splitL, (i + 1) * splitL + r_d):
                            r_w = pointList[i][j]['rowPoint'][0]
                            r_l = pointList[i][j]['rowPoint'][1]
                            r = ((pow(abs(k - r_w),2) + pow(abs(z - r_l),2))) ** 0.5
                            if r < r_d:
                                new_img_imb[k - j * splitW, z - i * splitL] = pimg[k, z]
                                new_down_img_imb[k - j * splitW, z - (i + 1) * splitL] = (255,255,255,0)
            
                    imbList[i + 1][j] = new_down_img     
            imbList[i][j] = new_img
            
        
    for i in range(row):
        for j in range(col):
            dirPath = path_name + "/" + str(i*10 + j) + ".png"
            imbList[i][j].save(dirPath)

最终版

随机凹凸, 考虑圆心偏移

import os
from PIL import Image
import random

# 分割几行几列, 二维数组保存
def SplitImages(img_path, row, col):
    path_name = os.path.dirname(img_path)
    img = Image.open(img_path).convert("RGBA")
    imgSize = img.size

    splitW = int(imgSize[0]/col)
    splitL = int(imgSize[1]/row)

    pimg = img.load()

    imbList = []
    for i in range(row):
        rowList = []
        l = (i + 1) * splitL
        for j in range(col):
            w = (j + 1) * splitW
            imb = Image.new('RGBA', (splitW, splitL),(255,255,255,0))
            pimb = imb.load()

            for k in range(j * splitW, w):
                for z in range(i * splitL, l):
                    pimb[k - splitW * j, z - i * splitL] = pimg[k,z]
            dirPath = path_name + "/" + str(i*10 + j) + ".png"
            # imb.save(dirPath)
            rowList.append(imb)
        imbList.append(rowList)

    return imbList

def Resize(img, rizeW, rizel, pastePoint=None): 
    if pastePoint is None:
        pastePoint = [0, 0]

    new_im = Image.new('RGBA', [rizeW, rizel],(255,255,255,0))
    new_im.paste(img, pastePoint)

    return new_im

def SplitCircle(imbList, imgPath):
    path_name = os.path.dirname(imgPath)
    img = Image.open(imgPath).convert("RGBA")
    imgSize = img.size

    col = len(imbList[0])
    row = len(imbList)

    if col == 1 and row == 1:
        return 

    splitW = int(imgSize[0]/col)
    splitL = int(imgSize[1]/row)

    minV = min(splitW, splitL)
    r_d = int(minV / 4) # 要计算 两个不能比 l 长 并且加上 offset 也不能超过 l
    r_offset = int(minV / 8)

    pSplitW = splitW + (r_d + r_offset) * 2
    pSplitL = splitL + (r_d + r_offset) * 2

    pimg = img.load()
    
	# 存(row - 1) * (col - 1) 个中心点
    pointList = []
    for i in range(row):
        colPointList = []
        for j in range(col):
            colPoint = []
            rowPoint = []
            if j != col - 1:
                colPoint = [splitW * (j + 1), int(splitL/2) + i * splitL]
            if i != row - 1:
                rowPoint = [int(splitW / 2) + j * splitW, splitL * (i + 1)]
            colPointList.append({'colPoint': colPoint, 'rowPoint': rowPoint})

            imbList[i][j] = Resize(imbList[i][j], pSplitW, pSplitL, [r_d + r_offset, r_d + r_offset])
            dirPath = path_name + "/" + str(i*10 + j) + ".png"
            # imbList[i][j].save(dirPath)
        pointList.append(colPointList)

    for i in range(row):
        for j in range(col):
            imbImg = imbList[i][j]
            new_img = imbImg

            # 圆心靠左 靠右, 默认靠右
            lrandNum = random.randint(0, 999999)
            drandNum = random.randint(0, 999999)
            lrRight = True
            drRight = True
        
            if lrandNum < 500000:
                lrRight = False
            if drandNum < 500000:
                drRight = False

            new_img_imb = new_img.load()

            if j != col - 1:

                if lrRight :
                    new_next_img = imbList[i][j + 1]
                    new_next_img_imb = new_next_img.load()
                    # 左右
                    for k in range((j + 1) * splitW, (j + 1) * splitW + r_d + r_offset):
                        for z in range(i * splitL, (i + 1) * splitL):
                            r_w = pointList[i][j]['colPoint'][0] + r_offset
                            r_l = pointList[i][j]['colPoint'][1] 
                            r = ((pow(abs(k - r_w),2) + pow(abs(z - r_l),2))) ** 0.5
                            if r < r_d:
                                new_img_imb[k - j * splitW + r_d + r_offset, z - i * splitL + r_d + r_offset] = pimg[k, z]
                                new_next_img_imb[k - (j + 1) * splitW + r_d + r_offset, z - i * splitL + r_d + r_offset] = (255,255,255,0)

                    imbList[i][j + 1] = new_next_img
                else:
                    new_next_img = imbList[i][j + 1]
                    new_next_img_imb = new_next_img.load()
                    # 左右
                    for k in range((j + 1) * splitW - r_d - r_offset, (j + 1) * splitW):
                        for z in range(i * splitL, (i + 1) * splitL):
                            r_w = pointList[i][j]['colPoint'][0] - r_offset
                            r_l = pointList[i][j]['colPoint'][1]
                            r = ((pow(abs(k - r_w),2) + pow(abs(z - r_l),2))) ** 0.5
                            if r < r_d:
                                new_next_img_imb[k - (j + 1) * splitW + r_d + r_offset, z - i 

以上是关于python pillow 把图片切成拼图块的主要内容,如果未能解决你的问题,请参考以下文章

如何给一个Sprite切成9片

C# 拼图游戏(超详细)

使用 Python Pillow 裁剪图像

使用python-pillow替换图片中的单一颜色

移动迷宫——拼图游戏

移动迷宫——拼图游戏