用Python处理图像
入门知识
颜色。如果你有使用颜料画画的经历,那么一定知道混合红、黄、蓝三种颜料可以得到其他的颜色,事实上这三种颜色就是美术中的三原色,它们是不能再分解的基本颜色。在计算机中,我们可以将红、绿、蓝三种色光以不同的比例叠加来组合成其他的颜色,因此这三种颜色就是色光三原色。在计算机系统中,我们通常会将一个颜色表示为一个RGB值或RGBA值(其中的A表示Alpha通道,它决定了透过这个图像的像素,也就是透明度)。
| 名称 | RGBA值 | 名称 | RGBA值 |
|---|---|---|---|
| White | (255, 255, 255, 255) | Red | (255, 0, 0, 255) |
| Green | (0, 255, 0, 255) | Blue | (0, 0, 255, 255) |
| Gray | (128, 128, 128, 255) | Yellow | (255, 255, 0, 255) |
| Black | (0, 0, 0, 255) | Purple | (128, 0, 128, 255) |
像素。对于一个由数字序列表示的图像来说,最小的单位就是图像上单一颜色的小方格,这些小方块都有一个明确的位置和被分配的色彩数值,而这些一小方格的颜色和位置决定了该图像最终呈现出来的样子,它们是不可分割的单位,我们通常称之为像素(pixel)。每一个图像都包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。
Pillow库
Pillow是由从著名的Python图像处理库PIL发展出来的一个分支,通过Pillow可以实现图像压缩和图像处理等各种操作。可以使用下面的命令来安装Pillow。
pip install pillow
Pillow中最为重要的是Image类,可以通过Image模块的open函数来读取图像并获得Image类型的对象。
读取和显示图像
from PIL import Image# 读取图像获得Image对象image = Image.open('image/guido.png')# 通过Image对象的format属性获得图像的格式print(image.format)# 通过Image对象的size属性获得图像的尺寸print(image.size)# 通过Image对象的mode属性获取图像的模式print(image.mode)# 通过Image对象的show方法显示图像image.show()
剪裁图像
# 通过Image对象的crop方法指定剪裁区域图像image.crop((80, 20, 310, 360)).show()
生成缩略图
# 通过Image对象的thumbnail方法生成指定尺寸的缩略图image.thumbnail((128, 128))image.show()
缩放和黏贴图像
from PIL import Imagetouxiang_image = Image.open('image/touxiang.png')guido_image = Image.open('image/guido.png')# 从吉多的照片上裁剪出吉多的头head = touxiang_image.crop((140, 70, 495, 495))width, height = head.size# 使用Image对象的resize方法修改图像尺寸# 使用Image对象的paste方法将吉多的头粘贴到头像图片上guido_image.paste(head.resize((int(width / 1.5), int(height / 1.5))), (172, 40))guido_image.show()
旋转和翻转
guido_image = Image.open('image/guido.png')# 使用Image对象的rotate方法实现图像的旋转guido_image.rotate(45).show()# 使用Image对象的transpose方法实现图像翻转# Image.FLIP_LEFT_RIGHT - 水平翻转# Image.FLIP_TOP_BOTTOM - 垂直翻转guido_image.transpose(Image.FLIP_LEFT_RIGHT).show()
操作像素
guido_image = Image.open('image/guido.png')for x in range(80, 310):for y in range(20, 360):# 通过Image对象的putpixel方法修改图像指定像素点guido_image.putpixel((x, y), (128, 128, 128))guido_image.show()
滤镜效果
from PIL import Image, ImageFilterguido_image = Image.open('image/guido.png')# 使用Image对象的filter方法对图像进行滤镜处理# ImageFilter模块包含了诸多预设的滤镜也可以自定义滤镜guido_image.filter(ImageFilter.CONTOUR).show()
使用Pillow绘图
Pillow中有一个名为ImageDraw的模块,该模块的Draw函数会返回一个ImageDraw对象,通过ImageDraw对象的arc、line、rectangle、ellipse、polygon等方法,可以在图像上绘制出圆弧、线条、矩形、椭圆、多边形等形状,也可以通过该对象的text方法在图像上添加文字。
import randomfrom PIL import Image, ImageDraw, ImageFontdef random_color():"""生成随机颜色"""r = random.randint(0, 255)g = random.randint(0, 255)b = random.randint(0, 255)return r, g, bwidth, height = 800, 600# 创建一个800*600的图像,背景颜色为白色image = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))# 创建一个ImageDraw对象drawer = ImageDraw.Draw(image)# 通过指定字体和大小获得ImageFont对象font = ImageFont.truetype('Kongxin.ttf', 32)# 通过ImageDraw对象的text方法绘制文字drawer.text((300, 50), 'Hello world!', fill=(255, 0, 0), font=font)# 通过ImageDraw对象的line方法绘制两条对角线drawer.line((0, 0, width, height), fill=(0, 0, 255), width=2)drawer.line((width, 0, 0, height), fill=(0, 0, 255), width=2)# 通过ImageDraw对象的rectangle方法绘制矩形xy = width // 2 - 60, height // 2 - 60, width // 2 + 60, height // 2 + 60drawer.rectangle(xy, outline=(255, 0, 0), width=2)# 通过ImageDraw对象的ellipse方法绘制椭圆for i in range(4):left, top, right, bottom = 150 + i * 120, 220, 310 + i * 120, 380drawer.ellipse((left, top, right, bottom), outline=random_color(), width=8)# 显示图像image.show()# 保存图像image.save('image/result.png')

