python:PIL圖像處理

簡介

PIL (Python Imaging Library)

Python圖像處理庫径玖,該庫支持多種文件格式赁咙,提供強大的圖像處理功能拢军。

使用Image類

PIL中最重要的類是Image類尉剩,該類在Image模塊中定義。

從文件加載圖像:

import Image
im = Image.open("lena.ppm")

如果成功菌瘫,這個函數(shù)返回一個Image對象∧纹現(xiàn)在你可以使用該對象的屬性來探索文件的內(nèi)容飞几。

print im.format, im.size, im.mode
# PPM (512, 512) RGB

format屬性指定了圖像文件的格式后室,如果圖像不是從文件中加載的則為None缩膝。
size屬性是一個2個元素的元組,包含圖像寬度和高度(像素)岸霹。
mode屬性定義了像素格式疾层,常用的像素格式為:“L” (luminance) - 灰度圖, “RGB” , “CMYK”。

如果文件打開失敗, 將拋出IOError異常贡避。

一旦你擁有一個Image類的實例痛黎,你就可以用該類定義的方法操作圖像。比如:顯示

im.show()

(show()的標準實現(xiàn)不是很有效率刮吧,因為它將圖像保存到一個臨時文件湖饱,然后調(diào)用外部工具(比如系統(tǒng)的默認圖片查看軟件)顯示圖像。該函數(shù)將是一個非常方便的調(diào)試和測試工具杀捻。)

接下來的部分展示了該庫提供的不同功能井厌。

讀寫圖像

PIL支持多種圖像格式。從磁盤中讀取文件,只需使用Image模塊中的open函數(shù)旗笔。不需要提供文件的圖像格式。PIL庫將根據(jù)文件內(nèi)容自動檢測拄踪。

如果要保存到文件蝇恶,使用Image模塊中的save函數(shù)。當保存文件時惶桐,文件名很重要撮弧,除非指定格式,否則PIL庫將根據(jù)文件的擴展名來決定使用哪種格式保存姚糊。

** 轉(zhuǎn)換文件到JPEG **

import os, sys
import Image

for infile in sys.argv[1:]:
    f, e = os.path.splitext(infile)
    outfile = f + ".jpg"
    if infile != outfile:
        try:
            Image.open(infile).save(outfile)
        except IOError:
            print "cannot convert", infile

save函數(shù)的第二個參數(shù)可以指定使用的文件格式贿衍。如果文件名中使用了一個非標準的擴展名,則必須通過第二個參數(shù)來指定文件格式救恨。

** 創(chuàng)建JPEG縮略圖 **

import os, sys
import Image

size = 128, 128

for infile in sys.argv[1:]:
    outfile = os.path.splitext(infile)[0] + ".thumbnail"
    if infile != outfile:
        try:
            im = Image.open(infile)
            im.thumbnail(size)
            im.save(outfile, "JPEG")
        except IOError:
            print "cannot create thumbnail for", infile

需要注意的是贸辈,PIL只有在需要的時候才加載像素數(shù)據(jù)。當你打開一個文件時肠槽,PIL只是讀取文件頭獲得文件格式擎淤、圖像模式、圖像大小等屬性秸仙,而像素數(shù)據(jù)只有在需要的時候才會加載嘴拢。

這意味著打開一個圖像文件是一個非常快的操作寂纪,不會受文件大小和壓縮算法類型的影響席吴。

** 獲得圖像信息 **

import sys
import Image

for infile in sys.argv[1:]:
    try:
        im = Image.open(infile)
        print infile, im.format, "%dx%d" % im.size, im.mode
    except IOError:
        pass

剪切、粘貼捞蛋、合并圖像

Image類提供了某些方法孝冒,可以操作圖像的子區(qū)域。提取圖像的某個子區(qū)域拟杉,使用crop()函數(shù)迈倍。

** 復制圖像的子區(qū)域 **

box = (100, 100, 400, 400)
region = im.crop(box)

定義區(qū)域使用一個包含4個元素的元組,(left, upper, right, lower)捣域。坐標原點位于左上角啼染。上面的例子提取的子區(qū)域包含300x300個像素。

該區(qū)域可以做接下來的處理然后再粘貼回去焕梅。

** 處理子區(qū)域然后粘貼回去 **

region = region.transpose(Image.ROTATE_180)
im.paste(region, box)

當往回粘貼時迹鹅,區(qū)域的大小必須和參數(shù)匹配。另外區(qū)域不能超出圖像的邊界贞言。然而原圖像和區(qū)域的顏色模式無需匹配斜棚。區(qū)域會自動轉(zhuǎn)換。

** 滾動圖像 **

def roll(image, delta):
    "Roll an image sideways"

    xsize, ysize = image.size

    delta = delta % xsize
    if delta == 0: return image

    part1 = image.crop((0, 0, delta, ysize))
    part2 = image.crop((delta, 0, xsize, ysize))
    image.paste(part2, (0, 0, xsize-delta, ysize))
    image.paste(part1, (xsize-delta, 0, xsize, ysize))

    return image

