前言

该工具是我认识的一位中科大的大佬在本科毕业的时候做的一个小工具,去打印店打印全彩的毕业论文的话会比较贵,他想到有没有一种方案可以实现有彩色页面的pdf和没有彩色页面的pdf分开打印,前者打印彩色,后者打印黑白,这样做的话可以节约很多打印彩色pdf页面的钱,然后这位大佬就做了这样一个小工具,省了一笔打印费,代码和原文链接在文末,我这里介绍下其大致的代码逻辑

1. is_color_image 函数

功能: 检查给定图像是否为彩色图像。

逻辑:

  • 将图像转换为 RGB 模式。
  • 将图像转换为 NumPy 数组,并归一化像素值到 [0,1] 范围。
  • 计算每个像素的最大和最小 RGB 值。
  • 计算每个像素的饱和度 (saturation)。
  • 统计饱和度大于阈值 (saturation_threshold) 的像素占总像素的比例 (color_fraction)。
  • 如果 color_fraction 大于设定的阈值 (color_fraction_threshold),则认为图像是彩色的。
def is_color_image(image, saturation_threshold=0.35, color_fraction_threshold=0.001):
    image = image.convert('RGB')
    pixels = np.array(image) / 255.0

    max_rgb = np.max(pixels, axis=2)
    min_rgb = np.min(pixels, axis=2)
    delta = max_rgb - min_rgb

    saturation = delta / (max_rgb + 1e-7)  # 防止除以零

    color_pixels = saturation > saturation_threshold
    color_fraction = np.mean(color_pixels)

    return color_fraction > color_fraction_threshold

2. is_color_page 函数

功能: 检查 PDF 页面是否为彩色页面。

逻辑:

  • 将页面渲染为 pixmap 对象。
  • 将 pixmap 转换为 PNG 格式的字节数据。
  • 使用 PIL 库将字节数据转换为图像对象。
  • 调用 is_color_image 函数检查图像是否为彩色图像。
def is_color_page(page):
    pix = page.get_pixmap()
    img = pix.tobytes("png")

    from PIL import Image
    from io import BytesIO
    image = Image.open(BytesIO(img))

    return is_color_image(image)

3. split_pdf 函数

功能: 将输入的 PDF 分割为彩色页面和黑白页面两个 PDF。

逻辑:

  • 打开输入 PDF。
  • 创建新的 PDF 对象来保存彩色页面和黑白页面。
  • 遍历输入 PDF 的每一页,检查页面是否为彩色页面。
  • 如果设置了双面打印 (is_double_sized_printing),确保彩色页面的前后页也包括在内。
  • 将彩色页面和黑白页面分别插入到新的 PDF 对象中。
  • 保存新的彩色和黑白 PDF。
  • 关闭所有文档对象。
def split_pdf(input_pdf_path, output_color_pdf_path, output_bw_pdf_path, is_double_sized_printing):
    doc = fitz.open(input_pdf_path)

    color_doc = fitz.open()
    bw_doc = fitz.open()

    color_pages = []
    bw_pages = []

    for page_num in tqdm(range(len(doc))):
        page = doc.load_page(page_num)
        if is_color_page(page):
            color_pages.append(page_num)

    if is_double_sized_printing:
        for page_num in color_pages:
            if page_num % 2 == 0 and page_num + 1 not in color_pages and page_num + 1 < len(doc):
                color_pages.append(page_num + 1)
            if page_num % 2 == 1 and page_num - 1 not in color_pages and page_num - 1 > 0:
                color_pages.append(page_num - 1)

    for page_num in range(len(doc)):
        if page_num not in color_pages:
            bw_pages.append(page_num)

    for page_num in sorted(color_pages):
        color_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)

    for page_num in sorted(bw_pages):
        bw_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)

    color_doc.save(output_color_pdf_path)
    bw_doc.save(output_bw_pdf_path)

    doc.close()
    color_doc.close()
    bw_doc.close()

主程序部分

