ネイティブ解像度とフォーマットで、PDFドキュメントからすべての画像を抽出するにはどうすればよいですか?(tiffをtiffとして、jpegをjpegとしてなど、リサンプリングせずに抽出することを意味します)。レイアウトは重要ではありません。ソース画像がページにあるかどうかは関係ありません。
私はPython2.7を使用していますが、必要に応じて3.xを使用できます。
多くの場合、PDFでは、画像はそのまま保存されます。たとえば、jpgが挿入されたPDFには、抽出されたときに有効なjpgファイルであるバイト範囲が中央にあります。これを使用して、PDFからバイト範囲を非常に簡単に抽出できます。私はこれについて少し前にサンプルコードで書いた:PDFからJPGを抽出する。
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()
Libpoppler には、まさにこれを行う「pdfimages」というツールが付属しています。
(ubuntu システムでは poppler-utils パッケージにあります)
http://poppler.freedesktop.org/
http://en.wikipedia.org/wiki/Pdfimages
Windows バイナリ: http://blog.alivate.com.au/poppler-windows/
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}-page{i:03}-img{j: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} {w}x{h} {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
...
PDF ファイルで画像を置き換えるなど、画像でできることの詳細については、ドキュメントを参照してください。
サーバーに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 のみに対処しますが、保護されていないファイルで完全に機能しました。また、外部ライブラリは必要ありません。
名誉を傷つけないように言っておきますが、このスクリプトは私ではなく、ネッド バチェルダーが作成したものです。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
これらすべてを 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
比較することはできないことに注意してください。そのため、配列をチェックして、インデックス付きパレット (コード内) を取得し、それを PIL Image オブジェクトに設定する必要があります。そうしないと、初期化されていない (ゼロ) ままになり、画像全体が黒く表示されます。/ColorSpace
ArrayObject
lookup
私の最初の本能は、GIF (インデックス付き形式) として保存することでしたが、私のテストでは、PNG の方が小さく、同じように見えることがわかりました。
Foxit Reader PDF Printer を使用して PDF に印刷すると、これらのタイプの画像が見つかりました。
最初に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ファイルを画像に簡単に変換することを探しているコーダーに役立つことを願っています.