¿Extraer imágenes de PDF sin remuestrear, en python?

80

¿Cómo se pueden extraer todas las imágenes de un documento pdf, con resolución y formato nativo? (Es decir, extraer tiff como tiff, jpeg como jpeg, etc. y sin remuestreo). El diseño no es importante, no me importa dónde se encuentra la imagen de origen en la página.

Estoy usando Python 2.7 pero puedo usar 3.x si es necesario.

Matt Wilkie
fuente
Gracias. Esa url de "cómo se almacenan las imágenes en PDF" no funcionó, pero esto parece ser: jpedal.org/PDFblog/2010/04/…
nealmcb
Hay una biblioteca java JPedal que hace esto llamada Extracción de imágenes recortadas en PDF . El autor, Mark Stephens, tiene una descripción general concisa de alto nivel de cómo se almacenan las imágenes en PDF, lo que puede ayudar a alguien a construir un extractor de Python.
Matt Wilkie

Respuestas:

46

Puede utilizar el módulo PyMuPDF. Esto genera todas las imágenes como archivos .png, pero funciona de inmediato y es 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

ver aquí para más recursos

Kateryna
fuente
2
¡Esto funciona muy bien! (se pip install pymudfnecesita primero obviamente)
Basj
9
* pip install pymupdfpara los compañeros de Google que se preguntan por qué falla la instalación anterior
VSZM
9
En lugar de pip install pymupdfintentar pip install PyMuPDF más información
Damotorie
1
Con este código que obtengo RuntimeError: pixmap must be grayscale or rgb to write as png, ¿alguien puede ayudarme?
bóveda
5
@vault Este comentario está desactualizado. Debe cambiar "si pix.n <5" por "si pix.n - pix.alpha <4", ya que la condición original no encuentra imágenes CMYK correctamente.
Oringa
41

En Python con las bibliotecas PyPDF2 y Pillow es simple:

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()
Sylvain
fuente
14
Excitado inicialmente por esto, pero vomitó NotImplementedError: unsupported filter /DCTDecodeo ... /JPXDecodedesde xObject[obj].getData()en el primer par de pdf probé. Detalles en gist.github.com/maphew/fe6ba4bf9ed2bc98ecf5
matt wilkie
4
Recientemente envié la modificación '/ DCTDecode' a la biblioteca PyPDF2. Puede usar mi repositorio: github.com/sylvainpelissier/PyPDF2 mientras está integrado en la rama principal.
Sylvain
1
Gracias por la actualización, pero lo siento, aún así no. Gist actualizado. Obtengo ValueError: not enough image datapara dctdecode imágenes incrustadas y unsupported filter /JPXDecodeen otro pdf.
Matt Wilkie
1
avanzando! Los pdf de dctdecode se procesan sin errores ahora (aunque a veces las imágenes de salida están al revés). Sin embargo, el archivo JPXDecode ahora arroja en su KeyError:/Filterlugar. Actualicé la esencia en consecuencia. Los archivos PDF son simplemente aleatorios de la red. La esencia tiene enlaces de origen.
Matt Wilkie
28
"Es simple ... "
mlissner
34

A menudo, en un PDF, la imagen simplemente se almacena como está. Por ejemplo, un PDF con un jpg insertado tendrá un rango de bytes en algún lugar en el medio que cuando se extrae es un archivo jpg válido. Puede usar esto para extraer rangos de bytes del PDF de manera muy simple. Escribí sobre esto hace algún tiempo, con un código de muestra: Extracción de JPG de PDF .

Ned Batchelder
fuente
1
gracias Ned. Parece que los PDF en particular para los que necesito esto no están usando jpeg in-situ, pero guardaré su muestra en caso de que coincida con otras cosas que aparezcan.
Matt Wilkie
3
¿Puede explicar algunas cosas en el código? Por ejemplo, ¿por qué buscarías "stream" primero y luego startmark? podría comenzar a buscar startmarkya que este es el comienzo de JPG, ¿no? y cuál es el punto de la startfixvariable, no la cambias en absoluto ..
user3599803
Esto funcionó perfectamente para el PDF del que quería extraer imágenes. (En caso de que ayude a alguien más, guardé su código como un archivo .py, luego instalé / usé Python 2.7.18 para ejecutarlo, pasando la ruta a mi PDF como el único argumento de la línea de comandos).
matt
25

En Python con PyPDF2 para el 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()
Sergey Shashkov
fuente
¡Esto funcionó de inmediato para mí, y es extremadamente rápido! Todas mis imágenes salieron invertidas, pero pude arreglar eso con OpenCV. He estado usando ImageMagick's convertusing subprocesspara llamarlo, pero es tremendamente lento. Gracias por compartir esta solución
crld
2
Como se ha señalado en otras partes de su tiff_header_structdebe leer '<' + '2s' + 'H' + 'L' + 'H' + 'HHLL' * 8 + 'L'. Tenga en cuenta en particular el 'L'al final.
Dispensador
Cualquier ayuda sobre esto, por favor: stackoverflow.com/questions/55899363/…
Aakash Basu
10

Prefiero minecart porque es extremadamente fácil de usar. El siguiente fragmento muestra cómo extraer imágenes de un 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)
VSZM
fuente
Hola, minecart funciona perfectamente pero tengo un pequeño problema: a veces se cambia el diseño de las imágenes (horizontal -> vertical). ¿Tiene alguna idea de cómo podría evitar esto? ¡Gracias!
Sha Li
Con minecart obtengo: pdfminer.pdftypes.PDFNotImplementedError: Filtro no admitido: / CCITTFaxDecode
Javi12
7

Aquí está mi versión de 2019 que obtiene de forma recursiva todas las imágenes de PDF y las lee con PIL. Compatible con Python 2/3. También descubrí que a veces la imagen en PDF puede ser comprimida por zlib, por lo que mi código admite la descompresión.

#!/usr/bin/env python3
try:
    from StringIO import StringIO
except ImportError:
    from io import BytesIO as StringIO
from PIL import Image
from PyPDF2 import PdfFileReader, generic
import zlib


def get_color_mode(obj):

    try:
        cspace = obj['/ColorSpace']
    except KeyError:
        return None

    if cspace == '/DeviceRGB':
        return "RGB"
    elif cspace == '/DeviceCMYK':
        return "CMYK"
    elif cspace == '/DeviceGray':
        return "P"

    if isinstance(cspace, generic.ArrayObject) and cspace[0] == '/ICCBased':
        color_map = obj['/ColorSpace'][1].getObject()['/N']
        if color_map == 1:
            return "P"
        elif color_map == 3:
            return "RGB"
        elif color_map == 4:
            return "CMYK"


def get_object_images(x_obj):
    images = []
    for obj_name in x_obj:
        sub_obj = x_obj[obj_name]

        if '/Resources' in sub_obj and '/XObject' in sub_obj['/Resources']:
            images += get_object_images(sub_obj['/Resources']['/XObject'].getObject())

        elif sub_obj['/Subtype'] == '/Image':
            zlib_compressed = '/FlateDecode' in sub_obj.get('/Filter', '')
            if zlib_compressed:
               sub_obj._data = zlib.decompress(sub_obj._data)

            images.append((
                get_color_mode(sub_obj),
                (sub_obj['/Width'], sub_obj['/Height']),
                sub_obj._data
            ))

    return images


def get_pdf_images(pdf_fp):
    images = []
    try:
        pdf_in = PdfFileReader(open(pdf_fp, "rb"))
    except:
        return images

    for p_n in range(pdf_in.numPages):

        page = pdf_in.getPage(p_n)

        try:
            page_x_obj = page['/Resources']['/XObject'].getObject()
        except KeyError:
            continue

        images += get_object_images(page_x_obj)

    return images


if __name__ == "__main__":

    pdf_fp = "test.pdf"

    for image in get_pdf_images(pdf_fp):
        (mode, size, data) = image
        try:
            img = Image.open(StringIO(data))
        except Exception as e:
            print ("Failed to read image with PIL: {}".format(e))
            continue
        # Do whatever you want with the image
Alex Paramonov
fuente
Este código funcionó para mí, casi sin modificaciones. Gracias.
xax
6

Empecé desde el código de @sylvain. Hubo algunas fallas, como la excepción NotImplementedError: unsupported filter /DCTDecodede getData, o el hecho de que el código no pudo encontrar imágenes en algunas páginas porque estaban en un nivel más profundo que la página.

Ahí está mi 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)
Labo
fuente
Este código me falla en las imágenes filtradas '/ ICCBased' '/ FlateDecode' conimg = Image.frombytes(mode, size, data) ValueError: not enough image data
GrantD71
1
@ GrantD71 No soy un experto y nunca antes había oído hablar de ICCBased. Además, su error no se puede reproducir si no proporciona las entradas.
Labo
Obtengo un KeyError: '/ColorSpace', así que reemplazaría su línea con DeviceRGB por if '/ColorSpace' not in xObject[obj] or xObject[obj]['/ColorSpace'] == '/DeviceRGB':. De todos modos, esto no funcionó para mí al final porque las imágenes probablemente eran PNG (no estoy seguro).
Basj
@Basj se supone que mi código también funciona con PNG. ¿Cuál es el valor de xObject[obj]['/Filter']?
Labo
2
Adapté su código para trabajar tanto en Python 2 como en 3. También implementé el cambio / Indexado de Ronan Paixão. También cambié el filtro if / elif para que esté 'en' en lugar de igual. Tenía un PDF con el / Tipo de filtro ['/ ASCII85Decode', '/ FlateDecode']. También cambié la función para devolver blobs de imágenes en lugar de escribir en el archivo. El código actualizado se puede encontrar aquí: gist.github.com/gstorer/f6a9f1dfe41e8e64dcf58d07afa9ab2a
Gerald
4

Instalé ImageMagick en mi servidor y luego ejecuté llamadas de línea de comandos a través de Popen:

 #!/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)

Esto creará una imagen para cada página y las almacenará como temp-0.png, temp-1.png ... Esto es solo una 'extracción' si tienes un pdf con solo imágenes y sin texto.

TompaLompa
fuente
1
Magia de imágenes usa ghostscript para hacer esto. Puede consultar esta publicación para ver el comando ghostscript que la magia de imágenes usa debajo de las sábanas.
Filipe Correia
Debo decir que a veces el renderizado es realmente malo. Con poppler funciona sin problemas.
Raffi
4

Después de buscar, encontré el siguiente script que funciona muy bien con mis PDF. Solo hace frente a JPG, pero funcionó perfectamente con mis archivos desprotegidos. Además, no requiere bibliotecas externas.

Para no atribuirme ningún mérito, el guión proviene de Ned Batchelder, no yo. Código Python3: extrae archivos jpg de pdf. Rápido y sucio

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
Max AH Hartvigsen
fuente
1
Eso parece interesante. ¿Dónde lo encontraste? (Y, formato en su puesto es un poco en mal estado comillas desequilibradas creo..)
Wilkie mate
1
nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html puede encontrar la publicación original aquí ...
Max AH Hartvigsen
4

Solución mucho más sencilla:

Utilice el paquete poppler-utils. Para instalarlo, use homebrew (homebrew es específico de MacOS, pero puede encontrar el paquete poppler-utils para Widows o Linux aquí: https://poppler.freedesktop.org/ ). La primera línea de código a continuación instala poppler-utils usando homebrew. Después de la instalación, la segunda línea (ejecutar desde la línea de comando) extrae imágenes de un archivo PDF y las nombra "imagen *". Para ejecutar este programa desde Python, use el módulo de subproceso o sistema operativo. La tercera línea es el código que usa el módulo os, debajo hay un ejemplo con subproceso (python 3.5 o posterior para la función run ()). Más información aquí: 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')

o

import subprocess
subprocess.run('pdfimages file.pdf image', shell=True)
Colton Hicks
fuente
1
Gracias Colton. Homebrew es solo para MacOS. Es una buena práctica tener en cuenta el sistema operativo cuando las instrucciones son específicas de la plataforma.
Matt Wilkie
@mattwilkie - Gracias por el aviso. Notaré esto en mi respuesta.
Colton Hicks
3

Hice esto para mi propio programa y descubrí que la mejor biblioteca para usar era PyMuPDF. Le permite averiguar los números "xref" de cada imagen en cada página y utilizarlos para extraer los datos de la imagen sin procesar del PDF.

import fitz
from PIL import Image
import io

filePath = "path/to/file.pdf"
#opens doc using PyMuPDF
doc = fitz.Document(filePath)

#loads the first page
page = doc.loadPage(0)

#[First image on page described thru a list][First attribute on image list: xref n], check PyMuPDF docs under getImageList()
xref = page.getImageList()[0][0]

#gets the image as a dict, check docs under extractImage 
baseImage = doc.extractImage(xref)

#gets the raw string image data from the dictionary and wraps it in a BytesIO object before using PIL to open it
image = Image.open(io.BytesIO(baseImage['image']))

#Displays image for good measure
image.show()

Sin embargo, definitivamente revisa los documentos.

Muhsin Wahi-Anwar
fuente
La mejor opción OMI: después de instalar fitzen Win 10, recibí el error: ModuleNotFoundError: Ningún módulo llamado 'frontend', que se resolvió fácilmente instalando pip install PyMuPDFcomo se describe aquí: stackoverflow.com/questions/56467667/…
Peter
3

Bueno, he estado luchando con esto durante muchas semanas, muchas de estas respuestas me ayudaron, pero siempre faltaba algo, aparentemente nadie aquí ha tenido problemas con las imágenes codificadas con jbig2 .

En el montón de PDF que voy a escanear, las imágenes codificadas en jbig2 son muy populares.

Hasta donde tengo entendido, hay muchas máquinas de copia / escaneo que escanean papeles y los transforman en archivos PDF llenos de imágenes codificadas con jbig2.

Entonces, después de muchos días de pruebas, decidí ir por la respuesta propuesta aquí por dkagedal hace mucho tiempo.

Aquí está mi paso a paso en Linux: (si tiene otro sistema operativo, sugiero que use una ventana acoplable de Linux , será mucho más fácil).

Primer paso:

apt-get install poppler-utils

Luego pude ejecutar la herramienta de línea de comandos llamada pdfimages como esta:

pdfimages -all myfile.pdf ./images_found/

Con el comando anterior podrás extraer todas las imágenes contenidas en myfile.pdf y las tendrás guardadas dentro de images_found (tienes que crear images_found antes)

En la lista encontrará varios tipos de imágenes, png, jpg, tiff; todos estos son fácilmente legibles con cualquier herramienta gráfica.

Entonces tendrás algunos archivos con nombres como: -145.jb2e y -145.jb2g.

Estos 2 archivos contienen UNA IMAGEN codificada en jbig2 guardada en 2 archivos diferentes, uno para el encabezado y otro para los datos

Nuevamente he perdido muchos días tratando de averiguar cómo convertir esos archivos en algo legible y finalmente me encontré con esta herramienta llamada jbig2dec

Entonces, primero necesitas instalar esta herramienta mágica:

apt-get install jbig2dec

entonces puedes ejecutar:

jbig2dec -t png -145.jb2g -145.jb2e

Finalmente podrás convertir todas las imágenes extraídas en algo útil.

¡buena suerte!

Marco
fuente
Esta es información útil y debe documentarse y compartirse , como acaba de hacer. +1. Sin embargo, sugiero publicar como su propia pregunta nueva y luego auto-responder porque no aborda hacer esto en Python, que es el punto de esta Q. (Siéntase libre de vincular las publicaciones ya que esto está relacionado)
matt wilkie
Hola @mattwilkie, gracias por el consejo, aquí está la pregunta: stackoverflow.com/questions/60851124/…
Marco
2

A partir de febrero de 2019, la solución dada por @sylvain (al menos en mi configuración) no funciona sin una pequeña modificación: xObject[obj]['/Filter']no es un valor, sino una lista, por lo tanto, para que el script funcione, tuve que modificar el formato comprobando lo siguiente:

import PyPDF2, traceback

from PIL import Image

input1 = PyPDF2.PdfFileReader(open(src, "rb"))
nPages = input1.getNumPages()
print nPages

for i in range(nPages) :
    print i
    page0 = input1.getPage(i)
    try :
        xObject = page0['/Resources']['/XObject'].getObject()
    except : xObject = []

    for obj in xObject:
        if xObject[obj]['/Subtype'] == '/Image':
            size = (xObject[obj]['/Width'], xObject[obj]['/Height'])
            data = xObject[obj].getData()
            try :
                if xObject[obj]['/ColorSpace'] == '/DeviceRGB':
                    mode = "RGB"
                elif xObject[obj]['/ColorSpace'] == '/DeviceCMYK':
                    mode = "CMYK"
                    # will cause errors when saving
                else:
                    mode = "P"

                fn = 'p%03d-%s' % (i + 1, obj[1:])
                print '\t', fn
                if '/FlateDecode' in xObject[obj]['/Filter'] :
                    img = Image.frombytes(mode, size, data)
                    img.save(fn + ".png")
                elif '/DCTDecode' in xObject[obj]['/Filter']:
                    img = open(fn + ".jpg", "wb")
                    img.write(data)
                    img.close()
                elif '/JPXDecode' in xObject[obj]['/Filter'] :
                    img = open(fn + ".jp2", "wb")
                    img.write(data)
                    img.close()
                elif '/LZWDecode' in xObject[obj]['/Filter'] :
                    img = open(fn + ".tif", "wb")
                    img.write(data)
                    img.close()
                else :
                    print 'Unknown format:', xObject[obj]['/Filter']
            except :
                traceback.print_exc()
mxl
fuente
1
Los filtros DCTDecode CCITTFaxDecode aún no están implementados.
Abhimanyu
Hola @Modem Rakesh goud, ¿podrías proporcionar el archivo PDF que provocó este error? ¡Gracias!
mxl
Desafortunadamente, no puedo compartir ese pdf.
Modem Rakesh goud
¿O eventualmente estarías en posesión de un programa como Acrobat (no Reader, sino la versión PRO), o alternativamente otro programa de edición de PDF que pueda extraer una parte del PDF y proporcionar solo esa parte, o simplemente dame la traceback.print_exc()de la línea de error dada, para que pueda ver qué lo desencadenó; o tal vez opte por otra de las soluciones aquí en este sitio, ya que la que se proporciona aquí (según tengo entendido) se centra en proporcionar una extracción de datos sin pérdidas 1: 1 de un PDF y puede que no sea lo que está buscando, ¡gracias!
mxl
1

Agregué todos esos juntos en PyPDFTK aquí .

Mi propia contribución es el manejo de /Indexedarchivos como tales:

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))

Tenga en cuenta que cuando /Indexedse encuentran archivos, no puede simplemente compararlos /ColorSpacecon una cadena, porque viene como un ArrayObject. Entonces, tenemos que verificar la matriz y recuperar la paleta indexada ( lookupen el código) y configurarla en el objeto Imagen PIL; de lo contrario, permanece sin inicializar (cero) y toda la imagen se muestra en negro.

Mi primer instinto fue guardarlos como GIF (que es un formato indexado), pero mis pruebas resultaron que los PNG eran más pequeños y tenían el mismo aspecto.

Encontré ese tipo de imágenes al imprimir en PDF con Foxit Reader PDF Printer.

Ronan Paixão
fuente
1

También puede usar el pdfimagescomando en Ubuntu.

Instale poppler lib usando los siguientes comandos.

sudo apt install poppler-utils

sudo apt-get install python-poppler

pdfimages file.pdf image

Lista de archivos creados son, (por ejemplo,. Hay dos imágenes en pdf)

image-000.png
image-001.png

Funciona ! Ahora puede usar a subprocess.runpara ejecutar esto desde python.

SuperNova
fuente
1

Después de leer las publicaciones usando pyPDF2 .

El error al usar el código de @ sylvain NotImplementedError: unsupported filter /DCTDecodedebe provenir del método .getData(): se resuelve cuando se usa en su ._datalugar, por @Alex Paramonov.

Hasta ahora solo he conocido casos de "DCTDecode", pero estoy compartiendo el código adaptado que incluye comentarios de las diferentes publicaciones: De zilbpor @Alex Paramonov, sub_obj['/Filter']siendo una lista, por @mxl.

