前言
????最近在下载PDF书籍的时候,发现有些PDF有水印,于是就寻思着能不能用Python去除这些讨厌的水印
一、PDF文件
????关于PDF文件,想必大家都很熟悉了,这里就不过多的介绍了。PDF主要有两种类型,一种是文字版,另外一种就是扫描版(图片)。这个去除水印主要就是针对扫描版的PDF 代码路径
二、思路整理
????在开始写代码之前,先捋一下实现的思路
1、分割图片
????对于一个PDF文件,我们只需要图片信息就可以了,所以首先需要先提取里边的图片,再把图片存在一个目录下。这里需要用到fitz模块,直接安装即可,如下:
pip install PyMuPDF
模块安装完之后,代码就很简单了,只需要注意一下图片按顺序命名即可,如下:
def split_pdf(file_path, out_path):
"""
切割pdf为图片
:param file_path: pdf路径
:param out_path: 输出图片路径
:return: 输出路径
"""
pdf = fitz.open(file_path)
count = 0
print("##### 开始保存切割图片 #####")
for page in pdf:
image_list = page.get_images()
for img_info in image_list:
pix = fitz.Pixmap(pdf, img_info[0])
pix.save(os.path.join(out_path, '%d.jpg' % count))
count += 1
print("##### 保存切割图片完毕 #####")
print("##### {0} 包含 {1} 张图片 #####".format(file_path, count))
return out_path
2、去除水印
????分割完图片后,接下来的问题也随之而来了,要如何区分水印和正常图片? 要替换成什么? 先来看第一个问题,如何区分水印
- 按水印位置
这个问题,最直观的想法就是根据水印的位置以及水印的大小,进行替换就可以。但是这样存在问题,首先就是不通用(PDF水印的位置可能不一样),再者就是水印和字混在一起就不好弄了
像这种水印,坐标和大小是可以去除的
像这种字和水印混在一起就难办了
- 按颜色
????我们在观察下水印,发现水印的颜色一般偏亮一点,而字都是偏暗色的(黑色、灰色)。我们可以根据颜色将亮一点的颜色都替换掉。但这种方式也有问题,如果是彩色的,就够呛了
????对于第二个问题:要替换成什么? 如果只有简单的颜色(黑白灰等),我们直接把水印替换成白色即可
????接下来来看下代码吧,这里需要用到PIL模块,直接安装就行了,如下:
pip install pillow
思路有了,代码就简单了,如下:
def get_image_arr(img):
"""
获取图片三色数组
:param img:图片
:return: 图片编码、三色数组
"""
img_arr = np.asarray(img, dtype=np.double)
r_img = img_arr[:, :, 0].copy()
g_img = img_arr[:, :, 1].copy()
b_img = img_arr[:, :, 2].copy()
img = r_img * 256 * 256 + g_img * 256 + b_img
return img, r_img, g_img, b_img
def replace_clr_color(img, src_clr, dst_clr):
"""
通过矩阵操作颜色替换程序
@param img: 图像矩阵
@param src_clr: 需要替换的颜色(r,g,b)
@param dst_clr: 目标颜色 (r,g,b)
@return 替换后的图像矩阵
"""
img, r_img, g_img, b_img = get_image_arr(img)
src_color = src_clr[0] * 256 * 256 + src_clr[1] * 256 + src_clr[2]
r_img[img == src_color] = dst_clr[0]
g_img[img == src_color] = dst_clr[1]
b_img[img == src_color] = dst_clr[2]
return compound_img(r_img, g_img, b_img)
def compound_img(r_img, g_img, b_img):
"""
合并图片
:param r_img: 红色
:param g_img: 绿色
:param b_img: 蓝色
:return: 图片
"""
dst_img = np.array([r_img, g_img, b_img], dtype=np.uint8)
dst_img = dst_img.transpose(1, 2, 0)
return dst_img
def replace_pure_color(img, src_color, dst_color):
"""
通过矩阵操作颜色替换程序(纯色)
:param img: 图像矩阵
:param src_color: 需要替换的颜色
:param dst_color: 目标颜色
:return: 图片
"""
img, r_img, g_img, b_img = get_image_arr(img)
src_color = src_color * 256 * 256 + src_color * 256 + src_color
r_img[img >= src_color] = dst_color
g_img[img >= src_color] = dst_color
b_img[img >= src_color] = dst_color
return compound_img(r_img, g_img, b_img)
def wipe_watermark(img_file, start_color):
"""
去除图片水印
:param start_color: 颜色起始替换位置
:param img_file: 图片文件
:return:
"""
img = replace_pure_color(Image.open(img_file).convert('RGB'), start_color, 255)
res_img = Image.fromarray(img)
res_img.save(img_file)
3、替换图片
????保存完去除水印后的图片,接下来只要把他一个个替换进去就行了
????由于原始PDF文件可以有其他东西(书签等),所以我们先把原始文件读取进去,在进行替换,代码如下:
def save_pdf(src_pdf, dest_pdf, file_list):
"""
生成最终pdf文件
:param src_pdf: 源文件
:param dest_pdf: 目标文件
:param file_list: 图片列表
:return:
"""
pdf = fitz.open(src_pdf)
index = 0
try:
for page in pdf:
for link in page.get_links():
page.delete_link(link)
for img in page.get_images():
page._insert_image(filename=file_list[index], _imgname=img[7])
index = index + 1
pdf.save(dest_pdf)
finally:
pdf.close()
三、实现效果
????lu完代码,再来看一下效果怎么样,这边用了两个PDF做实验,第一个PDF效果如下:
去除水印前: 去除水印后:
第二个PDF效果如下:
去除水印前:
去除水印后:
感觉效果还阔以,哈哈。不过这里有个问题,对于文字logo,查了PyMuPDF文档没找到罒ω罒,像这种
四、代码实现
????全部代码如下:
import os
import fitz
import numpy as np
from PIL import Image
def split_pdf(file_path, out_path):
"""
切割pdf为图片
:param file_path: pdf路径
:param out_path: 输出图片路径
:return: 输出路径
"""
pdf = fitz.open(file_path)
count = 0
print("##### 开始保存切割图片 #####")
for page in pdf:
image_list = page.get_images()
for img_info in image_list:
pix = fitz.Pixmap(pdf, img_info[0])
pix.save(os.path.join(out_path, '%d.jpg' % count))
count += 1
print("##### 保存切割图片完毕 #####")
print("##### {0} 包含 {1} 张图片 #####".format(file_path, count))
return out_path
def get_image_arr(img):
"""
获取图片三色数组
:param img:图片
:return: 图片编码、三色数组
"""
img_arr = np.asarray(img, dtype=np.double)
r_img = img_arr[:, :, 0].copy()
g_img = img_arr[:, :, 1].copy()
b_img = img_arr[:, :, 2].copy()
img = r_img * 256 * 256 + g_img * 256 + b_img
return img, r_img, g_img, b_img
def replace_clr_color(img, src_clr, dst_clr):
"""
通过矩阵操作颜色替换程序
@param img: 图像矩阵
@param src_clr: 需要替换的颜色(r,g,b)
@param dst_clr: 目标颜色 (r,g,b)
@return 替换后的图像矩阵
"""
img, r_img, g_img, b_img = get_image_arr(img)
src_color = src_clr[0] * 256 * 256 + src_clr[1] * 256 + src_clr[2]
r_img[img == src_color] = dst_clr[0]
g_img[img == src_color] = dst_clr[1]
b_img[img == src_color] = dst_clr[2]
return compound_img(r_img, g_img, b_img)
def compound_img(r_img, g_img, b_img):
"""
合并图片
:param r_img: 红色
:param g_img: 绿色
:param b_img: 蓝色
:return: 图片
"""
dst_img = np.array([r_img, g_img, b_img], dtype=np.uint8)
dst_img = dst_img.transpose(1, 2, 0)
return dst_img
def replace_pure_color(img, src_color, dst_color):
"""
通过矩阵操作颜色替换程序(纯色)
:param img: 图像矩阵
:param src_color: 需要替换的颜色
:param dst_color: 目标颜色
:return: 图片
"""
img, r_img, g_img, b_img = get_image_arr(img)
src_color = src_color * 256 * 256 + src_color * 256 + src_color
r_img[img >= src_color] = dst_color
g_img[img >= src_color] = dst_color
b_img[img >= src_color] = dst_color
return compound_img(r_img, g_img, b_img)
def list_file(path, suffix=None):
"""
获取指定目录下指定后缀文件
:param path: 路径
:param suffix: 后缀名
:return: 文件集合
"""
file_names = os.listdir(path);
if suffix is not None:
file_names = [file_name for file_name in file_names if file_name.endswith(suffix)]
file_names.sort(key=lambda x: int(x[:(-len(suffix))]))
return [os.path.join(path, file) for file in file_names]
def wipe_watermark(img_file, start_color):
"""
去除图片水印
:param start_color: 颜色起始替换位置
:param img_file: 图片文件
:return:
"""
img = replace_pure_color(Image.open(img_file).convert('RGB'), start_color, 255)
res_img = Image.fromarray(img)
res_img.save(img_file)
def save_pdf(src_pdf, dest_pdf, file_list):
"""
生成最终pdf文件
:param src_pdf: 源文件
:param dest_pdf: 目标文件
:param file_list: 图片列表
:return:
"""
pdf = fitz.open(src_pdf)
index = 0
try:
for page in pdf:
for link in page.get_links():
page.delete_link(link)
for img in page.get_images():
page._insert_image(filename=file_list[index], _imgname=img[7])
index = index + 1
pdf.save(dest_pdf)
finally:
pdf.close()
def start(file_path, dest_path, start_color, out_path=r'out'):
if os.path.exists(out_path):
raise FileExistsError('文件夹:{0} 已存在'.format(out_path))
os.mkdir(out_path)
print("####### 开始切割pdf:{0} #######".format(file_path))
split_pdf(file_path, out_path)
print("####### 切割pdf完毕:{0} #######".format(file_path))
file_list = list_file(out_path, ".jpg")
print("####### 开始去除水印 #######")
for img_file in file_list:
wipe_watermark(img_file, start_color)
print("####### 去除水印结束 #######")
print("####### 生成pdf文件:{0} #######".format(dest_path))
save_pdf(src_pdf=file_path, dest_pdf=dest_path, file_list=file_list)
print("####### 生成pdf文件:{0} 完成 #######".format(dest_path))
if __name__ == '__main__':
start(file_path="重构-改善既有代码的设计.pdf", dest_path="b.pdf", start_color=175)
总结
????这里实现相对比较简单,只能去除一些纯色图片的PDF,这里没能去除文字logo(懂得大佬麻烦告知一下(〃‘▽’〃))。这里就作为个简单的demo吧。主要是用Python做点骚操作,用来熟悉一下Python语言
|