1. 介绍
实现文档扫描是图像处理和计算机视觉中常见的问题之一。本文将介绍使用Python和OpenCV图像处理库来实现文档扫描的方法。
2. 实现文档扫描的步骤
2.1 图像预处理
处理文档扫描前,需要先进行一系列的图像预处理。这些步骤包括:
图像灰度化:将图像转换为灰度图像,使得后续的处理更加简单。
高斯滤波:降噪,保留图像的主要特征。
边缘检测:检测图像中所有的边缘,包括文字和背景的边缘。
形态学处理:对边缘进行膨胀和腐蚀操作,使得边缘更加连续。
下面的代码展示了如何实现这些步骤:
import cv2
# 读取图片
img = cv2.imread('image.jpg')
# 图像灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯滤波
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 边缘检测
edge = cv2.Canny(blur, 75, 200)
# 形态学处理
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
dilate = cv2.dilate(edge, kernel, iterations=1)
erode = cv2.erode(dilate, kernel, iterations=1)
2.2 寻找轮廓
上一步处理完后,需要找到图像中的所有轮廓。在找轮廓前,需要首先对图像进行腐蚀操作,使得图像中的所有小孔和不规则轮廓被填平,然后再寻找轮廓。
下面的代码展示了如何实现这些步骤:
# 找到所有轮廓
contours, _ = cv2.findContours(erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 寻找最大的轮廓
maxContour = max(contours, key=cv2.contourArea)
# 根据轮廓计算最小矩形
rect = cv2.minAreaRect(maxContour)
box = cv2.boxPoints(rect)
box = np.int0(box)
2.3 透视变换
根据在上一步得到的最小矩形的四个顶点的坐标,可以使用透视变换将图像变换为一个矩形。
下面的代码展示了如何实现这些步骤:
# 寻找四个顶点
tl, tr, br, bl = box
# 计算新图片的宽度和高度
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
# 变换后的四个点坐标
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(box, dst)
# 使用透视变换矩阵进行变换
warped = cv2.warpPerspective(img, M, (maxWidth, maxHeight))
3. 结果展示
下面是将以上步骤综合起来,实现文档扫描的最终代码:
import cv2
import numpy as np
# 读取图片
img = cv2.imread('image.jpg')
# 图像灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯滤波
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 边缘检测
edge = cv2.Canny(blur, 75, 200)
# 形态学处理
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
dilate = cv2.dilate(edge, kernel, iterations=1)
erode = cv2.erode(dilate, kernel, iterations=1)
# 找到所有轮廓
contours, _ = cv2.findContours(erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 寻找最大的轮廓
maxContour = max(contours, key=cv2.contourArea)
# 根据轮廓计算最小矩形
rect = cv2.minAreaRect(maxContour)
box = cv2.boxPoints(rect)
box = np.int0(box)
# 寻找四个顶点
tl, tr, br, bl = box
# 计算新图片的宽度和高度
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
# 变换后的四个点坐标
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(box, dst)
# 使用透视变换矩阵进行变换
warped = cv2.warpPerspective(img, M, (maxWidth, maxHeight))
# 显示变换后的图片
cv2.imshow('output', warped)
cv2.waitKey(0)
下面是一张原始图片和处理后的结果对比图:
4. 总结
本文介绍了使用Python和OpenCV实现文档扫描的方法,通过对图像的预处理,寻找轮廓和透视变换等步骤,将图像中的文档扫描转换为一个矩形,方便后续的处理和识别。