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()做接缝雕刻,但是若要启用这个功能必须先安装一个叫liblqr(Liquid 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')
# 保存指定页为图片