用Python水平合并几个图像
我正在尝试在Python中水平合并一些JPEG图像。
问题:我有3个图像 – 每个是148×95 – 见附件。 我只是做了3张相同的图像 – 这就是为什么他们是相同的。
我的尝试:我试图水平join他们使用下面的代码:
import sys from PIL import Image list_im = ['Test1.jpg','Test2.jpg','Test3.jpg'] new_im = Image.new('RGB', (444,95)) #creates a new empty image, RGB mode, and size 444 by 95 for elem in list_im: for i in xrange(0,444,95): im=Image.open(elem) new_im.paste(im, (i,0)) new_im.save('test.jpg')
但是,这是产生作为test.jpg附加的输出。
问题:有没有办法水平连接这些图像,使test.jpg中的子图像没有额外的部分图像显示?
附加信息:我正在寻找一种方法来水平连接n个图像。 我想通常使用这个代码,所以我宁愿:
- 如果可能,不要硬编码图像尺寸
- 在一行中指定尺寸,以便可以轻松更改
你可以做这样的事情:
import sys from PIL import Image images = map(Image.open, ['Test1.jpg', 'Test2.jpg', 'Test3.jpg']) widths, heights = zip(*(i.size for i in images)) total_width = sum(widths) max_height = max(heights) new_im = Image.new('RGB', (total_width, max_height)) x_offset = 0 for im in images: new_im.paste(im, (x_offset,0)) x_offset += im.size[0] new_im.save('test.jpg')
Test1.jpg
Test2.jpg
Test3.jpg
test.jpg
for i in xrange(0,444,95):
嵌套了for i in xrange(0,444,95):
粘贴每个图像5次,相差95个像素。 每个外部循环迭代粘贴在前面。
for elem in list_im: for i in xrange(0,444,95): im=Image.open(elem) new_im.paste(im, (i,0)) new_im.save('new_' + elem + '.jpg')
我会试试这个:
import numpy as np import PIL list_im = ['Test1.jpg', 'Test2.jpg', 'Test3.jpg'] imgs = [ PIL.Image.open(i) for i in list_im ] # pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here) min_shape = sorted( [(np.sum(i.size), i.size ) for i in imgs])[0][1] imgs_comb = np.hstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) ) # save that beautiful picture imgs_comb = PIL.Image.fromarray( imgs_comb) imgs_comb.save( 'Trifecta.jpg' ) # for a vertical stacking it is simple: use vstack imgs_comb = np.vstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) ) imgs_comb = PIL.Image.fromarray( imgs_comb) imgs_comb.save( 'Trifecta_vertical.jpg' )
只要所有图像具有相同的品种(全部为RGB,全部为RGBA或全部灰度),就应该可以工作。 用几行代码来确保这种情况应该不难。 这里是我的示例图像,结果:
Test1.jpg
Test2.jpg
Test3.jpg
Trifecta.jpg:
Trifecta_vertical.jpg
编辑:DTing的答案是更适用于你的问题,因为它使用PIL,但我会留下来,以防止你想知道如何在numpy做到这一点。
这是一个numpy / matplotlib解决scheme应该适用于任何大小/形状的N图像(只有彩色图像)。
import numpy as np import matplotlib.pyplot as plt def concat_images(imga, imgb): """ Combines two color image ndarrays side-by-side. """ ha,wa = imga.shape[:2] hb,wb = imgb.shape[:2] max_height = np.max([ha, hb]) total_width = wa+wb new_img = np.zeros(shape=(max_height, total_width, 3), dtype=np.uint8) new_img[:ha,:wa]=imga new_img[:hb,wa:wa+wb]=imgb return new_img def concat_n_images(image_path_list): """ Combines N color images from a list of image paths. """ output = None for i, img_path in enumerate(image_path_list): img = plt.imread(img_path)[:,:,:3] if i==0: output = img else: output = concat_images(output, img) return output
这里是示例使用:
>>> images = ["ronda.jpeg", "rhod.jpeg", "ronda.jpeg", "rhod.jpeg"] >>> output = concat_n_images(images) >>> import matplotlib.pyplot as plt >>> plt.imshow(output) >>> plt.show()
基于DTing的答案,我创build了一个更易于使用的function:
from PIL import Image def append_images(images, direction='horizontal', bg_color=(255,255,255), aligment='center'): """ Appends images in horizontal/vertical direction. Args: images: List of PIL images direction: direction of concatenation, 'horizontal' or 'vertical' bg_color: Background color (default: white) aligment: alignment mode if images need padding; 'left', 'right', 'top', 'bottom', or 'center' Returns: Concatenated image as a new PIL image object. """ widths, heights = zip(*(i.size for i in images)) if direction=='horizontal': new_width = sum(widths) new_height = max(heights) else: new_width = max(widths) new_height = sum(heights) new_im = Image.new('RGB', (new_width, new_height), color=bg_color) offset = 0 for im in images: if direction=='horizontal': y = 0 if aligment == 'center': y = int((new_height - im.size[1])/2) elif aligment == 'bottom': y = new_height - im.size[1] new_im.paste(im, (offset, y)) offset += im.size[0] else: x = 0 if aligment == 'center': x = int((new_width - im.size[0])/2) elif aligment == 'right': x = new_width - im.size[0] new_im.paste(im, (x, offset)) offset += im.size[1] return new_im
它允许select背景颜色和图像alignment。 recursion也很容易:
images = map(Image.open, ['hummingbird.jpg', 'tiger.jpg', 'monarch.png']) combo_1 = append_images(images, direction='horizontal') combo_2 = append_images(images, direction='horizontal', aligment='top', bg_color=(220, 140, 60)) combo_3 = append_images([combo_1, combo_2], direction='vertical') combo_3.save('combo_3.png')
这是一个泛化以前方法的函数,在PIL中创build一个图像网格:
from PIL import Image import numpy as np def pil_grid(images, max_horiz=np.iinfo(int).max): n_images = len(images) n_horiz = min(n_images, max_horiz) h_sizes, v_sizes = [0] * n_horiz, [0] * (n_images // n_horiz) for i, im in enumerate(images): h, v = i % n_horiz, i // n_horiz h_sizes[h] = max(h_sizes[h], im.size[0]) v_sizes[v] = max(v_sizes[v], im.size[1]) h_sizes, v_sizes = np.cumsum([0] + h_sizes), np.cumsum([0] + v_sizes) im_grid = Image.new('RGB', (h_sizes[-1], v_sizes[-1]), color='white') for i, im in enumerate(images): im_grid.paste(im, (h_sizes[i % n_horiz], v_sizes[i // n_horiz])) return im_grid
它会将网格的每一行和列缩小到最小。 通过使用pil_grid(图像)可以只有一行,或者通过使用pil_grid(图像,1)只能有一列。
在基于numpyarrays的解决scheme上使用PIL的一个好处是可以处理不同结构的图像(如灰度或基于调色板的图像)。
示例输出
def dummy(w, h): "Produces a dummy PIL image of given dimensions" from PIL import ImageDraw im = Image.new('RGB', (w, h), color=tuple((np.random.rand(3) * 255).astype(np.uint8))) draw = ImageDraw.Draw(im) points = [(i, j) for i in (0, im.size[0]) for j in (0, im.size[1])] for i in range(len(points) - 1): for j in range(i+1, len(points)): draw.line(points[i] + points[j], fill='black', width=2) return im dummy_images = [dummy(20 + np.random.randint(30), 20 + np.random.randint(30)) for _ in range(10)]
pil_grid(dummy_images)
:
pil_grid(dummy_images, 3)
:
pil_grid(dummy_images, 1)
: