Wand处理图片(ImageMagick)

Wand的内核是imagemagick,wand充当python与imagemagick的一个接口,从而使得python也可调用imagemagick内核进行图片处理,在OCR这里提到wand是因为python程序要调用要用Wand将pdf转成图片。安装Wand详见1.2 安装第三方模块

看起来Wand目前不支持imagemagick7,如果安装的imagegick版本是7,运行应用了Wand模块的python程序时会出现错误:“ImportError: MagickWand shared library not found”找不到共享库。解决办法是再安装一份版本为6的imagemagick:

1. 在Mac的teminal上运行

brew install imagemagick@6

2. 查看安装的imagemagick@6的版本

如果已经安装了imagemagick 7,就不要用命令:magick –version了,否则显示的会是imagemagick 7的版本号。

可以在terminal上用命令:

ls /usr/local/Cellar/imagemagick@6

记住显示的版本号,这里假设显示的是6.9.10-14

3. 给要用到的库做个链接

要用到的库是dylib,可以通过ls命令确认下库的存在:

ls /usr/local/Cellar/imagemagick@6/6.9.10-14/lib/libMagickWand-6.Q16.dylib

用命令ln做一个链接:

ln -s /usr/local/Cellar/imagemagick@6/6.9.10-14/lib/libMagickWand-6.Q16.dylib /usr/local/lib/libMagickWand.dylib

这个链接做好后,python解释器就知道到哪里去找MagickWand的共享库了。

先来看wand对图像的基本操作和信息获得:

1. 从图片对象属性获取图片信息:

wand对图片的操作通过打开图片文件生成图片对象来完成。如果出现wand.resource.DestroyedResourceError的错误提示,那是因为试图操作一个已经关闭的图片。用with打开图片文件生成图形对象的方式可以省却这些麻烦:

from wand.image import Image		# 引入图像处理模块
from wand.display import display		# 引入显示图片功能

with Image(filename='QingYi.jpg') as img:
# 生成指向图片QingYi.jpg的图片对象img,用with打开图片用完不必关闭

    print('width =', img.width)		
    # 通过图片对象的属性width获得图片的宽,输出:width = 334
    print('height =', img.height)	# 图片的高,height = 504
    print('format: ', img.format)	
    # 图片的格式,输出为:format = JPEG,format是图片对象的一个属性,不一定与图片的保存格式相同
    print('size: ', img.size)		# 图片的尺寸,size:(334, 504)

    img.save(filename='ballet.png')	# 保存时转换图片保存格式为png
    print('format: ', img.format)	
    # format的属性值还是jpeg,跟输出格式没关系的属性

    display(img)		# 打开img对象所指的图片文件,实参必须是Image对象  

2. 调整图片大小和裁剪图片

用图形对象的方法函数resize()调整图片大小。若对速度要求比较高,则使用方法函数sample()。以下代码还用到了复制对象的方法函数clone():

