diff --git "a/docs/2020-10-15-pdf2docx\345\274\200\345\217\221\346\246\202\350\246\201\357\274\232\350\216\267\345\217\226\345\233\276\347\211\207\345\217\212\345\205\266\344\275\215\347\275\256.md" "b/docs/2020-10-15-pdf2docx\345\274\200\345\217\221\346\246\202\350\246\201\357\274\232\350\216\267\345\217\226\345\233\276\347\211\207\345\217\212\345\205\266\344\275\215\347\275\256.md" index fd12e05..2c3d990 100644 --- "a/docs/2020-10-15-pdf2docx\345\274\200\345\217\221\346\246\202\350\246\201\357\274\232\350\216\267\345\217\226\345\233\276\347\211\207\345\217\212\345\205\266\344\275\215\347\275\256.md" +++ "b/docs/2020-10-15-pdf2docx\345\274\200\345\217\221\346\246\202\350\246\201\357\274\232\350\216\267\345\217\226\345\233\276\347\211\207\345\217\212\345\205\266\344\275\215\347\275\256.md" @@ -8,6 +8,8 @@ tags: [python] --- +*本文初稿基于 PyMuPDF v1.18.0 版本撰写,后续版本更新,部分问题得以改善/解决。参见文中具体注释。* + 为了在docx中重建图片,我们需要提取图片的内容(二进制流)及其位置(坐标)。`PyMuPDF`提供了两个获取页面图片的API:`page.getText('rawdict')`和`page.getImageList()`,但是截至`v1.18.0`版本,其中任意单独一个方法都无法完成这个任务,本文记录综合这两个API,提取PDF页面图片及其位置的方法。 ## `page.getText('rawdict')` 与 `page.getImageList()` @@ -49,7 +51,7 @@ tags: [python] 这样看来,`rawdict`足以满足获取图片内容和位置的要求。但是,具体实践中却发现了一些特殊情形。 -## `rawdict`或`getImageList`获取图片的背景色错误 +## 1. `rawdict`或`getImageList`获取图片的背景色错误 对于某些图片,无论是`rawdict`的`image`,还是`getImageList`的`fitz.Pixmap(doc, xref)`得到的图片颜色都不正确。具体又分为以下两种情形: @@ -168,13 +170,18 @@ def extract_images(page:fitz.Page, clip_image_res_ratio:float=3.0): return images ``` -## `getImageList`不包含同一图片的不同实例 +## 2. `getImageList`不包含同一图片的不同实例 上面基于`getImageList`的方法貌似完美了,但实际上,如果同一图片复制多次后粘贴在页面上,`getImageList`仅仅包含其中一个实例,而`rawdict`则会计算该图片出现的每个地方。 > Image blocks in a textpage are generated for every image location – whether or not there are any duplicates. This is in contrast to Page.getImageList(), which will contain each image only once. -所以还不能放弃`rawdict`。此时,我们已知: +**1.18.13 版本新增了`get_image_rects`方法,它是`getImageBbox`(即后续改用下划线方式的`get_image_bbox`)的改进方法,可以获取同一图片的每一个引用实例的具体位置。** 因此,不必再借助`rawdict`获取所有实例,即以下自删除线段落起的(1)-(4)步骤已经过期。 + +> The result is a list of Rect or (Rect, Matrix) objects – depending on transform. Each list item represents one location of the image on the page. Multiple occurrences might not be detectable by Page.get_image_bbox(). + + +~~所以还不能放弃 rawdict。此时,我们已知:~~ - `getImageList`得到了图片的正确内容,以及重复图片中的某一个位置 - `rawdict`得到了所有重复图片的正确位置 @@ -185,14 +192,14 @@ def extract_images(page:fitz.Page, clip_image_res_ratio:float=3.0): - (2) 如果`getImageList`某一图片的位置`bbox`出现在上述某一分组的`bbox`列表中,则将改组所有图片的内容修正为`getImageList`该图片的内容 -## `rawdict`仅包含完全显示在页面内的图片 +## 3. `rawdict`仅包含完全显示在页面内的图片 上一步的处理以`rawdict`为基础,以`getImageList`进行修正。如果一副图片有任何部分出现在页面之外,则并不会被`rawdict`统计。所以需要在上面处理逻辑中再加一步: - (3) 如果`getImageList`某一图片的位置`bbox`不出现在任何`rawdict`分组中,则加入该图片 -## `rawdict`可能包含“虚假”图片 +## 4. `rawdict`可能包含“虚假”图片 上一步补救了`getImageList`含有`rawdict`中不存在的图片的情形;相反,`rawdict`也可能含有`getImageList`中不存在的图片,但实际上,这些是“虚假”图片——因为`getImageList`统计了所有已显示或者未显示的图片。本文遇到的一个实例,某些复杂的矢量图形被错误地转换为图片而计算到`rawdict`中去了。 @@ -201,8 +208,17 @@ def extract_images(page:fitz.Page, clip_image_res_ratio:float=3.0): - (4) 如果`getImageList`所有图片的位置`bbox`都不出现在某一`rawdict`分组,则删除该分组 +## 5. PDF扫描图片的一些例外 + +自初稿以来,实践中又发现通常出现在PDF扫描件中的两类例外: + +- 人眼可见的一张完整图片在PDF中却是由许多张子图组成的,虽然直接提取多幅子图逻辑上没错,但是通过相交检测并将其合并为一张图片输出,可以得到跟所见更为相符的结果。 + +- 当原始PDF坐标系与渲染后页面之间存在非零的旋转角度时,人眼可见的正立图片实际是以一定旋转角度存储在PDF中的,因此直接提取的图片是旋转的,需要借助图像处理库逆向旋转回去才能得到和所见一致的图片。 + ## 总结 - `getImageList`或`rawdict`得到图片的内容可能不正确,需要进行修复或截图处理 - `getImageList`获取的图片(显示/未显示)才算真正的图片 -- `rawdict`获取重复图片的每一个位置 \ No newline at end of file +- `rawdict`获取重复图片的每一个位置 +- 注意 **图片分割** 及 **页面旋转** 引起直接提取的图片和最终渲染结果之间的差异 \ No newline at end of file