2009-10-22 4 views
20

Sto cercando di usare Python per ridimensionare l'immagine. Con la mia fotocamera, i file sono tutti scritti in modo orizzontale.Come utilizzare PIL per ridimensionare e applicare le informazioni EXIF ​​di rotazione al file?

Le informazioni exif gestiscono un tag per chiedere al visualizzatore di immagini di ruotare in un modo o nell'altro. Poiché la maggior parte del browser non capisce queste informazioni, voglio ruotare l'immagine usando queste informazioni EXIF ​​e mantenendo tutte le altre informazioni EXIF.

Sai come posso farlo usando Python?

Leggendo il codice sorgente EXIF.py, ho trovato qualcosa di simile:

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

Come posso utilizzare queste informazioni e PIL per applicarlo?

+0

Ulteriori informazioni qui: http://www.abc-view.com/articles/article5.html Pensi che dovrei utilizzare una funzione con un processo specifico relativo a questo valore? – Natim

+2

buona domanda! PIL può ruotare il JPEG senza perdite (come 'jpegtran')? Senza trasformazioni senza perdite, non prenderei in considerazione l'idea di farlo. – u0b34a0f6ae

risposta

15

Infine ho utilizzato pyexiv2, ma è un po 'complicato da installare su piattaforme diverse da 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 

Se vedi qualcosa che potrebbe essere migliorata (tranne il fatto che è ancora per Python 2.5), allora per favore fatemelo sapere.

+1

Penso che la funzione listJPEG potrebbe essere un po 'più breve se si utilizza il modulo glob per ottenere i percorsi dei file, c'è anche un os.makedirs nella libreria standard che rende obsoleto _mkdir(), il blocco di codice per scrivere i JPEG ridimensionati e copiare i metadati dovrebbe essere rifattorizzato su una funzione per evitare il codice duplicato, forse si vogliono parametri aggiuntivi della riga di comando per regolare il formato del file, ecc. – fbuchinger

+0

Ok suona bene.Grazie :) – Natim

+0

Sono in ritardo per la festa qui, ma le risposte StackOverflow sono concesse in licenza su cc-wiki, che è in conflitto con il contenuto di questa risposta GPL'ed. –

2

Per prima cosa è necessario assicurarsi che la propria videocamera abbia effettivamente un sensore di rotazione. La maggior parte dei modelli di fotocamere senza sensore imposta semplicemente il tag di orientamento su 1 (orizzontale) per TUTTE le immagini.

Quindi si consiglia di utilizzare pyexiv2 e pyjpegtran nel tuo caso. PIL non supporta la rotazione senza perdita, che è il dominio di pyjpegtran. pyexiv2 è una libreria che consente di copiare i metadati da un'immagine all'altra (penso che il nome del metodo sia copyMetadata).

Sei sicuro di non voler ridimensionare le foto prima di visualizzarle nel browser? Un JPEG da 8 Megapixel è troppo grande per la finestra del browser e causerà tempi di caricamento infiniti.

+0

Come ho detto, voglio ridimensionare l'immagine e ruotarla e conservare alcune utili informazioni EXIF. – Natim

6

Sebbene PIL possa leggere i metadati EXIF, non ha la possibilità di modificarlo e di scriverlo su un file immagine.

Una scelta migliore è la libreria pyexiv2. Con questa libreria è abbastanza semplice capovolgere l'orientamento della foto. Ecco un esempio:

import sys 
import pyexiv2 

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

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

Imposta l'orientamento su 6, corrispondente a "90 CW ruotati".

+0

In realtà, la fotocamera ha già impostato il tag Exif.Image.Orientation, voglio scrivere l'immagine nell'orientamento a destra per consentire al browser di visualizzarlo anche se non sono in grado di capire le informazioni EXIF. Ad ogni modo, pyexiv2 è la libreria di cui avevo bisogno. Come puoi vedere nel mio codice. – Natim

Problemi correlati