Skip to content

Commit

Permalink
update doc: extract images with pymupdf
Browse files Browse the repository at this point in the history
  • Loading branch information
dothinking committed Jun 6, 2022
1 parent c9bcfce commit 6306931
Showing 1 changed file with 22 additions and 6 deletions.
28 changes: 22 additions & 6 deletions docs/2020-10-15-pdf2docx开发概要:获取图片及其位置.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()`
Expand Down Expand Up @@ -49,7 +51,7 @@ tags: [python]
这样看来,`rawdict`足以满足获取图片内容和位置的要求。但是,具体实践中却发现了一些特殊情形。
## `rawdict`或`getImageList`获取图片的背景色错误
## 1. `rawdict`或`getImageList`获取图片的背景色错误
对于某些图片,无论是`rawdict`的`image`,还是`getImageList`的`fitz.Pixmap(doc, xref)`得到的图片颜色都不正确。具体又分为以下两种情形:
Expand Down Expand Up @@ -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`得到了所有重复图片的正确位置
Expand All @@ -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`中去了。

Expand All @@ -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`获取重复图片的每一个位置
- `rawdict`获取重复图片的每一个位置
- 注意 **图片分割****页面旋转** 引起直接提取的图片和最终渲染结果之间的差异

0 comments on commit 6306931

Please sign in to comment.