paste()函數(shù)有個可選參數(shù),接受一個掩碼圖像弟蚀。掩碼中255表示指定位置為不透明蚤霞,0表示粘貼的圖像完全透明,中間的值表示不同級別的透明度义钉。

PIL允許分別操作多通道圖像的每個通道昧绣,比如RGB圖像。split()函數(shù)創(chuàng)建一個圖像集合捶闸,每個圖像包含一個通道夜畴。merge()函數(shù)接受一個顏色模式和一個圖像元組,然后將它們合并為一個新的圖像删壮。接下來的例子交換了一個RGB圖像的三個通道贪绘。

** 分離和合并圖像通道 **

r, g, b = im.split()
im = Image.merge("RGB", (b, g, r));

對于單通道圖像,split()函數(shù)返回圖像本身央碟。如果想處理各個顏色通道税灌,你可能需要先將圖像轉(zhuǎn)為RGB模式。

幾何變換

resize()函數(shù)接受一個元組亿虽,指定圖像的新大小垄琐。
rotate()函數(shù)接受一個角度值,逆時針旋轉(zhuǎn)经柴。

** 基本幾何變換 **

out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise

圖像旋轉(zhuǎn)90度也可以使用transpose()函數(shù)狸窘。transpose()函數(shù)也可以水平或垂直翻轉(zhuǎn)圖像。

** transpose **

out = im.transpose(Image.FLIP_LEFT_RIGHT)
out = im.transpose(Image.FLIP_TOP_BOTTOM)
out = im.transpose(Image.ROTATE_90)
out = im.transpose(Image.ROTATE_180)
out = im.transpose(Image.ROTATE_270)

transpose()rotate()函數(shù)在性能和結(jié)果上沒有區(qū)別坯认。

更通用的圖像變換函數(shù)為transform()翻擒。

顏色模式變換

PIL可以轉(zhuǎn)換圖像的像素模式。

** 轉(zhuǎn)換顏色模式 **

im = Image.open("lena.ppm").convert("L")

PIL庫支持從其他模式轉(zhuǎn)為“L”或“RGB”模式牛哺,其他模式之間轉(zhuǎn)換陋气,則需要使用一個中間圖像,通常是“RGB”圖像引润。

圖像增強(Image Enhancement)

過濾器

ImageFilter模塊包含多個預定義的圖像增強過濾器用于filter()函數(shù)巩趁。

** 應用過濾器 **

import ImageFilter
out = im.filter(ImageFilter.DETAIL)

點操作

point()函數(shù)用于操作圖像的像素值。該函數(shù)通常需要傳入一個函數(shù)對象淳附,用于操作圖像的每個像素:

** 應用點操作 **

# 每個像素值乘以1.2
out = im.point(lambda i: i * 1.2)

使用以上技術(shù)可以快速地對圖像像素應用任何簡單的表達式议慰。可以結(jié)合point()函數(shù)和paste函數(shù)修改圖像奴曙。

** 處理圖像的各個通道 **

# split the image into individual bands
source = im.split()

R, G, B = 0, 1, 2

# select regions where red is less than 100
mask = source[R].point(lambda i: i < 100 and 255)

# process the green band
out = source[G].point(lambda i: i * 0.7)

# paste the processed band back, but only where red was < 100
source[G].paste(out, None, mask)

# build a new multiband image
im = Image.merge(im.mode, source)

注意用于創(chuàng)建掩碼圖像的語法:

imout = im.point(lambda i: expression and 255)

Python計算邏輯表達式采用短路方式别凹,即:如果and運算符左側(cè)為false,就不再計算and右側(cè)的表達式洽糟,而且返回結(jié)果是表達式的結(jié)果炉菲。比如a and b如果a為false則返回a堕战,如果a為true則返回b,詳見Python語法拍霜。

增強

對于更多高級的圖像增強功能嘱丢,可以使用ImageEnhance模塊中的類。

可以調(diào)整圖像對比度祠饺、亮度越驻、色彩平衡、銳度等吠裆。

** 增強圖像 **

import ImageEnhance

enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")

圖像序列

PIL庫包含對圖像序列(動畫格式)的基本支持。支持的序列格式包括FLI/FLC烂完、GIF和一些實驗性的格式试疙。TIFF文件也可以包含多個幀。

當打開一個序列文件時抠蚣,PIL庫自動加載第一幀祝旷。你可以使用seek()函數(shù)tell()函數(shù)在不同幀之間移動。

** 讀取序列 **

import Image

im = Image.open("animation.gif")
im.seek(1) # skip to the second frame

try:
    while 1:
        im.seek(im.tell() + 1)
        # do something to im
except EOFError:
    pass # end of sequence

如例子中展示的嘶窄,當序列到達結(jié)尾時怀跛,將拋出EOFError異常。

注意當前版本的庫中多數(shù)底層驅(qū)動只允許seek到下一幀柄冲。如果想回到前面的幀吻谋,只能重新打開圖像。

