Python 多图拼接,多图转PDF,一气呵成

对于一些零散的图片,想要把它们给拼接起来,有时候呢,再把它转化为PDF格式可能会更方便一点,那么接下来讲的可能就会派上用场。

没错,如标题所说,就一个库,PIL,就能实现这些功能【完整代码在文章最后】

当然整个过程还得依靠一下其他库来获取下文件,或者实现一些其它小操作
  • 首先,把需要拼接的图片保存到一个文件夹,保证该文件下除了需要的文件外没有其他图片文件。
  • 还有,要保证图片按名称排列的次序和拼接顺序一致(也不一定非这样,不这样的话,就需要写额外代码排序),而一般保存的时候一系列的图片的名称已经是有序的了,所以就先这么来吧。
    • 弄几张表情包来做例子吧:

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

  • 接下来要做的就是利用 os 库获取到这些图片文件的路径,并存入列表等待使用
    • 首先得获得文件夹的路径 path (当然是自己输入啦)
    • 然后再获取到文件夹的文件路径
path = r'c:\users\...\...'
imgs = [op.join(path, name) for name in os.listdir(path)]
不难发现,这获取到的不仅是文件夹下的图片路径,而是该文件下所有的文件路径, 没关系,后面可以通过试错来给它筛喽(当然也可以多写几行代码筛选一下,保留住图片的文件路径[依据后缀],甚至筛选出一些特定的图片文件)
  • 还有一个考虑,就是要拼接的所有图像尺寸是不是一致?

    • 很多情况下,像ppt、漫画,大多数情况每张图的尺寸是一样的,但是也不能排除尺寸不一的情况
    • 那么可以设置一个阀值,判断需不需要考虑尺寸不一样的情况
    • 如果需要考虑的话,就得获取每一张图像的尺寸,然后来计算拼图画布的大小
  • 接下来,就先循环遍历图片路径,把图片打开,并转换为 ‘RGB’ 格式(傻瓜式的统一下格式),然后把打开的图像注意添加到图像列表中
    • 设拼接所用画布高为hight,稍微调整一下,每张图间的间隔来个 5, 然后整体上下留白分别为 10, 那可以先算出所有留白的值
hight = 20 + 5*len(img_list)
  • 然后,遍历的时候,如果需要考虑图拍尺寸不一的情况,就得获取文件尺寸的列表,顺便还可以计算出画布的高度
for im in imgs:
    try:
        img = Image.open(im).convert('RGB')
        img_list.append(img)
        if if_parse:
            size_list.append(list(img.size))
            hight += img.size[1]
    except:
        pass

try 的使用,就不用怕遍历打开路径的时候遇到非图片而报错停止了

  • 获取到图片(或以及尺寸), 接下来就可以开始贴图了

  • 第一种情况,图片的尺寸不一
    • 先按图片宽度把尺寸排序,以获得最大宽
    • 然后再左右两边各留白 5, 计算出画布宽度
    • 得到尺寸后创建画布 canv ,底色默认白色,格式统一为 RGB
size_lt = sorted(size_list, key=lambda x: x[0])
width = size_lt[-1][0] + 10
canv = Image.new('RGB', (width, hight), (255, 255, 255))
然后就可以遍历贴图了
先获取当前要贴的图片的尺寸,然后用画布宽度减去图片宽度最后整除2得到贴图时左上角坐标,使得图片位置居中
y = 10  # 10 为上边的留白值
for i in range(len(img_list)):
    size = size_list[i]
    dx = (width - size[0])//2
    canv.paste(img_list[i], (dx, y))
y 为每次贴图的 ‘纵’ 坐标,每次贴图后要加上所贴图的高度以及留白值 5, 得到下一次贴图的 ‘纵’ 坐标
y = 10
for i in range(len(img_list)):
    size = size_list[i]
    dx = (width - size[0]) // 2
    canv.paste(img_list[i], (dx, y))
    img_list[i].close() # 用完就顺手关闭一下图片
    y += size[1] + 5
  • 另一种情况,图片尺寸一样
    • 先计算画布大小 ,画布宽度即是:随便一张图的宽度+留白值;画布高度即为:图片数x(任一张图的高度+图间留白值)+上下留白值
    • 同样,按尺寸创建画布
canv = Image.new('RGB', \
	(10 + img_list[0].size[0], 20 + len(img_list)*(img_list[0].size[1] + 5)), (255, 255, 255))  
