IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python中的PIL库处理图片实例(个人笔记) -> 正文阅读

[Python知识库]Python中的PIL库处理图片实例(个人笔记)

最近老爸在整族谱的事,用PIL等库处理了一下,这里做个笔记
从家谱网上下载下来的图片的格式不太合他的心意,一张一张改吧,图片数量实在多了(几百张),提了这么几个需求:

  1. 下载下来的图片全是两页合在一起的,但打印的话需要是单页,那么就需要对半切;(图片切片 crop)
  2. 下载下来的图片中有页码,每个支系的页码都是从1开始,但最终要所有支系合成一册,要改页码;(更改图片中某一块位置的像素值 putpixel)(图片粘贴 paste)
  3. 图片文件转pdf文件,并将所有图片合并为一个pdf文件 (图片转pdf文件 ImgToPdf)

?

“小爷我凭啥放着懒觉不睡、游戏不打来给您做苦力啊?”
“事成之后村里给两万劳务费”
“咳咳~~为咱村里做点贡献实乃吾辈分内之事”

在这里插入图片描述
咳嗯~言归正传,开始码思路

任务背景介绍

????1.待处理图片格式:JPG(因为不了解这个格式的具体情况,然后就捅了挺大一篓子的,下面会说)
????2.待处理文件目录格式:如下图
在这里插入图片描述
在这里插入图片描述
可以看见,所有图片的名字都是数字,这是按照内容顺序起的,所以咱切了之后也要保证有序,就需要在遍历出来的文件路径中把这些数字提取出来,然后处理一下作为新图片的名称
比如:原来叫1.jpg的图片,对半切之后应该产生1.jpg和2.jpg;原来叫2.jpg的图片,对半切之后应该产生3.jpg和4.jpg;原来叫25.jpg的图片对半切之后就应该产生49.jpg和50.jpg…

问题分析+代码实现

需求1:图片切片(CropPic)

