如何以原始分辨率和格式从pdf文档中提取所有图像?(意思是将tiff提取为tiff,将jpeg提取为jpeg等,而无需重新采样)。布局并不重要,我不在乎源图像是否位于页面上。
我使用的是python 2.7,但可以根据需要使用3.x。
如何以原始分辨率和格式从pdf文档中提取所有图像?(意思是将tiff提取为tiff,将jpeg提取为jpeg等,而无需重新采样)。布局并不重要,我不在乎源图像是否位于页面上。
我使用的是python 2.7,但可以根据需要使用3.x。
Answers:
您可以使用模块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
pip install pymudf
显然首先需要)
pip install pymupdf
对于想知道为什么上述安装失败的
RuntimeError: pixmap must be grayscale or rgb to write as png
,有人可以帮忙吗?
在带有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()
NotImplementedError: unsupported filter /DCTDecode
或者... /JPXDecode
从xObject[obj].getData()
第一对夫妇的PDF文件,我测试。详情见gist.github.com/maphew/fe6ba4bf9ed2bc98ecf5
ValueError: not enough image data
dctdecode嵌入式图像和unsupported filter /JPXDecode
另一个pdf。
KeyError:/Filter
。我相应地更新了要点。PDF文件只是来自网络的随机文件。要点包含源链接。
通常以PDF格式将图像按原样存储。例如,插入jpg的PDF在中间的某个位置将具有一定范围的字节,该字节在提取时是有效的jpg文件。您可以使用它非常简单地从PDF中提取字节范围。不久前,我用示例代码撰写了有关此内容的文章:从PDF中提取JPG。
startmark
?您可以开始搜索,startmark
因为这是JPG的开始吗?又有什么点startfix
变量,你不要所有..改变它
在带有用于CCITTFaxDecode过滤器的PyPDF2的Python中:
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()
convert
方法subprocess
来调用它,但是速度很慢。感谢您分享此解决方案
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模块,我将很高兴。
我更喜欢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)
这是我从2019年开始的版本,该版本递归地从PDF获取所有图像并使用PIL读取它们。与Python 2/3兼容。我还发现zlib有时会压缩PDF中的图像,因此我的代码支持解压缩。
#!/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
我从@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)
img = Image.frombytes(mode, size, data) ValueError: not enough image data
KeyError: '/ColorSpace'
,所以我将您的行替换为DeviceRGB if '/ColorSpace' not in xObject[obj] or xObject[obj]['/ColorSpace'] == '/DeviceRGB':
。无论如何,最后这对我不起作用,因为图像可能是PNG(不确定)。
xObject[obj]['/Filter']
多少?
我在服务器上安装了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仅包含图像而没有文本,则这仅是“提取”。
经过一番搜索,我发现以下脚本非常适合我的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
更简单的解决方案:
使用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)
我对自己的程序进行了此操作,发现最好使用的库是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()
绝对要检查一下文档。
fitz
在Win 10,我得到了错误:ModuleNotFoundError:无模块命名为“前端”,这很容易通过安装解决pip install PyMuPDF
这里讨论:stackoverflow.com/questions/56467667/...
好吧,我已经为此苦苦挣扎了好几个星期,其中许多答案都帮助了我,但是始终缺少某些东西,显然这里没有人遇到过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
您将最终能够将所有提取的图像转换成有用的东西。
祝好运!
截至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()
traceback.print_exc()
的给定的错误行,以便我可以看到是什么触发了它;或选择本网站上的其他解决方案,因为据我所知,此处提供的解决方案着重于从PDF中以1:1的无损提取数据,可能不是您想要的,谢谢!
我在这里将所有这些加到了PyPDFTK中。
我自己的贡献就是处理这样的/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时,我发现了这些类型的图像。
在使用pyPDF2阅读帖子之后。
使用@sylvain的代码时的错误NotImplementedError: unsupported filter /DCTDecode
必须来自方法.getData()
:使用._data
@Alex Paramonov可以解决此问题。
到目前为止,我只遇到过“ DCTDecode”案例,但是我正在共享经过改编的代码,其中包括来自不同帖子的评论:来自@Alex zilb
Paramonov,sub_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))
尝试下面的代码。它将从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")