2010-11-19 56 views
37

我試圖從數碼相機拍攝大(巨大)圖像,並將它們轉換爲我可以在網上顯示的東西。這似乎很直接,可能應該是。但是,當我嘗試使用PIL創建縮略圖版本時,如果我的源圖像高於寬度,則生成的圖像將旋轉90度,以使源圖像的頂部位於生成圖像的左側。如果源圖像比較高,則生成的圖像是正確的(原始)方向。它可能與我以尺寸發送的二元組有關嗎?我使用縮略圖,因爲它看起來是爲了保持寬高比。還是我完全失明,做些愚蠢的事情?大小元組爲1000,1000,因爲我希望最長的一側縮小到1000像素,同時保留AR。PIL縮略圖正在旋轉我的圖像?

代碼似乎提前任何幫助,簡單

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

感謝。

回答

7

請注意,下面有更好的答案。


當圖片高於寬度時,表示相機已旋轉。有些相機可以檢測到這一點,並在圖片的EXIF元數據中寫入該信息。一些觀衆注意到這個元數據並適當地顯示圖像。

PIL可以讀取圖片的元數據,但是當您保存圖像時它不會寫入/複製元數據。因此,您的智能圖像查看器不會像以前那樣旋轉圖像。

上@Ignacio巴斯克斯 - 艾布拉姆斯的評論之後,你就可以使用PIL這種方式讀取元數據,並旋轉如有必要:

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

但要注意的是,上面的代碼可能不適用於所有的相機工作。

最簡單的解決方案可能是使用其他程序製作縮略圖。

phatch是一個用Python編寫的批處理照片編輯器,可以處理/保存EXIF元數據。你可以使用這個程序來製作你的縮略圖,或者查看它的源代碼,看看如何在Python中做到這一點。我相信它使用pyexiv2來處理EXIF元數據。 pyexiv2可能比PIL的ExifTags模塊更好地處理EXIF。

imagemagick是製作批量縮略圖的另一種可能性。

+0

或者事先讀取EXIF數據並手動應用轉換。 – 2010-11-19 19:59:27

+0

感謝你們的答覆。我試圖去除所有的EXIF數據,但是如果必須旋轉的話就加回數據。這變成了PITA比我原先假定的要多得多。只需要制定腳本即可完成。再次感謝! – Hoopes 2010-11-22 21:37:45

+0

由於您正在調整大小,因此您可能不在意,但請不要忘記,即使是簡單的旋轉,對於jpeg,有時也是一種有損操作。 – 2011-04-10 21:02:28

51

我的回答「unutbu」和伊格納西奧巴斯克斯 - 艾布拉姆斯幾乎都同意,但是......

EXIF方向標誌可以在1和8之間取決於相機是如何保持有一個值。

肖像照片可以在相機的左上方或右側邊緣拍攝,風景照片可以上下顛倒。

這裏是代碼,考慮到這一點(與尼康數碼單反相機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() 
+0

這是正確的,比接受的答案更好。 – 2011-09-21 23:22:27

+0

請注意,如果用於非JPEG或EXIF數據不存在,則會導致疏漏。 – 2013-01-04 11:23:00

+1

這段代碼的縮進有問題嗎? – 2013-06-16 20:28:52

6

胡普斯的答案是偉大的,但它是很多更有效地使用轉置方法,而不是旋轉。旋轉爲每個像素執行實際的過濾計算,有效地對整個圖像進行復雜的調整。此外,目前的PIL庫似乎有一個黑線被添加到旋轉圖像邊緣的錯誤。轉置速度更快,並且缺少該錯誤。我只是調整hoopes來代替使用轉置。

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

你可以粉碎那條件下來是 if exif[orientation] in [3,6,8]: image = image.transpose(Image.ROTATE_180) 2012-05-02 17:02:00

28

xilvar的答案是非常好的,但有,我想在一個拒絕編輯解決兩個小缺點,所以我會後它作爲一個答案。

首先,如果文件不是JPEG格式,或者沒有exif數據,xilvar的解決方案將失敗。另一方面,它總是旋轉180度而不是適當的量。

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

我會使用'orientation = exif.get(orientation,None)',然後'如果orientation爲None:return'並添加一些圖像exif可能無效的日誌。我並不是說這可能會給每個人造成錯誤,但它發生在我身上,可能非常罕見。 – 2013-11-29 15:30:57

+0

如果v =='方向'),我會使用'orientation = next(k代表k,v代表ExifTags.TAGS.items()',因爲這個腳本依賴於這個標籤,PIL的'ExifTags.py'似乎有它。 – kirpit 2017-09-04 11:36:40

