Объединить несколько изображений по горизонтали с Python
Я пытаюсь горизонтально объединить некоторые изображения JPEG в Python.
8 ответов:
вы можете сделать что-то вроде этого:
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):
вставляет каждое изображение 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 или все оттенки серого). Это не должно быть трудно убедиться, что это так с несколькими строками кода. Вот мой пример изображения, и результат:
Тест1.jpg
Условие_2.jpg
Test3.jpg
трио.jpg:
Trifecta_vertical.jpg
Edit: ответ DTing более применим к вашему вопросу, поскольку он использует PIL, но я оставлю это, если вы хотите знать, как это сделать в numpy.
вот решение numpy / matplotlib, которое должно работать для 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)) 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 я создал функцию, которая проще в использовании:
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
позволяет выбрать цвет фона и выравнивание изображения. Это также легко сделать рекурсию:
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:
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).
одним из преимуществ использования PIL над решениями на основе numpy-array является то, что вы можете работать с изображениями, структурированными по-разному (например, в оттенках серого или на основе палитры).
пример выходы
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)
:
""" merge_image takes three parameters first two parameters specify the two images to be merged and third parameter i.e. vertically is a boolean type which if True merges images vertically and finally saves and returns the file_name """ def merge_image(img1, img2, vertically): images = list(map(Image.open, [img1, img2])) widths, heights = zip(*(i.size for i in images)) if vertically: max_width = max(widths) total_height = sum(heights) new_im = Image.new('RGB', (max_width, total_height)) y_offset = 0 for im in images: new_im.paste(im, (0, y_offset)) y_offset += im.size[1] else: 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') return 'test.jpg'
просто добавляя к уже предложенным решениям. Предполагает одинаковую высоту, без изменения размера.
import sys import glob from PIL import Image Image.MAX_IMAGE_PIXELS = 100000000 # For PIL Image error when handling very large images imgs = [ Image.open(i) for i in list_im ] widths, heights = zip(*(i.size for i in imgs)) total_width = sum(widths) max_height = max(heights) new_im = Image.new('RGB', (total_width, max_height)) # Place first image new_im.paste(imgs[0],(0,0)) # Iteratively append images in list horizontally hoffset=0 for i in range(1,len(imgs),1): **hoffset=imgs[i-1].size[0]+hoffset # update offset** new_im.paste(imgs[i],**(hoffset,0)**) new_im.save('output_horizontal_montage.jpg')
from __future__ import print_function import os from pil import Image files = [ '1.png', '2.png', '3.png', '4.png'] result = Image.new("RGB", (800, 800)) for index, file in enumerate(files): path = os.path.expanduser(file) img = Image.open(path) img.thumbnail((400, 400), Image.ANTIALIAS) x = index // 2 * 400 y = index % 2 * 400 w, h = img.size result.paste(img, (x, y, x + w, y + h)) result.save(os.path.expanduser('output.jpg'))
выход