AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79527090
Accepted
Pubg Mobile
Pubg Mobile
Asked: 2025-03-22 15:34:01 +0800 CST2025-03-22 15:34:01 +0800 CST 2025-03-22 15:34:01 +0800 CST

Converta arquivos PDF de várias páginas para PNG rapidamente

  • 772

Tenho uma pasta contendo 600 arquivos PDF , e cada PDF tem 20 páginas . Preciso converter cada página em um PNG de alta qualidade o mais rápido possível.

Eu escrevi o seguinte script para esta tarefa:

import os
import multiprocessing
import fitz  # PyMuPDF
from PIL import Image

def process_pdf(pdf_path, output_folder):
    try:
        pdf_name = os.path.splitext(os.path.basename(pdf_path))[0]
        pdf_output_folder = os.path.join(output_folder, pdf_name)
        os.makedirs(pdf_output_folder, exist_ok=True)

        doc = fitz.open(pdf_path)

        for i, page in enumerate(doc):
            pix = page.get_pixmap(dpi=850)  # Render page at high DPI
            img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
            
            img_path = os.path.join(pdf_output_folder, f"page_{i+1}.png")
            img.save(img_path, "PNG")

        print(f"Processed: {pdf_path}")
    except Exception as e:
        print(f"Error processing {pdf_path}: {e}")

def main():
    input_folder = r"E:\Desktop\New folder (5)\New folder (4)"
    output_folder = r"E:\Desktop\New folder (5)\New folder (5)"

    pdf_files = [os.path.join(input_folder, f) for f in os.listdir(input_folder) if f.lower().endswith(".pdf")]

    with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
        pool.starmap(process_pdf, [(pdf, output_folder) for pdf in pdf_files])

    print("All PDFs processed successfully!")

if __name__ == "__main__":
    main()

Emitir:

Este script é muito lento , especialmente ao processar um grande número de PDFs. Tentei as seguintes otimizações, mas elas não melhoraram a velocidade significativamente :

  • DPI reduzido ligeiramente – Reduzido de 1200 DPI para 850 DPI . (Eu também testei 600-800 DPI.)
  • Habilitado alpha=False em get_pixmap() – Uso de memória reduzido.
  • Usado ThreadPoolExecutor em vez de multiprocessing.Pool – Nenhuma melhoria significativa.
  • Compressão PNG reduzida – Definida optimize=Falseao salvar imagens.
  • Imagens convertidas em tons de cinza – Ajudou um pouco, mas preciso de imagens coloridas para minha tarefa.

Possíveis soluções que considerei:

  • Processamento paralelo de páginas em vez de arquivos – Em vez de processar um arquivo por vez, processe cada página em paralelo para utilizar totalmente os núcleos da CPU.
  • Use ProcessPoolExecutor em vez de ThreadPoolExecutor – Como a renderização exige muita CPU , o multiprocessamento deve ser melhor.
  • Use JPEG em vez de PNG – JPEG é muito mais rápido de salvar e ocupa menos espaço de armazenamento, mas preciso de imagens de alta qualidade .
  • Reduzir DPI para 500-600 – Oferece um equilíbrio entre velocidade e qualidade .
  • Grave arquivos em lote em vez de salvar um por um – Reduz a sobrecarga de E/S.

Com o que preciso de ajuda:

  • Como posso acelerar significativamente essa conversão de PDF para PNG mantendo alta qualidade de imagem?
  • Existem bibliotecas ou técnicas melhores que eu deva usar?
  • Existe uma maneira de utilizar totalmente os núcleos da CPU de forma eficiente?

Qualquer sugestão será muito apreciada!

python
  • 4 4 respostas
  • 138 Views

4 respostas

  • Voted
  1. Best Answer
    Adon Bilivit
    2025-03-22T17:44:41+08:002025-03-22T17:44:41+08:00

    Este processo não só é altamente intensivo em CPU, como também requer RAM significativa. No MacOS (M2), rodar em apenas 4 CPUs (ou seja, metade do número disponível) melhora o desempenho significativamente. Mesmo assim, o tempo médio para processar uma página é de ~1,3s

    Para este teste, tenho 80 PDFs. Um máximo de 20 páginas é processado por PDF.

    Aqui está o teste:

    import fitz
    from pathlib import Path
    from multiprocessing import Pool
    from PIL import Image
    from time import monotonic
    from os import process_cpu_count
    
    SOURCE_DIR = Path("/Volumes/Spare/Downloads")
    TARGET_DIR = Path("/Volumes/Spare/PDFs")
    
    def cpus() -> int:
        if ncpus := process_cpu_count():
            ncpus //= 2
            return ncpus if ncpus > 1 else 2
        return 2
        
    def process(path: Path) -> tuple[float, int]:
        print(f"Processing {path.name}")
        try:
            with fitz.open(path) as pdf:
                start = monotonic()
                for i, page in enumerate(pdf.pages(), 1):
                    pix = page.get_pixmap(dpi=850)
                    img = Image.frombytes("RGB", (pix.width, pix.height), pix.samples)
                    img_path = TARGET_DIR / f"{path.stem}_page_{i}.png"
                    img.save(img_path, "PNG")
                    if i >= 20:
                        break
                return (monotonic() - start, i)
        except Exception:
            pass
        return (0.0, 0)
    
    def main() -> None:
        TARGET_DIR.mkdir(parents=True, exist_ok=True)
        with Pool(cpus()) as pool:
            sum_d = 0.0
            sum_p = 0
            for duration, page_count in pool.map(process, SOURCE_DIR.glob("*.pdf")):
                sum_d += duration
                sum_p += page_count
            if sum_p > 0:
                print(f"Average duration per page = {sum_d/sum_p:,.4f}s")
            else:
                print("No files were processed")
    
    if __name__ == "__main__":
        main()
    

    Saída excluindo nomes de arquivos:

    Average duration per page = 1.2667s

    Resumo:

    Renderizar a 850 dpi com fitz / PyMuPDF é lento. Reduzir a renderização para, por exemplo, 300 dpi diminuiu o tempo por página para ~0,17 s

    • 1
  2. Obedient Timothy
    2025-03-22T21:11:03+08:002025-03-22T21:11:03+08:00

    Instalar a ferramenta Ghost Script

    Você pode baixar o software de conversão de PDF para imagem do Ghost Script neste site-> https://ghostscript.com/releases/gsdnld.html

    Instale o software no seu sistema.

    Instalar Ghost Script Python Wrapper

    Você então precisa de uma ferramenta python para chamar as funções de conversão de PDF para imagem no SDK que instalamos na etapa anterior, chamado ghostscript . Execute o comando pip install ghostscript

    Use o seguinte código Python para converter

    import os
    import subprocess
    import multiprocessing
    
    def convert_page(pdf_path, output_folder, page_number, dpi):
        """Converts a single PDF page to PNG using Ghostscript."""
        output_file = os.path.join(output_folder, f"page_{page_number}.png")
        
        gs_command = [
            "gs",  # Ghostscript command
            "-dNOPAUSE", "-dBATCH", "-dSAFER",  # No interruptions
            "-sDEVICE=png16m",  # High-quality 24-bit PNG output
            f"-r{dpi}",  # Set DPI
            f"-dFirstPage={page_number}", f"-dLastPage={page_number}",  # Process only one page
            f"-sOutputFile={output_file}",  # Define output file
            pdf_path
        ]
        
        subprocess.run(gs_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    
    def pdf_to_png(pdf_path, output_folder="output", dpi=900):
        """Converts a PDF into PNG images using multiprocessing for speed."""
        if not os.path.exists(output_folder):
            os.makedirs(output_folder)
    
        # Get total page count
        count_command = [
            "gs", "-q", "-dNODISPLAY", "-c",
            f"({pdf_path}) (r) file runpdfbegin pdfpagecount = quit"
        ]
        result = subprocess.run(count_command, capture_output=True, text=True)
        total_pages = int(result.stdout.strip())
    
        print(f"Total pages to convert: {total_pages}")
    
        # Use multiprocessing to speed up the conversion
        with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
            pool.starmap(convert_page, [(pdf_path, output_folder, page, dpi) for page in range(1, total_pages + 1)])
    
        print(f"✅ Conversion complete! Images saved in '{output_folder}'")
    
    #call the method to generate the images
    pdf_to_png("sample.pdf")
    
    • 1
  3. K J
    2025-03-23T00:49:42+08:002025-03-23T00:49:42+08:00

    A conversão de PDF é naturalmente lenta, então use apenas arquivos de 9 x 20 páginas (pois a vida é muito curta). Alguns minutos devem ser suficientes para cronometrar 9 arquivos, pois têm apenas 180 páginas.

    Esses são tempos CLI executáveis ​​brutos sem nenhuma sobrecarga do Python. Os resultados sempre serão diferentes dependendo do único processador gráfico, então uma estação de trabalho dedicada com GPUs multithread terá um desempenho melhor que meus valores. Portanto, oferecido apenas para comparar os mesmos PDFs de 20 KB (diagramas com texto compactado aproximadamente para 1 KB por página) como um teste relativo "exe Vs exe".

    Aqui estão alguns tempos baseados em impressões em PDF para dispositivos de imagem/papel, que são tradicionalmente considerados de alta velocidade, com 60 páginas por minuto.

    Para comparação, eu uso Artifex GS e MuPDF. Vamos mostrar os tempos de leitura final do MuPDF, pois eles são mais fáceis de relatar e mais rápidos também.

    page New folder (4)/file009.pdf 20 469ms (interpretation) 453ms (rendering) 922ms (total)
    total 9188ms (0ms layout) / 20 pages for an average of 459ms
    fastest page 1: 15ms (interpretation) 407ms (rendering) 422ms(total)
    slowest page 19: 484ms (interpretation) 469ms (rendering) 953ms(total)
    

    As páginas 1 e 19 são, na verdade, conteúdos bem similares. No entanto, ler um PDF compactado leva tempos variáveis. Aqui, cerca de 1/2 segundo a 1 segundo inteiro, e isso se deve à necessidade de percorrer para frente e para trás entre (neste PDF de 20 páginas) quase 140 objetos compactados discretos. O PDF de página única deve ser mais rápido, sem significativamente menos "vai e vem". PDFs menores com conteúdo descompactado são sempre melhores do que imagens grandes compactadas.

    Então, quais são as restrições de tempo e volumes retangulares envolvidos?

    density 720 dpi for 2304 x 3456 imagery
    page pixels =   7,962,624
    per PNG file=  22.78 MB
    per PDF file= 442.96 MB memory bus IO + source for decompression.
    
    GS start 15:18:46.12 
    GS end   15:21:03.76 
       pages      180
       seconds =  137.64
      600 files= 9176.00
      hours    =    2.55
    
    Mu start 16:11:37.92 
    Mu end   16:13:00.06
       pages      180
       seconds =   82.14
      600 files= 5476.00
      hours    =    1.52
    

    Portanto, o Mu PDF deve reduzir em uma hora o tempo gasto.

    E quanto a outras ferramentas comuns usadas em aplicativos Python? Os arquivos são naturalmente mais lentos, pois geralmente são significativamente compactados muito mais (cerca de -40% do tamanho do MuPDF).

    xpdf
    PDFtoPNG start 17:40:14.63 
    PDFtoPNG end   17:43:14.85 
    9 files in seconds  180.22
    
    PDFtoPPM (-PNG) prefered for its FOSS permisive license
    pdftoppm start 18:33:47.17 
    pdftoppm end   18:37:22.03 
    9 files in seconds  214.86
    

    Muitas sugestões dizem para usar Multi-threading e acima dos tempos do MuPDF foram baseados em 4 Threads. E se mudarmos essa configuração?

    4 Threads  82.14
    3 Threads  81.81
    2 Threads  71.45
    1 Thread   79.38 
    

    Portanto, sem compartilhar o tempo entre 3 ou mais threads, os tempos de renderização sequencial podem ser melhorados usando 2 threads neste dispositivo de 2 núcleos.

    Com um pouco mais de ajustes, podemos chegar a180 pages = 64.54 seconds

    Portanto, 600 arquivos com mais de 20 páginas = 4.302,66 segundos = tempo estimado de 1 hora e 12 minutos.

    Em um forloop lento do Windows:

    mutool draw -st -P -T 2 -B 32 -r 720 -F png -o "New folder (5)\%%~nc-page%%2d.png" "New folder (4)/%%~nxc"
    
    • 1
  4. Jorj McKie
    2025-03-22T20:00:11+08:002025-03-22T20:00:11+08:00

    Aqui está um script de exemplo que renderiza todas as páginas de um PDF para imagens usando multiprocessamento Python. Você pode esperar uma execução geral 2-4 vezes mais rápida do que a linear:

    import pymupdf
    import concurrent.futures
    from concurrent.futures import ProcessPoolExecutor
    import time
    
    
    def render_page(x):
        filename, numbers = x
        doc = pymupdf.open(filename)
        for pno in numbers:
            pix = doc[pno].get_pixmap(dpi=300)
            pix.save(f"img-{pno}.jpg")
    
    
    if __name__ == "__main__":
        t0 = time.perf_counter()
        doc = pymupdf.open("adobe.pdf")
        pc = doc.page_count
        with ProcessPoolExecutor(max_workers=10) as executor:
            for i in range(0, pc, 50):
                r = range(i, i + min(50, pc - i))
                executor.submit(render_page, (doc.name, r))
    
        t1 = time.perf_counter()
        print(f"Duration {t1-t0}")
    
    • 0

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve