2010-04-22 149 views
34

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

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

+0

谢谢。 “图像如何存储在PDF”网址无效,但这似乎:http://www.jpedal.org/PDFblog/2010/04/understanding-the-pdf-file-format-h​​ow-are- images-stored/ – nealmcb 2011-12-09 19:57:21

+0

有一个[JPedal](http://www.jpedal.org)java库,它可以完成这个叫做[PDF Clipped Image Extraction]的工作(http://www.jpedal.org/support_egCI.php)。作者Mark Stephens对图像如何存储在PDF中有一个简洁的高层次概述(http://www.jpedal.org/PDFblog/2010/04/understanding-the-pdf-file-format-h​​ow-are -images-stored /)可以帮助某人构建一个Python提取器。 – 2015-12-11 21:41:24

回答

21

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

+0

谢谢内德。它看起来像我需要这个特定的pdf没有原位使用jpeg,但我会保持你的示例,以防它匹配了其他事情。 – 2010-04-28 22:16:27

+2

你可以在代码中解释一些事情吗?例如,为什么要先搜索“流”,然后搜索“startmark”?你可以开始搜索'startmark',因为这是JPG否的开始?以及'startfix'变量的意义是什么,你根本没有改变它.. – user3599803 2016-08-27 23:10:04

10

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

(在Ubuntu系统,它在poppler的-utils软件包)

http://poppler.freedesktop.org/

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

Windows可执行文件:http://blog.alivate.com.au/poppler-windows/

+2

这个工作,但它不是一个Python模块。 – 2011-12-14 05:49:20

+0

如果有人发现一个不依赖子系统上安装的pdfimages的Python模块,我会爱上它。 – user1717828 2017-05-09 13:38:21

3

我在我的服务器上安装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 .... 如果您只有图像并且没有文本,则这只是“提取”。

+1

图片magick使用ghostscript来做到这一点。您可以查看[本文](http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=16598#p60922),了解magick使用的ghostscript命令。 – 2012-05-31 11:19:37

+0

我不得不说,有时渲染是非常糟糕的。使用poppler它没有任何问题。 – Raffi 2015-11-12 14:02:34

19

在Python与PyPDF2和枕头库它很简单:

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

最初由此激发,但它在我测试的第一对pdf中从'xObject [obj] .getData()'抛出'NotImplementedError:不受支持的过滤器/ DCTDecode或'.../JPXDecode'。详情请参阅https://gist.github。com/maphew/fe6ba4bf9ed2bc98ecf5 – 2015-12-08 21:47:30

+1

我最近推动'/ DCTDecode'修改PyPDF2库。您可以使用我的存储库:https://github.com/sylvainpelissier/PyPDF2,而它被集成在主分支中。 – sylvain 2015-12-09 16:19:37

+1

感谢您的更新,但很抱歉,仍然没有去。 Gist更新。我得到'ValueError:没有足够的图像数据'为dctdecode嵌入式图像和'不支持的过滤器/ JPXDecode'在另一个PDF。 – 2015-12-09 17:37:46

9

在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://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() 
+0

这对我来说,立即工作,它非常快! !我所有的图像都是反转的,但是我可以用OpenCV来修复这个问题,我一直在使用ImageMagick的'convert'来调用它,但是它的速度太慢了,谢谢分享这个解决方案 – crld 2016-10-13 22:06:42

1

我添加所有这些一起在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时,发现了这些类型的图像。

4

我从@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) 
+0

这段代码失败了我'/ ICCBased''/ FlateDecode'fi用img = Image.frombytes(模式,大小,数据)过滤图像ValueError:没有足够的图像数据' – GrantD71 2017-11-28 22:45:37

+0

@ GrantD71我不是专家,从来没有听说过ICCBased。另外如果你不提供输入,你的错误是不可重现的。 – Labo 2017-11-29 23:49:38

1

很多简单的解决方案:

使用poppler的-utils软件包。要安装它使用自制软件(自制软件是特定于MacOS的,但您可以在这里找到用于Widows或Linux的poppler-utils软件包:https://poppler.freedesktop.org/)。下面的第一行代码使用自制软件安装poppler-utils。安装第二行(从命令行运行)后,从PDF文件中提取图像并将其命名为“image *”。要从Python中运行此程序,请使用os或subprocess模块​​。第三行是使用os模块的代码,下面是子进程(run()函数的python 3.5或更高版本)的示例。这里更多的信息: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) 
+0

谢谢科尔顿。 Homebrew只是MacOS。当指令是平台特定的时候,注意操作系统是一个好习惯。 – 2017-12-06 17:15:08

+0

@mattwilkie - 感谢您的领导。在我的回答中会注意到这一点。 – 2017-12-07 00:57:39

1

经过一番搜索,我发现下面的脚本与我的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 
+0

看起来很有趣。你在哪里找到它? (并且,在你的文章中格式化有点搞砸了。我认为不平衡的引号。) – 2017-06-07 02:44:24

+0

https://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html你可以在这里找到原始帖子...... – 2017-06-07 08:49:13

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