功能: 定义输入、输出文件路径和是否双面打印的参数,并调用 split_pdf 函数。

if __name__ == '__main__':
    INPUT_PDF_PATH = '1.pdf'
    OUTPUT_COLOR_PDF_PATH = 'color_pages.pdf'
    OUTPUT_BW_PDF_PATH = 'bw_pages.pdf'
    IS_DOUBLE_SIZED_PRINTING = True

    split_pdf(INPUT_PDF_PATH, OUTPUT_COLOR_PDF_PATH, OUTPUT_BW_PDF_PATH, IS_DOUBLE_SIZED_PRINTING)

完整代码

import pymupdf as fitz
import numpy as np
from tqdm import tqdm


def is_color_image(image, saturation_threshold=0.35, color_fraction_threshold=0.001):
    image = image.convert('RGB')
    pixels = np.array(image) / 255.0  # 归一化像素值到[0,1]范围

    # 将RGB转换为HSV
    max_rgb = np.max(pixels, axis=2)
    min_rgb = np.min(pixels, axis=2)
    delta = max_rgb - min_rgb

    # 饱和度
    saturation = delta / (max_rgb + 1e-7)  # 防止除以零

    # 判断饱和度大于阈值的彩色像素
    color_pixels = saturation > saturation_threshold
    color_fraction = np.mean(color_pixels)

    return color_fraction > color_fraction_threshold


def is_color_page(page):
    """
    Check if a page is a color page.
    """
    # Render page to a pixmap
    pix = page.get_pixmap()
    # Convert pixmap to an image
    img = pix.tobytes("png")


    # Create an image object using PIL
    from PIL import Image
    from io import BytesIO
    image = Image.open(BytesIO(img))

    return is_color_image(image)


def split_pdf(input_pdf_path, output_color_pdf_path, output_bw_pdf_path, is_double_sized_printing):
    # Open the input PDF
    doc = fitz.open(input_pdf_path)

    # Create new PDFs for color and black & white pages
    color_doc = fitz.open()
    bw_doc = fitz.open()

    # Save color and bw pages number
    color_pages = []
    bw_pages = []

    # Iterate over each page in the input PDF
    for page_num in tqdm(range(len(doc))):
        page = doc.load_page(page_num)

        # Check if the page is a color page
        if is_color_page(page):
            color_pages.append(page_num)

    # Handle double sized printing
    if is_double_sized_printing:
        for page_num in color_pages:
            if page_num % 2 == 0 and page_num + 1 not in color_pages and page_num + 1 < len(doc):
                color_pages.append(page_num + 1)
            if page_num % 2 == 1 and page_num - 1 not in color_pages and page_num - 1 > 0:
                color_pages.append(page_num - 1)

    # Insert BW Pages
    for page_num in range(len(doc)):
        if page_num not in color_pages:
            bw_pages.append(page_num)

    # Insert PDF pages
    for page_num in sorted(color_pages):
        color_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)

    for page_num in sorted(bw_pages):
        bw_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)

    # Save the new PDFs
    color_doc.save(output_color_pdf_path)
    bw_doc.save(output_bw_pdf_path)

    # Close all documents
    doc.close()
    color_doc.close()
    bw_doc.close()

if __name__ == '__main__':
    INPUT_PDF_PATH = '1.pdf'  # 待转换的PDF路径
    OUTPUT_COLOR_PDF_PATH = 'color_pages.pdf'  # 彩色部分PDF输出路径
    OUTPUT_BW_PDF_PATH = 'bw_pages.pdf'  # 黑白部分PDF输出路径
    IS_DOUBLE_SIZED_PRINTING = True  # 是否双面打印

    split_pdf(INPUT_PDF_PATH, OUTPUT_COLOR_PDF_PATH, OUTPUT_BW_PDF_PATH, IS_DOUBLE_SIZED_PRINTING)

原文链接:http://t.csdnimg.cn/VY0dE 

代码链接:https://github.com/RicePasteM/Color-BW-Separator-for-PDF.git

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部