注意:上面代码中使用的字体文件需要根据自己准备,可以选择自己喜欢的字体文件并放置在代码目录下。
除了可以用Pillow来处理图像外,还可以使用更为强大的OpenCV库来完成图形图像的处理,OpenCV(Open Source Computer Vision Library)是一个跨平台的计算机视觉库,可以用来开发实时图像处理、计算机视觉和模式识别程序。
用Python读写CSV文件
CSV文件介绍
CSV(Comma Separated Values)全称逗号分隔值文件是一种简单、通用的文件格式,被广泛的应用于应用程序(数据库、电子表格等)数据的导入和导出以及异构系统之间的数据交换。因为CSV是纯文本文件,不管是什么操作系统和编程语言都是可以处理纯文本的,而且很多编程语言中都提供了对读写CSV文件的支持,因此CSV格式在数据处理和数据科学中被广泛应用。
CSV文件有以下特点:
- 纯文本,使用某种字符集(如ASCII、Unicode、GB2312)等);
- 由一条条的记录组成(典型的是每行一条记录);
- 每条记录被分隔符(如逗号、分号、制表符等)分隔为字段(列);
- 每条记录都有同样的字段序列。
CSV文件可以使用文本编辑器或类似于Excel电子表格这类工具打开和编辑,当使用Excel这类电子表格打开CSV文件时,你甚至感觉不到CSV和Excel文件的区别。很多数据库系统都支持将数据导出到CSV文件中,当然也支持从CSV文件中读入数据保存到数据库中。
将数据写入CSV文件
现有五个学生三门课程的考试成绩需要保存到一个CSV文件中,要达成这个目标,可以使用Python标准库中的csv模块,该模块的writer函数会返回一个csvwriter对象,通过该对象的writerow或writerows方法就可以将数据写入到CSV文件中,具体的代码如下所示。
import csvimport randomwith open('res/scores.csv', 'w') as file:writer = csv.writer(file)writer.writerow(['姓名', '骑马', '射箭', '兵器'])names = ['关羽', '张飞', '赵云', '马超', '黄忠']for i in range(len(names)):riding = random.randint(50, 100)archery = random.randint(40, 100)weapon = random.randint(30, 100)writer.writerow([names[i], riding, archery, weapon])
需要说明的是上面的writer函数,该函数除了传入要写入数据的文件对象外,还可以dialect参数,它表示CSV文件的方言,默认值是excel。除此之外,还可以通过delimiter、quotechar、quoting参数来指定分隔符(默认是逗号)、包围值的字符(默认是双引号)以及包围的方式。其中,包围值的字符主要用于当字段中有特殊符号时,通过添加包围值的字符可以避免二义性。大家可以尝试将上面第5行代码修改为下面的代码,看看生成的CSV文件到底有什么区别。
writer = csv.writer(file, delimiter='|', quoting=csv.QUOTE_ALL)
从CSV文件读取数据
如果要读取刚才创建的CSV文件,可以使用下面的代码,通过csv模块的reader函数可以创建出csvreader对象,该对象是一个迭代器,可以通过next函数或for-in循环读取到文件中的数据。
import csvwith open('res/scores.csv', 'r') as file:reader = csv.reader(file, delimiter=',')for line in reader:print(reader.line_num, end='\t')for elem in line:print(elem, end='\t')print()
注意:上面的代码对
csvreader对象做for循环时,每次会取出一个列表对象,该列表对象包含了一行中所有的字段。
将来如果大家使用Python做数据分析,很有可能会用到名为pandas的三方库,它是Python数据分析的神器之一。pandas中封装了名为read_csv和to_csv的函数用来读写CSV文件,其中read_CSV会将读取到的数据变成一个DataFrame对象,而这个对象就是pandas库中最重要的类,它封装了一系列的方法用于对数据进行处理(清洗、转换、聚合等);而to_csv会将DataFrame对象中的数据写入CSV文件,完成数据的持久化。
用Python读写Excel文件
Excel简介
Excel是Microsoft(微软)为使用Windows和macOS操作系统开发的一款电子表格软件。Excel凭借其直观的界面、出色的计算功能和图表工具,再加上成功的市场营销,一直以来都是最为流行的个人计算机数据处理软件。当然,Excel也有很多竞品,例如Google Sheets、LibreOffice Calc、Numbers等,这些竞品基本上也能够兼容Excel,至少能够读写较新版本的Excel文件,当然这些不是我们讨论的重点。掌握用Python程序操作Excel文件,可以让日常办公自动化的工作更加轻松愉快,而且在很多商业项目中,导入导出Excel文件都是特别常见的功能。
Python操作Excel需要三方库的支持,如果要兼容Excel 2007以前的版本,也就是xls格式的Excel文件,可以使用三方库xlrd和xlwt,前者用于读Excel文件,后者用于写Excel文件。如果使用较新版本的Excel,即操作xlsx格式的Excel文件,也可以使用openpyxl库,当然这个库不仅仅可以操作Excel,还可以操作其他基于Office Open XML的电子表格文件。
下面我们以xlwt和xlrd为例讲解如何读写Excel文件,大家可以先使用下面的命令安装这两个三方库文件。
pip install xlwt xlrd -i https://pypi.doubanio.com/simple
使用xlwt和xlrd
读Excel文件
例如在当前文件夹下有一个名为“HistoricalData.xls”的Excel文件,如果想读取并显示该文件的内容,可以通过如下所示的代码来完成。
import xlrd# 使用xlrd模块的open_workbook函数打开指定Excel文件并获得Book对象(工作簿)wb = xlrd.open_workbook('res/HistoricalData.xls')# 通过Book对象的sheet_names方法可以获取所有表单名称sheetname = wb.sheet_names()[0] # [0]第一个表单# 通过指定的表单名称获取sheet对象(工作表)sheet = wb.sheet_by_name(sheetname)# 通过sheet对象的nrows和ncols属性获取表单的行数和列数print(sheetname)print(sheet.nrows, sheet.ncols)for row in range(sheet.nrows):for col in range(sheet.ncols):# 通过Sheet对象的cell方法获取指定Cell对象(单元格)# 通过Cell对象的value属性获取单元格中的值value = sheet.cell(row, col).value# 对除首行外的欺负他行进行数据格式化处理if row > 0:# 第一列的xldate类型先转成元组再格式化为"年月日"的格式if col == 0:# xldate_as_tuple函数的第二个参数只有0和1两个取值# 其中0代表以1900-01-01为基准的日期,1代表以1904-01-01为基准的日期# value = xlrd.xldate_as_tuple(value, 1)value = f'{value[-4:]}年{value[:2]}月{value[3:5]}日'# 其他列的number类型处理成小数点后保留两位有效数字的浮点数else:value = f'{value:.2f}'print(value, end='\t')print()# 获取最后一个单元格的数据类型# 0 - 空值,1 - 字符串,2 - 数字,3 - 日期,4 - 布尔,5 - 错误last_cell_type = sheet.cell_type(sheet.nrows - 1, sheet.ncols - 1)print(last_cell_type)# 获取第一行的值(列表)print(sheet.row_values(0))# 获取指定行指定列范围的数据(列表)# 第一个参数代表行索引,第二个和第三个参数代表列的开始(含)和结束(不含)索引print(sheet.row_slice(3, 0, 5))
注意:xlrd现已更新到了2.0.1版本,只支持.xls文件,不支持.xlsx文件。下面命令可以安装旧版 pip uninstall xlrd pip install xlrd==1.2.0 更多关于
xlrd模块的知识,可以阅读它的官方文档。
写Excel文件
写入Excel文件可以通过xlwt 模块的Workbook类创建工作簿对象,通过工作簿对象的add_sheet方法可以添加工作表,通过工作表对象的write方法可以向指定单元格中写入数据,最后通过工作簿对象的save方法将工作簿写入到指定的文件或内存中。下面的代码实现了将5个学生3门课程的考试成绩写入Excel文件的操作。
import randomimport xlwtstudent_names = ['关羽', '张飞', '赵云', '马超', '黄忠']scores = [[random.randint(40, 100) for _ in range(3)] for _ in range(5)]# 创建工作簿对象(Workbook)wb = xlwt.Workbook()# 创建工作表对象(Worksheet)sheet = wb.add_sheet('蜀国')# 添加表头数据titles = ('姓名', '骑马', '射箭', '兵器')for index, title in enumerate(titles):# write(行, 列, '内容')sheet.write(0, index, title)# 将学生姓名和考试成绩写入单元格for row in range(len(scores)):sheet.write(row + 1, 0, student_names[row])for col in range(len(scores[row])):sheet.write(row + 1, col + 1, scores[row][col])# 保存Excel工作簿wb.save('res/蜀国成绩表.xls')
调整单元格样式
在写Excel文件时,我们还可以为单元格设置样式,主要包括字体(Font)、对齐方式(Alignment)、边框(Border)和背景(Background)的设置,xlwt对这几项设置都封装了对应的类来支持。要设置单元格样式需要首先创建一个XFStyle对象,再通过该对象的属性对字体、对齐方式、边框等进行设定,例如在上面的例子中,如果希望将表头单元格的背景色修改为黄色,可以按照如下的方式进行操作。
header_style = xlwt.XFStyle()pattern = xlwt.Pattern()pattern.pattern = xlwt.Pattern.SOLID_PATTERN# 0 - 黑色、1 - 白色、2 - 红色、3 - 绿色、4 - 蓝色、5 - 黄色、6 - 粉色、7 - 青色、pattern.pattern_fore_colour = 5header_style.pattern = pattern# 添加表头数据titles = ('姓名', '骑马', '射箭', '兵器')for index, title in enumerate(titles):# write(行, 列, '内容')sheet.write(0, index, title, header_style)
如果希望为表头设置指定的字体,可以使用Font类并添加如下所示的代码。
font = xlwt.Font()font.name = '华文楷体' # 字体名称font.height = 20 * 18 # 字体大小(20是基准单位,18表示18px)font.bold = True # 是否使用粗体font.italic = False # 是否使用斜体font.colour_index = 2 # 字体颜色header_style.font = font
如果希望表头垂直居中对齐,可以使用下面的代码进行设置。
align = xlwt.Alignment()# 垂直方向的对齐方式align.vert = xlwt.Alignment.VERT_CENTER# 水平方向的对齐方式align.horz = xlwt.Alignment.HORZ_CENTERheader_style.alignment = align
如果希望给表头加上黄色的虚线边框,可以使用下面的代码来设置。
borders = xlwt.Borders()props = (('top', 'top_colour'), ('right', 'right_colour'),('bottom', 'bottom_colour'), ('left', 'left_colour'))# 通过循环对四个方向的边框样式及颜色进行设定for position, color in props:setattr(borders, position, xlwt.Borders.DASHED)setattr(borders, color, 5)header_style.borders = borders
如果要调整单元格的宽度(列宽)和表头的高度(行高),可以按照下面的代码进行操作。
# 设置行高为40pxsheet.row(0).set_style(xlwt.easyxf(f'font:height {20 * 40}'))titles = ('姓名', '语文', '数学', '英语')for index, title in enumerate(titles):# 设置列宽为200pxsheet.col(index).width = 20 * 200# 设置单元格的数据和样式sheet.write(0, index, title, header_style)
其他操作Excel文件的三方库(如openpyxl)大家有兴趣可以自行了解。掌握了Python程序操作Excel的方法,可以解决日常办公中很多繁琐的处理Excel电子表格工作,最常见就是将多个数据格式相同的Excel文件合并到一个文件以及从多个Excel文件或表单中提取指定的数据。当然,如果要对表格数据进行处理,使用Python数据分析神器之一的pandas库可能更为方便,因为pandas库封装的函数以及DataFrame类可以完成大多数数据处理的任务。
用Python操作PDF文件
PDF是Portable Document Format的缩写,这类文件通常使用.pdf作为其扩展名。在日常开发工作中,最容易遇到的就是从PDF中读取文本内容以及用已有的内容生成PDF文档这两个任务。
从PDF中提取文本
在Python中,可以使用名为PyPDF2的三方库来读取PDF文件,可以使用下面的命令来安装它。
pip install PyPDF2 -i https://pypi.doubanio.com/simple
PyPDF2没有办法从PDF文档中提取图像、图表或其他媒体,但它可以提取文本,并将其返回为Python字符串。
import PyPDF2reader = PyPDF2.PdfFileReader('res/test.pdf')page = reader.getPage(0)print(page.extractText())
当然,PyPDF2并不是什么样的PDF文档都能提取出文字来,尤其是在提取中文的时候。网上有很多讲解从PDF中提取文字的文章,推荐阅读《三大神器助力Python提取pdf文档信息》一文进行了解。
要从PDF文件中提取文本也可以使用一个三方的命令行工具,具体的做法如下所示。
pip install pdfminer.sixpdf2text.py test.pdf
旋转和叠加页面
上面的代码中通过创建PdfFileReader对象的方式来读取PDF文档,该对象的getPage方法可以获得PDF文档的指定页并得到一个Page对象,利用Page对象的rotateClockwise和rotateCounterClockwise方法可以实现页面的顺时针和逆时针方向旋转,代码如下所示。
import PyPDF2reader = PyPDF2.PdfFileReader('test.pdf')page = reader.getPage(0)page.rotateClockwise(90)writer = PyPDF2.PdfFileWriter()writer.addPage(page)with open('test-rotated.pdf', 'wb') as file:writer.write(file)
Page对象还有一个名为mergePage的方法,可以将一个页面和另一个页面进行叠加,对于叠加后的页面,我们还是使用PdfFileWriter对象的addPage将其添加到一个新的PDF文档中。
加密PDF文件
使用PyPDF2中的PdfFileWrite对象可以为PDF文档加密,如果需要给一系列的PDF文档设置统一的访问口令,使用Python程序来处理就会非常的方便。
import PyPDF2with open('res/test.pdf', 'rb') as file:# 通过PdfReader读取未加密的PDF文档reader = PyPDF2.PdfFileReader(file)writer = PyPDF2.PdfFileWriter()for page_num in range(reader.numPages):# 通过PdfReader的getPage方法获取指定页码的页# 通过PdfWriter方法的addPage将添加读取到的页writer.addPage(reader.getPage(page_num))# 通过PdfWriter的encrypt方法加密PDF文档writer.encrypt('foobared')# 将加密后的PDF文档写入指定的文件中with open('res/test-encrypted.pdf', 'wb') as file2:writer.write(file2)
提示:按照上面的方法也可以读取多个PDF文件的多个页面并将其合并到一个PDF文件中。
创建PDF文件
创建PDF文档需要三方库reportlab的支持,该库的使用方法可以参考它的官方文档,安装的方法如下所示。
pip install reportlab
下面通过一个例子展示了reportlab的用法。
import randomfrom reportlab.lib import colorsfrom reportlab.lib.pagesizes import A4from reportlab.pdfgen import canvasfrom reportlab.platypus import Table, TableStyle# 创建Canvas对象(PDF文档对象)doc = canvas.Canvas('demo.pdf', pagesize=A4)# 获取A4纸的尺寸width, height = A4# 读取图像image = canvas.ImageReader('guido.jpg')# 通过PDF文档对象的drawImage绘制图像内容doc.drawImage(image, (width - 250) // 2, height - 475, 250, 375)# 设置字体和颜色doc.setFont('Helvetica', 32)doc.setFillColorRGB(0.8, 0.4, 0.2)# 通过PDF文档对象的drawString输出字符串内容doc.drawString(10, height - 50, "Life is short, I use Python!")# 保存当前页创建新页面doc.showPage()# 准备表格需要的数据scores = [[random.randint(60, 100) for _ in range(3)] for _ in range(5)]names = ('Alice', 'Bob', 'Jack', 'Lily', 'Tom')for row, name in enumerate(names):scores[row].insert(0, name)scores.insert(0, ['Name', 'Verbal', 'Math', 'Logic'])# 创建一个Table对象(第一个参数是数据,第二个和第三个参数是列宽和行高)table = Table(scores, 50, 20)# 设置表格样式(对齐方式和内外边框粗细颜色)table.setStyle(TableStyle([('ALIGN', (0, 0), (-1, -1), 'CENTER'),('INNERGRID', (0, 0), (-1, -1), 0.25, colors.red),('BOX', (0, 0), (-1, -1), 0.25, colors.black)]))table.split(0, 0)# 通过Table对象的drawOn在PDF文档上绘制表格table.drawOn(doc, (width - 200) // 2, height - 150)# 保存当前页创建新页面doc.showPage()# 保存PDF文档doc.save()
说明:上面的代码使用了很多字面常量来指定位置和尺寸,在商业项目开发中应该避免这样的硬代码(hard code),因为这样的代码不仅可读性差,而且维护起来也是一场噩梦。如果项目中需要动态生成PDF文档且PDF文档的格式是相对固定的,可以将上面的字面常量处理成符号常量。记住:符号常量优于字面常量。Python语言没有内置定义常量的语法,但是可以约定变量名使用全大写字母的变量就是常量。

