Python中Pillow使用介绍

  • A+
所属分类:Python库介绍
摘要这一篇简单介绍一下Pillow库的使用, 特别是介绍图像的palette与如何进行转换.

简介

这一篇会介绍一下Python中Pillow的简单使用. Pillow是在Python中常见的图像处理的库. 我们在这里会通过例子, 介绍一下常见的功能.

参考资料

 

Pillow常见功能介绍

我们使用如下的图片进行所有功能的测试.

Python中Pillow使用介绍

打开图片并显示

  1. from PIL import Image
  2. im = Image.open("./test.jpg")
  3. im.rotate(45).show()

这里我们不仅打开了图片, 还对其进行旋转. 最终得到了如下的效果.

Python中Pillow使用介绍

 

获取图像大小与获得缩略图

下面的代码可以将图片大小转换为128×128.

  1. from PIL import Image
  2. im = Image.open("./test.jpg")
  3. print(im.size) # (414, 338)
  4. # 获得缩略图
  5. im.thumbnail((128, 128), resample=Image.NEAREST)
  6. im.save("test_thumbnail.jpg", "JPEG")

 

Image.Crop

使用Image.Crop来进行图像的裁剪. 图像的坐标如下图所示:

Python中Pillow使用介绍

我们首先获得图像的长和宽, 之后通过除2, 获得图像的左上角.

  1. from PIL import Image, ImageOps
  2. img = Image.open('./profile_color.jpg')
  3. width, height = img.size
  4. new_img = img.crop((0, 0, width/2, height/2))
  5. new_img.show()

 

Image.putpalette