接下来就是没有感情的贴图
for i in range(len(img_list)):
    canv.paste(img_list[i], (5, 10 + i * (5 + img_list[0].size[1])))
    img_list[i].close()
  • 再来点内容,白花花的底色,贴完图后加个框吧

draw = ImageDraw.Draw(canv)
draw.rectangle((0, 0, canv.size[0]-1, canv.size[1]-1), outline='black', width=5)

在这里插入图片描述

第一个元组参数,前两个是框框左上角的坐标,后两个是框框右下角的坐标
outline 是框框的颜色; width 是框框的宽度
弄完这些,保存一下吧
canv.save(out_path, 'PDF', resolution=100.0, save_all=True)

Ps: 要是不需要拼长图,而是需要一张图,一页pdf,那么就改一下保存的代码。

img_list[0].save(out.path, "PDF", resolution=100.0, save_all=True, append_images=img_list[1:])
这里解释一下,img_list 是Image对象的列表。另,这样的话,效果如下在这里插入图片描述

完整代码如下

  • path 参数即为上文的文件夹路径

  • show 用于判断是否展示最终图片

  • if_parse 用于告诉函数是否需要考虑图片尺寸不一的情况

  • out_path 即为输出的文件名(或者是路径,包含文件名)

from PIL import Image, ImageDraw
from random import randint
import os.path as op
import os

def paste_and_pdf(path, show=True, if_parse=None, out_path=r'c:\users\pxo\desktop\document_{}.pdf'.format(randint(0, 999))):
    while op.exists(out_path):
        out_path = r'c:\users\pxo\desktop\document_{}.pdf'.format(randint(0, 999))
    imgs = [op.join(path, name) for name in os.listdir(path)]
    size_list, img_list = [], []
    hight = 20 + 5*len(img_list)
    for im in imgs:
        try:
            img = Image.open(im).convert('RGB')
            img_list.append(img)
            if if_parse:
                size_list.append(list(img.size))
                hight += img.size[1]
        except:
            pass

    if if_parse:
        size_lt = sorted(size_list, key=lambda x: x[0])
        width = size_lt[-1][0] + 10
        canv = Image.new('RGB', (width, hight), (255, 255, 255))
        y = 10
        for i in range(len(img_list)):
            size = size_list[i]
            dx = (width - size[0])//2
            canv.paste(img_list[i], (dx, y))
            img_list[i].close()
            y += size[1]+5
    else:
        canv = Image.new('RGB', (10 + img_list[0].size[0], 20 + len(img_list)*(img_list[0].size[1] + 5)), (255, 255, 255))
        for i in range(len(img_list)):
            canv.paste(img_list[i], (5, 10 + i*(5+img_list[0].size[1])))
            img_list[i].close()
    draw = ImageDraw.Draw(canv)
    draw.rectangle((0, 0, canv.size[0]-1, canv.size[1]-1), outline='black', width=5)
    canv.save(out_path, 'PDF', resolution=100.0, save_all=True)
    if show:
        canv.show()


def old_paste_pic():
    #  im1.save(pdf_name, "PDF", resolution=100.0, save_all=True, append_images=im_list)
    path = r'c:\users\pxo\desktop\tt'
    imgs = [op.join(path, name) for name in os.listdir(path)]
    size = Image.open(imgs[0]).size
    new = Image.new('RGB', (size[0], size[1]*len(imgs)))
    for i in range(len(imgs)):
        im = Image.open(imgs[i])
        new.paste(im, (0, size[1]*i))

    new.show()
    new.save(op.join(path, 'new.jpeg'), quality=90)

def main():
    path = r'c:\users\pxo\desktop\ttt'
    out_path = op.join(path, 'out.pdf')
    paste_and_pdf(path, if_parse=1, out_path=out_path)

main()
  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
### 回答1: Opencv-python拼接是指将多张拼接成一张大的操作。这个操作可以用于制作全景拼接等等。在opencv-python中,可以使用cv2库中的函数cv2.stitcher_create()来实现多拼接。这个函数可以将多张拼接成一张大,并且可以自动调整片的位置和角度,使得拼接后的片更加自然。需要注意的是,多拼接需要保证每张片之间有重叠的部分,否则拼接后的片会出现断裂的情况。 ### 回答2: OpenCV是一个流行的计算机视觉库,由C++编写而成,也提供了Python接口。OpenCV-Python是Opencv的Python接口,它提供了大量方便易用的函数,可以处理像和视频等数据。 在OpenCV-Python中,可以使用cv2库中的函数来实现多拼接。通常情况下,多拼接需遵循以下步骤: 1. 加载每个像到程序中 2. 确定每个像相对于它们相关联的位置 3. 将这些像组合成最终的拼接像 下面是实现多拼接的基本流程: Step 1: 加载像 使用cv2.imread()函数加载所有需要拼接像,然后将这些像存储到一个列表中,以方便后续操作。 ```python import cv2 import numpy as np # Load all the images img1 = cv2.imread('image1.jpg') img2 = cv2.imread('image2.jpg') img3 = cv2.imread('image3.jpg') # Store all the images in a list images = [img1, img2, img3] ``` Step 2:确定每个像的位置 计算每个像与参考像之间的平移量和旋角度以获得它们的位置。可以使用cv2.estimateRigidTransform()函数来计算每个像相对于参考像的偏移量。该函数需要两张像作为参数,并返回一个2x3的仿射变换矩阵。 ```python # Estimate the translation and rotation # to align all the images with the first image num_images = len(images) reference_image = images[0] transforms = [] for i in range(1, num_images): # Calculate the transformation matrix transform = cv2.estimateRigidTransform(images[i], reference_image, False) transforms.append(transform) ``` Step 3:拼接像 使用cv2.warpAffine()函数将每个像变换,并将它们组合成一个大的拼接像。首先创建一个空像,然后使用cv2.warpAffine()函数将每个像变换为它们的实际位置,并将它们添加到空像中。 ```python # Combine all the images max_x = 0 max_y = 0 for i in range(num_images): rows, cols, channels = images[i].shape dst = cv2.warpAffine(images[i], transforms[i], (cols, rows)) max_x = max(max_x, transforms[i][0, 2]) max_y = max(max_y, transforms[i][1, 2]) # Create an empty image panorama = np.zeros((max_y, max_x + cols, 3), np.uint8) # Add the images to the panorama panorama[0:rows, 0:cols] = img1 panorama[0:rows, int(transforms[0][0, 2]):cols+int(transforms[0][0, 2])] = img2 panorama[0:rows, int(transforms[1][0, 2]):cols+int(transforms[1][0, 2])] = img3 ``` 以上就是基本的多拼接流程。当然,这种拼接方式有很多的局限性,例如对光线的要求高,对像的重叠区域要求高,需要手动校准等。因此,如果需要更加高质量的拼接,需使用更加复杂的算法,例如SIFT和SURF算法。这些算法可以实现自动化的拼接,可以处理复杂的场景,但也需要更多的计算资源和算法知识来实现。 ### 回答3: 对于需要将多张拼接到一起的应用场景,opencv-python提供了一种有效的解决方案。在opencv-python中,我们可以使用函数cv2.hconcat和cv2.vconcat来进行像的水平和竖直方向的拼接。 假设我们有三张大小相同的片A、B和C,它们的宽度分别为width,高度为height。现在我们需要将这三张片水平拼接在一起,那么我们可以使用以下代码: ``` import cv2 # 读取片 img_a = cv2.imread('a.jpg') img_b = cv2.imread('b.jpg') img_c = cv2.imread('c.jpg') # 拼接 result = cv2.hconcat([img_a, img_b, img_c]) # 显示拼接后的片 cv2.imshow('Result', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这段代码中,我们首先通过cv2.imread函数读取了三张片A、B和C。然后,我们使用cv2.hconcat函数将这三张片在水平方向上拼接在一起,得到了一个新的像result。最后,我们使用cv2.imshow函数将拼接后的片显示在窗口中。 在进行拼接时,我们还需要注意以下几点: 1. 片尺寸必须相同。如果进行拼接的多张片尺寸不同,我们需要使用cv2.resize函数将其缩放到相同的尺寸。 2. 片数量不能太多。如果需要拼接片数量过多,可能会导致拼接后的片过于宽或过于高,无法在屏幕上完整显示。 3. 拼接后的片可能存在黑边。如果拼接的多张片宽度不足,拼接后的片可能会在右侧出现黑边;如果拼接的多张片高度不足,拼接后的片可能会在下方出现黑边。解决这个问题的方法是在拼接前使用cv2.copyMakeBorder函数在片边缘添加一定量的填充。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的壳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值