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