【问题标题】:How do I concatenate *up to* 4 images in a grid with Python?如何使用 Python 在网格中连接 * 最多 * 4 个图像?
【发布时间】:2020-12-27 16:35:48
【问题描述】:

我正在学习如何使用 PIL,我想将 4 个单独的图像连接到一个网格中(全部为 256x256 PNG)。使用 PIL(以及可选的 NumPy)。通过我找到的几个示例,我已经能够通过stacking them all horizontally/verticallyas a grid when I have exactly 4 images. 连接图像

我现在想要做的是将 最多 4 张图像组合成一个网格,即将 1 到 4 张图像之间的任意位置传递给一个函数。

img1 img2 img3 img4

用例:

1 image 2 images 3 images 4 images
or

我的第一个想法是将图像列表分成两对,水平连接每个结果,然后垂直连接两个结果,但感觉效率低下。

编辑:我已经使用 OpenCV 完成了上述工作,因为它比 PIL 更容易使用。这是我的代码:

images = ["images/img1.png", "images/img2.png", "images/img3.png", "images/img4.png"]

def combine(images):
    if len(images) == 1:
        img1 = cv2.imread(images[0])
        return cv2.imwrite("Combined.png", img1)
    elif len(images) == 2:
        img1 = cv2.imread(images[0])
        img2 = cv2.imread(images[1])

        combined_image = cv2.hconcat([img1, img2])
        return cv2.imwrite("Combined.png", combined_image)
    elif len(images) == 3:
        img1 = cv2.imread(images[0])
        img2 = cv2.imread(images[1])
        img3 = cv2.imread(images[2])
        img4 = cv2.imread("images/Blank.png") # Just a transparent PNG

        image_row_1 = cv2.hconcat([img1, img2])
        image_row_2 = cv2.hconcat([img3, img4])
        combined_image = cv2.vconcat([image_row_1, image_row_2])

        return cv2.imwrite("Combined.png", combined_image)
    elif len(images) == 4:
        img1 = cv2.imread(images[0])
        img2 = cv2.imread(images[1])
        img3 = cv2.imread(images[2])
        img4 = cv2.imread(images[3])

        image_row_1 = cv2.hconcat([img1, img2])
        image_row_2 = cv2.hconcat([img3, img4])
        combined_image = cv2.vconcat([image_row_1, image_row_2])

        return cv2.imwrite("Combined.png", combined_image)


combine(images)

有没有更好的办法?

【问题讨论】:

    标签: python python-3.x numpy python-imaging-library


    【解决方案1】:

    如果您对 NumPy 的替代方案感兴趣。这是在 2x2 网格上平铺 14 图像的一种方法。

    考虑将输入x 定义为形状为(n, c, h, w) 的数组,其中n 是图像的数量(可以是12、3 或 4),c 通道数(此处为 3)和 h, w 图像的高度和宽度(我们将坚持 2x2 但它适用于任何尺寸。

    出于演示目的,x 定义为:

    x = np.array([
        np.arange(1, 5).reshape((2,2))*1,
        np.arange(1, 5).reshape((2,2))*10,
        np.arange(1, 5).reshape((2,2))*100,
        np.arange(1, 5).reshape((2,2))*1000,
    ])
    x = np.stack([x]*3, axis=1) # make it 3-channel
    

    首先用零填充输入作为丢失图像的占位符。这将确保我们拥有4, c, h, w 的形状,即dim=0 上的四个图像:

    x_fill = np.concatenate([x, np.zeros((4-n, c, h, w))])
    

    首先广播到 2x2x... 形状(按列!使用order='F'):

    x_fill = x_fill.reshape((2, 2, c, h, w), order='F')
    

    最后连接axis=3然后axis=1

    grid = np.concatenate(np.concatenate(x_fill, axis=3), axis=1)
    

    这里是输出(只显示grid的第一个频道):

    • 1 张图片:

      array([[[  1.,   2.,   0.,   0.],
              [  3.,   4.,   0.,   0.],
              [  0.,   0.,   0.,   0.],
              [  0.,   0.,   0.,   0.]]])
      
    • 2 张图片:

      array([[[  1.,   2.,  10.,  20.],
              [  3.,   4.,  30.,  40.],
              [  0.,   0.,   0.,   0.],
              [  0.,   0.,   0.,   0.]]])
      
    • 3 张图片:

      array([[[  1.,   2.,  10.,  20.],
              [  3.,   4.,  30.,  40.],
              [100., 200.,   0.,   0.],
              [300., 400.,   0.,   0.]]])
      
    • 4 张图片:

      array([[[  1.,   2.,  10.,  20.],
              [  3.,   4.,  30.,  40.],
              [100., 200.,1000.,2000.],
              [300., 400.,3000.,4000.]]])
      

    输入 x 如下所示:

    array([[[[   1,    2],
             [   3,    4]],
    
            [[   1,    2],
             [   3,    4]],
    
            [[   1,    2],
             [   3,    4]]],
    
    
           [[[  10,   20],
             [  30,   40]],
    
            [[  10,   20],
             [  30,   40]],
    
            [[  10,   20],
             [  30,   40]]],
    
    
           [[[ 100,  200],
             [ 300,  400]],
    
            [[ 100,  200],
             [ 300,  400]],
    
            [[ 100,  200],
             [ 300,  400]]],
    
    
           [[[1000, 2000],
             [3000, 4000]],
    
            [[1000, 2000],
             [3000, 4000]],
    
            [[1000, 2000],
             [3000, 4000]]]])
    

    【讨论】:

    • 嗨@Ivan,谢谢你的回答!当我尝试阅读和理解这一点时,我编辑了我的帖子以添加您要求的当前解决方案的工作示例。我唯一的问题是a)它会产生一个空白区域,它应该在3图像输出中是透明的,b)我想知道它是否是一种有效的做事方式。让我知道你的想法。
    • 如果您想坚持使用 OpenCV 并在那里找到可行的解决方案,那么您当然可以。但是,我相信使用 NumPy 处理您的数据会更快。不要引用我的话,这可能取决于执行的操作类型。如果您真的想知道哪个更快,您可以随时运行这两个版本并分析/计时它们以进行比较。
    【解决方案2】:
    1. 创建一个所需大小的新 Image(),可以使用 Image.new() 以所需形状保存所有图像
    2. 使用 paste() 方法将所有图像以您想要的形状和位置粘贴到较大的图像中。

    您必须做出一些决定,例如,如果您有 3 张图片,您是把一张放在另一张旁边,第三张放在它们的下方或上方,还是将它们堆叠起来,或者将三张并排放置。 如果您需要保持其原始尺寸,事情会变得复杂。然后,您必须计算如何安排它们以使网格保持美观,而不仅仅是一个接一个。

    如果您可以将所有子图像调整为相同大小,那么您只需要通过行和列进行嵌套循环并将每个子图像粘贴到其分配的位置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      • 1970-01-01
      • 2016-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      相关资源
      最近更新 更多