我心中的王者:Python-用Python处理图像文件
当前,高画质的手机已经普及,也许你可以使用许多图像软件处理手机所拍摄的相片,本章笔者将教导您以Python处理这些相片。本章将使用Pillow模块,所以请先导入此模块。
pip install pillow
注意在程序设计中需导入的是PIL模块,主要原因是要向旧版Python Image Library兼容,如下所示:
from PIL import ImageColor
27-1 认识Pillow模块的RGBA
在Pillow模块中RGBA分别代表红色(Red)、绿色(Green)、蓝色(Blue)和透明度(Alpha),这4个与颜色有关的数值组成元组(tuple),每个数值都是在0~255之间。如果Alpha的值是255,代表完全不透明,值越小透明度越高。其他有关颜色的细节可参考附录D。其实它的色彩使用方式也与HTML相同,笔者在所著的《HTML5+CSS3王者归来》一书的附录E有完整的色彩名称与颜色值相对应色彩表。
27-1-1 getrgb( )
这个函数可以将颜色符号或字符串转为元组,在这里可以使用英文名称(例如,“red”)、色彩数值(例如,#00ff00)、rgb函数(例如,rgb(0, 255,0)或rgb函数以百分比代表颜色(例如,rgb(0%,100%,0%))。这个函数在使用时,如果字符串无法被解析判别,将造成ValueError异常。这个函数的使用格式如下:
程序实例ch27_1.py:使用getrgb( )方法返回色彩的元组。
(r, g, b) = getrgb(color) # 返回色彩元组
# ch27_1.py
from PIL import ImageColor
print(ImageColor.getrgb("#0000ff"))
print(ImageColor.getrgb("rgb(0, 0, 255)"))
print(ImageColor.getrgb("rgb(0%, 0%, 100%)"))
print(ImageColor.getrgb("Blue"))
print(ImageColor.getrgb("blue"))
27-1-2 getcolor( )
执行结果
(0, 0, 255)
(0, 0, 255)
(0, 0, 255)
(0, 0, 255)
(0, 0, 255)
功能基本上与getrgb( )相同,它的使用格式如下:
(r, g, b) = getcolor(color, “mode”) # 返回色彩元组
mode若是填写“RGBA”则可返回RGBA元组,如果填写“RGB”则返回RGB元组,如果mode不是色彩,此函数将返回一个整数值。
程序实例ch27_2.py:测试使用getcolor( )函数,了解返回值。
# ch27_2.py
from PIL import ImageColor
print(ImageColor.getcolor("#0000ff", "RGB"))
print(ImageColor.getcolor("rgb(0, 0, 255)", "RGB"))
print(ImageColor.getcolor("Blue", "RGB"))
print(ImageColor.getcolor("#0000ff", "RGBA"))
print(ImageColor.getcolor("rgb(0, 0, 255)", "RGBA"))
print(ImageColor.getcolor("Blue", "RGBA"))
执行结果
(0, 0, 255)
(0, 0, 255)
(0, 0, 255)
(0, 0, 255, 255)
(0, 0, 255, 255)
(0, 0, 255, 255)
27-2 Pillow模块的盒子元组(Box tuple)
下图是Pillow模块的图像坐标的观念。
最左上角的像素是(x,y)是(0,0),x轴像素值往右递增,y轴像素值往下递增。盒子元组的参数是(left, top, right, bottom),意义如下:
left:盒子左上角的x轴坐标。
top:盒子左上角的y轴坐标。
right:盒子右下角的x轴坐标。
bottom:盒子右下角的y轴坐标。
若是上图蓝底是一张图片,则可以用(2, 1, 4, 2)表示它的盒子元组(box tuple),可想成它的图像坐标。
27-3 图像的基本操作
本节使用的图像文件是rushmore.jpg,在ch27文件夹可以找到,此图片内容如下。
27-3-1 打开图像对象
可以使用open( )方法打开一个图像对象,参数是放置欲打开的图像文件。
27-3-2 图像大小属性
可以使用size属性获得图像大小,这个属性可传回图像宽(width)和高(height)。
程序实例ch27_3.py:在ch27文件夹有rushmore.jpg文件,这个程序会列出此图像文件的宽和高。
# ch27_3.py
from PIL import Image
rushMore = Image.open("rushmore.jpg") # 建立Pillow对象
print("列出对象型态 : ", type(rushMore))
width, height = rushMore.size # 获得影像宽度和高度
print("宽度 = ", width)
print("高度 = ", height)
执行结果
列出对象型态 : <class 'PIL.JpegImagePlugin.JpegImageFile'>
宽度 = 270
高度 = 161
27-3-3 取得图像对象文件名
可以使用filename属性获得图像的源文件名称。
程序实例ch27_4.py:获得图像对象的文件名。
# ch27_4.py
from PIL import Image
rushMore = Image.open("rushmore.jpg") # 建立Pillow对象
print("列出物件文件名 : ", rushMore.filename)
执行结果
列出物件文件名 : rushmore.jpg
27-3-4 取得图像对象的文件格式
可以使用format属性获得图像文件格式(可想成图像文件案的扩展名),此外,可以使用format_description属性获得更详细的文件格式描述。
程序实例ch27_5.py:获得图像对象的扩展名与描述。
# ch27_5.py
from PIL import Image
rushMore = Image.open("rushmore.jpg") # 建立Pillow对象
print("列出物件扩展名 : ", rushMore.format)
print("列出对象描述 : ", rushMore.format_description)
执行结果
列出物件扩展名 : JPEG
列出对象描述 : JPEG (ISO 10918)
27-3-5 存储文件
可以使用save( )方法存储文件,甚至我们也可以将jpg文件转存成png文件,同样是图片文件但是以不同格式存储。
程序实例ch27_6.py:将rushmore.jpg转存成out27_6.png。
# ch27_6.py
from PIL import Image
rushMore = Image.open("rushmore.jpg") # 建立Pillow对象
rushMore.save("out27_6.png")
执行结果 在ch27文件夹将可以看到所建的out27_6.png。
27-3-6 建立新的图像对象
可以使用new( )方法建立新的图像对象,它的语法格式如下:
new(mode, size, color=0)
mode可以有多种设定,一般建议用“RGBA”(建立png文件)或“RGB”(建立jpg文件)即可。size参数是一个元组(tuple),可以设定新图像的宽度和高度。color预设是黑色,不过我们可以参考附录D建立不同的颜色。
程序实例ch27_7.py:建立一个水蓝色(aqua)的图像文件out27_7.jpg。
# ch27_7.py
from PIL import Image
pictObj = Image.new("RGB", (300, 180), "aqua") # 建立aqua颜色影像
pictObj.save("out27_7.jpg")
执行结果 在ch27文件夹可以看到下列out27_7.jpg文件。
程序实例ch27_8.py:建立一个透明的黑色的图像文件out27_8.png。
# ch27_8.py
from PIL import Image
pictObj = Image.new("RGBA", (300, 180)) # 建立完全透明影像
pictObj.save("out27_8.png")
执行结果 文件打开后因为透明,看不出任何效果。
27-4 图像的编辑
27-4-1 更改图像大小
Pillow模块提供resize( )方法可以调整图像大小,它的使用语法如下:
resize((width, heigh), Image.BILINEAR) # 双线取样法,也可以省略
第一个参数是新图像的宽与高,以元组表示。第二个参数主要是设定更改图像所使用的方法,常见的除上述方法外,也可以设定Image.NEAREST最低质量,Image.ANTIALIAS最高质量,Image.BISCUBIC三次方取样法,一般可以省略。
程序实例ch27_9.py:分别将图片宽度与高度增加为原先的2倍。
# ch27_9.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
width, height = pict.size
newPict1 = pict.resize((width*2, height)) # 宽度是2倍
newPict1.save("out27_9_1.jpg")
newPict2 = pict.resize((width, height*2)) # 高度是2倍
newPict2.save("out27_9_2.jpg")
执行结果 下列分别是out27_9_1.jpg(左)与out27_9_2.jpg(右)的执行结果。
27-4-2 图像的旋转
Pillow模块提供rotate( )方法可以逆时针旋转图像,如果旋转是90度或270度,图像的宽度与高度会有变化,图像本身比率不变,多的部分以黑色图像替代,如果是其他角度则图像维持不变。
程序实例ch27_10.py:将图像分别旋转90度、180度和270度。
# ch27_10.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
pict.rotate(90).save("out27_10_1.jpg") # 旋转90度
pict.rotate(180).save("out27_10_2.jpg") # 旋转180度
pict.rotate(270).save("out27_10_3.jpg") # 旋转270度
执行结果 下列分别是旋转90、180、270度的结果。
在使用rotate( )方法时也可以增加第2个参数expand=True,如果有这个参数会放大图像,让整个图像显示,多余部分用黑色填满。
程序实例ch27_11.py:没有使用expand=True参数与使用此参数的比较。
# ch27_11.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
pict.rotate(45).save("out27_11_1.jpg") # 旋转45度
pict.rotate(45, expand=True).save("out27_11_2.jpg") # 旋转45度图像扩充
执行结果 下列分别是out27_11_1.jpg与out27_11_2.jpg图像内容。
27-4-3 图像的翻转
可以使用transpose( )让图像翻转,这个方法使用语法如下:
程序实例ch27_12.py:图像左右翻转与上下翻转的实例。
transpose(Image.FLIP_LEFT_RIGHT) # 图像左右翻转
transpose(Image.FLIP_TOP_BOTTOM) # 图像上下翻转
# ch27_12.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
pict.transpose(Image.FLIP_LEFT_RIGHT).save("out27_12_1.jpg") # 左右
pict.transpose(Image.FLIP_TOP_BOTTOM).save("out27_12_2.jpg") # 上下
执行结果 下列分别是左右翻转与上下翻转的结果。
27-4-4 图像像素的编辑
Pillow模块的getpixel( )方法可以取得图像某一位置像素(pixel)的色彩。
getpixel((x,y)) # 参数是元组(x,y),这是像素位置
程序实例ch27_13.py:先建立一个图像,大小是(300,100),色彩是Yellow,然后列出图像中心点的色彩。最后将图像存储至out27_13.png。
# ch27_13.py
from PIL import Image
newImage = Image.new('RGBA', (300, 100), "Yellow")
print(newImage.getpixel((150, 50))) # 打印中心点的色彩
newImage.save("out27_13.png")
执行结果 下列是执行结果与out27_13.png内容。
(255, 255, 0, 255)
Pillow模块的putpixel( )方法可以在影响的某一个位置填入色彩,常用的使用语法如下:
putpixel((x,y), (r, g, b, a)) # 2个参数分别是位置与色彩元组
上述色彩元组的值是在0~255间,若是省略a代表是不透明。另外我们也可以用27-1-2小节的getcolor( )当做第2个参数,用这种方法可以直接用附录D的色彩名称填入指定像素位置,例如,下列是填入蓝色(blue)的方法:
putpixel((x,y), ImageColor.getcolor(“Blue”, “RGBA”)) # 需先导入
ImageColor
程序实例ch27_14.py:建立一个300×300的图像,底色是黄色(Yellow),然后(50, 50, 250, 150)是填入青色(Cyan),此时将上述执行结果存入out27_14_1.png。然后将蓝色(Blue)填入(50, 151,250, 250),最后将结果存入out27_14_2.png。
# ch27_14.py
from PIL import Image
from PIL import ImageColor
newImage = Image.new('RGBA', (300, 300), "Yellow")
for x in range(50, 251): # x轴区间在50-250
for y in range(50, 151): # y轴区间在50-150
newImage.putpixel((x, y), (0, 255, 255, 255)) # 填青色
newImage.save("out27_14_1.png") # 第一阶段存档
for x in range(50, 251): # x轴区间在50-250
for y in range(151, 251): # y轴区间在151-250
newImage.putpixel((x, y), ImageColor.getcolor("Blue", "RGBA"))
newImage.save("out27_14_2.png") # 第一阶段存档
执行结果 下列分别是第一阶段与第二阶段的执行结果。
27-5 裁切、复制与图像合成
27-5-1 裁切图像
Pillow模块有提供crop( )方法可以裁切图像,其中参数是一个元组,元组内容是(左,上,右,下)的区间坐标。
程序实例ch27_15.py:裁切(80, 30, 150, 100)区间。
# ch27_15.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
cropPict = pict.crop((80, 30, 150, 100)) # 裁切区间
cropPict.save("out27_15.jpg")
执行结果 右图是out27_15.jpg的裁切结果。
27-5-2 复制图像
假设我们想要执行图像合成处理,为了不破坏原图像内容,建议可以先保存图像,再执行合成动作。Pillow模块有提供copy( )方法可以复制图像。
程序实例ch27_16.py:复制图像,再将所复制的图像存储。
# ch27_16.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
copyPict = pict.copy() # 复制
copyPict.save("out27_16.jpg")
执行结果 下列是out27_16.jpg的执行结果。
27-5-3 图像合成
Pillow模块有提供paste( )方法可以图像合成,它的语法如下:
底图图像.paste(插入图像, (x,y)) # (x,y)元组是插入位置
程序实例ch27_17.py:使用rushmore.jpg图像,为这个图像复制一份copyPict,裁切一份cropPict,将cropPict合成至copyPict内2次,将结果存入out27_17.jpg。
# ch27_17.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
copyPict = pict.copy() # 复制
cropPict = copyPict.crop((80, 30, 150, 100)) # 裁切区间
copyPict.paste(cropPict, (20, 20)) # 第一次合成
copyPict.paste(cropPict, (20, 100)) # 第二次合成
copyPict.save("out27_17.jpg") # 储存
执行结果
27-5-4 将裁切图片填满图像区间
在Windows操作系统使用中常看到图片填满某一区间,其实我们可以用双层循环完成这个工作。
程序实例ch27_18.py:将一个裁切的图片填满某一个图像区间,最后存储此图像,在这个图像设计中,笔者也设定了留白区间,这区间是图像建立时的颜色。
# ch27_18.py
from PIL import Image
pict = Image.open("rushmore.jpg") # 建立Pillow对象
copyPict = pict.copy() # 复制
cropPict = copyPict.crop((80, 30, 150, 100)) # 裁切区间
cropWidth, cropHeight = cropPict.size # 获得裁切区间的宽与高
width, height = 600, 320 # 新影像宽与高
newImage = Image.new('RGB', (width, height), "Yellow") # 建立新影像
for x in range(20, width-20, cropWidth): # 双层循环合成
for y in range(20, height-20, cropHeight):
newImage.paste(cropPict, (x, y)) # 合成
newImage.save("out27_18.jpg") # 储存
执行结果
27-6 在图像内绘制图案
Pillow模块内有一个ImageDraw模块,可以利用此模块绘制点(Points)、线(Lines)、矩形(Rectangles)、椭圆(Ellipses)、多边形(Polygons)。
在图像内建立图案对象方式如下:
27-6-1 绘制点
ImageDraw模块的point( )方法可以绘制点,语法如下:
point([(x1,y1), … (xn,yn)],fill) #fill是设定颜色
第一个参数是由元组(tuple)组成的列表,(x,y)是欲绘制的点坐标。fill可以是RGBA( )或是直接指定颜色。
27-6-2 绘制线条
ImageDraw模块的line( )方法可以绘制线条,语法如下:
line([(x1,y1), … (xn,yn)], width,fill) # width是宽度,预设是1
第一个参数是由元组(tuple)组成的列表,(x,y)是欲绘制线条的点坐标,如果多于2个点,则这些点会串接起来。fill可以是RGBA( )或是直接指定颜色。
程序实例ch27_19.py:绘制点和线条的应用。
# ch27_19.py
from PIL import Image, ImageDraw
newImage = Image.new('RGBA', (300, 300), "Yellow") # 建立300*300黄色底的影像
drawObj = ImageDraw.Draw(newImage)
# 绘制点
for x in range(100, 200, 3):
for y in range(100, 200, 3):
drawObj.point([(x,y)], fill='Green')
# 绘制线条, 绘外框线
drawObj.line([(0,0), (299,0), (299,299), (0,299), (0,0)], fill="Black")
# 绘制右上角美工线
for x in range(150, 300, 10):
drawObj.line([(x,0), (300,x-150)], fill="Blue")
# 绘制左下角美工线
for y in range(150, 300, 10):
drawObj.line([(0,y), (y-150,300)], fill="Blue")
newImage.save("out27_19.png")
执行结果
27-6-3 绘制圆或椭圆
ImageDraw模块的ellipse( )方法可以绘制圆或椭圆,语法如下:
ellipse((left,top,right,bottom),fill, outline) # outline是外框颜色
第一个参数是由元组(tuple)组成的,(left,top,right,bottom)是包住圆或椭圆的矩形左上角与右下角的坐标。fill可以是RGBA( )或是直接指定颜色,outline是可选择是否加上。
27-6-4 绘制矩形
ImageDraw模块的rectangle( )方法可以绘制矩形,语法如下:
rectangle((left,top,right,bottom),fill, outline) # outline是外框颜色
第一个参数是由元组(tuple)组成的,(left,top,right,bottom)是矩形左上角与右下角的坐标。fill可以是RGBA( )或是直接指定颜色,outline是可选择是否加上。
27-6-5 绘制多边形
ImageDraw模块的polygon( )方法可以绘制多边形,语法如下:
polygon([(x1,y1), … (xn,yn)],fill, outline) # outline是外框颜色
第一个参数是由元组(tuple)组成的列表,(x,y)是欲绘制多边形的点坐标,在此需填上多边形各端点坐标。fill可以是RGBA( )或是直接指定颜色,outline是可选择是否加上。
程序实例ch27_20.py:设计一个图案。
# ch27_20.py
from PIL import Image, ImageDraw
newImage = Image.new('RGBA', (300, 300), 'Yellow') # 建立300*300黄色底的影像
drawObj = ImageDraw.Draw(newImage)
drawObj.rectangle((0,0,299,299), outline='Black') # 影像外框线
drawObj.ellipse((30,60,130,100),outline='Black') # 左眼外框
drawObj.ellipse((65,65,95,95),fill='Blue') # 左眼
drawObj.ellipse((170,60,270,100),outline='Black') # 右眼外框
drawObj.ellipse((205,65,235,95),fill='Blue') # 右眼
drawObj.polygon([(150,120),(180,180),(120,180),(150,120)],fill='Aqua') # 鼻子
drawObj.rectangle((100,210,200,240), fill='Red') # 嘴
newImage.save("out27_20.png")
执行结果
27-7 在图像内填写文字
ImageDraw模块也可以用于在图像内填写英文或中文,所使用的函数是text( ),语法如下:
text((x,y), text,fill, font) # text是想要写入的文字
如果要使用默认方式填写文字,可以省略font参数,可以参考ch27_21.py第8行。如果想要使用其他字体填写文字,需调用ImageFont.truetype( )方法选用字体,同时设定字号。在使用ImageFont.truetype( )方法前需在程序前方导入ImageFont模块,可参考ch27_21.py第2行,这个方法的语法如下:
text(字体路径, 字号)
在Windows系统字体是放在C:\Windows\Fonts文件夹内,在此你可以选择想要的字体。
点选字体,单击鼠标右键,执行内容,再选安全性卷标可以看到此字体的文件名。下列是点选Old English Text的示范输出。
读者可以用复制方式获得字体的路径,有了字体路径后,就可以轻松地在图像内输出各种字体了。
程序实例ch27_21.py:在图像内填写文字,第8~9行是使用默认字体,执行英文字符串“Ming-Chi Institute of Technology”的输出。第10~11行是设定字体为Old English Text,字号是36,输出相同的字符串。第13~15行是设定字体为华康新综艺体,字号是48,输出中文字符串“明志科技大学”。
注 如果你的计算机没有华康新综艺体,执行这个程序会有错误,所以笔者有附一个ch27_21_1.py是使用Microsoft的新细明体字体,你可以自行体会中文的输出。
# ch27_21_1.py
from PIL import Image, ImageDraw, ImageFont
newImage = Image.new('RGBA', (600, 300), 'Yellow') # 建立300*300黄色底的影像
drawObj = ImageDraw.Draw(newImage)
strText = 'Ming-Chi Institute of Technology' # 设定欲打印英文字符串
drawObj.text((50,50), strText, fill='Blue') # 使用默认字型与字号
# 使用古老英文字型, 字号是36
fontInfo = ImageFont.truetype('C:\Windows\Fonts\OLDENGL.TTF', 36)
drawObj.text((50,100), strText, fill='Blue', font=fontInfo)
# 使用Microsoft所提供的新细明体中文字型处理中文字体
strCtext = '明志科技大學' # 设定欲打印中文字符串
fontInfo = ImageFont.truetype('C:\Windows\Fonts\mingliu.ttc', 48)
drawObj.text((50,180), strCtext, fill='Blue', font=fontInfo)
newImage.save("out27_21_1.png")
执行结果
27-8 建立QR code
QR code是目前最流行的二维扫描码,1994年日本Denso-Wave公司发明的,英文字QR所代表的意义是Quick Response(快速反应)。它的最大特色是可以存储比普通条形码更多的资料,同时也不需对准扫描仪。使用前需安装模块:
pip install qrcode
常用的几个方法如下:
img = qrcode.make(“网址数据”) # 产生网址数据的QR code对象img
img.save(“filename”) #filename是存储QR code的文件名
程序实例ch27_22.py:建立http://www.deepstone.com.tw的QR code,这个程序会先列出img对象的数据类型,同时将此对象存入out27_22.jpg文件内。
# ch27_22.py
import qrcode
codeText = 'http://www.deepstone.com.tw'
img = qrcode.make(codeText) # 建立QR code 对象
print("文件格式", type(img))
img.save("out27_22.jpg")
执行结果 下列分别是执行结果与out27_22.jpg的QR code结果。
文件格式 <class 'qrcode.image.pil.PilImage'>