with Image(filename='QingYi.jpg') as img: 
# 打开图片文件生成图片对象

    with img.clone() as convertImage:	# convertImage是img的克隆体
        print('原始尺寸: ', img.size)	# 原始尺寸:(334, 504)
        convertImage.resize(100, 100)
        print('调整后的尺寸: ', convertImage.size)	
        # 调整后的尺寸:(100, 100)
        display(convertImage)	# 显示调整了尺寸后的图片

    convertImage = img.clone()	# 再克隆一份img到convertImage
    convertImage.sample(100, 100 )	# 这样调整大小速度更快
    print('sample()调整的尺寸: ', convertImage.size
    # sample()调整的尺寸:(100, 100)
    display(convertImage)

裁剪图片可以用图片对象的方法函数crop(),也可以直接指定剪切区域:

with Image(filename='QingYi.jpg') as img: 

    with img.clone() as convertImage:	
    # 克隆一份图片到convertImage供剪切用
        convertImage.crop(70, 50, 300, 320)					(1)
        # 从左上坐标(70, 50)到右下坐标(300, 320)的矩形区域被裁剪下来
        display(convertImage)	# 显示裁剪结果
        convertImage.save(filename=’cropImage.jpg’)	# 保存裁剪结果

    convertImage = img.clone()	# 克隆一份图片到convertImage供剪切用
    convertImage.crop(70, 50, width=200, height=250)			(2)
    # 从坐标(70, 50)裁剪宽200高250的矩形区域
    display(convertImage)

    convertImage = img.clone()
    convertImage.crop(width=200, height=300, gravity='center')	(3)
    # 一图片中心为中心,裁剪宽200高300的矩形区域
    display(convertImage)

    with img[150:250, 50:250] as cropped:					(4)
    # 裁剪横坐标50到250,纵坐标50到250的矩形区域
        display(cropped)

 原图和(1)(2)(3)(4)的裁剪结果从左向右的排列见图:

3. 裁剪和调整大小一起做

用图片对象的方法函数transform(),先裁剪后调整大小,给的参数也是先给要裁剪的大小,再给要调整的大小:

with Image(filename='HuaDan.jpeg') as img:
# 生成图像对象
	convertImage = img.clone()		# 克隆一份变形用
	convertImage.transform('200x200', '200%')		(1)
	# 从图片上左上角裁剪,裁剪尺寸200x200,然后把裁剪部分宽高都放大2倍
	display(convertImage)

	convertImage = img.clone()
	convertImage.transform(resize='50%')			(2)
	# 把图片宽和高都缩小一半
	display(convertImage)

	convertImage = img.clone()
	convertImage.transform(resize='x100')			(3)
	# 按比例缩小图片直到高为100
	display(convertImage)

	convertImage = img.clone()
	convertImage.transform(resize='480x320>')		(4)
	# 如果图片不能放进480x320的矩形框就按比例缩小,直到能放进去
	display(convertImage)

	convertImage = img.clone()
	convertImage.transform(crop='300x320+50+10')	(5)
	# 左上角为(0,0)。从图片上(50,10)的位置裁剪个宽300高320的矩形	display(convertImage)

原图和(1)(2)(3)(4)(5)调整的效果从左到右排列见图19.2

4. 接缝雕刻(Seam carving)

接缝雕刻是对图片内容有感知的调整大小,比如调小图片时线计算图片上比较无关要紧的部分,然后通过去除这些“无关要紧”的部分来调整图片大小。

wand用图片对象的方法函数liquid_rescale()做接缝雕刻,但是若要启用这个功能必须先安装一个叫liblqrLiquid Rescale Library)的开源库,这个库专门做content-aware resizing,即考虑到图片内容的调整大小。

如果不安装这个库就调用方法liquid_rescale()就会出现无法载入liblqr的错误“wand ImageMagick in the system is likely to be impossible to load liblqr. You might not install liblqr”,所以必须下载安装这个库。

虽然这个库单独也能装(brew install liblqr),但装完Wand的内核imagemagick找不到liblqr,所以单独装行不通。为了使imagemagick能够得到liblqr的支持,我是先把imagemagick@6卸载:

brew uninstall imagemagick@6

然后带上—-with-liblqr参数重新装:

brew install imagemagick@6 -–with-liblqr

结果又因为Mac上没有安装xcode安装失败,执行这条命令时出现了无可用编译器的错误提示:“CompilerSelectionError: imagemagick@6 cannot be built with any available compilers.

只好去app store下载安装了xcode,而后再回终端重新运行:

brew install imagemagick@6 -–with-liblqr

这才成功将得到liblqr支持的imagemagick@6安装上。

接下来分别对图片进行硬性调整大小resize()、裁剪crop()和考虑内容的调整大小liquid_rescale(),对比它们的效果:

with Image(filename='ice-cubes.jpg') as img:
# 打开图片ice-cubes.jpg,生成图片文件,图片的尺寸是510x340
    with img.clone() as resizeImg:
        resizeImg.resize(300, 300)	# 硬性将大小调整成300x300
        resizeImg.save(filename='resizeI.jpg') # 将调整结果保存下来

    with img.clone()[:300, :] as cropImg:
    # 裁剪图片的矩形区域:x是0~300,保留原样,生成图形对象cropImg
        cropImg.save(filename='cropI.jpg')	# 保存裁剪结果

    with img.clone() as seamImg:		# 克隆一份供接缝雕刻
        seamImg.liquid_rescale(300, 300)	
        # 根据图片内容做“雕刻”,将经过算法计算相对“无关要紧”的部分裁剪下去
        seamImg.save(filename='seamI.jpg')	# 保存“雕刻”结果

