使用 Python 去除 PDF 水印:一步一步的实现(二)

前言

在日常工作中,我们经常会遇到带有水印的 PDF 文件。水印可能会影响文档的可读性或者在打印时造成不便。为了自动化地去除 PDF 文件中的水印,我们可以使用 Python 及其强大的库,如 PyMuPDF、Pillow 和 OpenCV。本文将详细介绍如何实现这一过程。

所需库

首先,我们需要安装以下 Python 库:

  • PyMuPDF:用于处理 PDF 文件。
  • Pillow:用于图像处理。
  • OpenCV:用于图像操作。

使用以下命令安装这些库:

pip install pymupdf pillow opencv-python-headless

实现步骤

导入必要的库

我们首先导入所需的库:

import fitz  # PyMuPDF
from PIL import Image
import numpy as np
import cv2
import os
import concurrent.futures

定义去除水印的函数

接下来,我们定义一个函数来去除图像中的水印。该函数利用颜色范围创建掩码,并将掩码区域设置为白色:

def remove_watermark(image, lower_bound, upper_bound):
    """去除水印"""
    # 将 PIL 图像转换为 OpenCV 格式
    open_cv_image = np.array(image)
    open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR)
    
    # 创建掩码,查找在指定颜色范围内的像素
    mask = cv2.inRange(open_cv_image, lower_bound, upper_bound)
    
    # 使用膨胀和侵蚀操作优化掩码
    kernel = np.ones((3, 3), np.uint8)
    mask = cv2.dilate(mask, kernel, iterations=1)
    mask = cv2.erode(mask, kernel, iterations=1)
    
    # 将掩码范围内的像素设为白色
    open_cv_image[mask != 0] = [255, 255, 255]
    
    # 将图像转换回 PIL 格式
    return Image.fromarray(cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2RGB))

处理单页 PDF 的函数

我们需要一个函数来处理 PDF 文件的每一页,将其转换为图像,去除水印后保存为 PNG 文件:

def process_page(pdf_path, page_num, output_folder, mat, lower_bound, upper_bound):
    """处理单页"""
    try:
        # 打开 PDF 文件并加载特定页
        pdf = fitz.open(pdf_path)
        page = pdf.load_page(page_num)
        pixmap = page.get_pixmap(matrix=mat)
        img = Image.frombytes("RGB", [pixmap.width, pixmap.height], pixmap.samples)
        
        # 调用 remove_watermark 函数去除水印
        img = remove_watermark(img, lower_bound, upper_bound)
        
        # 将处理后的图像保存为 PNG 文件
        img_path = os.path.join(output_folder, f"{page_num}.png")
        img.save(img_path, format="PNG")
        
        print(f"第{page_num}页水印去除完成")
        pdf.close()
    except fitz.FileDataError:
        print(f"无法读取第{page_num}页的数据。")
    except fitz.PDFPageError:
        print(f"第{page_num}页无法加载。")
    except Exception as e:
        print(f"处理第{page_num}页时出错: {e}")

主函数

主函数 remove_pdf 负责管理整个 PDF 的处理过程,包括创建输出文件夹、并行处理每一页以及将处理后的图像合并回 PDF:

def remove_pdf(pdf_file, output_folder, output_pdf_path, dpi=1800, lower_bound=(168, 168, 168), upper_bound=(172, 172, 172)):
    """去除 PDF 水印的主函数"""
    if not os.path.exists(pdf_file):
        print(f"文件 {pdf_file} 未找到。")
        return

    if not pdf_file.lower().endswith('.pdf'):
        print(f"文件 {pdf_file} 不是 PDF 文件。")
        return

    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    zoom = dpi / 72
    mat = fitz.Matrix(zoom, zoom)
    
    try:
        pdf = fitz.open(pdf_file)
        total_pages = len(pdf)
        pdf.close()
        
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures = [
                executor.submit(process_page, pdf_file, page_num, output_folder, mat, lower_bound, upper_bound)
                for page_num in range(total_pages)
            ]
            for future in concurrent.futures.as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    print(f"处理过程中发生错误: {e}")
        
        image_files = [os.path.join(output_folder, f"{page_num}.png") for page_num in range(total_pages)]
        image_list = [Image.open(img_file).convert("RGB") for img_file in image_files]
        
        if image_list:
            image_list[0].save(output_pdf_path, save_all=True, append_images=image_list[1:])
            print(f"处理后的PDF文件保存为: {output_pdf_path}")
        else:
            print("没有处理好的图片可以合并为PDF。")
    
    except FileNotFoundError:
        print(f"文件 {pdf_file} 未找到。")
    except fitz.FileDataError:
        print(f"无法读取文件数据 {pdf_file}。")
    except Exception as e:
        print(f"处理PDF文件时出错: {e}")

主程序入口

在主程序中,我们从用户获取输入路径、输出路径以及水印颜色的上下界,然后调用 remove_pdf 函数:

if __name__ == "__main__":
    pdf_path = input("请输入 PDF 地址:")
    output_path = input("请输入保存处理后的图片的文件夹地址:")
    output_pdf_path = input("请输入保存处理后的 PDF 地址:")
    lower_bound = tuple(map(int, input("请输入水印颜色的下界(例如:168,168,168):").split(',')))
    upper_bound = tuple(map(int, input("请输入水印颜色的上界(例如:172,172,172):").split(',')))
    
    remove_pdf(pdf_path, output_path, output_pdf_path, lower_bound=lower_bound, upper_bound=upper_bound)

