texto - pil python 3
¿La miniatura de PIL está rotando mi imagen? (11)
Además de las otras respuestas, estaba teniendo problemas porque usaría im.copy()
antes de ejecutar las funciones; esto eliminaría los datos exif necesarios. Asegúrese de que antes de ejecutar im.copy()
guarde los datos exif:
try:
exif = im._getexif()
except Exception:
exif = None
# ...
# im = im.copy() somewhere
# ...
if exif:
im = transpose_im(im, exif)
Estoy intentando tomar imágenes grandes (enormes) (de una cámara digital) y convertirlas en algo que pueda mostrar en la web. Esto parece sencillo, y probablemente debería serlo. Sin embargo, cuando intento utilizar PIL para crear versiones en miniatura, si mi imagen de origen es más alta que ancha, la imagen resultante se gira 90 grados, de modo que la parte superior de la imagen de origen está a la izquierda de la imagen resultante. Si la imagen de origen es más ancha que alta, la imagen resultante tiene la orientación correcta (original). ¿Podría tener que ver con la tupla de 2 unidades que envío como tamaño? Estoy usando la miniatura, porque parece que estaba destinada a preservar la relación de aspecto. ¿O simplemente estoy siendo completamente ciego y haciendo algo tonto? El tamaño de la tupla es 1000,1000 porque quiero que el lado más largo se reduzca a 1000 píxeles, manteniendo el AR conservado.
El código parece simple
img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
Gracias de antemano por cualquier ayuda.
Aquí hay una versión que funciona para las 8 orientaciones:
def flip_horizontal(im): return im.transpose(Image.FLIP_LEFT_RIGHT)
def flip_vertical(im): return im.transpose(Image.FLIP_TOP_BOTTOM)
def rotate_180(im): return im.transpose(Image.ROTATE_180)
def rotate_90(im): return im.transpose(Image.ROTATE_90)
def rotate_270(im): return im.transpose(Image.ROTATE_270)
def transpose(im): return rotate_90(flip_horizontal(im))
def transverse(im): return rotate_90(flip_vertical(im))
orientation_funcs = [None,
lambda x: x,
flip_horizontal,
rotate_180,
flip_vertical,
transpose,
rotate_270,
transverse,
rotate_90
]
def apply_orientation(im):
"""
Extract the oritentation EXIF tag from the image, which should be a PIL Image instance,
and if there is an orientation tag that would rotate the image, apply that rotation to
the Image instance given to do an in-place rotation.
:param Image im: Image instance to inspect
:return: A possibly transposed image instance
"""
try:
kOrientationEXIFTag = 0x0112
if hasattr(im, ''_getexif''): # only present in JPEGs
e = im._getexif() # returns None if no EXIF data
if e is not None:
#log.info(''EXIF data found: %r'', e)
orientation = e[kOrientationEXIFTag]
f = orientation_funcs[orientation]
return f(im)
except:
# We''d be here with an invalid orientation value or some random error?
pass # log.exception("Error applying EXIF Orientation tag")
return im
Estoy de acuerdo con casi todo lo que responde "unutbu" e Ignacio Vazquez-Abrams, sin embargo ...
La bandera de orientación EXIF puede tener un valor entre 1 y 8, según la forma en que se sostuvo la cámara.
La foto de retrato se puede tomar con la parte superior de la cámara en el borde izquierdo o derecho, la foto de paisaje se puede tomar al revés.
Aquí está el código que tiene esto en cuenta (Probado con DSLR Nikon D80)
import Image, ExifTags
try :
image=Image.open(os.path.join(path, fileName))
for orientation in ExifTags.TAGS.keys() :
if ExifTags.TAGS[orientation]==''Orientation'' : break
exif=dict(image._getexif().items())
if exif[orientation] == 3 :
image=image.rotate(180, expand=True)
elif exif[orientation] == 6 :
image=image.rotate(270, expand=True)
elif exif[orientation] == 8 :
image=image.rotate(90, expand=True)
image.thumbnail((THUMB_WIDTH , THUMB_HIGHT), Image.ANTIALIAS)
image.save(os.path.join(path,fileName))
except:
traceback.print_exc()
Hay algunas respuestas buenas aquí, solo quería publicar una versión limpia ... La función asume que ya has hecho Image.open () en algún lugar, y haré image.save () en otro lugar y solo quieres una función que puedas caer para arreglar la rotación.
def _fix_image_rotation(image):
orientation_to_rotation_map = {
3: Image.ROTATE_180,
6: Image.ROTATE_270,
8: Image.ROTATE_90,
}
try:
exif = _get_exif_from_image(image)
orientation = _get_orientation_from_exif(exif)
rotation = orientation_to_rotation_map.get(orientation)
if rotation:
image = image.transpose(rotation)
except Exception as e:
# Would like to catch specific exceptions, but PIL library is poorly documented on Exceptions thrown
# Log error here
finally:
return image
def _get_exif_from_image(image):
exif = {}
if hasattr(image, ''_getexif''): # only jpegs have _getexif
exif_or_none = image._getexif()
if exif_or_none is not None:
exif = exif_or_none
return exif
def _get_orientation_from_exif(exif):
ORIENTATION_TAG = ''Orientation''
orientation_iterator = (
exif.get(tag_key) for tag_key, tag_value in ExifTags.TAGS.items()
if tag_value == ORIENTATION_TAG
)
orientation = next(orientation_iterator, None)
return orientation
Hola, estaba tratando de lograr la rotación de la imagen y gracias a las respuestas anteriores en este post lo hice. Pero actualicé la solución y me gustaría compartirla. Espero que alguien encuentre esto útil.
def get_rotation_code(img):
"""
Returns rotation code which say how much photo is rotated.
Returns None if photo does not have exif tag information.
Raises Exception if cannot get Orientation number from python
image library.
"""
if not hasattr(img, ''_getexif'') or img._getexif() is None:
return None
for code, name in ExifTags.TAGS.iteritems():
if name == ''Orientation'':
orientation_code = code
break
else:
raise Exception(''Cannot get orientation code from library.'')
return img._getexif().get(orientation_code, None)
class IncorrectRotationCode(Exception):
pass
def rotate_image(img, rotation_code):
"""
Returns rotated image file.
img: PIL.Image file.
rotation_code: is rotation code retrieved from get_rotation_code.
"""
if rotation_code == 1:
return img
if rotation_code == 3:
img = img.transpose(Image.ROTATE_180)
elif rotation_code == 6:
img = img.transpose(Image.ROTATE_270)
elif rotation_code == 8:
img = img.transpose(Image.ROTATE_90)
else:
raise IncorrectRotationCode(''{} is unrecognized ''
''rotation code.''
.format(rotation_code))
return img
Utilizar:
>>> img = Image.open(''/path/to/image.jpeg'')
>>> rotation_code = get_rotation_code(img)
>>> if rotation_code is not None:
... img = rotate_image(img, rotation_code)
... img.save(''/path/to/image.jpeg'')
...
La respuesta de los Hoopes es excelente, pero es mucho más eficiente usar el método de transposición en lugar de rotar. Rotar realiza un cálculo filtrado real para cada píxel, efectivamente un cambio de tamaño complejo de toda la imagen. Además, la biblioteca PIL actual parece tener un error en el que se agrega una línea negra a los bordes de las imágenes rotadas. Transposición es mucho más rápido y carece de ese error. Acabo de modificar las respuestas de los hoopes para usar la transposición en su lugar.
import Image, ExifTags
try :
image=Image.open(os.path.join(path, fileName))
for orientation in ExifTags.TAGS.keys() :
if ExifTags.TAGS[orientation]==''Orientation'' : break
exif=dict(image._getexif().items())
if exif[orientation] == 3 :
image=image.transpose(Image.ROTATE_180)
elif exif[orientation] == 6 :
image=image.rotate(Image.ROTATE_180)
elif exif[orientation] == 8 :
image=image.rotate(Image.ROTATE_180)
image.thumbnail((THUMB_WIDTH , THUMB_HIGHT), Image.ANTIALIAS)
image.save(os.path.join(path,fileName))
except:
traceback.print_exc()
La respuesta de xilvar es muy buena, pero tenía dos deficiencias menores que quería corregir en una edición rechazada, así que la publicaré como respuesta.
Por un lado, la solución de xilvar falla si el archivo no es un JPEG o si no hay datos exif presentes. Y para el otro, siempre giraba 180 grados en lugar de la cantidad apropiada.
import Image, ExifTags
try:
image=Image.open(os.path.join(path, fileName))
if hasattr(image, ''_getexif''): # only present in JPEGs
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation]==''Orientation'':
break
e = image._getexif() # returns None if no EXIF data
if e is not None:
exif=dict(e.items())
orientation = exif[orientation]
if orientation == 3: image = image.transpose(Image.ROTATE_180)
elif orientation == 6: image = image.transpose(Image.ROTATE_270)
elif orientation == 8: image = image.transpose(Image.ROTATE_90)
image.thumbnail((THUMB_WIDTH , THUMB_HIGHT), Image.ANTIALIAS)
image.save(os.path.join(path,fileName))
except:
traceback.print_exc()
Me siento obligado a compartir mi versión, que es funcionalmente idéntica a las sugeridas en otras respuestas, pero, en mi opinión, es más limpia:
import Image
import functools
def image_transpose_exif(im):
"""
Apply Image.transpose to ensure 0th row of pixels is at the visual
top of the image, and 0th column is the visual left-hand side.
Return the original image if unable to determine the orientation.
As per CIPA DC-008-2012, the orientation field contains an integer,
1 through 8. Other values are reserved.
"""
exif_orientation_tag = 0x0112
exif_transpose_sequences = [ # Val 0th row 0th col
[], # 0 (reserved)
[], # 1 top left
[Image.FLIP_LEFT_RIGHT], # 2 top right
[Image.ROTATE_180], # 3 bottom right
[Image.FLIP_TOP_BOTTOM], # 4 bottom left
[Image.FLIP_LEFT_RIGHT, Image.ROTATE_90], # 5 left top
[Image.ROTATE_270], # 6 right top
[Image.FLIP_TOP_BOTTOM, Image.ROTATE_90], # 7 right bottom
[Image.ROTATE_90], # 8 left bottom
]
try:
seq = exif_transpose_sequences[im._getexif()[exif_orientation_tag]]
except Exception:
return im
else:
return functools.reduce(type(im).transpose, seq, im)
Necesitaba una solución que cuidara todas las orientaciones, no solo 3
, 6
y 8
.
Probé la solution Roman Odaisky: se veía completa y limpia. Sin embargo, probarlo con imágenes reales con varios valores de orientación a veces condujo a resultados erróneos (por ejemplo, imgur.com/a/053MR con la orientación establecida en 0
).
Otra solución viable podría ser Dobes Vandermeer. Pero no lo he intentado, porque creo que uno puede escribir la lógica más sencillamente (lo que prefiero).
Así que sin más preámbulos, aquí hay una versión más simple, más mantenible (en mi opinión):
from PIL import Image
def reorient_image(im):
try:
image_exif = im._getexif()
image_orientation = image_exif[274]
if image_orientation in (2,''2''):
return im.transpose(Image.FLIP_LEFT_RIGHT)
elif image_orientation in (3,''3''):
return im.transpose(Image.ROTATE_180)
elif image_orientation in (4,''4''):
return im.transpose(Image.FLIP_TOP_BOTTOM)
elif image_orientation in (5,''5''):
return im.transpose(Image.ROTATE_90).transpose(Image.FLIP_TOP_BOTTOM)
elif image_orientation in (6,''6''):
return im.transpose(Image.ROTATE_270)
elif image_orientation in (7,''7''):
return im.transpose(Image.ROTATE_270).transpose(Image.FLIP_TOP_BOTTOM)
elif image_orientation in (8,''8''):
return im.transpose(Image.ROTATE_90)
else:
return im
except (KeyError, AttributeError, TypeError, IndexError):
return im
Probado, y encontrado para trabajar en imágenes con todas las orientaciones exif mencionadas. Sin embargo, por favor también haz tus propias pruebas también.
Soy un noob de la programación, Python y PIL, por lo que los ejemplos de código de las respuestas anteriores me parecen complicados. En lugar de iterar a través de las etiquetas, simplemente fui directamente a la clave de la etiqueta. En el shell de python, puedes ver que la clave de la orientación es 274.
>>>from PIL import ExifTags
>>>ExifTags.TAGS
Utilizo la función image._getexif()
para capturar los ExifTags que hay en la imagen. Si la etiqueta de orientación no está presente, produce un error, por lo que uso try / except.
La documentación de Pillow dice que no hay diferencia en el rendimiento o los resultados entre rotar y transponer. Lo he confirmado sincronizando ambas funciones. Yo uso rotar porque es más conciso.
rotate(90)
gira en sentido antihorario. La función parece aceptar grados negativos.
from PIL import Image, ExifTags
# Open file with Pillow
image = Image.open(''IMG_0002.jpg'')
#If no ExifTags, no rotating needed.
try:
# Grab orientation value.
image_exif = image._getexif()
image_orientation = image_exif[274]
# Rotate depending on orientation.
if image_orientation == 3:
rotated = image.rotate(180)
if image_orientation == 6:
rotated = image.rotate(-90)
if image_orientation == 8:
rotated = image.rotate(90)
# Save rotated image.
rotated.save(''rotated.jpg'')
except:
pass
Tenga en cuenta que hay mejores respuestas a continuación.
Cuando una imagen es más alta que ancha, significa que la cámara fue rotada. Algunas cámaras pueden detectar esto y escribir esa información en los metadatos EXIF de la imagen. Algunos espectadores toman nota de estos metadatos y muestran la imagen apropiadamente.
PIL puede leer los metadatos de la imagen, pero no escribe / copia metadatos cuando guarda una imagen. En consecuencia, su visor de imágenes inteligente no girará la imagen como lo hizo antes.
Siguiendo el comentario de @Ignacio Vazquez-Abrams, puedes leer los metadatos usando PIL de esta manera, y rotarlos si es necesario:
import ExifTags
import Image
img = Image.open(filename)
print(img._getexif().items())
exif=dict((ExifTags.TAGS[k], v) for k, v in img._getexif().items() if k in ExifTags.TAGS)
if not exif[''Orientation'']:
img=img.rotate(90, expand=True)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
Pero tenga en cuenta que el código anterior puede no funcionar para todas las cámaras.
La solución más fácil tal vez es usar algún otro programa para hacer miniaturas.
phatch es un editor de fotos por lotes escrito en Python que puede manejar / preservar metadatos EXIF. Puedes usar este programa para hacer tus miniaturas o mirar su código fuente para ver cómo hacerlo en Python. Creo que usa el pyexiv2 para manejar los metadatos EXIF. pyexiv2 puede manejar EXIF mejor que el módulo ExifTags de PIL.
imagemagick es otra posibilidad para hacer miniaturas por lotes.