在说这个函数之前, 我们首先介绍一下什么是一个图像的palette. Palette相当于是一个图像的所使用的颜色的集合, 类似于画家手中的颜色盘 (An image palette is a collection of colors that an image uses — similar to a painter's palette).

一个图像的palette表示我们在显示器上能看到的所有颜色, 只有在16-256色之间的图片会有palette. (An image palette is a subset of all the colors your monitor can display. Not all images have image palettes — only images with color depths between 16 and 256 colors have palettes that you can fine-tune and edit)

Images with a color depth of 16 million colors do not have an image palette because they can contain all the colors your computer can display. For these images, you can load an image palette to decrease the image's color depth to 256 colors (8-bit). (对于一些图片, 我们可以减少palette的格式)

下面我们自定义一个palette, 然后将其应用到一张图片上. 我们会使用putpalette来完成. 下面是putpalette的详细介绍.

Attaches a palette to this image. The image must be a "P" or "L" image, and the palette sequence must contain 768 integer values (这里是256色, 每一个颜色有RGB三通道, 所以一共是768个整数), where each group of three values represent the red, green, and blue values for the corresponding pixel index. Instead of an integer sequence, you can use an 8-bit string.

我们在这个palette里面只使用4种颜色, 对于的RGB分别是:

  • (0, 0, 0)
  • (102, 102, 102)
  • (176, 176, 176)
  • (255, 255, 255)
  1. from PIL import Image
  2. oldimage = Image.open("./test.jpg")
  3. palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
  4. newimage = Image.new('P', oldimage.size) # 创建一个和oldimage一样大的图像, 模式为P
  5. newimage.putpalette(palettedata * 64) # 指定palette, 这里需要将palette变换为768长度的list
  6. newimage.paste(oldimage, (0, 0) + oldimage.size) # 将原始图片粘贴过来
  7. newimage.show()

于是, 还是使用上面的图像, 我们可以将其转换为只包含四种颜色.

Python中Pillow使用介绍

 

Image.quantize

除了上面的方式进行转换外, 我们可以使用quantize来完成相同的工作.

Convert the image to 'P' mode with the specified number of colors.

在这里我们的palette里面还是只使用4种颜色. 我们是直接用过quantize进行转换, 而不需要新建一个图像, 然后通过paste来完成.

  1. from PIL import Image
  2. image = Image.open("./test.jpg")
  3. palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
  4. palimage = Image.new('P', (16, 16))
  5. palimage.putpalette(palettedata * 64)
  6. newimage = image.quantize(palette=palimage)
  7. newimage.show()

最终的效果图如下所示:

Python中Pillow使用介绍

 

Image.convert

同样的功能, 我们可以使用Image.convert来完成. 在这里会遇到ImageCore的相关问题. 下面做一些简单的说明.

The parts of PIL implemented in C are in the PIL._imaging module, also available as Image.core after you from PIL import Image. Current versions of Pillow give every PIL.Image.Image instance a member named im which is an instance of ImagingCore, a class defined within PIL._imaging. You can list its methods with help(oldimage.im), but the methods themselves are undocumented from within Python.

The convert method of ImagingCore objects is implemented in _imaging.c. It takes one to three arguments and creates a new ImagingCore object (called Imaging_Type within _imaging.c).

  • mode (required): mode string (e.g. "P")
  • dither (optional, default 0): PIL passes 0 or 1
  • paletteimage (optional): An ImagingCore with a palette

下面是完整的代码:

  1. def quantizetopalette(silf, palette, dither=False):
  2.     """Convert an RGB or L mode image to use a given P image's palette."""
  3.     silf.load()
  4.     # use palette from reference image
  5.     palette.load()
  6.     if palette.mode != "P":
  7.         raise ValueError("bad mode for palette image")
  8.     if silf.mode != "RGB" and silf.mode != "L":
  9.         raise ValueError(
  10.             "only RGB or L mode images can be quantized to a palette"
  11.             )
  12.     im = silf.im.convert("P", 1 if dither else 0, palette.im)
  13.     # the 0 above means turn OFF dithering
  14.     # Later versions of Pillow (4.x) rename _makeself to _new
  15.     try:
  16.         return silf._new(im)
  17.     except AttributeError:
  18.         return silf._makeself(im)
  19. if __name__ == "__main__":
  20.     # 读取图像
  21.     from PIL import Image
  22.     image = Image.open("./test.jpg")
  23.     palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
  24.     palimage = Image.new('P', (16, 16))
  25.     palimage.putpalette(palettedata * 64)
  26.     newimage = quantizetopalette(image, palimage, dither=False)
  27.     newimage.show()

同时, 这里我们可以选择是否要dither, 我们分别来看一下True和False时候的效果. 当dither=False的时候, 是没有散的色块的, 都是大的色块.

Python中Pillow使用介绍

dither=True的时候, 得到的图像就是会有小的散点状.

Python中Pillow使用介绍

 

转换为灰度图

上面提到了的convert, 还可以将图像转换为灰度图. 灰度图使用L来表示. 计算公式如下所示:

L = R * 299/1000 + G * 587/1000 + B * 114/1000
  1. from PIL import Image
  2. img = Image.open('test.jpg').convert('L')
  3. img.show()

 

Image.point

还有一个比较常用的函数就是point, 它可以依次对图像中的像素点进行处理. 我们看下面的一个例子.

  1. from PIL import Image
  2. img = Image.open('test.jpg').convert('L')
  3. img = img.point(lambda color: color > 100 and 255)
  4. img.show()

该段语句的作用就是, 对每个像素点进行判断, 如果大于100, 那么就是0, 否则就是255. 于是, 最终得到的图像如下所示:

Python中Pillow使用介绍

参考资料, How to I use PIL Image.point(table) method to apply a threshold to a 256 gray image

 

不同的滤波

  1. from PIL import Image, ImageFilter
  2. im = Image.open('./test.jpg')
  3. # im = im.filter(ImageFilter.BLUR) # 模糊
  4. # im = im.filter(ImageFilter.SMOOTH_MORE) # 平滑
  5. im = im.filter(ImageFilter.CONTOUR) # 边缘提取
  6. im.show()

例如上面的滤波可以对图像进行边缘检测, 结果如下所示:

Python中Pillow使用介绍

 

图片上添加文字

有的时候, 我们需要在图片上加上文字, 这个时候就需要使用ImageDraw来完成. 下面看一个简单的例子, 我们使用ImageDraw将导入的图片当作背景, 接着写入文字的内容.

  1. from PIL import Image, ImageDraw, ImageFont
  2. im = Image.open('./test.jpg')
  3. # initialise the drawing context with
  4. # the image object as background
  5. draw = ImageDraw.Draw(im)
  6. font_fname = './legofy/times.ttf'
  7. font_size = 50
  8. font = ImageFont.truetype(font_fname, font_size)
  9. (x, y) = (0, 0) # 文字显示位置
  10. message = "Hello World!"
  11. color = 'rgb(0, 0, 0)' # black color
  12. # draw the message on the background
  13. draw.text((x, y), message, fill=color, font=font)
  14. im.show()

最终的图片效果如下图所示:

Python中Pillow使用介绍

有的时候, 我们的文字过长, 图片里面一行是放不下的, 我们需要进行换行, 于是我们需要对输入的一段文字, 按照长度进行划分. 首先我们定义一个函数, 用来对文字的长度进行划分.

  1. def text_wrap(text, font, max_width):
  2.     lines = []
  3.     # If the width of the text is smaller than image width
  4.     # we don't need to split it, just add it to the lines array
  5.     # and return
  6.     if font.getsize(text)[0] <= max_width:
  7.         lines.append(text)
  8.     else:
  9.         # split the line by spaces to get words
  10.         words = text.split(' ')
  11.         i = 0
  12.         # append every word to a line while its width is shorter than image width
  13.         while i < len(words):
  14.             line = ''
  15.             while i < len(words) and font.getsize(line + words[i])[0] <= max_width:
  16.                 line = line + words[i] + " "
  17.                 i += 1
  18.             if not line:
  19.                 line = words[i]
  20.                 i += 1
  21.             # when the line gets longer than the max width do not append the word, 
  22.             # add the line to the lines array
  23.             lines.append(line)
  24.     return lines

这里函数的输入分别是:

  • text: 要分割的文字;
  • font: 文字的大小, 字体, 用来计算一行可以放多少文字;
  • max_width: 这里一般是图片的宽度;

接着我们定义绘图的函数, 借助上面的text_wrap, 对较长的文字进行分割之后, 我们使用循环进行绘制. 这里只需要依次改变y轴的位置即可. 于是我们定义了draw_text函数, 如下所示.

  1. def draw_text(text):
  2.     # open the background file
  3.     img = Image.open('./test.jpg')
  4.     draw = ImageDraw.Draw(img)
  5.     # size() returns a tuple of (width, height) 
  6.     image_size = img.size
  7.     # create the ImageFont instance
  8.     font_file_path = './legofy/fonts/times.ttf'
  9.     font = ImageFont.truetype(font_file_path, size=50)
  10.     # get shorter lines (自动进行分割)
  11.     lines = text_wrap(text, font, image_size[0])
  12.     print(lines) # ['This could be a single line text ', 'but its too long to fit in one. ']
  13.     x = 10
  14.     y = 20
  15.     color = 'rgb(0,0,0)'
  16.     line_height = font.getsize('hg')[1] # 获得行间距
  17.     for line in lines:
  18.         # draw the line on the image
  19.         draw.text((x, y), line, fill=color, font=font)
  20.         # update the y position so that we can use it for next line
  21.         y = y + line_height

最终, 我们来测试一下最后的效果.

  1. draw_text("This could be a single line text but its too long to fit in one.")

最终图像的效果如下所示:

Python中Pillow使用介绍

参考资料

  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin
王 茂南

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: