2009-10-22 7 views
20

J'essaie d'utiliser Python pour redimensionner l'image. Avec mon appareil photo, les fichiers sont tous écrits en mode paysage.Comment utiliser PIL pour redimensionner et appliquer des informations de rotation EXIF ​​au fichier?

Les informations exif gèrent une étiquette pour demander à la visionneuse d'image de tourner d'une manière ou d'une autre. Étant donné que la plupart des navigateurs ne comprennent pas ces informations, je souhaite faire pivoter l'image à l'aide de ces informations EXIF ​​et conserver toutes les autres informations EXIF. Savez-vous comment je peux faire cela en utilisant Python?

La lecture du code source EXIF.py, je trouve quelque chose comme ça:

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

Comment puis-je utiliser cette information et PIL pour l'appliquer?

+0

Plus d'informations ici: http://www.abc-view.com/articles/article5.html Pensez-vous que je devrais utiliser une fonction avec un processus spécifique concernant cette valeur? – Natim

+2

bonne question! Est-ce que PIL peut faire tourner le JPEG sans perte (comme 'jpegtran')? Sans transformations sans perte, je ne considérerais pas cela. – u0b34a0f6ae

Répondre

15

J'ai finalement utilisé pyexiv2, mais il est un peu difficile à installer sur d'autres plateformes que 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 vous voyez quelque chose qui pourrait être amélioré (à l'exception du fait qu'il est encore pour Python 2.5), alors s'il vous plaît laissez-moi savoir.

+1

Je pense que la fonction listJPEG pourrait être un peu plus courte si vous utilisiez le module glob pour obtenir les chemins de fichiers, il y a aussi un os.makedirs dans la bibliothèque standard qui rend obsolète votre _mkdir(), le bloc de code copier les métadonnées doit être refactorisé à une fonction pour éviter le code en double, peut-être que vous voulez des paramètres de ligne de commande supplémentaires pour ajuster le format de fichier, etc – fbuchinger

+0

Ok sonne bien.Merci :) – Natim

+0

Je suis un peu en retard à la fête ici, mais les réponses StackOverflow sont sous licence sous cc-wiki, ce qui est en conflit avec le contenu de cette réponse étant sous GPL. –

2

Vous devez d'abord vous assurer que votre caméra est équipée d'un capteur de rotation. La plupart des modèles d'appareil photo sans capteur définissent simplement l'étiquette d'orientation sur 1 (Horizontal) pour TOUTES les images.

Ensuite, je recommande d'utiliser pyexiv2 et pyjpegtran dans votre cas. PIL ne supporte pas la rotation sans perte, qui est le domaine du pyjpegtran. pyexiv2 est une bibliothèque qui vous permet de copier des métadonnées d'une image à une autre (je pense que le nom de la méthode est copyMetadata). Etes-vous sûr de ne pas vouloir redimensionner vos photos avant de les afficher dans le navigateur? Un JPEG de 8 mégapixels est beaucoup trop grand pour la fenêtre du navigateur et entraînera des temps de chargement interminables.

+0

Comme je l'ai dit, je veux redimensionner l'image et les faire pivoter et conserver des informations EXIF ​​utiles. – Natim

6

Bien que PIL puisse lire les métadonnées EXIF, il n'a pas la possibilité de les modifier et de les réécrire dans un fichier image.

Un meilleur choix est la bibliothèque pyexiv2. Avec cette bibliothèque, il est assez simple de retourner l'orientation de la photo. Voici un exemple:

import sys 
import pyexiv2 

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

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

Ceci définit l'orientation à 6, correspondant à "Rotated 90 CW".

+0

En fait, la caméra a déjà défini la balise Exif.Image.Orientation, je veux écrire l'image dans le bon Orientation pour permettre au navigateur de l'afficher même s'ils ne peuvent pas comprendre les informations EXIF. Quoi qu'il en soit, pyexiv2 est la bibliothèque dont j'avais besoin. Comme vous pouvez le voir dans mon code derrière. – Natim

Questions connexes