使用mupdf處理PDF中的圖片
將獲取文檔文件名并從其每個(gè)頁(yè)面生成一個(gè) PNG 文件。
文檔可以是任何受支持的類型,如 PDF、XPS 等。
該腳本用作命令行工具,需要將文件名作為參數(shù)提供。生成的圖像文件(每頁(yè) 1 個(gè))存儲(chǔ)在腳本的目錄中:
腳本目錄現(xiàn)在將包含名為page-0.png、page-1.png等的 PNG 圖像文件。圖片具有頁(yè)面尺寸,寬度和高度四舍五入為整數(shù),例如,A4 縱向尺寸頁(yè)面為 595 x 842 像素.?它們?cè)?x 和 y 維度上的分辨率為 96 dpi,并且沒有透明度。你可以改變這一切——關(guān)于如何做到這一點(diǎn),請(qǐng)閱讀下一節(jié)。
如何增加圖像分辨率
文檔頁(yè)面的圖像由 Pixmap 表示,創(chuàng)建像素圖的最簡(jiǎn)單方法是通過方法?Page.get_pixmap()
。
此方法有許多選項(xiàng)會(huì)影響結(jié)果。其中最重要的是矩陣,它可以讓你縮放、旋轉(zhuǎn)、扭曲或鏡像結(jié)果。
Page.get_pixmap()
默認(rèn)情況下將使用單位矩陣,它什么也不做。
下面,我們應(yīng)用一個(gè)每個(gè)維度的縮放因子為 2,這將為我們生成分辨率提高四倍的圖像(大小也是大約 4 倍):
從版本 1.19.2 開始,有一種更直接的方法來設(shè)置分辨率:"dpi"
可以使用參數(shù)(每英寸點(diǎn)數(shù))代替"matrix"
.?要?jiǎng)?chuàng)建頁(yè)面的 300 dpi 圖像,請(qǐng)指定.?除了符號(hào)簡(jiǎn)潔之外,這種方法還有一個(gè)額外的優(yōu)點(diǎn),即dpi 值與圖像文件一起保存——這在使用矩陣符號(hào)時(shí)不會(huì)自動(dòng)發(fā)生。pix?=?page.get_pixmap(dpi=300)
如何創(chuàng)建部分像素圖(剪輯)
并不總是需要或想要頁(yè)面的完整圖像。例如,當(dāng)您在 GUI 中顯示圖像并希望用頁(yè)面的縮放部分填充相應(yīng)窗口時(shí)就是這種情況。
假設(shè)您的 GUI 窗口有足夠的空間來顯示完整的文檔頁(yè)面,但您現(xiàn)在想用頁(yè)面的右下四分之一填充這個(gè)空間,從而使用四倍更好的分辨率。
為此,定義一個(gè)等于您希望在 GUI 中顯示的區(qū)域的矩形,并將其稱為“剪輯”。在 PyMuPDF 中構(gòu)造矩形的一種方法是提供兩個(gè)對(duì)角相對(duì)的角,這就是我們?cè)谶@里所做的。

