Extrair imagens do PDF sem recolocar, em python?

Como extrair todas as imagens de um documento pdf, em resolução e formato nativo? (Significa extrair o tiff como tiff, o jpeg como jpeg, etc. e sem recolocação). O Layout não é importante, eu não me importo se a imagem de origem está localizada na página.

estou a usar o python 2.7 mas posso usar o 3.x se necessário.

Author: matt wilkie, 2010-04-22

12 answers

Muitas vezes em PDF, a imagem é simplesmente armazenada como está. Por exemplo, um PDF com um jpg inserido terá um intervalo de bytes algures no meio que, quando extraído, é um ficheiro JPG válido. Você pode usar isso para muito simplesmente extrair intervalos de byte do PDF. Eu escrevi sobre isso há algum tempo, com o código de exemplo: extraindo JPGs de PDFs.

 26
Author: Ned Batchelder, 2010-04-23 00:08:43

Em Python com bibliotecas de PyPDF2 e almofadas é simples:

import PyPDF2

from PIL import Image

if __name__ == '__main__':
    input1 = PyPDF2.PdfFileReader(open("input.pdf", "rb"))
    page0 = input1.getPage(0)
    xObject = page0['/Resources']['/XObject'].getObject()

    for obj in xObject:
        if xObject[obj]['/Subtype'] == '/Image':
            size = (xObject[obj]['/Width'], xObject[obj]['/Height'])
            data = xObject[obj].getData()
            if xObject[obj]['/ColorSpace'] == '/DeviceRGB':
                mode = "RGB"
            else:
                mode = "P"

            if xObject[obj]['/Filter'] == '/FlateDecode':
                img = Image.frombytes(mode, size, data)
                img.save(obj[1:] + ".png")
            elif xObject[obj]['/Filter'] == '/DCTDecode':
                img = open(obj[1:] + ".jpg", "wb")
                img.write(data)
                img.close()
            elif xObject[obj]['/Filter'] == '/JPXDecode':
                img = open(obj[1:] + ".jp2", "wb")
                img.write(data)
                img.close()
 22
Author: sylvain, 2015-12-10 10:00:05

Em Python com o PyPDF2 para o filtro CCITTFaxDecode:

import PyPDF2
import struct

"""
Links:
PDF format: http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
CCITT Group 4: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.6-198811-I!!PDF-E&type=items
Extract images from pdf: http://stackoverflow.com/questions/2693820/extract-images-from-pdf-without-resampling-in-python
Extract images coded with CCITTFaxDecode in .net: http://stackoverflow.com/questions/2641770/extracting-image-from-pdf-with-ccittfaxdecode-filter
TIFF format and tags: http://www.awaresystems.be/imaging/tiff/faq.html
"""


def tiff_header_for_CCITT(width, height, img_size, CCITT_group=4):
    tiff_header_struct = '<' + '2s' + 'h' + 'l' + 'h' + 'hhll' * 8 + 'h'
    return struct.pack(tiff_header_struct,
                       b'II',  # Byte order indication: Little indian
                       42,  # Version number (always 42)
                       8,  # Offset to first IFD
                       8,  # Number of tags in IFD
                       256, 4, 1, width,  # ImageWidth, LONG, 1, width
                       257, 4, 1, height,  # ImageLength, LONG, 1, lenght
                       258, 3, 1, 1,  # BitsPerSample, SHORT, 1, 1
                       259, 3, 1, CCITT_group,  # Compression, SHORT, 1, 4 = CCITT Group 4 fax encoding
                       262, 3, 1, 0,  # Threshholding, SHORT, 1, 0 = WhiteIsZero
                       273, 4, 1, struct.calcsize(tiff_header_struct),  # StripOffsets, LONG, 1, len of header
                       278, 4, 1, height,  # RowsPerStrip, LONG, 1, lenght
                       279, 4, 1, img_size,  # StripByteCounts, LONG, 1, size of image
                       0  # last IFD
                       )

pdf_filename = 'scan.pdf'
pdf_file = open(pdf_filename, 'rb')
cond_scan_reader = PyPDF2.PdfFileReader(pdf_file)
for i in range(0, cond_scan_reader.getNumPages()):
    page = cond_scan_reader.getPage(i)
    xObject = page['/Resources']['/XObject'].getObject()
    for obj in xObject:
        if xObject[obj]['/Subtype'] == '/Image':
            """
            The  CCITTFaxDecode filter decodes image data that has been encoded using
            either Group 3 or Group 4 CCITT facsimile (fax) encoding. CCITT encoding is
            designed to achieve efficient compression of monochrome (1 bit per pixel) image
            data at relatively low resolutions, and so is useful only for bitmap image data, not
            for color images, grayscale images, or general data.

            K < 0 --- Pure two-dimensional encoding (Group 4)
            K = 0 --- Pure one-dimensional encoding (Group 3, 1-D)
            K > 0 --- Mixed one- and two-dimensional encoding (Group 3, 2-D)
            """
            if xObject[obj]['/Filter'] == '/CCITTFaxDecode':
                if xObject[obj]['/DecodeParms']['/K'] == -1:
                    CCITT_group = 4
                else:
                    CCITT_group = 3
                width = xObject[obj]['/Width']
                height = xObject[obj]['/Height']
                data = xObject[obj]._data  # sorry, getData() does not work for CCITTFaxDecode
                img_size = len(data)
                tiff_header = tiff_header_for_CCITT(width, height, img_size, CCITT_group)
                img_name = obj[1:] + '.tiff'
                with open(img_name, 'wb') as img_file:
                    img_file.write(tiff_header + data)
                #
                # import io
                # from PIL import Image
                # im = Image.open(io.BytesIO(tiff_header + data))
pdf_file.close()
 16
Author: Sergey Shashkov, 2016-01-01 10:34:50
A Libpoppler vem com uma ferramenta chamada "pdfimages" que faz exatamente isso.

(nos sistemas ubuntu está no Pacote poppler-utils)

Http://poppler.freedesktop.org/

Http://en.wikipedia.org/wiki/Pdfimages

Binários do Windows: http://blog.alivate.com.au/poppler-windows/

 10
Author: dkagedal, 2017-12-06 16:57:34
Comecei pelo código de @sylvain. Havia algumas falhas, como a exceção NotImplementedError: unsupported filter /DCTDecode de getData, ou o fato de que o código não conseguiu encontrar imagens em algumas páginas porque eles estavam em um nível mais profundo do que a página.

Eis o meu código:

import PyPDF2

from PIL import Image

import sys
from os import path
import warnings
warnings.filterwarnings("ignore")

number = 0

def recurse(page, xObject):
    global number

    xObject = xObject['/Resources']['/XObject'].getObject()

    for obj in xObject:

        if xObject[obj]['/Subtype'] == '/Image':
            size = (xObject[obj]['/Width'], xObject[obj]['/Height'])
            data = xObject[obj]._data
            if xObject[obj]['/ColorSpace'] == '/DeviceRGB':
                mode = "RGB"
            else:
                mode = "P"

            imagename = "%s - p. %s - %s"%(abspath[:-4], p, obj[1:])

            if xObject[obj]['/Filter'] == '/FlateDecode':
                img = Image.frombytes(mode, size, data)
                img.save(imagename + ".png")
                number += 1
            elif xObject[obj]['/Filter'] == '/DCTDecode':
                img = open(imagename + ".jpg", "wb")
                img.write(data)
                img.close()
                number += 1
            elif xObject[obj]['/Filter'] == '/JPXDecode':
                img = open(imagename + ".jp2", "wb")
                img.write(data)
                img.close()
                number += 1
        else:
            recurse(page, xObject[obj])



try:
    _, filename, *pages = sys.argv
    *pages, = map(int, pages)
    abspath = path.abspath(filename)
except BaseException:
    print('Usage :\nPDF_extract_images file.pdf page1 page2 page3 …')
    sys.exit()


file = PyPDF2.PdfFileReader(open(filename, "rb"))

for p in pages:    
    page0 = file.getPage(p-1)
    recurse(p, page0)

print('%s extracted images'% number)
 6
Author: Labo, 2016-05-05 15:57:47

Pode usar o módulo PyMuPDF. Isto produz todas as imagens como .arquivos png, mas trabalhou fora da caixa e é rápido.

import fitz
doc = fitz.open("file.pdf")
for i in range(len(doc)):
for img in doc.getPageImageList(i):
    xref = img[0]
    pix = fitz.Pixmap(doc, xref)
    if pix.n < 5:       # this is GRAY or RGB
        pix.writePNG("p%s-%s.png" % (i, xref))
    else:               # CMYK: convert to RGB first
        pix1 = fitz.Pixmap(fitz.csRGB, pix)
        pix1.writePNG("p%s-%s.png" % (i, xref))
        pix1 = None
    pix = None

Veja aqui para mais recursos

 5
Author: kateryna, 2017-12-18 23:26:47

Instalei o ImageMagick no meu servidor e depois executei as chamadas através da linha de comandosPopen:

 #!/usr/bin/python

 import sys
 import os
 import subprocess
 import settings

 IMAGE_PATH = os.path.join(settings.MEDIA_ROOT , 'pdf_input' )

 def extract_images(pdf):
     output = 'temp.png'
     cmd = 'convert ' + os.path.join(IMAGE_PATH, pdf) + ' ' + os.path.join(IMAGE_PATH, output)
     subprocess.Popen(cmd.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)

Isto irá criar uma imagem para cada página e armazená-los como temp-0.png, temp-1.Forum .... Isto é apenas 'extração' se você tiver um pdf com apenas imagens e sem texto.

 3
Author: TompaLompa, 2012-03-29 13:00:07

Depois de alguma pesquisa encontrei o seguinte script que funciona muito bem com os meus PDF. ele só lida com JPG, mas funcionou perfeitamente com os meus arquivos desprotegidos. Também não requer bibliotecas externas.