以下迭代器類允許在for語句中循環(huán)遍歷序列:

** 一個序列迭代器類 **

class ImageSequence:
    def __init__(self, im):
        self.im = im
    def __getitem__(self, ix):
        try:
            if ix:
                self.im.seek(ix)
            return self.im
        except EOFError:
            raise IndexError # end of sequence

for frame in ImageSequence(im):
    # ...do something to frame...

Postscript打印

PIL庫包含一些函數(shù)用于將圖像现横、文本打印到Postscript打印機漓拾。以下是一個簡單的例子。

** 打印到Postscript **

import Image
import PSDraw

im = Image.open("lena.ppm")
title = "lena"
box = (1*72, 2*72, 7*72, 10*72) # in points

ps = PSDraw.PSDraw() # default is sys.stdout
ps.begin_document(title)

# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)

# draw centered title
ps.setfont("HelveticaNarrow-Bold", 36)
w, h, b = ps.textsize(title)
ps.text((4*72-w/2, 1*72-h), title)

ps.end_document()

讀取圖像進階

如前所述戒祠,可以使用open()函數(shù)打開圖像文件骇两,通常傳入一個文件名作為參數(shù):

im = Image.open("lena.ppm")

如果打開成功,返回一個Image對象姜盈,否則拋出IOError異常低千。

也可以使用一個file-like object代替文件名(暫可以理解為文件句柄)。該對象必須實現(xiàn)read馏颂,seek示血,tell函數(shù),必須以二進制模式打開救拉。

** 從文件句柄打開圖像 **

fp = open("lena.ppm", "rb")
im = Image.open(fp)

如果從字符串數(shù)據(jù)中讀取圖像矾芙,使用StringIO類:

** 從字符串中讀取 **

import StringIO

im = Image.open(StringIO.StringIO(buffer))

如果圖像文件內(nèi)嵌在一個大文件里,比如tar文件中近上√尴埽可以使用ContainerIO或TarIO模塊來訪問。

** 從tar文檔中讀取 **

import TarIO

fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm")
im = Image.open(fp)

控制解碼器

** 該小節(jié)不太理解,請參考原文 **

有些解碼器允許當讀取文件時操作圖像葱绒。通常用于在創(chuàng)建縮略圖時加速解碼(當速度比質(zhì)量重要時)和輸出一個灰度圖到激光打印機時感帅。

draft()函數(shù)。

** Reading in draft mode **

im = Image.open(file)
print "original = ", im.mode, im.size

im.draft("L", (100, 100))
print "draft = ", im.mode, im.size

輸出類似以下內(nèi)容:

original = RGB (512, 512)
draft = L (128, 128) 

注意結(jié)果圖像可能不會和請求的模式和大小匹配地淀。如果要確保圖像不大于指定的大小失球,請使用thumbnail函數(shù)。

擴展閱讀

Python2.7 教程 PIL
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00140767171357714f87a053a824ffd811d98a83b58ec13000

Python 之 使用 PIL 庫做圖像處理
http://www.cnblogs.com/way_testlife/archive/2011/04/17/2019013.html

來自 http://effbot.org/imagingbook/introduction.htm

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帮毁,一起剝皮案震驚了整個濱河市实苞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烈疚,老刑警劉巖黔牵,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異爷肝,居然都是意外死亡猾浦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門灯抛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來金赦,“玉大人,你說我怎么就攤上這事对嚼〖锌梗” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵纵竖,是天一觀的道長兔朦。 經(jīng)常有香客問我,道長磨确,這世上最難降的妖魔是什么沽甥? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮乏奥,結(jié)果婚禮上摆舟,老公的妹妹穿的比我還像新娘。我一直安慰自己邓了,他們只是感情好恨诱,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骗炉,像睡著了一般照宝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上句葵,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天厕鹃,我揣著相機與錄音兢仰,去河邊找鬼。 笑死剂碴,一個胖子當著我的面吹牛把将,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播忆矛,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼察蹲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了催训?” 一聲冷哼從身側(cè)響起洽议,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漫拭,沒想到半個月后亚兄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡嫂侍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年儿捧,在試婚紗的時候發(fā)現(xiàn)自己被綠了荚坞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挑宠。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖颓影,靈堂內(nèi)的尸體忽然破棺而出各淀,到底是詐尸還是另有隱情,我是刑警寧澤诡挂,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布碎浇,位于F島的核電站,受9級特大地震影響璃俗,放射性物質(zhì)發(fā)生泄漏奴璃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一城豁、第九天 我趴在偏房一處隱蔽的房頂上張望苟穆。 院中可真熱鬧,春花似錦唱星、人聲如沸雳旅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攒盈。三九已至,卻和暖如春哎榴,著一層夾襖步出監(jiān)牢的瞬間型豁,已是汗流浹背僵蛛。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偷遗,地道東北人墩瞳。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像氏豌,于是被迫代替她去往敵國和親喉酌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容