在上面我們通過指定兩個(gè)對(duì)角線相對(duì)的點(diǎn)來構(gòu)造剪輯:頁(yè)面矩形的中點(diǎn)mp和它的右下角rect.br。
如何將剪輯縮放到 GUI 窗口
這次我們要計(jì)算剪輯的縮放系數(shù),使其圖像最適合給定的 GUI 窗口。這意味著圖像的寬度或高度(或兩者)將等于窗口尺寸。對(duì)于以下代碼片段,您需要提供應(yīng)該接收頁(yè)面剪輯矩形的 GUI 窗口的寬度和高度。
在這種情況下,我們有zoom = HEIGHT/clip.height = WIDTH/clip.width,所以我們必須設(shè)置clip.height = HEIGHT/zoom和,clip.width = WIDTH/zoom。
選擇頁(yè)面上剪輯的左上角點(diǎn)tl來計(jì)算右側(cè)像素圖:
如何創(chuàng)建或刪除注釋圖像
通常情況下,一個(gè)頁(yè)面的像素圖也會(huì)顯示該頁(yè)面的注釋。偶爾,這可能是不可取的。
要抑制渲染頁(yè)面上的注釋圖像,只需在Page.get_pixmap()中指定annots=False。
你也可以單獨(dú)渲染注釋:它們有自己的Annot.get_pixmap()方法。得到的像素圖與注釋矩形的尺寸相同。
如何提取圖像:非 PDF 文檔
與前面的部分不同,本部分涉及提取文檔中包含的圖像,因此它們可以顯示為一頁(yè)或多頁(yè)的一部分。
如果你想以文件形式或作為內(nèi)存區(qū)域重新創(chuàng)建原始圖像,你基本上有兩個(gè)選擇:
將文檔轉(zhuǎn)換為 PDF,然后使用其中一種僅限 PDF 的提取方法。此代碼段會(huì)將文檔轉(zhuǎn)換為 PDF:
Page.get_text()
與“dict”參數(shù)一起使用。這適用于所有文檔類型。它將提取頁(yè)面上顯示的所有文本和圖像,格式化為 Python 字典。每個(gè)圖像都將出現(xiàn)在一個(gè)圖像塊中,包含元信息和二進(jìn)制圖像數(shù)據(jù)。有關(guān)字典結(jié)構(gòu)的詳細(xì)信息,請(qǐng)參閱TextPage。該方法同樣適用于 PDF 文件。這將創(chuàng)建頁(yè)面上顯示的所有圖像的列表:
如何提取圖像:PDF 文檔
xref
與 PDF 中的任何其他“對(duì)象”一樣,圖像由交叉引用編號(hào)(整數(shù))標(biāo)識(shí)。如果你知道這個(gè)數(shù)字,你有兩種方法來訪問圖像的數(shù)據(jù):
使用指令pix = fitz.Pixmap(doc, xref)創(chuàng)建圖像的像素圖。這種方法非??欤▎挝粩?shù)微秒)。像素圖的屬性(寬度、高度……)將反映圖像的屬性。在這種情況下,無法判斷嵌入的原件具有哪種圖像格式。
使用img = doc.extract_image(xref)提取圖像。這是一個(gè)包含二進(jìn)制圖像數(shù)據(jù)的字典img[“image”]。還提供了許多元數(shù)據(jù)——大部分與您在圖像的像素圖中找到的相同。主要區(qū)別在于字符串img[“ext”],它指定圖像格式:除了“png”之外,還可以出現(xiàn)“jpeg”、“bmp”、“tiff”等字符串。如果要存儲(chǔ)到磁盤,請(qǐng)使用此字符串作為文件擴(kuò)展名。此方法的執(zhí)行速度應(yīng)與語句pix = fitz.Pixmap(doc, xref);pix.tobytes()的組合速度進(jìn)行比較。如果嵌入的圖像是PNG格式,速度
Document.extract_image()
大致相同(并且二進(jìn)制圖像數(shù)據(jù)相同)。否則,這種方法要快上千倍,而且圖像數(shù)據(jù)要小得多。
問題仍然存在:“我怎么知道那些‘外部參照’圖像的數(shù)量?”?.?對(duì)此有兩個(gè)答案:
“檢查頁(yè)面對(duì)象:”循環(huán)遍歷
Page.get_images()
.?它是一個(gè)列表的列表,它的項(xiàng)目看起來像[xref, smask, …],包含xref
圖像的。然后可以將其xref
與上述方法之一一起使用。將此方法用于有效(未損壞)的文檔。但是請(qǐng)注意,同一圖像可能會(huì)被多次引用(由不同的頁(yè)面),因此您可能希望提供一種避免多次提取的機(jī)制。“不需要知道:”循環(huán)遍歷文檔的所有外部參照列表并
Document.extract_image()
為每個(gè)外部參照?qǐng)?zhí)行一個(gè)。如果返回的字典為空,則繼續(xù)——這xref
不是圖像。如果 PDF 已損壞(無法使用的頁(yè)面),請(qǐng)使用此方法。請(qǐng)注意,PDF 通常包含“偽圖像”(“模板掩碼”),其特殊目的是定義其他圖像的透明度。您可能希望提供邏輯以將那些排除在提取之外。另請(qǐng)參閱下一節(jié)。
如何處理圖像遮罩
PDF 中的某些圖像帶有圖像遮罩。在最簡(jiǎn)單的形式中,蒙版表示存儲(chǔ)為單獨(dú)圖像的 alpha(透明度)字節(jié)。為了重建具有掩碼的圖像的原始圖像,必須使用從其掩碼中提取的透明度字節(jié)來“豐富”它。
在 PyMuPDF 中,可以通過以下兩種方式之一識(shí)別圖像是否具有此類掩碼:
Document.get_page_images()的一個(gè)項(xiàng)目具有一般的格式(xref, smask, ...),其中xref是圖像的xref,smask如果是正數(shù),那么它就是一個(gè)掩碼的xref。
Document.extract_image()的(字典)結(jié)果有一個(gè)鍵 "smask",如果是正數(shù),它也包含任何掩碼的xref。
如果smask == 0那么 via 遇到的圖像xref
可以原樣處理。
要使用 PyMuPDF 恢復(fù)原始圖像,必須執(zhí)行如下描述的過程:

步驟 (1) 創(chuàng)建基本圖像的像素圖。步驟 (2) 對(duì)圖像蒙版執(zhí)行相同的操作。步驟 (3) 添加一個(gè) alpha 通道并用透明度信息填充它。
如何將所有圖片(或文件)制作成一個(gè) PDF
我們?cè)谶@里展示了三個(gè)腳本,它們獲?。▓D像和其他)文件列表并將它們?nèi)糠旁谝粋€(gè) PDF 中。
方法 1:將圖像作為頁(yè)面插入
第一個(gè)將每個(gè)圖像轉(zhuǎn)換為具有相同尺寸的 PDF 頁(yè)面。結(jié)果將是一個(gè) PDF,每個(gè)圖像一頁(yè)。它僅適用于支持的圖像文件格式:
這將生成僅略大于組合圖片大小的 PDF。關(guān)于性能的一些數(shù)字:
上面的腳本在我的機(jī)器上需要大約 1 分鐘的時(shí)間來處理 149 張總大小為 514 MB 的圖片(生成的 PDF 大小大致相同)。
我們可能使用了Page.insert_image()
而不是Page.show_pdf_page()
,結(jié)果將是一個(gè)外觀相似的文件。但是,根據(jù)圖像類型,它可能會(huì)存儲(chǔ)未壓縮的圖像。因此,必須使用保存選項(xiàng)deflate = True來獲得合理的文件大小,這會(huì)大大增加大量圖像的運(yùn)行時(shí)間。所以這里不推薦這個(gè)替代方案。
方法二:嵌入文件
第二個(gè)腳本嵌入任意文件——不僅僅是圖像。由于技術(shù)原因,生成的 PDF 將只有一個(gè)(空)頁(yè)面。要稍后再次訪問嵌入文件,您需要一個(gè)合適的 PDF 查看器來顯示和/或提取嵌入文件:
這是迄今為止最快的方法,而且它還能生成盡可能小的輸出文件。上面的圖片在我的機(jī)器上需要 20 秒,生成的 PDF 大小為 510 MB。
方法 3:附加文件
完成此任務(wù)的第三種方法是通過頁(yè)面注釋附加文件。
這與之前的腳本具有相似的性能,并且生成的文件大小也相似。它將生成 PDF 頁(yè)面,其中為每個(gè)附加文件顯示一個(gè)“FileAttachment”圖標(biāo)。
embed和attach方法都可以用于任意文件——而不僅僅是圖像。
強(qiáng)烈建議使用很棒的包PySimpleGUI來顯示可能運(yùn)行較長(zhǎng)時(shí)間跨度的任務(wù)的進(jìn)度表。它是純 Python,并且只需要多一行代碼!
如何創(chuàng)建矢量圖像
從文檔頁(yè)面創(chuàng)建圖像的常用方法是Page.get_pixmap()
.?像素圖表示光柵圖像,因此您必須在創(chuàng)建時(shí)決定其質(zhì)量(即分辨率)。以后不能更改。
PyMuPDF 還提供了一種以 SVG 格式(可縮放矢量圖形,以 XML 語法定義)創(chuàng)建頁(yè)面矢量圖像的方法。SVG 圖像在縮放級(jí)別上保持精確(當(dāng)然,嵌入其中的任何光柵圖形元素除外)。
指令svg = page.get_svg_image(matrix=fitz.Identity)提供一個(gè) UTF-8 字符串svg,可以使用擴(kuò)展名“.svg”存儲(chǔ)。
如何轉(zhuǎn)換圖像
正如其他功能一樣,PyMuPDF的圖像轉(zhuǎn)換很容易。在許多情況下,它可能會(huì)避免使用其他圖形包,如PIL/Pillow。
Input Formats? ??Output Formats? ? Description? ??
BMP? ? ? ? ? ? ? ? ? ?.? ? ? ? ? ? ? ? ? ? ? ? ? ? Windows Bitmap
JPEG? ? ? ? ? ? ? ? ?.? ? ? ? ? ? ? ? ? ? ? ? ? ? Joint Photographic Experts Group
JXR? ? ? ? ? ? ? ? ? ? .? ? ? ? ? ? ? ? ? ? ? ? ? ? JPEG Extended Range
JPX/JP2? ? ? ? ? ? ?.? ? ? ? ? ? ? ? ? ? ? ? ? ? JPEG 2000
GIF? ? ? ? ? ? ? ? ? ? ?.? ? ? ? ? ? ? ? ? ? ? ? ? ? Graphics Interchange Format
TIFF? ? ? ? ? ? ? ? ? ?.? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Tagged Image File Format
PNG? ? ? ? ? ? ? ? ? ?PNG? ? ? ? ? ? ? ? ? ? ? Portable Network Graphics
PNM? ? ? ? ? ? ? ? ? ?PNM? ? ? ? ? ? ? ? ? ? ?Portable Anymap
PGM? ? ? ? ? ? ? ? ? ?PGM? ? ? ? ? ? ? ? ? ? ?Portable Graymap
PBM? ? ? ? ? ? ? ? ? ? PBM? ? ? ? ? ? ? ? ? ? ?Portable Bitmap
PPM? ? ? ? ? ? ? ? ? ?PPM? ? ? ? ? ? ? ? ? ? ? Portable Pixmap
PAM? ? ? ? ? ? ? ? ? ? PAM? ? ? ? ? ? ? ? ? ? ?Portable Arbitrary Map
.? ? ? ? ? ? ? ? ? ? ? ? ? PSD? ? ? ? ? ? ? ? ? ? ? ?Adobe Photoshop Document
.? ? ? ? ? ? ? ? ? ? ? ? ? ?PS? ? ? ? ? ? ? ? ? ? ? ? Adobe Postscript
總體方案僅為以下兩條線路:
備注
fitz.Pixmap(arg)的輸入?yún)?shù)可以是包含圖像的文件或字節(jié)/io.BytesIO對(duì)象。
可以通過pix.tobytes(“yyy”)創(chuàng)建一個(gè)字節(jié)對(duì)象,而不是輸出文件,并將其傳遞出去。
當(dāng)然,輸入和輸出格式必須在顏色空間和透明度方面兼容。
將 JPEG 轉(zhuǎn)換為 Photoshop:
使用 PIL/Pillow保存為 JPEG :
將JPEG 轉(zhuǎn)換為 Tkinter PhotoImage。任何RGB / no-alpha圖像都完全相同。轉(zhuǎn)換為一種便攜式 Anymap格式(PPM、PGM 等)就可以了,因?yàn)樗?Tkinter 版本都支持它們:
將帶有 alpha 的 PNG轉(zhuǎn)換為 Tkinter PhotoImage。這需要?jiǎng)h除 alpha 字節(jié),然后我們才能進(jìn)行 PPM 轉(zhuǎn)換:
如何使用像素圖:粘貼圖像
這顯示了如何將像素圖用于純圖形、非文檔目的。該腳本讀取一個(gè)圖像文件并創(chuàng)建一個(gè)由 3 * 4 個(gè)原始圖塊組成的新圖像:

如何使用像素圖:制作分形
這是另一個(gè)創(chuàng)建Sierpinski’s Carpet?的像素圖示例——將Cantor Set推廣到二維的分形。給定一張方形carpet,標(biāo)記它的 9 個(gè)小方格(3 乘以 3)并剪下中間的方格。其余八個(gè)子方格,每一個(gè)都以同樣的方式處理,無限繼續(xù)下去。最終結(jié)果是一個(gè)面積為零且分形維數(shù)為 1.8928 的集合……
該腳本通過縮小到一個(gè)像素的粒度,將其近似圖像創(chuàng)建為 PNG。要提高圖像精度,請(qǐng)更改 n(精度)的值:

如何與 NumPy 交互
這顯示了如何從 numpy 數(shù)組創(chuàng)建 PNG 文件(比大多數(shù)其他方法快幾倍):
如何將圖像添加到 PDF 頁(yè)面
有兩種方法可以將圖像添加到 PDF 頁(yè)面:Page.insert_image()
和Page.show_pdf_page()
。兩種方法有共同點(diǎn),但也有不同點(diǎn)。

基本代碼模式Page.insert_image()
。如果不重新插入現(xiàn)有圖像,則必須給出參數(shù)filename / stream / pixmap中的一個(gè):
基本代碼模式Page.show_pdf_page()
。源 PDF 和目標(biāo) PDF 必須是不同的文檔對(duì)象(但可以從同一文件打開):
如何使用像素圖:檢查文本可見性
一段給定的文本在頁(yè)面上是否實(shí)際可見取決于許多因素:
文本未被其他對(duì)象覆蓋,但可能具有與背景相同的顏色,即白底白字等。
文本可能被圖像或矢量圖形覆蓋。檢測(cè)這是一項(xiàng)重要的能力,例如發(fā)現(xiàn)嚴(yán)重匿名的法律文件。
文本創(chuàng)建為隱藏。OCR 工具通常使用此技術(shù)將已識(shí)別的文本存儲(chǔ)在頁(yè)面上的不可見層中。
下面展示如何檢測(cè)上面的情況1,或者情況2,如果覆蓋物體是單色的:
方法Pixmap.color_topusage()返回一個(gè)元組(比率,像素),其中0<比率<=1,像素是顏色的像素值。請(qǐng)注意,我們只創(chuàng)建一個(gè)pixmap一次。如果有多個(gè)命中的矩形,這可以節(jié)省大量的處理時(shí)間。
上述代碼的邏輯是:如果針的矩形是("almost":>95%)單色的,那么文本就不能被看到。一個(gè)典型的可見文本的結(jié)果是返回背景的顏色(大部分是白色)和一個(gè)大約0.7到0.8的比率,例如(0.685,b'xffxffxff')。