Python CV2 图片转为拼图 Puzzle,素材库这不就又丰富了~

在这里插入图片描述
在这里插入图片描述

import math
import random
import cv2
import numpy as np
import base64


def convex_concave():
    if random.random() < 0.5:
        return -1
    return 1


def random_blueprint(row, col):
    blueprint = []
    # init
    for i in range(0, row):
        blueprint.append([])
        for j in range(0, col):
            blueprint[i].append({
                'top': 0,
                'bottom': 0,
                'left': 0,
                'right': 0
            })

    for i in range(0, row):
        for j in range(0, col):
            # top
            if i != 0:
                blueprint[i][j]['top'] = blueprint[i - 1][j]['bottom'] * -1
            # bottom
            if i != row - 1:
                blueprint[i][j]['bottom'] = convex_concave()
            # left
            if j != 0:
                blueprint[i][j]['left'] = blueprint[i][j - 1]['right'] * -1
            # right
            if j != col - 1:
                blueprint[i][j]['right'] = convex_concave()

    return blueprint


def resize_image(row: int, col: int, image):
    size = image.shape
    L = math.floor(size[0] / row)
    if L % 2 == 0:
        L = L + 1

    return cv2.resize(image, (L * col, L * row))


def split_image_to_puzzle(image, row, col):
    image = resize_image(row, col, image)
    size = image.shape

    L = size[0] // row
    R = (L // 3 // 4) * 4

    a = (R * 3 // 4)
    blueprint = random_blueprint(row, col)
    image = add_image_transparent(image, a)
    cv2.imwrite(f"./image.png", image)

    puzzle_box = []
    for i in range(row):
        layer = []
        for j in range(col):
            batch = add_image_transparent(image[a + i * L:a + (i + 1) * L, a + j * L:a + (j + 1) * L, 0:3], a)
            convex_mask = build_circle_mask(image, blueprint, i, j, L, 1)
            concave_mask = build_circle_mask(image, blueprint, i, j, L, -1)
            puzzle = batch + convex_mask - concave_mask
            # cv2.imwrite(f"./batch_{i}_{j}.png", batch)
            # cv2.imwrite(f"./convex_mask_{i}_{j}.png", convex_mask)
            # cv2.imwrite(f"./concave_mask_{i}_{j}.png", concave_mask)
            cv2.imwrite(f"./puzzle_{i}_{j}.png", puzzle)
            puzzle_str = cv2.imencode('.png', puzzle)[1].tobytes()
            base64_code = base64.b64encode(puzzle_str)
            layer.append(puzzle)
        puzzle_box.append(layer)
    do_the_puzzle(puzzle_box, row, col, L, True)
    do_the_puzzle(puzzle_box, row, col, L, False)
    return puzzle_box


def do_the_puzzle(puzzle_box, row, col, L, rand):
    if len(puzzle_box) == 0 or len(puzzle_box[0]) == 0:
        return
    if not rand:
        R = (L // 3 // 4) * 4

        a = (R * 3 // 4)

        d = 5

        transparent = np.zeros((row * L + d * row + 2 * a, col * L + d * col + 2 * a, 4), dtype=np.uint8)
        for i in range(row):
            for j in range(col):
                ret, mask = cv2.threshold(puzzle_box[i][j][:, :, 3:4].reshape(L + 2 * a, L + 2 * a), 10, 255,
                                          cv2.THRESH_BINARY)

                # transparent[(10 + L) * i:(10 + L) * i + L + 2 * a, (10 + L) * j:(10 + L) * j + L + 2 * a, :] = \
                #     puzzle_box[i][j]
                batch = transparent[(d + L) * i:(d + L) * i + L + 2 * a, (d + L) * j:(d + L) * j + L + 2 * a, :]
                batch = cv2.bitwise_and(batch, batch, mask=cv2.bitwise_not(mask))
                transparent[(d + L) * i:(d + L) * i + L + 2 * a, (d + L) * j:(d + L) * j + L + 2 * a, :] = cv2.add(
                    batch, puzzle_box[i][j])
    else:
        size = puzzle_box[0][0].shape
        h = size[0]
        LL = math.floor(h * math.sqrt(3) / math.sqrt(2))
        if LL % 2 == 0:
            LL = LL + 1
        ll = LL // 2
        transparent = np.zeros((LL * row, LL * col, 4), dtype=np.uint8)

        for i in range(row):
            for j in range(col):
                rotate = rotate_bound(puzzle_box[i][j], random.randint(-10, 10))
                x, y = ll + j * LL, ll + i * LL
                h, w = rotate.shape[0:2]
                xx, yy = (col // 2 - j) * random.randint(ll // 2, ll), (row // 2 - i) * random.randint(ll // 2,
                                                                                                       ll)

                ret, mask = cv2.threshold(rotate[:, :, 3:4].reshape(h, w), 10, 255, cv2.THRESH_BINARY)

                batch = transparent[y - h // 2 + yy:y - h // 2 + h + yy, x - w // 2 + xx:x - w // 2 + w + xx, :]
                batch = cv2.bitwise_and(batch, batch, mask=cv2.bitwise_not(mask))
                transparent[y - h // 2 + yy:y - h // 2 + h + yy, x - w // 2 + xx:x - w // 2 + w + xx, :] = cv2.add(
                    batch, rotate)

    if rand:
        cv2.imwrite(f"./puzzle_rand.png", transparent)
    else:
        cv2.imwrite(f"./puzzle_fixed.png", transparent)

def rotate_bound(image, angle):
    # grab the dimensions of the image and then determine the
    # center
    (h, w) = image.shape[:2]
    (cX, cY) = (w / 2, h / 2)

    # grab the rotation matrix (applying the negative of the
    # angle to rotate clockwise), then grab the sine and cosine
    # (i.e., the rotation components of the matrix)
    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    # compute the new bounding dimensions of the image
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    # adjust the rotation matrix to take into account translation
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY

    # perform the actual rotation and return the image
    return cv2.warpAffine(image, M, (nW, nH))


def build_circle_mask(image, blueprint, i, j, L, type):
    item = blueprint[i][j]
    R = (L // 3 // 4) * 4
    l = L // 2
    a = (R * 3 // 4)
    b = R // 2
    transparent = np.zeros((L + 2 * a, L + 2 * a, 4), dtype=np.uint8)
    if type == 1:
        coordinate = [
            [
                [a + l + j * L, i * L + b],
                [a + l, b],
                [a + l + j * L - b, a + l + j * L + b],
                [i * L, a + i * L],
                item['top'],
            ],
            [
                [a + l + L * j, (i + 1) * L + R],
                [a + l, L + R],
                [a + l + j * L - b, a + l + j * L + b],
                [a + L + i * L, 2 * a + L + i * L],
                item['bottom'],
            ],
            [
                [j * L + b, a + l + i * L],
                [b, a + l],
                [j * L, a + j * L],
                [a + l + i * L - b, a + l + i * L + b],
                item['left']
            ],
            [
                [j * L + L + R, a + l + i * L],
                [L + R, a + l],
                [a + L + j * L, 2 * a + L + j * L],
                [a + l + i * L - b, a + l + i * L + b],
                item['right']
            ],
        ]
    elif type == -1:
        coordinate = [
            [
                [a + l + j * L, i * L + R],
                [a + l, R],
                [a + l + j * L - b, a + l + j * L + b],
                [i * L + a, 2 * a + i * L],
                item['top'],
            ],
            [
                [a + l + j * L, b + L + i * L],
                [a + l, L + b],
                [a + l + j * L - b, a + l + j * L + b],
                [L + i * L, a + L + i * L],
                item['bottom'],
            ],
            [
                [j * L + R, a + l + i * L],
                [R, a + l],
                [a + j * L, 2 * a + j * L],
                [a + l + i * L - b, a + l + i * L + b],
                item['left']
            ],
            [
                [j * L + L + b, a + l + i * L],
                [L + b, a + l],
                [L + j * L, a + L + j * L],
                [a + l + i * L - b, a + l + i * L + b],
                item['right']
            ],
        ]

    for coord in coordinate:
        x0, y0 = coord[0]
        x1, y1 = coord[1]
        if coord[4] == type:
            for ii in range(coord[2][0], coord[2][1]):
                for jj in range(coord[3][0], coord[3][1]):
                    if math.pow(ii - x0, 2) + math.pow(jj - y0, 2) < math.pow(b, 2):
                        transparent[jj - y0 + y1, ii - x0 + x1, :] = image[jj, ii, :]

                        # transparent[ii - x0 + x1, jj - y0 + y1, :] = transparent[ii - x0 + x1, jj - y0 + y1, :] * coord[2]
    return transparent


def add_image_transparent(image, a):
    size = image.shape

    transparent = np.zeros((size[0] + 2 * a, size[1] + 2 * a, 4), dtype=np.uint8)

    if size[2] == 3:
        transparent[a:-a, a:-a, 0:3] = image
        transparent[a:-a, a:-a, 3:4] = np.ones((size[0], size[1], 1), dtype=np.uint8) * 255
    elif size[2] == 4:
        transparent[a:-a, a:-a, :] = image

    return transparent


if __name__ == "__main__":
    image = cv2.imread('./jinx fan.png', cv2.IMREAD_UNCHANGED)
    split_image_to_puzzle(image, 6, 10)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值