Para não ficar com nenhum crédito, o guião tem origem no Ned Batchelder, e não em mim. Código Python3: extrair jpg de pdf. Quick and dirty
import sys

with open(sys.argv[1],"rb") as file:
    file.seek(0)
    pdf = file.read()

startmark = b"\xff\xd8"
startfix = 0
endmark = b"\xff\xd9"
endfix = 2
i = 0

njpg = 0
while True:
    istream = pdf.find(b"stream", i)
    if istream < 0:
        break
    istart = pdf.find(startmark, istream, istream + 20)
    if istart < 0:
        i = istream + 20
        continue
    iend = pdf.find(b"endstream", istart)
    if iend < 0:
        raise Exception("Didn't find end of stream!")
    iend = pdf.find(endmark, iend - 20)
    if iend < 0:
        raise Exception("Didn't find end of JPG!")

    istart += startfix
    iend += endfix
    print("JPG %d from %d to %d" % (njpg, istart, iend))
    jpg = pdf[istart:iend]
    with open("jpg%d.jpg" % njpg, "wb") as jpgfile:
        jpgfile.write(jpg)

    njpg += 1
    i = iend
 2
Author: Max A. H. Hartvigsen, 2017-06-08 04:31:55

Solução muito mais fácil:

Usa o pacote poppler-utils. Para o instalar, use o homebrew (o homebrew é específico para MacOS, mas poderá encontrar aqui o pacote poppler-utils para viúvas ou Linux: https://poppler.freedesktop.org/). primeira linha de código abaixo instala o poppler-utils usando o homebrew. Após a instalação, a segunda linha (executado a partir da linha de comando), em seguida, extrai imagens de um arquivo PDF e nomeá-los "imagem*". Para executar este programa dentro do Python use o SO ou módulo de subprocessamento. A terceira linha é o código usando o módulo os, abaixo desse é um exemplo com subprocessamento (Python 3.5 ou mais tarde para executar() função). Mais informações aqui: https://www.cyberciti.biz/faq/easily-extract-images-from-pdf-file/

brew install poppler

pdfimages file.pdf image

import os
os.system('pdfimages file.pdf image')

Ou

import subprocess
subprocess.run('pdfimages file.pdf image', shell=True)
 2
Author: Colton Hicks, 2017-12-11 20:58:25

Eu adicionei todos aqueles juntos em PyPDFTKAqui .

A minha própria contribuição é tratar de /Indexed ficheiros como tal:

for obj in xObject:
    if xObject[obj]['/Subtype'] == '/Image':
        size = (xObject[obj]['/Width'], xObject[obj]['/Height'])
        color_space = xObject[obj]['/ColorSpace']
        if isinstance(color_space, pdf.generic.ArrayObject) and color_space[0] == '/Indexed':
            color_space, base, hival, lookup = [v.getObject() for v in color_space] # pg 262
        mode = img_modes[color_space]

        if xObject[obj]['/Filter'] == '/FlateDecode':
            data = xObject[obj].getData()
            img = Image.frombytes(mode, size, data)
            if color_space == '/Indexed':
                img.putpalette(lookup.getData())
                img = img.convert('RGB')
            img.save("{}{:04}.png".format(filename_prefix, i))

Note que quando os arquivos /Indexed são encontrados, você não pode simplesmente comparar /ColorSpace com uma string, porque ela vem como um ArrayObject. Então, temos que verificar o array e recuperar a paleta indexada (lookup no código) e configurá-lo no objeto de imagem PIL, caso contrário, ele permanece não inicializado (zero) e toda a imagem aparece como preto.

O meu primeiro instinto foi ... salve-os como GIFs (que é um formato indexado), mas meus testes revelaram que PNGs eram menores e pareciam da mesma forma.

Encontrei esses tipos de imagens ao imprimir em PDF com a impressora Foxit Reader PDF.

 1
Author: Ronan Paixão, 2016-03-23 01:38:54

Também pode usar o comando {[[2]} no Ubuntu.

Instale o poppler lib usando os comandos abaixo.

sudo apt install poppler-utils

sudo apt-get install python-poppler

pdfimages file.pdf image

Lista de arquivos criados são, (por exemplo.,. há duas imagens em pdf)

image-000.png
image-001.png
Funciona ! Agora você pode usar um subprocess.run para executar isso em python.
 0
Author: Vikram S, 2018-08-10 08:20:58

Prefiro o minecart porque é extremamente fácil de usar. O excerto abaixo mostra como extrair imagens de um pdf:

#pip install minecart
import minecart

pdffile = open('Invoices.pdf', 'rb')
doc = minecart.Document(pdffile)

page = doc.get_page(0) # getting a single page

#iterating through all pages
for page in doc.iter_pages():
    im = page.images[0].as_pil()  # requires pillow
    display(im)
 0
Author: VSZM, 2018-09-19 23:29:22