19

下面是所有8個方向工作的版本:

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 
+0

偉大的解決方案。像魅力一樣工作。 – 2014-11-27 05:00:04

+0

美麗。謝謝,工作完美。 – 2015-03-10 23:15:43

+0

這可行,但我想知道是否有更多的OOP風格可以在這裏完成,比如從PIL添加到Image類。另外,我不認爲在try塊中包含那麼多代碼是一個好主意。你有幾件事情可能會失敗,很高興知道哪個恕我直言。 – Tatsh 2016-09-26 05:27:54

1

你好,我是想實現圖像的旋轉並感謝在這個崗位,我做到了以前的答案。但我升級瞭解決方案並希望分享它。我希望有人會覺得這很有用。

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 

用途:

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

我是小白編程,Python和PIL所以在以前的答案示例代碼看似複雜給我。我只是直接去了標籤的關鍵,而不是遍歷標籤。在Python外殼,可以看到該方向的主要是274

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

我用image._getexif()功能搶什麼ExifTags是在圖像中。如果方向標記不存在,則會引發錯誤,所以我使用try/except。

枕頭的文件說,旋轉和轉置之間的性能或結果沒有差異。我已通過計時兩種功能來確認它。我使用旋轉,因爲它更簡潔。

rotate(90)逆時針旋轉。該功能似乎接受負面的程度。

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 
19

感覺被迫分享我的版本,它在功能上等同於其他答案建議,卻又是的,在我看來,清潔:

import functools 

def image_transpose_exif(im): 
    exif_orientation_tag = 0x0112 # contains an integer, 1 through 8 
    exif_transpose_sequences = [ # corresponding to the following 
     [], 
     [Image.FLIP_LEFT_RIGHT], 
     [Image.ROTATE_180], 
     [Image.FLIP_TOP_BOTTOM], 
     [Image.FLIP_LEFT_RIGHT, Image.ROTATE_90], 
     [Image.ROTATE_270], 
     [Image.FLIP_TOP_BOTTOM, Image.ROTATE_90], 
     [Image.ROTATE_90], 
    ] 

    try: 
     seq = exif_transpose_sequences[im._getexif()[exif_orientation_tag] - 1] 
    except (AttributeError, TypeError, KeyError, IndexError): 
     return im 
    else: 
     return functools.reduce(lambda im, op: im.transpose(op), seq, im) 

而且在我的特別用途的情況下,這是使用Django和Nginx提供圖像縮略圖,我發現X-Accel-Redirect HTTP頭非常方便。它允許Python代碼將縮略圖保存到特定位置,然後將其傳遞給Nginx,以便將其發送到客戶端,從而釋放更多資源密集型Python代碼,以執行比這更復雜的任務。

+0

感謝分享:) – 2015-09-09 16:13:28

+1

只有'import functools'丟失了,仍然應該是Python 2.7中可以接​​受的答案。 – 2016-05-16 20:02:44

+2

您的榮譽。這是最完整的答案,使用最合適的工具。其他答案或者使用'rotate'而不是'transpose'或者缺少所有八種可能的狀態。 JPEG退出方向用於轉置,不用於旋轉。 http://jpegclub.org/exif_orientation.html – OdraEncoded 2016-06-06 03:44:04

0

這裏有一些很好的答案,我只想發佈一個清理好的版本... 該函數假設你已經在某處完成了Image.open(),並將在其他地方執行image.save()想要一個可以放入旋轉的功能。

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 
1

我需要一個解決方案,將所有方向的照顧,而不僅僅是368

我試過Roman Odaisky的solution - 它看起來全面和乾淨。但是,使用具有各種方向值的實際圖像進行測試有時會導致錯誤結果(例如this one的方向設置爲0)。其他viable solution可能是多布斯範德梅爾的。但我還沒有嘗試過,因爲我覺得可以更簡單地編寫邏輯(我更喜歡)。

因此,事不宜遲,這裏有一個更簡單,更易於維護的(在我看來)版本:

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 

測試,發現所有提到的EXIF方向上的圖像的工作。但是,請也做你自己的測試。