需要实现的功能如下图所示:
在这里插入图片描述
那不就是正中间切一刀然后分俩图片存起来嘛~ 开搞!
要用到的库:os,PIL

  • 因为要遍历文件夹,所以得import一个os进来,通过os.walk(文件夹路径)来把D:\20x10这个文件夹里存的所有图片的路径全读出来(关于os.walk的用法可以康康这篇文章[os.walk的简要介绍]
  • 因为要操作图片,所以要import一个PIL进来

废话不说了,上代码

import os
from PIL import Image

srcDirectory = input("输入待处理图片所在文件夹的路径:")
# 查一下,看看有没有srcDirectory这个文件夹,要是没有那我还读个锤子读
if not os.path.exists(srcDirectory):
	print("不存在的文件夹你也敢读?你很勇♂噢~")
	exit(-1)

dstDirectory = input("输入新文件存放的文件夹路径:")
# 查一下,看看有没有dstDirectory这个文件夹,要是没有咱自己给它造一个出来
if not os.path.exists(dstDirectory):
	os.mkdir(dstDirectory)

# 好啦~排除一切异常之后,开始遍历出所有图片的路径了
for root, dirs, files in os.walk(srcDirectory, topdown=False):
	# 1.新文件夹中的文件夹结构应该和原来的一致,所以这里我们需要提取出子文件夹的名称
	#   root里存的是绝对路径中,除了文件名字以外的东西,例如:
	#   D:\20x10\1卷之一\1.jpg,对于这个文件,root就是"D:\\20x10\\1卷之一"这个字符串
	#   所以先用split函数把root切成几个小片段
	#   然后观察到每个子文件夹的名字中都有“卷之”这俩字
	#   所以把包含这俩字的片段提出来就是子文件夹名
	roots = root.split("\\")   # 切成小片段
	dirName = ""      # 存子文件夹的名字
	for subRoot in roots:
		if "卷之" in subRoot:
			dirName = subRoot
			break
	NewDir = dstDirectory + "\\" + dirName
	# 还得看看NewDir存不存在,要是不存在还得咱来生成一个
	if not os.path.exists(NewDir):
		os.mkdir(NewDir)
	
	# 计算当前子文件夹里总共有几个图片文件,用来显示进度的,可要可不要
	Id = 1
	totalNum = len(files)
	# 遍历所有图片
	# files里存的都是: "1.jpg", "2.jpg", "3.jpg"这些文件的名字
	for file in files:
		# 源图片的绝对路径
		src_path = root + "\\" + file
		
		# 俩目标图片的绝对路径
		# str(int(file[:-4])*2-1)解释:为了提取出源图片名称中的数字
		# 假设此时file是"10.jpg",应该生成"19.png"和"20.png"
		# 那么file[:-4]就取出了file中从头到倒数第4个字符之间的字符串
		# 也就是 "10" 这个字符串,然后转换成整数
		# 处理一下就可以作为新文件夹的名字使用了
		dst_path_1 = NewDir + "\\" + str(int(file[:-4])*2-1) + ".png"
		dst_path_2 = NewDir + "\\" + str(int(file[:-4])*2) + ".png"
		# 这里之所以采用png格式,是因为:
		# jpg(又名jpeg),采用的是有损压缩方式
		# 说白了就是越存越模糊,每转存一次就丢一点数据
		# 不信可以把这里的".png"改成".jpg",然后对比一下就能看出来,图片脏了不少
		# 新生成的图片对象,在进行save这一操作时,如果采用jpg格式,
		# 就会比原图模糊一些,产生失真,变丑
		# 但png格式的图片就不会,它采用的是无损压缩方式
		
		# 打开图片文件,并获取图片的长、宽
		src_img = Image.open(src_path)
		length = img.size[0]
		height = img.size[1]
		
		# 设置裁剪位置
		# 右半张图:以(length//2, 0)为左上角,(length, height)为右下角
		crop_box_1 = (length//2, 0, length, height)
		# 左半张图:以(0, 0)为左上角,(length//2, height)为右下角
		crop_box_2 = (0, 0, length//2, height)
		
		# ----------------------正式开剪----------------------
		# 调用crop函数,根据两个crop_box剪出俩图片
		img1 = src_img.crop(crop_box_1)
		img2 = src_img.crop(crop_box_2)
		
		# 保存图片,如果是jpg格式,那么这里会因jpg格式的有损压缩产生图片失真
		img1.save(dst_path_1)
		img2.save(dst_path_2)
		
		# 关闭图片,虽然不关闭看起来也没啥事,但咱得有个好习惯
		src_img.close()
		img1.close()
		img2.close()
		# ---------------------- 剪完了 ----------------------
		
		# 闲着也是闲着,整个进度显示:
		print("\r当前子文件夹处理进度:{:.2f}%".format(Id*100/totalNum), end='')

print("处理完成!")

需求2:擦去原有页码并生成新页码(AddPageNum)

在这里插入图片描述
这是其中一张样例图,可以看见图中有页码“三、四”,最大页码达到三位数;
图片内容识别是不可能做的,因为我不会哈哈哈哈哈哈~
况且每幅图中页码位置均相同,而且每个图大小均相同,所以咱框定某一坐标范围内的内容,将该范围内的pixel值都设置成白色(255,255,255)就能实现页码擦除啦~
诶~那坐标范围我咋知道?乱猜?那不是,用电脑自带的画图打开图片,就能显示坐标的
然后,产生新页码也不麻烦:
首先咱把0~9这几个数字对应的汉字图片素材造出来(我是用PPT打出〇一二三四五六七八九这几个字,然后截图存起来之后,再裁剪出每个字,命名就用对应阿拉伯数字,下面是我的目录):
在这里插入图片描述
然后怎么在程序里通过整型数来将这几个图贴进去呢?
首先,整型数转换成字符串,然后遍历该字符串,每个字符刚好对应一个图片,使用paste函数将对应数字素材图片粘贴进原图限定位置即可

话不多说上代码
简要说明:

  • 遍历文件夹的操作与前面基本无异,只需改变“正片”部分即可。
  • 本过程处理的图片是前一步生成的单页文件
  • 单页文件中,图片名为奇数的,页码在左;为偶数的,页码在右,需要注意控制
import os
from PIL import Image

srcDirectory = input("输入待处理图片所在文件夹的路径:")
# 查一下,看看有没有srcDirectory这个文件夹,要是没有那我还读个锤子读
if not os.path.exists(srcDirectory):
	print("不存在的文件夹你也敢读?你很勇♂噢~")
	exit(-1)

dstDirectory = input("输入新文件存放的文件夹路径:")
# 查一下,看看有没有dstDirectory这个文件夹,要是没有咱自己给它造一个出来
if not os.path.exists(dstDirectory):
	os.mkdir(dstDirectory)

# 设置裁剪位置
left = 62 - 30    # 若页码在图片左侧时
right = 998 - 62  # 若页码在图片右侧时
top = 992         # 待擦除区域的上边界坐标
bottom = 1082     # 待擦除区域的下边界坐标
elem_size = 30    # 每个素材图片的边长

# 好啦~排除一切异常之后,开始遍历出所有图片的路径了
for root, dirs, files in os.walk(srcDirectory, topdown=False):
	# 1.新文件夹中的文件夹结构应该和原来的一致,所以这里我们需要提取出子文件夹的名称
	#   root里存的是绝对路径中,除了文件名字以外的东西,例如:
	#   D:\20x10\1卷之一\1.jpg,对于这个文件,root就是"D:\\20x10\\1卷之一"这个字符串
	#   所以先用split函数把root切成几个小片段
	#   然后观察到每个子文件夹的名字中都有“卷之”这俩字
	#   所以把包含这俩字的片段提出来就是子文件夹名
	# 跳过所有不包含特征词"卷之"的所有残次文件夹目录
	if "卷之" not in root:
		continue
	roots = root.split("\\")   # 切成小片段
	dirName = ""      # 存子文件夹的名字
	for subRoot in roots:
		if "卷之" in subRoot:
			dirName = subRoot
			break
	NewDir = dstDirectory + "\\" + dirName
	# 还得看看NewDir存不存在,要是不存在还得咱来生成一个
	if not os.path.exists(NewDir):
		os.mkdir(NewDir)
	
	# 计算当前子文件夹里总共有几个图片
	Id = 1
	totalNum = len(files)
	# 遍历所有图片
	# files里存的都是: "1.jpg", "2.jpg", "3.jpg"这些文件的名字
	# 控制左右页码
	isLeft = True

	# 新生成的页码
	PageNum = 1
	for i in range(0, totalNum):
		# 源图片的绝对路径
		# 万一文件夹中图片名称的数字之间不是连续的整数,此处进行处理
		count = 0
		while not os.path.exists(root+"\\"+str(i+count)+".jpg"
			count += 1
		src_path = root + "\\" + str(i+count) + ".jpg"
		dst_path = NewDir + "\\" + files[i].replace("jpg", "png")
		# 这里之所以采用png格式,再次解释一遍,是因为:
		# jpg(又名jpeg),采用的是有损压缩方式
		# 说白了就是越存越模糊,每转存一次就丢一点数据
		# 不信可以把这里的".png"改成".jpg",然后对比一下就能看出来,图片脏了不少
		# 新生成的图片对象,在进行save这一操作时,如果采用jpg格式,
		# 就会比原图模糊一些,产生失真,变丑
		# 但png格式的图片就不会,它采用的是无损压缩方式
		
		# ----------------------正片开始----------------------
		src_img = Image.open(src_path)
		# 改变特定区域的pixel值来实现内容擦除
		# 调用putpixel来改变图片某处像素值
		# 有的图页码在左边,有的图页码在右边,paste的位置不一样
		left_edge = left
		if isLeft:
			isLeft = False
		else:
			left_edge = right
			isLeft = True
		
		# 遍历该区域,然后涂白
		for x in range(left_edge, left_edge+31):
			for y in range(top, bottom):
				src_img.putpixel((x, y), (255, 255, 255))
		
		# 接下来,开始生成新页码,主要使用paste这一函数
		elemDirectory = "F:\\数字素材0~9\\"
		# 提取出页码的每一位数字
		PageDigit = [PageNum//100, PageNum%100//10,  PageNum%10]
		# 页码位数
		DigitNum = 1
		if PageNum >= 10:
			DigitNum += 1
		if PageNum >= 100:
			DigitNum += 1
		
		# 设定粘贴(paste)位置
		pos = ()
		if isLeft:
			pos = (left, top + elem_size * (3 - DigitNum) // 2)
            isLeft = False
        else:
            pos = (right, top + elem_size * (3 - DigitNum) // 2)
            isLeft = True
		
		# 打开素材图片,并贴入当前图片
        for j in range(0, DigitNum):
            elemImg = Image.open(elemDirectory+str(PageDigit[j])+".png")
            img.paste(elemImg, box=pos)
            pos = (pos[0], pos[1]+elem_size)
            elemImg.close()

		# 保存然后关闭
		src_img.save(dst_path)
		src.img.close()
		# ---------------------- 剪完了 ----------------------
		
		# 闲着也是闲着,整个进度显示:
		print("\r当前子文件夹处理进度:{:.2f}%".format(Id*100/totalNum), end='')

print("处理完成!")

需求3:图片转pdf文件(ImgToPdf)

很简单的一个需求,利用canvas(from reportlab.pdfgen import canvas)打开一个pdf文件画布,通过读取图片数据来填充画布即可生成图片对应的单页pdf文件。
这样就可以得到一个文件夹的pdf,但是要合成一整个文件夹中的pdf,就需要合并pdf(merge pdf)。
其实也好说,调用PyPDF2.PdfFileMerger()产生一个merger,然后将待合并的两个pdf文件用PyPDF2.PdfFileReader()打开后,append进merger,再将merger中的内容write进目的文件路径(dst_root),至此,pdf文件就做好了,然后,习惯问题,merger要记得关:merger.close()。
废话多了,上代码:

import os
import PyPDF2

from reportlab.pdfgen import canvas
from PIL import Image

def ImgToPdf(src_path, dst_path):
	# 获取src图片的宽、高
	Max_W, Max_H = Image.open(src_path).size
	# 设定pdf的宽、高,下面是标准的pdf尺寸
	pdf_w, pdf_h = (480, 702)
	c = canvas.Canvas(dst_path, pagesize=(pdf_w, pdf_h))
	# 如果图片相比于标准尺寸窄了
	if pdf_w/pdf_h < Max_W/Max_H:
		c.drawImage(src_path, 0, (pdf_h-Max_H*pdf_w/Max_W)/2, pdf_w, Max_H*pdf_w/Max_W)
	# 如果图片相比于标准尺寸宽了
	else:
		c.drawImage(src_path, (pdf_w-Max_W*pdf_h/Max_H)/2, 0, Max_W*pdf_h/Max_H, pdf_h)
	c.save()

if __name__ == "__main__":
	Directory = input('输入待处理文件夹路径:')
	if not os.path.exists(Directory):
		print("你他喵的巨勇")
		exit(-1)

	AimDir = input('输入目标文件夹路径:')
	if not os.path.exists(AimDir):
		os.makedirs(AimDir)

	for root, dirs, files in os.walk(Directory, topdown=False):
		# 跳过废物路径
		if "卷之" not in root:
			continue
		
		for file in files:
			ImgToPdf(root+"\\"+file, AimDir+"\\"+file.replace("png", "pdf"))
		
		length = len(files)
		# 防止数字文件名不连续
		count = 0
		AimPath = root+".pdf"
		merger = PyPDF2.PdfFileMerger()
		for i in range(1, length+1):
			while not os.path.exists(root+"\\"+str(i+count)+".pdf"):
				count += 1
			src_path = root+"\\"+str(i+count)+".pdf"
			merger.append(PyPDF2.PdfFileReader(src_path))
		merger.write(AimPath)
		merger.close()
			
			
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-09-04 17:28:29  更:2021-09-04 17:29:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:44:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码