Espero que pueda ayudar a los usuarios de pyPDF2. Siga el código:

    import sys
    import PyPDF2, traceback
    import zlib
    try:
        from PIL import Image
    except ImportError:
        import Image

    pdf_path = 'path_to_your_pdf_file.pdf'
    input1 = PyPDF2.PdfFileReader(open(pdf_path, "rb"))
    nPages = input1.getNumPages()

    for i in range(nPages) :
        page0 = input1.getPage(i)

        if '/XObject' in page0['/Resources']:
            try:
                xObject = page0['/Resources']['/XObject'].getObject()
            except :
                xObject = []

            for obj_name in xObject:
                sub_obj = xObject[obj_name]
                if sub_obj['/Subtype'] == '/Image':
                    zlib_compressed = '/FlateDecode' in sub_obj.get('/Filter', '')
                    if zlib_compressed:
                       sub_obj._data = zlib.decompress(sub_obj._data)

                    size = (sub_obj['/Width'], sub_obj['/Height'])
                    data = sub_obj._data#sub_obj.getData()
                    try :
                        if sub_obj['/ColorSpace'] == '/DeviceRGB':
                            mode = "RGB"
                        elif sub_obj['/ColorSpace'] == '/DeviceCMYK':
                            mode = "CMYK"
                            # will cause errors when saving (might need convert to RGB first)
                        else:
                            mode = "P"

                        fn = 'p%03d-%s' % (i + 1, obj_name[1:])
                        if '/Filter' in sub_obj:
                            if '/FlateDecode' in sub_obj['/Filter']:
                                img = Image.frombytes(mode, size, data)
                                img.save(fn + ".png")
                            elif '/DCTDecode' in sub_obj['/Filter']:
                                img = open(fn + ".jpg", "wb")
                                img.write(data)
                                img.close()
                            elif '/JPXDecode' in sub_obj['/Filter']:
                                img = open(fn + ".jp2", "wb")
                                img.write(data)
                                img.close()
                            elif '/CCITTFaxDecode' in sub_obj['/Filter']:
                                img = open(fn + ".tiff", "wb")
                                img.write(data)
                                img.close()
                            elif '/LZWDecode' in sub_obj['/Filter'] :
                                img = open(fn + ".tif", "wb")
                                img.write(data)
                                img.close()
                            else :
                                print('Unknown format:', sub_obj['/Filter'])
                        else:
                            img = Image.frombytes(mode, size, data)
                            img.save(fn + ".png")
                    except:
                        traceback.print_exc()
        else:
            print("No image found for page %d" % (i + 1))
Amstrad2Python
fuente
0

Pruebe el siguiente código. extraerá todas las imágenes de pdf.

    import sys
    import PyPDF2
    from PIL import Image
    pdf=sys.argv[1]
    print(pdf)
    input1 = PyPDF2.PdfFileReader(open(pdf, "rb"))
    for x in range(0,input1.numPages):
        xObject=input1.getPage(x)
        xObject = xObject['/Resources']['/XObject'].getObject()
        for obj in xObject:
            if xObject[obj]['/Subtype'] == '/Image':
                size = (xObject[obj]['/Width'], xObject[obj]['/Height'])
                print(size)
                data = xObject[obj]._data
                #print(data)
                print(xObject[obj]['/Filter'])
                if xObject[obj]['/Filter'][0] == '/DCTDecode':
                    img_name=str(x)+".jpg"
                    print(img_name)
                    img = open(img_name, "wb")
                    img.write(data)
                    img.close()
        print(str(x)+" is done")
Santosh Birje
fuente
0
  1. Primero instale pdf2image

    pip instalar pdf2image == 1.14.0

  2. Siga el siguiente código para la extracción de páginas de PDF.

    file_path="file path of PDF"
    info = pdfinfo_from_path(file_path, userpw=None, poppler_path=None)
    maxPages = info["Pages"]
    image_counter = 0
    if maxPages > 10:
        for page in range(1, maxPages, 10):
            pages = convert_from_path(file_path, dpi=300, first_page=page, 
                    last_page=min(page+10-1, maxPages))
            for page in pages:
                page.save(image_path+'/' + str(image_counter) + '.png', 'PNG')
                image_counter += 1
    else:
        pages = convert_from_path(file_path, 300)
        for i, j in enumerate(pages):
            j.save(image_path+'/' + str(i) + '.png', 'PNG')
    

Espero que ayude a los programadores que buscan una conversión fácil de archivos PDF a imágenes según las páginas de PDF.

Rahul Goyal
fuente