2009-10-22 12 views
20

Estoy tratando de usar Python para cambiar el tamaño de la imagen. Con mi cámara, todos los archivos están escritos de forma horizontal.¿Cómo se usa PIL para cambiar el tamaño y aplicar información EXIF ​​de rotación al archivo?

La información exif maneja una etiqueta para pedir al visor de imágenes que gire de una forma u otra. Como la mayoría del navegador no comprende esta información, quiero rotar la imagen usando esta información EXIF ​​y manteniendo cada otra información EXIF.

¿Sabes cómo puedo hacerlo usando Python?

lectura del código fuente EXIF.py, encontré algo así:

0x0112: ('Orientation', 
     {1: 'Horizontal (normal)', 
      2: 'Mirrored horizontal', 
      3: 'Rotated 180', 
      4: 'Mirrored vertical', 
      5: 'Mirrored horizontal then rotated 90 CCW', 
      6: 'Rotated 90 CW', 
      7: 'Mirrored horizontal then rotated 90 CW', 
      8: 'Rotated 90 CCW'}) 

¿Cómo puedo usar esta información y PIL para aplicarlo?

+0

Más información aquí: http://www.abc-view.com/articles/article5.html ¿Crees que debería usar una función con un proceso específico con respecto a este valor? – Natim

+2

¡buena pregunta! ¿Puede PIL rotar el JPEG sin pérdidas (como 'jpegtran')? Sin transformaciones sin pérdidas, no consideraría hacer esto. – u0b34a0f6ae

Respuesta

15

Finalmente utilicé pyexiv2, pero es un poco difícil de instalar en otras plataformas aparte de GNU.

#!/usr/bin/python 
# -*- coding: utf-8 -*- 
# Copyright (C) 2008-2009 Rémy HUBSCHER <[email protected]> - http://www.trunat.fr/portfolio/python.html 

# This program is free software; you can redistribute it and/or modify 
# it under the terms of the GNU General Public License as published by 
# the Free Software Foundation; either version 2 of the License, or 
# (at your option) any later version. 

# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
# GNU General Public License for more details. 

# You should have received a copy of the GNU General Public License along 
# with this program; if not, write to the Free Software Foundation, Inc., 
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

# Using : 
# - Python Imaging Library PIL http://www.pythonware.com/products/pil/index.htm 
# - pyexiv2      http://tilloy.net/dev/pyexiv2/ 

### 
# What is doing this script ? 
# 
# 1. Take a directory of picture from a Reflex Camera (Nikon D90 for example) 
# 2. Use the EXIF Orientation information to turn the image 
# 3. Remove the thumbnail from the EXIF Information 
# 4. Create 2 image one maxi map in 600x600, one mini map in 200x200 
# 5. Add a comment with the name of the Author and his Website 
# 6. Copy the EXIF information to the maxi and mini image 
# 7. Name the image files with a meanful name (Date of picture) 

import os, sys 
try: 
    import Image 
except: 
    print "To use this program, you need to install Python Imaging Library - http://www.pythonware.com/products/pil/" 
    sys.exit(1) 

try: 
    import pyexiv2 
except: 
    print "To use this program, you need to install pyexiv2 - http://tilloy.net/dev/pyexiv2/" 
    sys.exit(1) 

############# Configuration ############## 
size_mini = 200, 200 
size_maxi = 1024, 1024 

# Information about the Photograph should be in ASCII 
COPYRIGHT="Remy Hubscher - http://www.trunat.fr/" 
ARTIST="Remy Hubscher" 
########################################## 

def listJPEG(directory): 
    "Retourn a list of the JPEG files in the directory" 
    fileList = [os.path.normcase(f) for f in os.listdir(directory)] 
    fileList = [f for f in fileList if os.path.splitext(f)[1] in ('.jpg', '.JPG')] 
    fileList.sort() 
    return fileList 

def _mkdir(newdir): 
    """ 
    works the way a good mkdir should :) 
     - already exists, silently complete 
     - regular file in the way, raise an exception 
     - parent directory(ies) does not exist, make them as well 
    """ 
    if os.path.isdir(newdir): 
     pass 
    elif os.path.isfile(newdir): 
     raise OSError("a file with the same name as the desired " \ 
         "dir, '%s', already exists." % newdir) 
    else: 
     head, tail = os.path.split(newdir) 
     if head and not os.path.isdir(head): 
      _mkdir(head) 
     if tail: 
      os.mkdir(newdir) 

if len(sys.argv) < 3: 
    print "USAGE : python %s indir outdir [comment]" % sys.argv[0] 
    exit 

indir = sys.argv[1] 
outdir = sys.argv[2] 

if len(sys.argv) == 4: 
    comment = sys.argv[1] 
else: 
    comment = COPYRIGHT 

agrandie = os.path.join(outdir, 'agrandie') 
miniature = os.path.join(outdir, 'miniature') 

print agrandie, miniature 

_mkdir(agrandie) 
_mkdir(miniature) 

