python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python Pillow库图片拼接

Python使用Pillow库实现图片拼接的方法步骤

作者:detayun

在图像处理任务中,图片拼接是非常常见的需求,本文手把手教你用Pillow的Image.new()和paste()实现横向拼接、纵向拼接和矩阵拼接,完美复原九宫格切图,还会总结尺寸不一致、模式不匹配等常见踩坑点,让你的图片拼接不再出错所以,需要的朋友可以参考下

引言

在图像处理任务中,图片拼接是非常常见的需求:把多张小图拼成一张大图,常用于数据集还原、九宫格图片复原、图像切片后重组、拼图业务等。Pillow(PIL)通过Image.new()创建空白画布 + paste()粘贴图片实现拼接,支持横向拼接、纵向拼接、N×N矩阵拼接。

环境准备

pip install pillow

核心API说明

  1. Image.new(mode, size):创建空白画布
    • mode:图像模式,RGB彩色,RGBA带透明通道
    • size:(width, height)画布宽高
  2. canvas.paste(image, (x, y)):把图片粘贴到画布的(x,y)坐标位置
    • x:粘贴起始横坐标
    • y:粘贴起始纵坐标

注意:paste不会自动缩放图片,粘贴的子图尺寸必须和预留位置大小匹配。

场景1:横向拼接(左右排列)

把多张图片从左到右拼在一起,所有图片高度必须一致。

from PIL import Image
from typing import List


def concat_horizontal(img_list: List[Image.Image]) -> Image.Image:
    """横向拼接,图片从左向右排列,要求所有图片高度相同"""
    if not img_list:
        raise ValueError("图片列表不能为空")

    # 总宽度为所有图片宽度之和,高度取第一张图高度
    total_width = sum(img.width for img in img_list)
    height = img_list[0].height

    canvas = Image.new("RGB", (total_width, height))

    offset_x = 0
    for img in img_list:
        canvas.paste(img, (offset_x, 0))
        offset_x += img.width
    return canvas


# 示例调用
if __name__ == "__main__":
    im1 = Image.open("a.jpg")
    im2 = Image.open("b.jpg")
    im3 = Image.open("c.jpg")
    result = concat_horizontal([im1, im2, im3])
    result.save("horizontal.jpg")

场景2:纵向拼接(上下排列)

多张图片从上往下拼接,所有图片宽度必须一致。

def concat_vertical(img_list: List[Image.Image]) -> Image.Image:
    """纵向拼接,图片从上向下排列,要求所有图片宽度相同"""
    if not img_list:
        raise ValueError("图片列表不能为空")

    total_height = sum(img.height for img in img_list)
    width = img_list[0].width

    canvas = Image.new("RGB", (width, total_height))

    offset_y = 0
    for img in img_list:
        canvas.paste(img, (0, offset_y))
        offset_y += img.height
    return canvas


# result = concat_vertical([im1, im2, im3])
# result.save("vertical.jpg")

场景3:N×N矩阵拼接(和之前分割函数配套)

输入子图列表 + 矩阵阶数split_count,输出完整大图。

适用:3×3、4×4九宫格复原,子图尺寸全部相同。

def merge_image_grid(tile_list: List[Image.Image], split_count: int) -> Image.Image:
    """
    N × N 矩阵拼接,与split_image分割函数配套
    :param tile_list: split_image返回的子图列表,行优先顺序
    :param split_count: 分割份数,3代表3行3列
    :return: 拼接完成的PIL Image对象
    """
    if not tile_list:
        raise ValueError("子图列表不能为空")
    if len(tile_list) != split_count * split_count:
        raise ValueError(f"子图数量不匹配,需要{split_count*split_count}张,实际{len(tile_list)}张")

    tile_w, tile_h = tile_list[0].size
    canvas_w = tile_w * split_count
    canvas_h = tile_h * split_count

    canvas = Image.new("RGB", (canvas_w, canvas_h))

    index = 0
    for y in range(split_count):
        for x in range(split_count):
            canvas.paste(tile_list[index], (x * tile_w, y * tile_h))
            index += 1
    return canvas


if __name__ == "__main__":
    # 和分割代码完整联调示例
    from PIL import Image
    from your_module import split_image

    big_img = Image.open("300x300.jpg")
    tiles = split_image(big_img, split_count=3)
    merged_img = merge_image_grid(tiles, split_count=3)
    merged_img.save("merge_output.jpg")

场景4:自定义行列拼接(M行N列,非正方形)

如果你想要 2行3列,不是N×N正方形矩阵:

def merge_custom_grid(tile_list: List[Image.Image], rows: int, cols: int) -> Image.Image:
    """自定义行列矩阵拼接 rows行 cols列"""
    tile_w, tile_h = tile_list[0].size
    canvas = Image.new("RGB", (tile_w * cols, tile_h * rows))
    idx = 0
    for y in range(rows):
        for x in range(cols):
            canvas.paste(tile_list[idx], (x * tile_w, y * tile_h))
            idx += 1
    return canvas

# 示例:2行3列,共6张图片
# out = merge_custom_grid(tiles, rows=2, cols=3)

处理透明通道 RGBA

如果你的图片是png带透明,创建画布时把模式改为RGBA:

canvas = Image.new("RGBA", (canvas_w, canvas_h))

常见踩坑点

  1. 子图尺寸不一致:paste不会自动缩放,子图大小不一样会出现错位、溢出。需要先统一尺寸。
  2. 图片模式不匹配:RGB图片粘贴到RGBA画布可以;RGBA粘贴到RGB画布会丢失透明通道,建议统一模式。
  3. 列表顺序错误:矩阵拼接严格依赖列表顺序,必须和分割时遍历顺序(行优先)保持一致。
  4. 索引越界:子图数量不等于 rows×cols,直接抛出异常,避免生成错误图片。

完整闭环:分割 + 拼接完整示例

from PIL import Image
import os
from typing import List


def split_image(
    img: Image.Image,
    split_count: int,
    save_dir: str = None
) -> List[Image.Image]:
    w, h = img.size
    tile_w = w // split_count
    tile_h = h // split_count
    tile_list: List[Image.Image] = []
    for y in range(split_count):
        for x in range(split_count):
            left = x * tile_w
            upper = y * tile_h
            right = left + tile_w
            lower = upper + tile_h
            tile = img.crop((left, upper, right, lower))
            tile_list.append(tile)
            if save_dir is not None:
                os.makedirs(save_dir, exist_ok=True)
                tile.save(os.path.join(save_dir, f"tile_x{x}_y{y}.png"))
    return tile_list


def merge_image_grid(tile_list: List[Image.Image], split_count: int) -> Image.Image:
    if not tile_list:
        raise ValueError("子图列表不能为空")
    if len(tile_list) != split_count * split_count:
        raise ValueError(f"子图数量不匹配,需要{split_count*split_count}张,实际{len(tile_list)}张")
    tile_w, tile_h = tile_list[0].size
    canvas_w = tile_w * split_count
    canvas_h = tile_h * split_count
    canvas = Image.new("RGB", (canvas_w, canvas_h))
    index = 0
    for y in range(split_count):
        for x in range(split_count):
            canvas.paste(tile_list[index], (x * tile_w, y * tile_h))
            index += 1
    return canvas


if __name__ == "__main__":
    src_img = Image.open("test.jpg")
    tile_list = split_image(src_img, split_count=3)
    print(f"分割得到 {len(tile_list)} 张图片")
    restore_img = merge_image_grid(tile_list, split_count=3)
    restore_img.save("restore.jpg")
    print("完成分割复原,输出 restore.jpg")

总结

  1. Image.new()创建空白画布,paste()实现图片粘贴,是Pillow拼接的底层核心。
  2. 分为横向、纵向、矩阵N×N三种常用场景;矩阵拼接可以完美复原之前分割出来的图片。
  3. 注意图片尺寸、图像模式、列表顺序,这三个是拼接出错最高的地方。
  4. 分割与拼接组合,完成图片切片、复原完整流程,适合数据集预处理、九宫格业务。

拓展方向:可以增加边框、间隙,在子图之间添加空白间隔;也可以增加缩放逻辑自动适配子图大小。

以上就是Python使用Pillow库实现图片拼接的方法步骤的详细内容,更多关于Python Pillow库图片拼接的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文