在python中从PDF中提取图像而不重新采样?

Posted

技术标签:

【中文标题】在python中从PDF中提取图像而不重新采样?【英文标题】:Extract images from PDF without resampling, in python? 【发布时间】:2011-02-11 05:41:30 【问题描述】:

如何以原始分辨率和格式从 pdf 文档中提取所有图像? (意味着将 tiff 提取为 tiff,将 jpeg 提取为 jpeg 等,并且无需重新采样)。布局不重要,我不在乎源图像是否位于页面上。

我使用的是 python 2.7,但如果需要,可以使用 3.x。

【问题讨论】:

谢谢。 “如何将图像存储在 PDF 中” url 不起作用,但这似乎是:jpedal.org/PDFblog/2010/04/… 有一个名为PDF Clipped Image Extraction 的JPedal java 库执行此操作。作者 Mark Stephens 对 how images are stored in PDF 有一个简明的高级概述,这可能有助于构建 python 提取器。 以上链接从@nealmcb 移至blog.idrsolutions.com/2010/04/… 【参考方案1】:

您可以使用模块 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

see here for more resources

【讨论】:

这很好用! (显然首先需要pip install pymudf *pip install pymupdf 想知道为什么上述安装失败的谷歌同事 而不是pip install pymupdf 尝试pip install PyMuPDF more info 有了这个代码我得到RuntimeError: pixmap must be grayscale or rgb to write as png,有人可以帮忙吗? @vault 此评论已过时。您应该将“if pix.n 【参考方案2】:

在带有 PyPDF2 和 Pillow 库的 Python 中,这很简单:

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

【讨论】:

最初对此感到兴奋,但在我测试的第一对 pdf 文件中,它从 xObject[obj].getData() 抛出了 NotImplementedError: unsupported filter /DCTDecode... /JPXDecode。详情gist.github.com/maphew/fe6ba4bf9ed2bc98ecf5 我最近将 '/DCTDecode' 修改推送到 PyPDF2 库。您可以使用我的存储库:github.com/sylvainpelissier/PyPDF2,它集成在主分支中。 "这很简单..." 嗨,现在维护最多的库似乎是 PyPDF4:github.com/claird/PyPDF4 这不适用于 PyPDF2 或 PyPDF4。 (1) 它不处理/JBIG2Decode 过滤器。您可以通过保存原始图像数据(data = xObject[obj]._data 而不是data = xObject[obj].getData())来部分解决这个问题。但是 jbig2 文件没有得到广泛的支持,所以这不是很有用。 (2) /CCITTFaxDecode 过滤器在某些情况下也会崩溃(似乎是因为某些 PDF 将 DecodeParams 存储为字典数组,但 PyPDF 需要一个字典)。 PikePDF solution 效果更好。【参考方案3】:

通常在 PDF 中,图像只是按原样存储。例如,插入 jpg 的 PDF 将在中间某处有一系列字节,当提取时该字节是有效的 jpg 文件。您可以使用它来非常简单地从 PDF 中提取字节范围。我前段时间写过这个,示例代码:Extracting JPGs from PDFs。

【讨论】:

感谢内德。看起来我需要的特定 pdf 文件没有在原位使用 jpeg,但我会保留你的样本,以防它与其他出现的东西相匹配。 你能解释一下代码中的一些事情吗?例如,为什么要先搜索“stream”,然后再搜索startmark?您可以开始搜索startmark,因为这是 JPG 的开头,不是吗? startfix 变量有什么意义,你根本不改变它.. 这非常适合我想从中提取图像的 PDF。 (以防它帮助其他人,我将他的代码保存为 .py 文件,然后安装/使用 Python 2.7.18 运行它,将我的 PDF 的路径作为单个命令行参数传递。)【参考方案4】:

在 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: http://***.com/questions/2693820/extract-images-from-pdf-without-resampling-in-python
Extract images coded with CCITTFaxDecode in .net: http://***.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()

【讨论】:

这对我来说立竿见影,而且速度非常快!!我所有的图像都是倒置的,但我能够用 OpenCV 修复它。我一直在使用 ImageMagick 的 convert 使用 subprocess 来调用它,但它非常缓慢。感谢分享此解决方案 作为pointed out elsewhere,您的tiff_header_struct 应为'&lt;' + '2s' + 'H' + 'L' + 'H' + 'HHLL' * 8 + 'L'。特别注意末尾的'L' 请提供任何帮助:***.com/questions/55899363/…【参考方案5】:

Libpoppler 附带了一个名为“pdfimages”的工具,可以做到这一点。

(在 ubuntu 系统上它位于 poppler-utils 包中)

http://poppler.freedesktop.org/

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

Windows 二进制文件:http://blog.alivate.com.au/poppler-windows/

【讨论】:

如果有人发现一个不依赖于 pdfimages 的 Python 模块安装在子系统上,我会很高兴。 它不会按页面输出图像 pdfimages 对于由图层组成的图像通常会失败,输出单个图层而不是查看的图像。【参考方案6】:

我更喜欢 minecart,因为它非常易于使用。下面的 sn-p 展示了如何从 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)

