PIL thumbnail obraca moje zdjęcie?
Próbuję zrobić duże (ogromne) zdjęcia (z aparatu cyfrowego) i przekształcić je w coś, co mogę wyświetlić w Internecie. Wydaje się to proste i prawdopodobnie powinno być. Jednak, gdy próbuję użyć PIL do tworzenia miniaturek, jeśli mój obraz źródłowy jest wyższy niż szeroki, wynikowy obraz jest obrócony o 90 stopni, tak że góra obrazu źródłowego znajduje się po lewej stronie wynikowego obrazu. Jeśli obraz źródłowy jest szerszy niż wysoki, wynikający obraz jest prawidłową (oryginalną) orientacją. Czy to może mieć związek z 2 krotką, którą wysłałem jako rozmiar? Używam miniaturki, ponieważ wygląda na to, że miała ona zachować proporcje. Czy po prostu jestem ślepa i robię coś głupiego? Rozmiar krotki to 1000,1000, ponieważ chcę, aby najdłuższa strona została zmniejszona do 1000 pikseli, zachowując AR.
Kod wydaje się prosty
img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
Z góry dzięki za pomoc. 13 answers
Proszę zauważyć, że poniżej znajdują się lepsze odpowiedzi.
Gdy obraz jest wyższy niż szeroki, oznacza to, że kamera została obrócona. Niektóre Kamery Mogą to wykryć i zapisać te informacje w metadanych EXIF obrazu. Niektórzy widzowie odnotowują te metadane i odpowiednio wyświetlają obraz.
PIL może odczytywać metadane obrazu, ale nie zapisuje/nie kopiuje metadanych podczas zapisywania obrazu. W związku z tym inteligentna przeglądarka obrazów nie będzie obracać obrazu w taki sposób, jak to miało miejsce wcześniej.
Kontynuując komentarz @ Ignacio Vazquez-Abrams, możesz odczytać metadane używając PIL w ten sposób i obracać w razie potrzeby:
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")
Należy jednak pamiętać, że powyższy kod może nie działać dla wszystkich kamer.
Najprostszym rozwiązaniem może być użycie innego programu do tworzenia miniaturek.
Phatch to wsadowy edytor zdjęć napisany w Pythonie, który może obsługiwać / przechowywać metadane EXIF. Możesz albo użyć tego programu do tworzenia miniaturek, albo spojrzeć na jego kod źródłowy, aby zobaczyć, jak to zrobić w Pythonie. Wydaje mi się, że używa pyexiv2 do obsługi metadanych EXIF. pyexiv2 może być w stanie obsłużyć Exif lepiej niż Moduł ExifTags PIL.
Imagemagick to kolejna możliwość tworzenia miniatur wsadowych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-29 19:37:11
Zgadzam się z prawie wszystkim, na co odpowiedzieli jednak" unutbu " i Ignacio Vazquez-Abrams...
Znacznik orientacji EXIF może mieć wartość od 1 do 8 w zależności od sposobu trzymania kamery.
Zdjęcie portretowe można zrobić górną częścią aparatu po lewej lub prawej krawędzi, zdjęcie krajobrazowe można zrobić do góry nogami.
Oto kod, który bierze to pod uwagę (testowany z lustrzanką 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()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-02 18:05:44
Czuję się zmuszony do podzielenia się moją wersją, która jest funkcjonalnie identyczna z tą sugerowaną w innych odpowiedziach, ale jest moim zdaniem czystsza:
import functools
from PIL import Image
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.
Parameters
----------
im: PIL.Image
The image to be rotated.
"""
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-29 13:01:10
ODPOWIEDŹ Xilvara jest bardzo ładna, ale miała dwa drobne niedociągnięcia, które chciałem naprawić w odrzuconej edycji, więc wrzucę ją jako odpowiedź.
Po pierwsze, rozwiązanie xilvara nie powiedzie się, jeśli plik nie jest JPEG lub jeśli nie ma danych exif. A dla drugiego zawsze obracał się o 180 stopni zamiast odpowiedniej ilości.
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()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-21 17:50:08
Oto wersja, która działa dla wszystkich 8 orientacji:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-07 23:10:54
Jestem noobem do programowania, Pythona i PIL więc przykłady kodu w poprzednich odpowiedziach wydają mi się skomplikowane. Zamiast przeglądać tagi, po prostu poszedłem prosto do klucza tagu. W powłoce Pythona widać, że kluczem orientation jest 274.
>>>from PIL import ExifTags
>>>ExifTags.TAGS
Używam funkcji image._getexif()
, Aby pobrać to, co Exiftagi są na obrazku. Jeśli znacznik orientacji nie jest obecny, wyświetla błąd, więc używam try / except.
Dokumentacja poduszki mówi, że nie ma różnicy w wydajność lub Wyniki między rotacją a transpozycją. Potwierdziłem to, synchronizując obie funkcje. Używam rotate, ponieważ jest bardziej zwięzły.
rotate(90)
obraca się w kierunku przeciwnym do ruchu wskazówek zegara. Funkcja zdaje się przyjmować ujemne stopnie.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-31 06:52:44
Potrzebowałem rozwiązania, które zajmie się wszystkimi orientacjami, nie tylko 3
, 6
i 8
.
0
).
Inne realne rozwiązanie może być Dobes Vandermeer. ale nie próbowałem, bo czuję, że można napisać logikę więcej po prostu (co wolę).
Więc bez zbędnych ceregieli, oto prostsza, łatwiejsza do utrzymania (moim zdaniem) wersja:
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
Przetestowano i okazało się, że działa na obrazach ze wszystkimi wymienionymi orientacjami exif. Jednak prosimy również o wykonanie własnych testów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-08 17:34:22
Pillow posiada API do obsługi znaczników orientacji EXIF automatycznie:
from PIL import Image, ImageOps
original_image = Image.open(filename)
fixed_image = ImageOps.exif_transpose(original_image)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-29 03:31:06
Odpowiedź Hoopes jest świetna, ale o wiele bardziej efektywne jest stosowanie metody transpozycji niż obracanie. Rotate wykonuje rzeczywiste przefiltrowane obliczenia dla każdego piksela, skutecznie złożoną zmianę rozmiaru całego obrazu. Ponadto, obecna biblioteka PIL wydaje się mieć błąd, w którym czarna linia jest dodawana do krawędzi obróconych obrazów. Transpozycja jest o wiele szybsza i brakuje tego błędu. Właśnie podkręciłem hoopes answer, aby zamiast tego użyć transpozycji.
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()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-09 02:14:21
Dodając do innych odpowiedzi, miałem problemy, ponieważ używałem im.copy()
przed uruchomieniem funkcji-to pozbawiłoby niezbędne dane exif. Upewnij się, że przed uruchomieniem im.copy()
zapisujesz dane exif:
try:
exif = im._getexif()
except Exception:
exif = None
# ...
# im = im.copy() somewhere
# ...
if exif:
im = transpose_im(im, exif)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-16 00:48:57
Witam starałem się osiągnąć rotację obrazu i dzięki poprzednim odpowiedziom w tym poście zrobiłem to. Ale ulepszyłem rozwiązanie i chciałbym się nim podzielić. Mam nadzieję, że komuś się to przyda.
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
Użycie:
>>> 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')
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-05 16:00:02
Jest łatwiejsze podejście do tego wszystkiego:
from PIL import image
im1 = Image.open(path_to_image)
im1.thumbnail(size1, Image.ANTIALIAS)
y, z = im1.size
d = z * 1.5
if y > d:
im1.rotate(90, expand=1)
Mam nadzieję, że to pomoże:)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-18 10:29:46
Jest tu kilka dobrych odpowiedzi, chciałem tylko zamieścić wersję oczyszczoną... Funkcja zakłada, że już zrobiłeś Obraz.otwórz gdzieś () i wykona obraz.Zapisz () gdzie indziej i po prostu chcesz funkcję, którą możesz wpaść, aby naprawić rotację.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 20:25:13