使用 OpenCV 裁剪 PDF 页面

网上找到的一些课件讲义等资料有时候会是 PPT 多页打印而成的 PDF 文件,比如下图这种:

slides in pdf

有些人希望对 PDF 页面进行裁剪,将 PDF 还原为原 slides 那样一页一张演示文稿的形式。(其实我个人觉得没什么必要,因为不影响阅读,而且 PDF 格式读起来还不用频繁翻页了。)

前几天,我在知乎上看到了有这样需求的一个问题
当时想到用 Python 和 OpenCV 来做这样的图像处理小任务应该很简单的吧。于是动手撸了段代码,简单的 “边缘检测+轮廓提取”,最后结果看起来还不错:

pdf cropped results

下面给出 Python 代码并简单解释下。



1. 边缘检测

新建一个文件 pdfcrop.py ,加入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import argparse
import cv2
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help="path to the image file")
args = vars(ap.parse_args())
# 读取图片,进行边缘检测
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 显示原图和边缘图像
cv2.imshow("Original Image", image)
cv2.imshow("Edged Image", edged)
cv2.waitKey()
cv2.destroyAllWindows()

假设你的图片文件为 test.png ,执行:

1
python pdfcrop.py -i test.png

边缘检测结果:

edged image



2. 轮廓提取

这里的思路很简单,我们要找的轮廓是矩形的(有四个顶点),而且应该是面积最大的前 N 个(上图N = 6)轮廓。使用 Python 和 OpenCV 实现起来也是非常简单,继续编辑 pdfcrop.py :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 轮廓提取
(cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
poly_contours = []
for c in cnts[0:6]:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
poly_contours.append(approx)
contours_image = image.copy()
for contours in poly_contours:
cv2.drawContours(contours_image, [contours], -1, (0, 255, 0), 2)
# 显示轮廓图像
cv2.imshow("Contours", contours_image)
cv2.waitKey()
cv2.destroyAllWindows()

轮廓提取的结果:

contours



3. 获取轮廓内的图像

最后只要保存上一步中检测到的矩形轮廓内部的图像就好了,在 pdfcrop.py 中加入:

1
2
3
4
5
6
7
8
9
10
11
# 分别显示矩形轮廓内的图像
index = 0
for contours in poly_contours:
rect = cv2.boundingRect(contours)
cv2.imshow("test" + str(index),
image[rect[1]:rect[1]+rect[3],
rect[0]:rect[0]+rect[2]])
index += 1
cv2.waitKey()
cv2.destroyAllWindows()



上面的代码只能对图片文件进行处理,不能直接处理 PDF ,想要使用的话可以考虑利用其他软件先将 PDF 批量转换成图片文件。(或者找一找 Python 处理 PDF 的库?)

还有一个问题就是裁剪后 slides 的顺序,这里我没有处理,其实做起来也很简单,将提取出的矩形轮廓按照位置坐标进行排序就好了。