2009-10-22 13 views
20

Próbuję użyć Pythona do zmiany rozmiaru obrazu. W moim aparacie wszystkie pliki są zapisywane w sposób krajobrazowy.Jak korzystać z PIL w celu zmiany rozmiaru i zastosowania informacji EXIF ​​z rotacji do pliku?

Informacje w pliku exif zajmują się znacznikiem, aby poprosić przeglądarkę obrazów o obrót w taki czy inny sposób. Ponieważ większość przeglądarki nie rozumie tych informacji, chcę obrócić obraz przy użyciu tej informacji EXIF ​​i zachowując wszystkie inne informacje EXIF.

Czy wiesz, jak to zrobić, używając Pythona?

Odczyt kodu źródłowego EXIF.py, znalazłem coś takiego:

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

jaki sposób mogę korzystać z tych informacji i PIL je stosować?

+0

Więcej informacji tutaj: http://www.abc-view.com/articles/article5.html Czy uważasz, że powinienem użyć funkcji z konkretnym procesem związanym z tą wartością? – Natim

+2

dobre pytanie! Czy PIL może bezstratnie obracać JPEG (jak 'jpegtran')? Bez bezstratnych transformacji nie brałbym tego pod uwagę. – u0b34a0f6ae

Odpowiedz

15

W końcu użyłem pyexiv2, ale jest to nieco trudne do zainstalowania na innych platformach niż 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 

Jeśli widzisz coś, co mogłoby być lepsze (z wyjątkiem faktu, że jest jeszcze dla Pythona 2.5) to proszę dać mi znać.

+1

Myślę, że funkcja listJPEG mogłaby być nieco krótsza, jeśli używałeś modułu glob do pobierania ścieżek plików, jest także os.makedirs w standardowej bibliotece, która powoduje, że twój _mkdir() jest przestarzały, blok kodu do zapisu rozmiarów JPEG i Kopiowanie metadanych powinno być refaktoryzowane do funkcji, aby uniknąć duplikowania kodu, może potrzebujesz dodatkowych parametrów wiersza poleceń, aby dostosować format pliku itp. – fbuchinger

+0

OK brzmi dobrze.Dziękuję :) – Natim

+0

Jestem trochę spóźniony na przyjęcie tutaj, ale odpowiedzi StackOverflow są licencjonowane pod cc-wiki, co jest w konflikcie z treścią tej odpowiedzi na licencji GPL. –

2

Najpierw upewnij się, że Twój aparat ma czujnik obrotów. Większość modeli kamer bez czujnika ustawia po prostu znacznik orientacji na 1 (w poziomie) dla wszystkich zdjęć.

Następnie polecam używać pyexiv2 i pyjpegtran w twoim przypadku. PIL nie obsługuje bezstratnej rotacji, która jest domeną pyjpegtran. pyexiv2 to biblioteka, która pozwala na kopiowanie metadanych z jednego obrazu do drugiego (myślę, że nazwa metody to copyMetadata).

Czy jesteś pewny, że nie chcesz zmienić rozmiaru zdjęć przed wyświetleniem ich w przeglądarce? 8-megapikselowy JPEG jest o wiele za duży dla okna przeglądarki i spowoduje niekończące się czasy ładowania.

+0

Tak jak powiedziałem, chcę zmienić rozmiar obrazu i obrócić je i zachować pewne przydatne informacje EXIF. – Natim

6

Mimo że PIL może czytać metadane EXIF, nie ma możliwości jego zmiany i zapisania z powrotem do pliku obrazu.

Lepszym wyborem jest biblioteka pyexiv2. W tej bibliotece bardzo łatwo odwrócić orientację zdjęcia. Oto przykład:

import sys 
import pyexiv2 

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

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

Ustawia orientację na 6, odpowiadającą "Obrócone 90 CW".

+0

W rzeczywistości kamera już ustawiła znacznik Exif.Image.Orientation, chcę napisać obraz w odpowiedniej orientacji, aby umożliwić przeglądarce jego wyświetlenie, nawet jeśli nie są w stanie zrozumieć informacji EXIF. W każdym razie, pyexiv2 jest biblioteką, której potrzebowałem. Jak widać w moim kodzie za. – Natim