[OpenCV image implementation: Use OpenCV to generate puzzle effects]

overview

overview:

The puzzle effect is an artistic effect that cuts an image into adjacent squares and rearranges them. Different modes can be considered when generating a puzzle effect, including whether to consider intervals and how to handle non-divisible parts.

Ignore intervals and ignore parts that are not divisible:
There is no space between adjacent squares, and parts whose heights are not divisible are ignored.
The example image shows the squares arranged closely without any gaps, and the non-divisible parts are discarded.
Insert image description here

Irrespective of the spacing, the parts that are not divisible are filled with white space:
There is no gap between adjacent squares, and the parts whose height is not divisible are filled with white.
The example image shows seamless connections between squares, and the parts whose heights are not divisible are filled with white.
Insert image description here

Consider the gap and ignore the parts that are not divisible:
There is a gap between adjacent squares, the distance is 3 pixels, and the parts whose height is not divisible are ignored directly.
The example diagram shows the intervals between squares, and the parts that are not divisible are discarded.
Insert image description here

Consider the spacing and fill the parts that are not divisible with white space:
There is a gap between adjacent squares with a spacing of 3 pixels, and fill the parts whose height is not divisible with white.
The example image demonstrates the spacing between squares, and the parts whose height is not divisible are filled with white.
Insert image description here

common arrangement

In order to unify the above code, we set the general configuration items as follows:

config = {
    
    
    "cell_num":7,
    "whether_crop_image_height": True,
    "whether_with_gap": True,
    "gap_width":3
}

In the above configuration, the meanings of each item are as follows:

cell_num: represents the number of cells divided into each row

whether_crop_image_height: Indicates whether to fill in blanks for parts of the height that are not divisible

whether_with_gap: indicates whether there is a gap between adjacent squares

gap_width: represents the size of the gap between adjacent squares

Regardless of interval code implementation

1). Get the number of grids in each row and the input image information

w_count = config['cell_num']
src_height = img.shape[0]
src_width = img.shape[1]

2). Calculate the length of each grid and the width of the resulting image

sub_length = int(src_width / w_count)
new_width = sub_length * w_count

3). Calculate the height of the resulting image based on whether the height needs to be filled in the blanks.

if config['whether_crop_image_height']:
    h_count = int(src_height / sub_length)
else:
    h_count = math.ceil(src_height / sub_length)
new_height = sub_length * h_count

4).Assign values ​​to the result graph

img_t = np.zeros(shape=(new_height,new_width,3),dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    img_t = img[:new_height,:new_width,:]
else:
    img_t[:src_height, :new_width, :] = img[:src_height, :new_width, :]

5).Picture grid

 for x_i in range(1, w_count):
    cv2.line(img_t,(x_i*sub_length,0),(x_i*sub_length,new_height-1),color=(205,205,74),thickness=1)
for y_i in range(1, h_count):
    cv2.line(img_t,(0,y_i*sub_length),(new_width-1,y_i*sub_length), color=(205,205,74), thickness=1)

Complete code:

import cv2
import numpy as np
import math

# 通用配置
config = {
    
    
    "cell_num": 7,
    "whether_crop_image_height": True,
    "whether_with_gap": False,
    "gap_width": 0
}

# 读取图像
img = cv2.imread("img.png")

# 获取每行格子数目和输入图像信息
w_count = config['cell_num']
src_height = img.shape[0]
src_width = img.shape[1]

# 计算每个格子的长度以及结果图像的宽度
sub_length = int(src_width / w_count)
new_width = sub_length * w_count

# 根据是否需要对高度进行空白填充,计算结果图像的高度
if config['whether_crop_image_height']:
    h_count = int(src_height / sub_length)
else:
    h_count = math.ceil(src_height / sub_length)
new_height = sub_length * h_count