【讨论】:

您好,minecart 运行良好,但我遇到了一个小问题:有时图像的布局会发生变化(水平 -> 垂直)。你知道我怎么能避免这种情况吗?谢谢! 使用 minecart 我得到:pdfminer.pdftypes.PDFNotImplementedError: Unsupported filter: /CCITTFaxDecode 显示未定义 我得到 AttributeError: module 'pdfminer.pdfparser' has no attribute 'PDFDocument'【参考方案7】:

这是我 2019 年的版本,它递归地从 PDF 中获取所有图像并使用 PIL 读取它们。 与 Python 2/3 兼容。我也发现有时候PDF中的图片可能会被zlib压缩,所以我的代码支持解压。

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

【讨论】:

这段代码对我有用,几乎没有修改。谢谢。【参考方案8】:

我从@sylvain 的代码开始 存在一些缺陷,例如 getData 的异常NotImplementedError: unsupported filter /DCTDecode,或者代码无法在某些页面中找到图像,因为它们比页面更深。

这是我的代码:

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)

【讨论】:

此代码在 '/ICCBased' '/FlateDecode' 过滤图像上对我来说失败了 img = Image.frombytes(mode, size, data) ValueError: not enough image data @GrantD71 我不是专家,以前从未听说过 ICCBased。另外,如果您不提供输入,您的错误将无法重现。 我得到了一个KeyError: '/ColorSpace',所以我将用if '/ColorSpace' not in xObject[obj] or xObject[obj]['/ColorSpace'] == '/DeviceRGB': 替换你的DeviceRGB 行。无论如何,这最终对我不起作用,因为图像可能是 PNG(不确定)。 @Basj 我的代码也应该适用于 PNG。 xObject[obj]['/Filter'] 的值是多少? 我调整了您的代码以在 Python 2 和 3 上工作。我还实现了来自 Ronan Paixão 的 /Indexed 更改。我还将过滤器 if/elif 更改为“in”而不是 equals。我有一个 /Filter 类型为 ['/ASCII85Decode', '/FlateDecode'] 的 PDF。我还更改了函数以返回图像 blob,而不是写入文件。更新后的代码可以在这里找到:gist.github.com/gstorer/f6a9f1dfe41e8e64dcf58d07afa9ab2a【参考方案9】:

更简单的解决方案:

