Извлекать изображения из PDF без передискретизации в python? - PullRequest
53 голосов
/ 22 апреля 2010

Как можно извлечь все изображения из PDF-документа в собственном разрешении и формате?(Имеется в виду извлечь tiff как tiff, jpeg как jpeg и т. Д. И без повторной выборки).Макет не важен, мне все равно, где находится исходное изображение на странице.

Я использую Python 2.7, но при необходимости могу использовать 3.x.

Ответы [ 13 ]

28 голосов
/ 06 декабря 2015

В Python с библиотеками PyPDF2 и Pillow это просто:

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()
28 голосов
/ 23 апреля 2010

Часто в PDF изображение просто сохраняется как есть.Например, PDF со вставленным jpg будет иметь диапазон байтов где-то посередине, который при извлечении является допустимым файлом jpg.Вы можете использовать это, чтобы очень просто извлечь байтовые диапазоны из PDF.Я писал об этом некоторое время назад, с примером кода: Извлечение JPG из PDF .

20 голосов
/ 01 января 2016

В Python с PyPDF2 для фильтра 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: /2254414/izvlekat-izobrazheniya-iz-pdf-bez-perediskretizatsii-v-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 голосов
/ 19 декабря 2017

Вы можете использовать модуль PyMuPDF. Это выводит все изображения в виде файлов .png, но работает из коробки и работает быстро.

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

см. Здесь больше ресурсов

11 голосов
/ 30 августа 2010

Libpoppler поставляется с инструментом под названием "pdfimages", который делает именно это.

(В системах с Ubuntu он находится в пакете poppler-utils)

http://poppler.freedesktop.org/

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

Двоичные файлы Windows: http://blog.alivate.com.au/poppler-windows/

8 голосов
/ 05 мая 2016

Я начал с кода @sylvain Были некоторые недостатки, например исключение NotImplementedError: unsupported filter /DCTDecode из getData или тот факт, что код не смог найти изображения на некоторых страницах, потому что они были на более глубоком уровне, чем страница.

Вот мой код:

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)
5 голосов
/ 20 сентября 2018

Я предпочитаю MineCart, так как он чрезвычайно прост в использовании. В следующем фрагменте показано, как извлечь изображения из 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)
5 голосов
/ 07 июня 2017

После некоторых поисков я нашел следующий скрипт, который очень хорошо работает с моими PDF.Он работает только с JPG, но отлично работал с моими незащищенными файлами.Также is не требует каких-либо внешних библиотек.

Чтобы не брать на себя ответственность, скрипт написан Недом Батчелдером, а не мной.Код Python3: извлечение jpg из pdf.Быстро и грязно

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
4 голосов
/ 29 марта 2012

Я установил ImageMagick на моем сервере, а затем запустил вызовы из командной строки через 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)

Это создаст изображение для каждой страницы и сохранит их как temp-0.png, temp-1.png .... Это только «извлечение», если вы получили PDF с только изображениями и без текста.

3 голосов
/ 18 февраля 2017

Гораздо более простое решение:

Используйте пакет poppler-utils. Чтобы установить его, используйте homebrew (homebrew зависит от MacOS, но вы можете найти пакет poppler-utils для Widows или Linux здесь: https://poppler.freedesktop.org/). Первая строка кода ниже устанавливает poppler-utils с помощью homebrew. После установки вторая строка ( запускается из командной строки), затем извлекает изображения из файла PDF и называет их «image *». Для запуска этой программы из Python используйте модуль os или subprocess. Третья строка - это код с использованием модуля os, ниже приведен пример с подпроцессом. (Python 3.5 или более поздней версии для функции run ()). Более подробная информация здесь: 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')

или

import subprocess
subprocess.run('pdfimages file.pdf image', shell=True)
...