online exifread python-2.7 python-imaging-library paste

python-2.7 - exifread - get metadata python



Combina varias imágenes horizontalmente con Python (6)

Estoy tratando de combinar horizontalmente algunas imágenes JPEG en Python.

Problema

Tengo 3 imágenes, cada una es 148 x 95 - ver adjunto. Acabo de hacer 3 copias de la misma imagen, es por eso que son lo mismo.

Mi intento

Estoy tratando de unirlos horizontalmente utilizando el siguiente código:

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'')

Sin embargo, esto está produciendo el resultado adjunto como test.jpg .

Pregunta

¿Hay alguna manera de concatenar horizontalmente estas imágenes de modo que las subimágenes en test.jpg no muestren una imagen parcial adicional?

Información Adicional

Estoy buscando una forma de concatenar horizontalmente n imágenes. Me gustaría usar este código en general, así que preferiría:

  • no codificar las dimensiones de la imagen, si es posible
  • especifique dimensiones en una línea para que puedan cambiarse fácilmente

Aquí hay una función que generaliza enfoques previos, creando una grilla de imágenes en 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

Reducirá al mínimo cada fila y columna de la grilla. Puede tener solo una fila usando pil_grid (imágenes), o solo una columna usando pil_grid (imágenes, 1).

Una de las ventajas del uso de PIL sobre soluciones basadas en numpy-array es que puede tratar con imágenes estructuradas de forma diferente (como imágenes en escala de grises o basadas en paletas).

Ejemplos de salidas

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) :


Basado en la respuesta de DTing, creé una función que es más fácil de usar:

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

Permite elegir un color de fondo y alineación de imagen. También es fácil de hacer la recursión:

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'')


Editar: La respuesta de DTing es más aplicable a su pregunta ya que usa PIL, pero lo dejaré en caso de que quiera saber cómo hacerlo en numpy.

Aquí hay una solución numpy / matplotlib que debería funcionar para N imágenes (solo imágenes en color) de cualquier tamaño / forma.

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

Aquí está el uso del ejemplo:

>>> images = ["ronda.jpeg", "rhod.jpeg", "ronda.jpeg", "rhod.jpeg"] >>> output = concat_n_images(images) >>> import matplotlib.pyplot as plt >>> plt.imshow(output) >>> plt.show()


Intentaré esto:

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'' )

Debería funcionar siempre que todas las imágenes sean de la misma variedad (todas RGB, todas RGBA o todas las escala de grises). No debería ser difícil asegurar que este sea el caso con algunas líneas más de código. Aquí están mis imágenes de ejemplo, y el resultado:

Test1.jpg

Test2.jpg

Test3.jpg

Trifecta.jpg:

Trifecta_vertical.jpg


Puedes hacer algo como esto:

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): se pega 5 veces cada imagen, se separa 95 píxeles. Cada iteración del bucle externo pegando sobre el anterior.

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'')


""" 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''