# 对结果图进行赋值
img_t = np.zeros(shape=(new_height, new_width, 3), dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    img_t = img[:new_height, :new_width, :]
else:
    img_t[:src_height, :new_width, :] = img[:src_height, :new_width, :]

# 画格子
for x_i in range(1, w_count):
    cv2.line(img_t, (x_i * sub_length, 0), (x_i * sub_length, new_height-1), color=(205, 205, 74), thickness=1)
for y_i in range(1, h_count):
    cv2.line(img_t, (0, y_i * sub_length), (new_width-1, y_i * sub_length), color=(205, 205, 74), thickness=1)

# 展示生成的拼图效果
cv2.imshow('Mosaic Effect', img_t)
cv2.waitKey(0)
cv2.destroyAllWindows()

Insert image description here
Results with padding:

import cv2
import numpy as np
import math

# 通用配置
config = {
    
    
    "cell_num": 7,
    "whether_crop_image_height": True,
    "whether_with_gap": False,
    "gap_width": 0
}

# 读取图像
img = cv2.imread("img.png")

# 获取每行格子数目和输入图像信息
w_count = config['cell_num']
src_height = img.shape[0]
src_width = img.shape[1]

# 计算每个格子的长度、间隔长度以及结果图像的宽度
sub_length = int(src_width / w_count)
gap_length = int(config["gap_width"])
new_width = sub_length * w_count + gap_length * (w_count - 1)

# 根据是否需要对高度进行空白填充,计算结果图像的高度
if config['whether_crop_image_height']:
    h_count = int((src_height + gap_length) / (sub_length + gap_length))
else:
    h_count = math.ceil((src_height + gap_length) / (sub_length + gap_length))
new_height = sub_length * h_count + gap_length * (h_count - 1)

# 对结果图进行赋值
img_t = np.zeros(shape=(new_height, new_width, 3), dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    for i in range(h_count):
        for j in range(w_count):
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            img_t[src_y:src_y + sub_length, src_x:src_x + sub_length, :] = img[begin_y:begin_y + sub_length, begin_x:begin_x + sub_length, :]
else:
    for i in range(h_count):
        for j in range(w_count):
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            if i < h_count - 1:
                img_t[src_y:src_y + sub_length, src_x:src_x + sub_length, :] = img[begin_y:begin_y + sub_length, begin_x:begin_x + sub_length, :]
            else:
                diff_height = src_height - sub_length * (h_count - 1)
                img_t[src_y:src_y + diff_height, src_x:src_x + sub_length, :] = img[begin_y:begin_y + diff_height, begin_x:begin_x + sub_length, :]

# 展示生成的拼图效果
cv2.imshow('Mosaic Effect with Fill', img_t)
cv2.waitKey(0)
cv2.destroyAllWindows()

Insert image description here

不考虑间隔,带填充的情况:
img_t = np.zeros(shape=(new_height, new_width, 3), dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    img_t = img[:new_height, :new_width, :]
else:
    img_t[:src_height, :new_width, :] = img[:src_height, :new_width, :]
考虑间隔,带填充的情况:
img_t = np.zeros(shape=(new_height, new_width, 3), dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    for i in range(h_count):
        for j in range(w_count):
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            img_t[src_y:src_y + sub_length, src_x:src_x + sub_length, :] = img[begin_y:begin_y + sub_length, begin_x:begin_x + sub_length, :]
else:
    for i in range(h_count):
        for j in range(w_count):
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            if i < h_count - 1:
                img_t[src_y:src_y + sub_length, src_x:src_x + sub_length, :] = img[begin_y:begin_y + sub_length, begin_x:begin_x + sub_length, :]
            else:
                diff_height = src_height - sub_length * (h_count - 1)
                img_t[src_y:src_y + diff_height, src_x:src_x + sub_length, :] = img[begin_y:begin_y + diff_height, begin_x:begin_x + sub_length, :]

In the case of padding, the portion of the height that is not divisible is padded with blanks. Puzzles with padding will look neater, while without padding there may be some truncation.

Consider interval code implementation

1). Get the number of grids in each row and the input image information

w_count = config['cell_num']
src_height = img.shape[0]
src_width = img.shape[1]

2). Calculate the length of each grid, the length of the interval and the width of the resulting image

sub_length = int(src_width / w_count)
gap_length = int(config["gap_width"])
new_width = sub_length * w_count + gap_length * (w_count -1)

3). Calculate the height of the resulting image based on whether the height needs to be filled in the blanks.

if config['whether_crop_image_height']:
    h_count = int( (src_height + gap_length) / (sub_length+gap_length))
else:
    h_count = math.ceil((src_height + gap_length) / (sub_length+gap_length))
new_height = sub_length * h_count + gap_length * (h_count-1)

4).Assign values ​​to the result graph

img_t = np.zeros(shape=(new_height,new_width,3),dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    for i in range(h_count):  
        for j in range(w_count): 
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            img_t[src_y:src_y+sub_length,src_x:src_x + sub_length,:] = img[begin_y:begin_y + sub_length,begin_x:begin_x + sub_length,  :]
else:
    for i in range(h_count):  
        for j in range(w_count): 
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            if i<h_count-1:
                img_t[src_y:src_y+sub_length,src_x:src_x + sub_length,:] = img[begin_y:begin_y + sub_length,begin_x:begin_x + sub_length,  :]
            else:
                diff_height = src_height - sub_length * (h_count-1)
                img_t[src_y:src_y + diff_height, src_x:src_x + sub_length, :] = img[begin_y:begin_y + diff_height,begin_x:begin_x + sub_length, :]

All code

import cv2
import numpy as np
import math

# 通用配置
config = {
    
    
    "cell_num": 7,
    "whether_crop_image_height": True,
    "whether_with_gap": True,
    "gap_width": 3
}

# 读取图像
img = cv2.imread("img.png")

# 获取每行格子数目和输入图像信息
w_count = config['cell_num']
src_height = img.shape[0]
src_width = img.shape[1]

# 计算每个格子的长度、间隔长度以及结果图像的宽度
sub_length = int(src_width / w_count)
gap_length = int(config["gap_width"])
new_width = sub_length * w_count + gap_length * (w_count - 1)

# 根据是否需要对高度进行空白填充,计算结果图像的高度
if config['whether_crop_image_height']:
    h_count = int((src_height + gap_length) / (sub_length + gap_length))
else:
    h_count = math.ceil((src_height + gap_length) / (sub_length + gap_length))
new_height = sub_length * h_count + gap_length * (h_count - 1)

# 对结果图进行赋值
img_t = np.zeros(shape=(new_height, new_width, 3), dtype=np.uint8) + 255
if config['whether_crop_image_height']:
    for i in range(h_count):
        for j in range(w_count):
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            img_t[src_y:src_y + sub_length, src_x:src_x + sub_length, :] = img[begin_y:begin_y + sub_length, begin_x:begin_x + sub_length, :]
else:
    for i in range(h_count):
        for j in range(w_count):
            begin_x = sub_length * j
            begin_y = sub_length * i
            src_x = gap_length * j + begin_x
            src_y = gap_length * i + begin_y
            if i < h_count - 1:
                img_t[src_y:src_y + sub_length, src_x:src_x + sub_length, :] = img[begin_y:begin_y + sub_length, begin_x:begin_x + sub_length, :]
            else:
                diff_height = src_height - sub_length * (h_count - 1)
                img_t[src_y:src_y + diff_height, src_x:src_x + sub_length, :] = img[begin_y:begin_y + diff_height, begin_x:begin_x + sub_length, :]

# 绘制间隔
if config['whether_with_gap']:
    for i in range(h_count):
        for j in range(w_count - 1):
            begin_x = sub_length * (j + 1) + gap_length * j
            begin_y = sub_length * i + gap_length * i
            cv2.line(img_t, (begin_x, begin_y), (begin_x, begin_y + sub_length), color=(205, 205, 74), thickness=1)

# 展示生成的带填充和间隔的拼图效果
cv2.imshow('Mosaic Effect with Fill and Gap', img_t)
cv2.waitKey(0)
cv2.destroyAllWindows()

Insert image description here

summary

By using the OpenCV library and the Python programming language, the image puzzle effect is generated.

Puzzle effect generation: According to user needs, two different puzzle effect generation methods are implemented.
One is to ignore the spacing, and you can choose whether to fill in the blanks for parts whose height is not divisible; the other is to consider the spacing, and you can choose whether there is a gap between adjacent squares, and the distance between them. Size, you can also choose whether to fill in the blanks for parts whose height is not divisible.
General configuration: General configuration items are introduced. Users can modify the configuration to adjust the number of grids of the puzzle, whether to crop the height, whether to consider the interval and the width of the interval and other parameters.
Code optimization: Encapsulates some functional functions to make the code more modular and readable.

Guess you like

Origin blog.csdn.net/weixin_47869094/article/details/134551688