for infile in listJPEG(indir): 
    mini = os.path.join(miniature, infile) 
    grand = os.path.join(agrandie, infile) 
    file_path = os.path.join(indir, infile) 

    image = pyexiv2.Image(file_path) 
    image.readMetadata() 

    # We clean the file and add some information 
    image.deleteThumbnail() 

    image['Exif.Image.Artist'] = ARTIST 
    image['Exif.Image.Copyright'] = COPYRIGHT 

    image.setComment(comment) 

    # I prefer not to modify the input file 
    # image.writeMetadata() 

    # We look for a meanful name 
    if 'Exif.Image.DateTime' in image.exifKeys(): 
     filename = image['Exif.Image.DateTime'].strftime('%Y-%m-%d_%H-%M-%S.jpg') 
     mini = os.path.join(miniature, filename) 
     grand = os.path.join(agrandie, filename) 
    else: 
     # If no exif information, leave the old name 
     mini = os.path.join(miniature, infile) 
     grand = os.path.join(agrandie, infile) 

    # We create the thumbnail 
    #try: 
    im = Image.open(file_path) 
    im.thumbnail(size_maxi, Image.ANTIALIAS) 

    # We rotate regarding to the EXIF orientation information 
    if 'Exif.Image.Orientation' in image.exifKeys(): 
     orientation = image['Exif.Image.Orientation'] 
     if orientation == 1: 
      # Nothing 
      mirror = im.copy() 
     elif orientation == 2: 
      # Vertical Mirror 
      mirror = im.transpose(Image.FLIP_LEFT_RIGHT) 
     elif orientation == 3: 
      # Rotation 180° 
      mirror = im.transpose(Image.ROTATE_180) 
     elif orientation == 4: 
      # Horizontal Mirror 
      mirror = im.transpose(Image.FLIP_TOP_BOTTOM) 
     elif orientation == 5: 
      # Horizontal Mirror + Rotation 90° CCW 
      mirror = im.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.ROTATE_90) 
     elif orientation == 6: 
      # Rotation 270° 
      mirror = im.transpose(Image.ROTATE_270) 
     elif orientation == 7: 
      # Horizontal Mirror + Rotation 270° 
      mirror = im.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.ROTATE_270) 
     elif orientation == 8: 
      # Rotation 90° 
      mirror = im.transpose(Image.ROTATE_90) 

     # No more Orientation information 
     image['Exif.Image.Orientation'] = 1 
    else: 
     # No EXIF information, the user has to do it 
     mirror = im.copy() 

    mirror.save(grand, "JPEG", quality=85) 
    img_grand = pyexiv2.Image(grand) 
    img_grand.readMetadata() 
    image.copyMetadataTo(img_grand) 
    img_grand.writeMetadata() 
    print grand 

    mirror.thumbnail(size_mini, Image.ANTIALIAS) 
    mirror.save(mini, "JPEG", quality=85) 
    img_mini = pyexiv2.Image(mini) 
    img_mini.readMetadata() 
    image.copyMetadataTo(img_mini) 
    img_mini.writeMetadata() 
    print mini 

    print 

Si ves algo que podría mejorarse (excepto el hecho de que todavía es para Python 2.5), por favor hágamelo saber.

+1

Creo que la función listJPEG podría ser un poco más corta si usó el módulo glob para obtener los filepaths, también hay un os.makedirs en la biblioteca estándar que hace obsoleto a su _mkdir(), el bloque de código para escribir los archivos JPEG redimensionados y la copia de los metadatos se debe refactorizar a una función para evitar el código duplicado, tal vez desee parámetros de línea de comando adicionales para ajustar el formato de archivo, etc. – fbuchinger

+0

Ok suena bien.Gracias :) – Natim

+0

Llegué un poco tarde a la fiesta aquí, pero las respuestas de StackOverflow están autorizadas bajo cc-wiki, lo cual está en conflicto con el contenido de esta respuesta que está siendo GPL. –

2

Primero debe asegurarse de que su cámara tenga un sensor de rotación. La mayoría de los modelos de cámara sin sensor simplemente configuran la etiqueta de orientación en 1 (Horizontal) para TODAS las imágenes.

Entonces recomiendo usar pyexiv2 y pyjpegtran en su caso. PIL no admite rotación sin pérdida, que es el dominio de pyjpegtran. pyexiv2 es una biblioteca que te permite copiar metadatos de una imagen a otra (creo que el nombre del método es copyMetadata).

¿Estás seguro de que no quieres cambiar el tamaño de tus fotos antes de mostrarlas en el navegador? Un JPEG de 8 megapíxeles es demasiado grande para la ventana del navegador y provocará interminables tiempos de carga.

+0

Como dije, quiero cambiar el tamaño de la imagen, rotarla y mantener cierta información EXIF ​​útil. – Natim

6

Aunque PIL puede leer metadatos EXIF, no tiene la capacidad de cambiarlo y escribirlo en un archivo de imagen.

Una mejor opción es la biblioteca pyexiv2. Con esta biblioteca es bastante simple voltear la orientación de la foto. Aquí hay un ejemplo:

import sys 
import pyexiv2 

image = pyexiv2.Image(sys.argv[1]) 
image.readMetadata() 

image['Exif.Image.Orientation'] = 6 
image.writeMetadata() 

Esto establece la orientación en 6, correspondiente a "90 CW girados".

+0

En realidad, la cámara ya configuró la etiqueta Exif.Image.Orientation, quiero escribir la imagen en la orientación correcta para permitir que el navegador la muestre aunque no puedan comprender la información EXIF. De todos modos, pyexiv2 es la biblioteca que necesitaba. Como puedes ver en mi código detrás. – Natim

Cuestiones relacionadas