Frage PIL-Miniaturbild dreht mein Bild?


Ich versuche, große (riesige) Bilder (von einer Digitalkamera) aufzunehmen und sie in etwas umzuwandeln, das ich im Internet anzeigen kann. Dies scheint einfach und wahrscheinlich sollte es sein. Wenn ich jedoch versuche, PIL zu verwenden, um Miniaturversionen zu erstellen, wird das resultierende Bild um 90 Grad gedreht, wenn das Quellbild größer als breit ist, sodass sich das obere Bild des Quellbilds links vom resultierenden Bild befindet. Wenn das Quellbild breiter als hoch ist, hat das resultierende Bild die richtige (ursprüngliche) Ausrichtung. Könnte es mit dem 2-Tupel zu tun haben, das ich als Größe einsende? Ich verwende Thumbnail, weil es so aussieht, als ob es das Seitenverhältnis beibehalten würde. Oder bin ich einfach völlig blind und mache etwas Dummes? Das Tupel für die Größe ist 1000,1000, weil ich möchte, dass die längste Seite auf 1000 Pixel geschrumpft wird, während AR beibehalten wird.

Code scheint einfach zu sein

img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")

Vielen Dank im Voraus für jede Hilfe.


38
2017-11-19 19:14


Ursprung


Antworten:


Bitte beachten Sie, dass es unten bessere Antworten gibt.


Wenn ein Bild größer als breit ist, bedeutet dies, dass die Kamera gedreht wurde. Einige Kameras können dies erkennen und diese Informationen in die EXIF-Metadaten des Bildes schreiben. Einige Betrachter beachten diese Metadaten und zeigen das Bild entsprechend an.

PIL kann die Metadaten des Bildes lesen, aber es schreibt / kopiert keine Metadaten, wenn Sie ein Bild speichern. Folglich wird Ihr Smart Image Viewer das Bild nicht wie zuvor drehen.

Im Anschluss an den Kommentar von @Ignacio Vazquez-Abrams können Sie die Metadaten mit PIL auf diese Weise lesen und bei Bedarf drehen:

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")

Beachten Sie jedoch, dass der obige Code möglicherweise nicht für alle Kameras funktioniert.

Die einfachste Lösung ist vielleicht, ein anderes Programm zu verwenden, um Thumbnails zu erstellen.

blöd ist ein in Python geschriebener Batch-Foto-Editor, der EXIF-Metadaten verarbeiten kann. Sie können dieses Programm entweder verwenden, um Ihre Thumbnails zu erstellen, oder sich den Quellcode anschauen, um zu sehen, wie Sie dies in Python tun können. Ich glaube, es nutzt die pyexiv2 mit den EXIF-Metadaten umgehen. pyexiv2 kann EXIF ​​möglicherweise besser verarbeiten als das ExifTags-Modul von PIL.

Bildmagie ist eine weitere Möglichkeit, Batch-Thumbnails zu erstellen.


8
2017-11-19 19:38



Ich stimme fast allem zu, was von "unutbu" und Ignacio Vazquez-Abrams beantwortet wurde.

Das EXIF-Orientierungsflag kann einen Wert zwischen 1 und 8 haben, abhängig davon, wie die Kamera gehalten wurde.

Porträtfoto kann mit der Oberseite der Kamera auf dem linken oder rechten Rand genommen werden, Landschaftsfoto konnte auf den Kopf genommen werden.

Hier ist Code, der dies berücksichtigt (Getestet mit 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()

56
2018-06-02 18:05



xilvars Antwort ist sehr nett, hatte aber zwei kleinere Mängel, die ich in einer abgelehnten Bearbeitung beheben wollte, also poste ich sie als Antwort.

Zum einen schlägt die xilvar-Lösung fehl, wenn die Datei kein JPEG ist oder wenn keine Exif-Daten vorhanden sind. Und für den anderen rotierte es immer um 180 Grad statt der entsprechenden Menge.

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()

30
2017-07-18 14:12



Ich fühle mich dazu genötigt, meine Version zu teilen, die funktionell mit der in anderen Antworten vorgeschlagenen identisch ist, aber meiner Meinung nach sauberer ist:

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)

20
2018-05-26 15:30



Hier ist eine Version, die für alle 8 Orientierungen funktioniert:

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

19
2018-01-07 21:33



Hoopes Antwort ist großartig, aber es ist viel effizienter, die Transponiermethode zu verwenden, anstatt rotieren zu müssen. Drehen führt eine tatsächliche gefilterte Berechnung für jedes Pixel durch, effektiv eine komplexe Größenänderung des gesamten Bildes. Außerdem scheint die aktuelle PIL-Bibliothek einen Fehler zu haben, bei dem eine schwarze Linie zu den Kanten der gedrehten Bilder hinzugefügt wird. Transpose ist viel schneller und es fehlt dieser Bug. Ich habe nur die Antwort geändert, stattdessen transponieren.

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()

6
2018-03-09 02:14



Ich bin ein Anfänger auf Programmierung, Python und PIL, so dass die Codebeispiele in den vorherigen Antworten mir kompliziert erscheinen. Anstatt die Tags zu durchlaufen, ging ich einfach direkt zum Tag des Tags. In der Python-Shell können Sie sehen, dass der Schlüssel der Ausrichtung 274 ist.

>>>from PIL import ExifTags
>>>ExifTags.TAGS

Ich verwende das image._getexif() Funktion zu greifen, was ExifTags im Bild sind. Wenn das orientation-Tag nicht vorhanden ist, wird ein Fehler ausgegeben, daher verwende ich try / exception.

Die Dokumentation von Pillow sagt, es gibt keinen Unterschied in der Leistung oder den Ergebnissen zwischen Drehen und Transponieren. Ich habe es bestätigt, indem ich beide Funktionen zeitlich abgestimmt habe. Ich benutze drehen, weil es prägnanter ist.

rotate(90) dreht sich gegen den Uhrzeigersinn. Die Funktion scheint negative Grade zu akzeptieren.

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

5
2017-12-31 06:52



Hallo Ich habe versucht, Rotation des Bildes zu erreichen und dank vorheriger Antworten in diesem Beitrag habe ich es getan. Aber ich habe die Lösung aktualisiert und möchte sie teilen. Ich hoffe, dass jemand das nützlich finden wird.

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

Benutzen:

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

1
2018-02-05 16:00



Ich brauchte eine Lösung, die sich um alle Orientierungen kümmert, nicht nur 3, 6 und 8.

Ich habe Roman Odaisky ausprobiert Lösung - Es sah umfassend und sauber aus. Das Testen mit tatsächlichen Bildern mit verschiedenen Orientierungswerten führte jedoch manchmal zu fehlerhaften Ergebnissen (z. dieses mit Ausrichtung auf 0).

Ein weiterer machbare Lösung könnte Dobes Vandermeer sein. Aber ich habe es nicht versucht, weil ich denke, dass man die Logik einfacher schreiben kann (was ich bevorzuge).

Also, ohne weitere Umschweife, hier ist eine einfachere, wartungsfreundlichere (meiner Meinung nach) Version:

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

Getestet und gefunden, um Bilder mit allen genannten Exif-Orientierungen zu bearbeiten. Bitte führen Sie auch Ihre eigenen Tests durch.


1
2018-02-08 17:34



Es gibt einige gute Antworten hier, ich wollte nur eine aufgeräumte Version posten ... Die Funktion geht davon aus, dass Sie Image.open () bereits irgendwo ausgeführt haben und image.save () woanders verwenden und nur eine Funktion haben möchten, mit der Sie die Rotation fixieren können.

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

0
2018-04-13 20:25