排列的图分别为:原图、resizeI.jpg、cropI.jpg、seamI.jpg

从效果图可以看到将图片硬性调整到300x300,图片内容是会变心的;裁剪可以人为地裁剪掉“不重要”部分,留下关键部分,但确定裁剪的位置很麻烦;接缝雕刻seam carving用算法根据图片内容将不要紧的“接缝”剪掉,留下目标尺寸300x300大小的关键内容。

5. 旋转和翻转

用图形对象的方法函数flip()和flop()实现翻转,roate()实现旋转:

with Image(filename='xiaoSheng.jpg') as img:
    with img.clone() as flippedImg:
        flippedImg.flip()		# 上下翻转
        flippedImg.save(filename='flip.jpg')
    with img.clone() as floppedImg:
        floppedImg.flop()		# 左右翻转
        floppedImg.save(filename='flop.jpg')
    with img.clone() as rotatedImg:
        rotatedImg.rotate(90)		# 旋转90度
        rotatedImg.save(filename='rotate90.jpg')
	with img.clone() as rotatedImg:
        rotatedImg.rotate(135, background=Color('rgb(229,221,112)'))
        # 旋转135度,并对图片旋转后的留白设置了背景颜色(黄色)
        rotatedImg.save(filename='rotate135.jpg')

排列的图分别为:原图、flip.jpg、flop.jpg、rotate135.jpg、rotate90.jpg

6. 画图形和往图片上画文字

画图形就是两个要素:画笔draw(颜色、粗细、画的是啥……)和画布img(颜色、大小……):

from wand.color import Color	
# 引入wand中跟颜色有关的模块,至少可以用颜色名称引用颜色了
from wand.drawing import Drawing		# 引入画笔模块,方便做图

with Drawing() as draw:			        # 生成画笔对象
    draw.stroke_color = Color('black')		# 指定画笔画出来的线条颜色
    draw.stroke_width = 2			# 指定画笔画出来线条的粗细
    draw.fill_color = Color('pink')	        # 指定画出来图形内部的填充颜色
    draw.circle((100, 100),                     # 圆心位置
                (125, 125))                     # 圆心到这一点为半径画一个圆

    with Image(width=200, height=200, background=Color('Ivory')) as img:		
    # 生成画布对象,画布大小为200x200,画布颜色为Ivory象牙色。如果不设置backgound参数,则画布就是透明色
        draw(img)	                        # 用设置好的画笔在刚刚生成的画布上画图形
        img.save(filename='circle.jpg')

代码运行结果见后面circle.jpg

往图片上画文字需要设定文字的字体和颜色、采用线条的粗细和颜色、文字在图中的位置……:

with Drawing() as draw:	# 生成画笔对象
    with Image(filename='liYuan.jpg') as img: # 打开图片文件生成画布对象
        draw.font = '/Library/Fonts/XiaoHuYao-2.ttf' 
        #  设置字体。字体文件可以自己下载,放在自己的路径里,这里指明即可
        draw.font_size = 40		# 字体大小
        draw.fill_color = Color('white')		# 字体颜色
        draw.stroke_color = Color('white')		# 字体边框颜色	(1)
        draw.stroke_width = 2				# 字体边框粗细	(2)
        draw.gravity = 'north_east'	# 文字放在东北角,东北角是坐标原点 
        draw.text(15,30, '梨园好语君须听')	
        # 放文字的准确位置是相对于东北角坐标(15, 30),指定文字内容
        draw(img)		# 把文字放在图片上
        display(img)		

排列的图分别为:circle.jpg、添加的文字没带边框,即没写(1)(2)语句、添加的文字带边框

7. 图片叠加

图片叠加用被叠加的图片对象的方法函数composite()实现: 

with Image(filename='beauty.jpeg') as back:		# 被叠加图片的对象(人物)
    with Image(filename='lotus.png') as front:	        # 前景图片对象(莲花)
        back.composite(front, top=100, left=back.width-180)
        # composite()的三个参数:前景图片对象、放前景图片的y坐标与x坐标
        display(back)

8. 调节图片亮度