代码解析

以下是本文中使用的完整代码,添加了中文注释,帮助理解每个步骤的功能:

import fitz  # PyMuPDF
from PIL import Image
import numpy as np
import cv2
import os
import concurrent.futures

def remove_watermark(image, lower_bound, upper_bound):
    """去除水印"""
    # 将 PIL 图像转换为 OpenCV 格式
    open_cv_image = np.array(image)
    open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR)
    
    # 创建掩码,查找在指定颜色范围内的像素
    mask = cv2.inRange(open_cv_image, lower_bound, upper_bound)
    
    # 使用膨胀和侵蚀操作优化掩码
    kernel = np.ones((3, 3), np.uint8)
    mask = cv2.dilate(mask, kernel, iterations=1)
    mask = cv2.erode(mask, kernel, iterations=1)
    
    # 将掩码范围内的像素设为白色
    open_cv_image[mask != 0] = [255, 255, 255]
    
    # 将图像转换回 PIL 格式
    return Image.fromarray(cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2RGB))

def process_page(pdf_path, page_num, output_folder, mat, lower_bound, upper_bound):
    """处理单页"""
    try:
        # 打开 PDF 文件并加载特定页
        pdf = fitz.open(pdf_path)
        page = pdf.load_page(page_num)
        pixmap = page.get_pixmap(matrix=mat)
        img = Image.frombytes("RGB", [pixmap.width, pixmap.height], pixmap.samples)
        
        # 调用 remove_watermark 函数去除水印
        img = remove_watermark(img, lower_bound, upper_bound)
        
        # 将处理后的图像保存为 PNG 文件
        img_path = os.path.join(output_folder, f"{page_num}.png")
        img.save(img_path, format="PNG")
        
        print(f"第{page_num}页水印去除完成")
        pdf.close()
    except fitz.FileDataError:
        print(f"无法读取第{page_num}页的数据。")
    except fitz.PDFPageError:
        print(f"第{page_num}页无法加载。")
    except Exception as e:
        print(f"处理第{page_num}页时出错: {e}")

def remove_pdf(pdf_file, output_folder, output_pdf_path, dpi=1800, lower_bound=(168, 168, 168), upper_bound=(172, 172, 172)):
    """去除 PDF 水印的主函数"""
    if not os.path.exists(pdf_file):
        print(f"文件 {pdf_file} 未找到。")
        return

    if not pdf_file.lower().endswith('.pdf'):
        print(f"文件 {pdf_file} 不是 PDF 文件。")
        return

    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    zoom = dpi / 72
    mat = fitz.Matrix(zoom, zoom)
    
    try:
        pdf = fitz.open(pdf_file)
        total_pages = len(pdf)
        pdf.close()
        
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures = [
                executor.submit(process_page, pdf_file, page_num, output_folder, mat, lower_bound, upper_bound)
                for page_num in range(total_pages)
            ]
            for future in concurrent.futures.as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    print(f"处理过程中发生错误: {e}")
        
        image_files = [os.path.join(output_folder, f"{page_num}.png") for page_num in range(total_pages)]
        image_list = [Image.open(img_file).convert("RGB") for img_file in image_files]
        
        if image_list:
            image_list[0].save(output_pdf_path, save_all=True, append_images=image_list[1:])
            print(f"处理后的PDF文件保存为: {output_pdf_path}")
        else:
            print("没有处理好的图片可以合并为PDF。")
    
    except FileNotFoundError:
        print(f"文件 {pdf_file} 未找到。")
    except fitz.FileDataError:
        print(f"无法读取文件数据 {pdf_file}。")
    except Exception as e:
        print(f"处理PDF文件时出错: {e}")

if __name__ == "__main__":
    pdf_path = input("请输入 PDF 地址:")
    output_path = input("请输入保存处理后的图片的文件夹地址:")
    output_pdf_path = input("请输入保存处理后的 PDF 地址:")
    lower_bound = tuple(map(int, input("请输入水印颜色的下界(例如:168,168,168):").split(',')))
    upper_bound = tuple(map(int, input("请输入水印颜色的上界(例如:172,172,172):").split(',')))
    
    remove_pdf(pdf_path, output_path, output_pdf_path, lower_bound=lower_bound, upper_bound=upper_bound)

通过上面的代码,我们可以实现自动去除 PDF 水印的功能。如果你在实现过程中遇到问题或有更好的改进建议,欢迎在评论区分享。让我们一起学习进步!

对比前后

去除前

去除后

总结

本文介绍了如何使用 Python 去除 PDF 文件中的水印。我们通过结合使用 PyMuPDF、Pillow 和 OpenCV 库,实现了从 PDF 提取页面、处理图像并去除水印的完整流程。该方法自动化程度高,适用于批量处理带有水印的 PDF 文件。希望这篇文章能对你有所帮助!

如果你在实现过程中遇到问题或有更好的改进建议,欢迎在评论区分享。让我们一起学习进步!

 参考资料

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值