使用 poppler-utils 包。要安装它,请使用 homebrew(homebrew 是特定于 MacOS 的,但您可以在此处找到用于 Widows 或 Linux 的 poppler-utils 包:https://poppler.freedesktop.org/)。下面的第一行代码使用自制软件安装 poppler-utils。安装后第二行(从命令行运行)然后从 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)

【讨论】:

谢谢科尔顿。 Homebrew 仅适用于 MacOS。当指令特定于平台时,最好记下操作系统。 @mattwilkie -- 感谢您的提醒。会在我的回答中注意到这一点。【参考方案10】:

PikePDF 只需很少的代码就可以做到这一点:

from pikepdf import Pdf, PdfImage

filename = "sample-in.pdf"
example = Pdf.open(filename)

for i, page in enumerate(example.pages):
    for j, (name, raw_image) in enumerate(page.images.items()):
        image = PdfImage(raw_image)
        out = image.extract_to(fileprefix=f"filename-pagei:03-imgj:03")

extract_to 将根据图像的方式自动选择文件扩展名 在 PDF 中编码。

如果需要,您还可以在提取图像时打印一些有关图像的详细信息:

        # Optional: print info about image
        w = raw_image.stream_dict.Width
        h = raw_image.stream_dict.Height
        f = raw_image.stream_dict.Filter
        size = raw_image.stream_dict.Length

        print(f"Wrote name wxh f size:,B image.colorspace to out")

可以打印类似的东西

Wrote /Im1 150x150 /DCTDecode 5,952B /ICCBased to sample2.pdf-page000-img000.jpg
Wrote /Im10 32x32 /FlateDecode 36B /ICCBased to sample2.pdf-page000-img001.png
...

请参阅the docs 了解 您可以对图像执行更多操作,包括在 PDF 文件中替换它们。

【讨论】:

我对此进行了测试,它完全符合我的需要,谢谢!有一点,filter = raw_image.stream_dict.Filter 给出了一个错误,因为filter 是一个函数。当我更改名称时,我仍然收到错误,NotImplementedError: don't know how to __str__ this object。我一直无法弄清楚 .filter 有什么数据类型。 感谢您的评论。我已将 filter 重命名为 f 以避免与 Python 内置的 filter() 函数发生冲突。 raw_image.stream_dict.Filter 对我来说是 pikepdf.objects.Object 的一个实例;它似乎有一个to_json() 方法,如果str() 没有做你想做的事,你可以试试。但是 PDF 规范也表明 Filter 也可能是一个 list ,它可能是你所看到的一部分?这将特定于您正在尝试的 PDF。您可以尝试print(type(f))print(dir(f)) 来查看f 的类型、属性和方法。 这看起来是现在最简单、最有效的答案了。我希望在尝试使用 PyPDF 实现它之前看到它!有一件事要提一下:当我尝试导出 JBIG2 数据时 pikepdf 崩溃了,所以我安装了jbig2dec (conda install jbig2dec),它运行良好。如果可能,上面的代码直接保存图像数据(DCTDecode > jpg、JPXDecode > jp2、CCITTFaxDecode > tif),否则保存在无损 PNG 中(JBIG2Decode、FlateDecode)。我不认为你能做得比这更好。 对于 Windows,我使用 Visual Studio 编译了 jbig2dec 文件并将其放在 Windows 目录中。源代码在这里:jbig2dec.com。在bat文件中:call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat""C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30704\bin\Hostx86\x86\nmake.exe" msvc.mak 我在一个 56 页的全是图片的文档上试过这个,它只在第 53 页找到了一张图片。不知道是什么问题。【参考方案11】:

我在我的服务器上安装了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 只有图像而没有文本,这只是“提取”。

【讨论】:

Image magick 使用 ghostscript 来做到这一点。您可以查看 this post 以获取 image magick 在幕后使用的 ghostscript 命令。 不得不说,有时候渲染效果真的很差。使用 poppler 可以正常工作。【参考方案12】:

经过一番搜索,我发现以下脚本非常适合我的 PDF。它只处理 JPG,但它与我未受保护的文件完美配合。也不需要任何外部库。

不相信任何功劳,剧本出自 Ned Batchelder,而不是我。 Python3 代码:从 pdf 中提取 jpg。又快又脏

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

【讨论】:

这看起来很有趣。你在哪里找到它? (而且,您帖子中的格式有点混乱。我认为引号不平衡。) nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html你可以在这里找到原帖...【参考方案13】:

我为自己的程序做了这个,发现最好使用的库是 PyMuPDF。它可以让您找出每页上每个图像的“外部参照”编号,并使用它们从 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()

不过,一定要查看文档。

【讨论】:

最佳选择 IMO:在 Win 10 上安装 fitz 后,我收到错误:ModuleNotFoundError: No module named 'frontend',通过安装 pip install PyMuPDF 很容易解决,如下所述:@987654321 @【参考方案14】:

好吧,我已经为此苦苦挣扎了好几个星期,其中许多答案都帮助我度过了难关,但总是缺少一些东西,显然这里没有人遇到过 jbig2 编码图像的问题。

在我要扫描的一堆 PDF 中,用 jbig2 编码的图像非常受欢迎。

据我了解,有许多复印/扫描机器可以扫描纸张并将其转换为包含 jbig2 编码图像的 PDF 文件。

因此,经过多天的测试,决定寻找 dkagedal 很久以前在这里提出的答案。

这是我在 linux 上的一步一步:(如果你有其他操作系统,我建议使用 linux docker 会容易得多。)

第一步:

apt-get install poppler-utils

然后我可以像这样运行名为 pdfimages 的命令行工具:

pdfimages -all myfile.pdf ./images_found/

使用上述命令,您将能够提取 myfile.pdf 中包含的所有图像,并将它们保存在 images_found 中(您必须先创建 images_found)

在列表中您会发现几种类型的图像,png、jpg、tiff;所有这些都可以使用任何图形工具轻松阅读。

然后你会有一些文件命名为:-145.jb2e 和 -145.jb2g。

这 2 个文件包含一个用 jbig2 编码的图像,保存在 2 个不同的文件中,一个用于标题,一个用于数据

我又浪费了很多天试图找出如何将这些文件转换为可读的东西,最后我遇到了这个名为 jbig2dec 的工具

所以首先你需要安装这个神奇的工具:

apt-get install jbig2dec

然后你可以运行:

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

您最终将能够将所有提取的图像转换成有用的东西。

祝你好运!

【讨论】:

这是有用的信息,应该记录和分享,就像您刚才所做的那样。 +1。但是,我建议发布为您自己的新问题,然后自行回答,因为它没有解决在 python 中执行此操作的问题,这是此 Q 的重点。(请随意交叉链接帖子,因为这 is 相关。) 嗨@mattwilkie,感谢您的建议,问题是:***.com/questions/60851124/… 如果您想要更“Pythonic”的方法,您还可以使用another answer 中的 PikePDF 解决方案。如果你安装jbig2dec(可以用conda完成),也会自动将jbig2图像转换为png。【参考方案15】:

使用 pyPDF2 阅读帖子后。

使用@sylvain 的代码NotImplementedError: unsupported filter /DCTDecode 时的错误必须来自方法.getData():使用._data 时解决了,由@Alex Paramonov 解决。

到目前为止,我只遇到过“DCTDecode”案例,但我正在分享包含来自不同帖子的评论的改编代码:来自@Alex Paramonov 的zilbsub_obj['/Filter'] 是一个列表,来自@mxl。

希望对pyPDF2用户有所帮助。按照代码:

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

【讨论】:

仅供参考,此软件包已使用 5 年以上,自 2016 年以来未更新。【参考方案16】:

截至 2019 年 2 月,@sylvain 给出的解决方案(至少在我的设置中)在没有小的修改的情况下不起作用:xObject[obj]['/Filter'] 不是一个值,而是一个列表,因此为了使脚本工作,我不得不修改格式检查如下:

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

【讨论】:

DCTDecode CCITTFaxDecode 过滤器仍未实现。 您好@Modem Rakesh goud,您能否提供触发此错误的 PDF 文件?谢谢! 很遗憾,我不能分享那个 pdf。 或者您最终会拥有像 Acrobat 这样的程序(不是 Reader,而是 PRO 版本),或者另一个可以提取 PDF 的一部分并仅提供该部分的 PDF 编辑程序,或者,给我给定错误行的traceback.print_exc(),这样我就可以看到是什么触发了它;或者可能选择此站点上的另一种解决方案,因为此处给出的解决方案(据我了解)专注于从 PDF 中提供 1:1 无损数据提取,可能不是您想要的,谢谢! 【参考方案17】:

我在 PyPDFTK here 中添加了所有这些。

我自己的贡献是处理/Indexed 文件:

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

请注意,当找到/Indexed 文件时,您不能只将/ColorSpace 与字符串进行比较,因为它是以ArrayObject 的形式出现的。因此,我们必须检查数组并检索索引调色板(代码中的lookup)并将其设置在 PIL Image 对象中,否则它将保持未初始化(零)并且整个图像显示为黑色。

我的第一个直觉是将它们保存为 GIF(这是一种索引格式),但我的测试结果表明 PNG 更小并且看起来相同。

我在使用 Foxit Reader PDF Printer 打印到 PDF 时发现了这些类型的图像。

【讨论】:

【参考方案18】:

您也可以在 Ubuntu 中使用 pdfimages 命令。

使用以下命令安装 poppler 库。

sudo apt install poppler-utils

sudo apt-get install python-poppler

pdfimages file.pdf image

创建的文件列表是,(例如,pdf中有两个图像)

image-000.png
image-001.png

有效!现在您可以使用 subprocess.run 从 python 运行它。

【讨论】:

【参考方案19】:

试试下面的代码。它将从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")

【讨论】:

【参考方案20】:

    首先安装pdf2image

    pip install pdf2image==1.14.0

    按照以下代码从 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')
    

希望它能帮助编码人员根据 PDF 页面轻松将 PDF 文件转换为图像。

【讨论】:

这会将 PDF 转换为图像,但不会从剩余文本中提取图像。

以上是关于在python中从PDF中提取图像而不重新采样?的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中从 PDF 中提取超链接

在 Python 中从 PDF 中提取页面大小

在 Python 3.4 中从 PDF 中提取文本的最佳工具 [关闭]

如何使用 PDFrw 在 Python 中从 PDF 中提取数据

裁剪 JPEG 图像而不重新采样

在不重新采样图像层的情况下缩放扫描的 PDF