调节图片亮度通过图片对象的gamma(Y)校正来完成,Y值变化区间0.8~2.3,为1时对图片没影响,接下来通过改变Y值观察对图片亮度的影响:

with Image(filename='cottages.jpg') as img_src:
    for Y in [0.8, 0.9, 1.33, 1.66]:		# Y遍历列表,取不同的值
        with Image(img_src) as img_cpy:	# 拷贝一份图片对象出来
            img_cpy.gamma(Y)		# Gamma矫正
            img_cpy.save(filename='cottagesGamma{}.jpg'.format(Y))
            # 保存不同亮度的图片

排列的图分别为:原图、Gamma0.8、Gamma0.9、Gamma1.33、Gamma1.66

从Gamma校正值从小到大对图片亮度的调节,有没有天渐渐亮了的感觉?

9. 增加黑白对比度

图片黑白边界的控制通过方法函数level()控制,这个函数带三个参数:黑点、白点和亮度值。亮度值同gamma校正值,黑点白点取值区域0~1,代表百分比。接下来的代码用20%、90%和中间亮度1.1对图片进行了调解,效果见图19.7:

with Image(filename='wolf.jpg') as img:
    display(img)			# 显示原来的图片
    img.level(0.2, 0.9, gamma=1.1)	# 调整黑白对比度和亮度
    display(img)			# 显示调解后的图片
    img.save(filename='wolfEnhancement.jpg')	# 保存战果

排列的图分别为:调节前、调解后

有没有觉得调解后更清楚一些(雾散了一些的效果)?

10. 动态图gif的分解合成

先来看动态图的分解,动态图由很多静态帧组成,静态帧顺序排列,有自己的索引。接下来的代码先提取第一帧,然后提取14~20帧,见图19.8:

with Image(filename='pig.gif') as img:		# 生成指向gif文件的图像对象
    print(len(img.sequence))	# 该动态图包括了24帧
    firstImg = Image(image=img.sequence[0])	# 取出第一帧,转成图像对象
    display(firstImg)			# 显示第一帧,display()只接收图像对象做参数
    for frame in img.sequence[13:19]:	# 遍历第14帧到第19帧
        pageImg = Image(image=frame)	# 转成图像对象
        display(pageImg)

排列的帧分别为:1、14、15、16、17、18、19

生成动态图就是把单张图当作一帧添加进动态图序列帧的过程。Mac上查看gif动态图可以用鼠标先选中动态图,而后按住空格键即可查看到动态图的效果。 

with Image() as gifObj:	# gifObj图形对象作为容器接受各个帧
    with Image(filename='xiJu1.jpeg') as one:	
    # 打开第一张图片生成图片对象
        gifObj.sequence.append(one)	
        # 将生成的图像对象添加进gifObj的帧序列里,成为动态图的一帧
    with Image(filename='xiJu2.jpeg') as two:
        gifObj.sequence.append(two)
    with Image(filename='xiJu3.jpeg') as three:
        gifObj.sequence.append(three)

    # 定义动态图中的每一帧停留多久
    for cursor in range(len(gifObj.sequence)):	# 遍历添加进去的三帧
        with gifObj.sequence[cursor] as frame:	# 取每一帧
            frame.delay = 100 * (cursor + 1)	
            # 第一帧停留1秒,第二帧停留2秒,第三帧停留3秒

    gifObj.save(filename='xiJuAnimated.gif')	# 将动态图保存成gif文件

11. 将pdf文件转成图片

本质上wand是调用imagemagcik内核处理PDF文件,imagemagick本身解析不了PDF ,所以要安装专门解析这种格式的外部程序,如ghostscript,在Mac上安装用:

brew install ghostscript

处理过程就是先生成图片对象(为了更清晰可设置分辨率resolution参数),将图片对象保存成文件时会自动生成多张图片,也可保存指定页的图片:

with Image(filename='PythonABC.pdf', resolution=300) as img:
# 打开pdf文件,分辨率设为300,分辨率越高越清晰也越费时,生成图片对象img
    print('pages = ', len(img.sequence))	# 获取pdf文件的页数

    img.save(filename='PythonABC.jpg')		
    # 一页一张,pdf文件保存成多张图片

    Image(img.sequence[1]).save(filename='PythonABC_2ndPage.jpg')
    # 保存指定页为图片