python:PIL图像处理

简介

PIL (Python Imaging Library)

Python图像处理库,该库支持多种文件格式,提供强大的图像处理功能。

使用Image类

PIL中最重要的类是Image类,该类在Image模块中定义。

从文件加载图像:

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

如果成功,这个函数返回一个Image对象。现在你可以使用该对象的属性来探索文件的内容。

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()的标准实现不是很有效率,因为它将图像保存到一个临时文件,然后调用外部工具(比如系统的默认图片查看软件)显示图像。该函数将是一个非常方便的调试和测试工具。)

接下来的部分展示了该库提供的不同功能。

读写图像

PIL支持多种图像格式。从磁盘中读取文件,只需使用Image模块中的open函数。不需要提供文件的图像格式。PIL库将根据文件内容自动检测。

如果要保存到文件,使用Image模块中的save函数。当保存文件时,文件名很重要,除非指定格式,否则PIL库将根据文件的扩展名来决定使用哪种格式保存。

** 转换文件到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函数的第二个参数可以指定使用的文件格式。如果文件名中使用了一个非标准的扩展名,则必须通过第二个参数来指定文件格式。

** 创建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只有在需要的时候才加载像素数据。当你打开一个文件时,PIL只是读取文件头获得文件格式、图像模式、图像大小等属性,而像素数据只有在需要的时候才会加载。

这意味着打开一个图像文件是一个非常快的操作,不会受文件大小和压缩算法类型的影响。

** 获得图像信息 **

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类提供了某些方法,可以操作图像的子区域。提取图像的某个子区域,使用crop()函数。

** 复制图像的子区域 **

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

定义区域使用一个包含4个元素的元组,(left, upper, right, lower)。坐标原点位于左上角。上面的例子提取的子区域包含300x300个像素。

该区域可以做接下来的处理然后再粘贴回去。

** 处理子区域然后粘贴回去 **

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

当往回粘贴时,区域的大小必须和参数匹配。另外区域不能超出图像的边界。然而原图像和区域的颜色模式无需匹配。区域会自动转换。

** 滚动图像 **

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()函数有个可选参数,接受一个掩码图像。掩码中255表示指定位置为不透明,0表示粘贴的图像完全透明,中间的值表示不同级别的透明度。

PIL允许分别操作多通道图像的每个通道,比如RGB图像。split()函数创建一个图像集合,每个图像包含一个通道。merge()函数接受一个颜色模式和一个图像元组,然后将它们合并为一个新的图像。接下来的例子交换了一个RGB图像的三个通道。

** 分离和合并图像通道 **

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

对于单通道图像,split()函数返回图像本身。如果想处理各个颜色通道,你可能需要先将图像转为RGB模式。

几何变换

resize()函数接受一个元组,指定图像的新大小。
rotate()函数接受一个角度值,逆时针旋转。

** 基本几何变换 **

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

图像旋转90度也可以使用transpose()函数。transpose()函数也可以水平或垂直翻转图像。

** 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()函数在性能和结果上没有区别。

更通用的图像变换函数为transform()

颜色模式变换

PIL可以转换图像的像素模式。

** 转换颜色模式 **

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

PIL库支持从其他模式转为“L”或“RGB”模式,其他模式之间转换,则需要使用一个中间图像,通常是“RGB”图像。

图像增强(Image Enhancement)

过滤器

ImageFilter模块包含多个预定义的图像增强过滤器用于filter()函数。

** 应用过滤器 **

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

点操作

point()函数用于操作图像的像素值。该函数通常需要传入一个函数对象,用于操作图像的每个像素:

** 应用点操作 **

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

使用以上技术可以快速地对图像像素应用任何简单的表达式。可以结合point()函数和paste函数修改图像。

** 处理图像的各个通道 **

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

注意用于创建掩码图像的语法:

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

Python计算逻辑表达式采用短路方式,即:如果and运算符左侧为false,就不再计算and右侧的表达式,而且返回结果是表达式的结果。比如a and b如果a为false则返回a,如果a为true则返回b,详见Python语法。

增强

对于更多高级的图像增强功能,可以使用ImageEnhance模块中的类。

可以调整图像对比度、亮度、色彩平衡、锐度等。

** 增强图像 **

import ImageEnhance

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

图像序列

PIL库包含对图像序列(动画格式)的基本支持。支持的序列格式包括FLI/FLCGIF和一些实验性的格式。TIFF文件也可以包含多个帧。

当打开一个序列文件时,PIL库自动加载第一帧。你可以使用seek()函数tell()函数在不同帧之间移动。

** 读取序列 **

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

如例子中展示的,当序列到达结尾时,将抛出EOFError异常。

注意当前版本的库中多数底层驱动只允许seek到下一帧。如果想回到前面的帧,只能重新打开图像。

以下迭代器类允许在for语句中循环遍历序列:

** 一个序列迭代器类 **

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库包含一些函数用于将图像、文本打印到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()函数打开图像文件,通常传入一个文件名作为参数:

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

如果打开成功,返回一个Image对象,否则抛出IOError异常。

也可以使用一个file-like object代替文件名(暂可以理解为文件句柄)。该对象必须实现read,seek,tell函数,必须以二进制模式打开。

** 从文件句柄打开图像 **

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

如果从字符串数据中读取图像,使用StringIO类:

** 从字符串中读取 **

import StringIO

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

如果图像文件内嵌在一个大文件里,比如tar文件中。可以使用ContainerIO或TarIO模块来访问。

** 从tar文档中读取 **

import TarIO

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

控制解码器

** 该小节不太理解,请参考原文 **

有些解码器允许当读取文件时操作图像。通常用于在创建缩略图时加速解码(当速度比质量重要时)和输出一个灰度图到激光打印机时。

draft()函数。

** 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

输出类似以下内容:

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

注意结果图像可能不会和请求的模式和大小匹配。如果要确保图像不大于指定的大小,请使用thumbnail函数。

扩展阅读

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容