You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PDF转图片再转回PDF时如何兼顾文件大小与画质

PDF转图片再转回PDF时如何兼顾文件大小与画质

我完全懂你的困扰——把PDF转成图片处理后再转回,要么画质糊得没法看,要么文件体积直接翻倍,两头都难顾!咱们可以从几个细节调整你的代码逻辑,来平衡画质和文件大小:

  • 换用JPEG格式保存图片,调整压缩质量
    你现在用的PNG是无损格式,体积天生就大。如果你的PDF内容没有透明层,完全可以换成JPEG格式,通过调整压缩质量来平衡画质和体积。比如用cv2.imwrite时加上质量参数,85%的质量在视觉上和原图几乎没差别,但体积能小一大截:

    cv2.imwrite(f"test_im_{page_number}.jpg", img, [int(cv2.IMWRITE_JPEG_QUALITY), 85])
    

    要是非得用PNG,也可以加压缩级别参数[int(cv2.IMWRITE_PNG_COMPRESSION), 6](0-9级,6是兼顾压缩率和速度的平衡点)。

  • 用DPI替代Zoom,更精准控制画质
    你之前用的zoom参数其实对应PDF默认的72dpi,zoom=2就是144dpi。不如直接用dpi参数指定画质,比如150dpi是打印级别的清晰程度,比zoom=1的画质好太多,又不会像zoom=2那样让体积暴涨。修改get_pixmap的调用:

    pix = page.get_pixmap(dpi=150, alpha=False)  # alpha=False关闭透明层,进一步减小体积
    
  • 跳过本地文件读写,直接内存处理
    你现在先把图片存到本地再插入PDF,不仅慢,还可能因为文件格式的额外开销增加体积。可以用io.BytesIO在内存中处理图片字节流,直接插入到新PDF里,效率更高也更省空间:

    import io
    # 处理完img后,转成内存字节流
    img_bytes = io.BytesIO()
    cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), 85])[1].tofile(img_bytes)
    img_bytes.seek(0)
    # 直接插入字节流
    page.insert_image(rect, stream=img_bytes)
    
  • 直接复用原PDF的页面尺寸
    你之前手动计算img.shape[1]/zoom来得到页面尺寸,不如直接读取原页面的rect参数,这样能保证输出PDF和原文件的尺寸完全一致,避免不必要的缩放误差:

    original_rect = page.rect
    new_page = new_pdf_document.new_page(width=original_rect.width, height=original_rect.height)
    new_page.insert_image(original_rect, stream=img_bytes)
    

优化后的完整代码

import cv2
import fitz
import numpy as np
import io

def pdf_to_images_and_back(pdf_path: str, dpi=150, img_quality=85):
    new_pdf_document = fitz.open()
    with fitz.open(pdf_path) as pdf_document:
        for page_number in range(len(pdf_document)):
            page = pdf_document.load_page(page_number)
            # 用DPI控制画质,关闭透明层减少体积
            pix = page.get_pixmap(dpi=dpi, alpha=False)
            img = np.frombuffer(pix.samples, dtype=np.uint8).reshape(
                pix.height, pix.width, pix.n
            )
            if pix.n == 3: 
                img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
            
            # 内存中生成JPEG字节流,跳过本地文件
            img_bytes = io.BytesIO()
            cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), img_quality])[1].tofile(img_bytes)
            img_bytes.seek(0)
            
            # 复用原页面尺寸,保证输出PDF和原尺寸一致
            original_rect = page.rect
            new_page = new_pdf_document.new_page(width=original_rect.width, height=original_rect.height)
            new_page.insert_image(original_rect, stream=img_bytes)

    # 启用压缩和彻底的垃圾清理,进一步减小体积
    new_pdf_document.save("output_pdf.pdf", deflate=True, garbage=3, clean=True)

最后提个小建议:如果你的PDF是矢量内容(比如文字、线条),转成图片本身就会丢失矢量特性,导致体积变大。如果你的图像处理操作可以直接在PDF矢量层上完成,那会是更优的选择;但如果必须转图片处理,上面的方法应该能帮你兼顾画质和文件大小啦!

备注:内容来源于stack exchange,提问作者Jonas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 14:59:36