使用不包含缩略图的 PIL 使用 Python 旋转 EXIF 中指定的方向的图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13872331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rotating an image with orientation specified in EXIF using Python without PIL including the thumbnail
提问by ATOzTOA
I have the following scenario:
我有以下场景:
- I am sending an image from iPhone along with the EXIF information to my Pyhon socket server.
- I need the image to be properly oriented based on the actual orientation when the image was taken. I know IOS always saves the image as Landscape Left and adds the actual orientation as EXIF field (EXIF.Image.Orientation).
- I am reading the EXIF field to see the actual orientation. Then I am rotating the image using wxpython to the proper orientation.
- 我正在从 iPhone 发送图像以及 EXIF 信息到我的 Pyhon 套接字服务器。
- 我需要根据拍摄图像时的实际方向正确定向图像。我知道 IOS 总是将图像保存为横向左侧并将实际方向添加为 EXIF 字段 (EXIF.Image.Orientation)。
- 我正在阅读 EXIF 字段以查看实际方向。然后我使用 wxpython 将图像旋转到正确的方向。
I am using pyexiv2 for EXIF manipulation.
我正在使用 pyexiv2 进行 EXIF 操作。
Issue: The EXIF information incluiding the thumbnails lost while rotating the image using wxpython.
问题:EXIF 信息包括使用 wxpython 旋转图像时丢失的缩略图。
What I did: I am reading the EXIF before rotating the image. I reset the orientation field in the EXIF. Then I am putting it back after rotation.
我做了什么:我在旋转图像之前阅读 EXIF。我重置了 EXIF 中的方向字段。然后我在旋转后把它放回去。
The problem:
问题:
The thumbnail inside the EXIF is not rotated. So, the image and thumbnail have different orientations.
EXIF 内的缩略图不会旋转。因此,图像和缩略图具有不同的方向。
Questions?
问题?
Is there any module other than PIL to rotate an image keeping its EXIF info?
除了 PIL 之外,还有其他模块可以旋转图像以保留其 EXIF 信息吗?
Is there a separate EXIF field for thumbnail orientation?
缩略图方向是否有单独的 EXIF 字段?
Is there a way I can just rotate the Thumbnail alone?
有没有办法我可以单独旋转缩略图?
Thanks for your help...
谢谢你的帮助...
采纳答案by ATOzTOA
I have found a solution... check this out... http://www.atoztoa.com/2012/12/rotate-images-along-with-thumbnails-in.html
我找到了一个解决方案......看看这个...... http://www.atoztoa.com/2012/12/rotate-images-along-with-thumbnails-in.html
'''
Rotate Image
'''
import pyexiv2
import wx
import cStringIO
import os
def rotateImage(infile, device):
try:
# Read Metadata from the image
metadata = pyexiv2.metadata.ImageMetadata(infile)
metadata.read();
# Let's get the orientation
orientation = metadata.__getitem__("Exif.Image.Orientation")
orientation = int(str(orientation).split("=")[1][1:-1])
# Extract thumbnail
thumb = metadata.exif_thumbnail
angle = 0
# Check the orientation field in EXIF and rotate image accordingly
if device == "iPhone" or device == "iPad":
# Landscape Left : Do nothing
if orientation == ORIENTATION_NORMAL:
angle = 0
# Portrait Normal : Rotate Right
elif orientation == ORIENTATION_LEFT:
angle = -90
# Landscape Right : Rotate Right Twice
elif orientation == ORIENTATION_DOWN:
angle = 180
# Portrait Upside Down : Rotate Left
elif orientation == ORIENTATION_RIGHT:
angle = 90
# Resetting Exif field to normal
print "Resetting exif..."
orientation = 1
metadata.__setitem__("Exif.Image.Orientation", orientation)
# Rotate
if angle != 0:
# Just rotating the image based on the angle
print "Rotating image..."
angle = math.radians(angle)
img = wx.Image(infile, wx.BITMAP_TYPE_ANY)
img_centre = wx.Point( img.GetWidth()/2, img.GetHeight()/2 )
img = img.Rotate( angle, img_centre, True )
img.SaveFile(infile, wx.BITMAP_TYPE_JPEG)
# Create a stream out of the thumbnail and rotate it using wx
# Save the rotated image to a temporary file
print "Rotating thumbnail..."
t = wx.EmptyImage(100, 100)
thumbStream = cStringIO.StringIO(thumb.data)
t.LoadStream(thumbStream, wx.BITMAP_TYPE_ANY)
t_centre = wx.Point( t.GetWidth()/2, t.GetHeight()/2 )
t = t.Rotate( angle, t_centre, True )
t.SaveFile(infile + ".jpg", wx.BITMAP_TYPE_JPEG)
thumbStream.close()
# Read the rotated thumbnail and put it back in the rotated image
thumb.data = open(infile + ".jpg", "rb").read();
# Remove temporary file
os.remove(infile + ".jpg")
# Write back metadata
metadata.write();
except Exception, e:
print "Error rotating image... : " + str(e)
回答by scabbiaza
This solution works for me: PIL thumbnail is rotating my image?
此解决方案适用于我: PIL 缩略图正在旋转我的图像?
Don't need to check if it's iPhone or iPad: if photo has orientation tag – rotate it.
不需要检查它是 iPhone 还是 iPad:如果照片有方向标签 - 旋转它。
from PIL import Image, ExifTags
try:
image=Image.open(filepath)
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.save(filepath)
image.close()
except (AttributeError, KeyError, IndexError):
# cases: image don't have getexif
pass
Before:
前:


After:

后:

回答by joelvarma
https://medium.com/@giovanni_cortes/rotate-image-in-django-when-saved-in-a-model-8fd98aac8f2a
https://medium.com/@giovanni_cortes/rotate-image-in-django-when-saved-in-a-model-8fd98aac8f2a
This blog post explains it clearly. Just make sure you try keeping the @receiver..code in forms.pyor models.pyas I got cannot import model/viewerrors .
这篇博文解释得很清楚。只要确保您尝试将@receiver..代码保留在forms.py或models.py当我遇到cannot import model/view错误时。
keep the rotate_imagemethod in models.py&
@receiver..code also in models.py.
将rotate_image方法保留在models.py&
@receiver..代码中也保留在models.py.
I also got errors like No such directory. Just make sure full_pathis set correctly to media folder.
我也有类似没有这样的目录的错误。只要确保full_path正确设置到媒体文件夹。
I used this line
我用过这条线
fullpath = os.path.join(os.path.dirname(BASE_DIR)) + instance.fimage.url
fullpath = os.path.join(os.path.dirname(BASE_DIR)) + instance.fimage.url
回答by GaretJax
If you're using Pillow >= 6.0.0, you can use the built-in ImageOps.exif_transposefunction do correctly rotate an image according to its exif tag:
如果您使用Pillow >= 6.0.0,则可以使用内置ImageOps.exif_transpose函数根据其 exif 标签正确旋转图像:
from PIL import ImageOps
image = ImageOps.exif_transpose(image)
回答by user136036
Since this is the top answer for "python exif rotate" I'd like to add an addendum in case you only need the rotation value and not have PIL rotate the image- in my case I used QPixmap to rotate the image on a QGraphicsView, so I only needed the angle for the QPixmap transformation.
The answerabove using PIL to get exif is rather slow. In my test it took 6x the time as the piexiflibrary did (6 ms vs 1 ms) because creating/opening the PIL.Image takes a lot of time. So I'd recommend using piexifin this case.
由于这是“python exif rotate”的最佳答案,我想添加一个附录,以防您只需要旋转值而不需要 PIL 旋转图像- 在我的情况下,我使用 QPixmap 在 QGraphicsView 上旋转图像,所以我只需要 QPixmap 转换的角度。上面使用PIL 获取 exif
的答案相当慢。在我的测试中,它花费的时间是piexif库的6 倍(6 毫秒 vs 1 毫秒),因为创建/打开 PIL.Image 需要很多时间。所以我建议piexif在这种情况下使用。
import piexif
def get_exif_rotation_angle(picture)
exif_dict = piexif.load(picture)
if piexif.ImageIFD.Orientation in exif_dict["0th"]:
orientation = exif_dict["0th"][piexif.ImageIFD.Orientation]
if orientation == 3:
return 180
elif orientation == 6:
return 90
elif orientation == 8:
return 270
else:
return None
else:
return None
picturecan befile path or bytes object.
picture可以是文件路径或字节对象。
Ref: https://piexif.readthedocs.io/en/latest/sample.html#rotate-image-by-exif-orientation
参考:https: //piexif.readthedocs.io/en/latest/sample.html#rotate-image-by-exif-orientation
回答by Hyagoro
Pretty much the same answer than @scabbiaza, but using transpose instead of rotate (for performance purposes).
与@scabbiaza 的答案几乎相同,但使用转置而不是旋转(出于性能目的)。
from PIL import Image, ExifTags
try:
image=Image.open(filepath)
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.transpose(Image.ROTATE_270)
elif exif[orientation] == 8:
image=image.transpose(Image.ROTATE_90)
image.save(filepath)
image.close()
except (AttributeError, KeyError, IndexError):
# cases: image don't have getexif
pass

