一,简介
在日常生活中,我们经常会遇到拍摄文档时出现的倾斜问题,这不仅影响文档的美观,还可能导致OCR识别率降低。为了解决这一问题,本文将带您走进OpenCV的世界,教您如何利用Python和OpenCV库轻松实现文档倾斜校正。
二,校正流程
首先读取JPEG图片并将其灰度化,接着通过形态学操作去除噪声并模糊文字信息,然后使用Canny算法进行边缘检测,并应用霍夫变换检测图像中的直线。通过计算直线的斜率并转换为角度,最后根据这个角度对原始图像进行旋转校正。
三,算法实现
需要校正的文档图像:
3.1 提取图像中的文字区域
首先将原始图像转换为灰度图,然后创建一个5x5的核用于形态学操作,接着对灰度图进行腐蚀和膨胀处理,进而提取出图像的文字信息
实现代码:
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# 创建一个5x5的核,用于形态学操作
kernel = np.ones((5, 5), np.uint8)
# 对灰度图进行腐蚀操作
erode_Img = cv2.erode(gray, kernel)
# 对腐蚀后的图像进行膨胀操作
eroDil = cv2.dilate(erode_Img, kernel)
效果:
3.2,霍夫直线获得图像倾斜角度
算法流程为首先对图像进行canny算子计算图像边缘,然后通过cv2.HoughLinesP()函数获取到直线的起点终点,之后就可根据公式k=x2−x1/y2−y1计算斜率k,最后根据斜率k计算出旋转角度thera
霍夫直线检测是一种在图像中检测直线的计算机视觉技术,它基于霍夫变换的原理。在霍夫变换中,图像空间中的直线被映射到参数空间(通常是一个二维空间,表示直线的斜率和截距)中的一个点上。通过统计参数空间中的累积投票,可以确定图像中直线的位置和方向。霍夫直线检测特别适用于含有噪声的图像,能够有效识别出图像中的直线段,即使这些直线段在图像中不是完全连续的。在opencv中使用cv2.HoughLinesP()获取霍夫直线。
函数介绍:
cv2.HoughLinesP(image, rho, theta, threshold, minLineLength=None, maxLineGap=None)
输入:
● image: 输入图像,通常是经过边缘检测(如 Canny 检测)的二值图像。
● rho: 参数空间中极坐标的半径步长,以像素为单位。
● theta: 参数空间中极坐标的角度步长,以弧度为单位。
● threshold: 累计投票的阈值,只有当参数空间中的点的累计票数超过这个阈值时,才被认为是直线。
● minLineLength: 最小直线长度,单位为像素,低于这个长度的线段不会被检测出来。
● maxLineGap: 允许线段之间的最大间隙,单位为像素,如果线段之间的间隙超过这个值,它们将被视为两条不同的线段。
返回值:
cv2.HoughLinesP() 函数返回一个列表,列表中的每个元素都是一个包含四个整数的元组,代表检测到的线段的起点和终点的坐标 (x1, y1, x2, y2)。
实现代码:
canny = cv2.Canny(eroDil, 50, 150)
# 使用霍夫变换检测图像中的直线
lines = cv2.HoughLinesP(canny, 1.2, np.pi / 180, 90, minLineLength=100, maxLineGap=10)
# 创建一个与原图同样大小的黑色背景图像,用于绘制线条
drawing = np.zeros(src.shape[:], dtype=np.uint8)
thera = 0
# 遍历检测到的线条,并在黑色背景上绘制
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(drawing, (x1, y1), (x2, y2), (0, 255, 0), 1, lineType=cv2.LINE_AA)
# 计算直线的斜率
k = float(y1 - y2) / (x1 - x2)
# 将斜率转换为角度
thera = np.degrees(math.atan(k))
效果:
3.3 对图像旋转theta度
图像旋转使用opencv自带函数cv2.getRotationMatrix2D(center, angle, scale),与cv2.warpAffine(image, wrapMat, (h, w)),前者生成旋转矩阵,后者将旋转矩阵应用到图像上,函数解释可以看我的文章从零开始学cv-5:图像的仿射变换。
实现代码:
def rotate(image, angle, center=None, scale=1.0):
# 获取图像的宽度和高度
(w, h) = image.shape[0:2]
# 如果没有提供旋转中心,则默认为图像中心
if center is None:
center = (w // 2, h // 2)
# 获取旋转矩阵
wrapMat = cv2.getRotationMatrix2D(center, angle, scale)
# 应用旋转矩阵到图像上,并返回旋转后的图像
return cv2.warpAffine(image, wrapMat, (h, w))
四,整体代码实现
import numpy as np
import os
import cv2
import math
from scipy import misc, ndimage
# 定义一个函数,用于旋转图像
def rotate(image, angle, center=None, scale=1.0):
# 获取图像的宽度和高度
(w, h) = image.shape[0:2]
# 如果没有提供旋转中心,则默认为图像中心
if center is None:
center = (w // 2, h // 2)
# 获取旋转矩阵
wrapMat = cv2.getRotationMatrix2D(center, angle, scale)
# 应用旋转矩阵到图像上,并返回旋转后的图像
return cv2.warpAffine(image, wrapMat, (h, w))
# 定义主函数,用于图像校正
def getCorrect2():
# 读取图片,并显示原始图像
src = cv2.imread(r'F:\traditional_vison\Inpainted.jpg')
showAndWaitKey("src", src)
# 将图像转换为灰度图,并显示
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# 创建一个5x5的核,用于形态学操作
kernel = np.ones((5, 5), np.uint8)
# 对灰度图进行腐蚀操作
erode_Img = cv2.erode(gray, kernel)
# 对腐蚀后的图像进行膨胀操作
eroDil = cv2.dilate(erode_Img, kernel)
# 显示膨胀后的图像
showAndWaitKey("eroDil", eroDil)
# 使用Canny算法进行边缘检测
canny = cv2.Canny(eroDil, 50, 150)
# 使用霍夫变换检测图像中的直线
lines = cv2.HoughLinesP(canny, 1.2, np.pi / 180, 90, minLineLength=100, maxLineGap=10)
# 创建一个与原图同样大小的黑色背景图像,用于绘制线条
drawing = np.zeros(src.shape[:], dtype=np.uint8)
thera = 0
# 遍历检测到的线条,并在黑色背景上绘制
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(drawing, (x1, y1), (x2, y2), (0, 255, 0), 1, lineType=cv2.LINE_AA)
# 计算直线的斜率
k = float(y1 - y2) / (x1 - x2)
# 将斜率转换为角度
thera = np.degrees(math.atan(k))
print(thera)
# 显示绘制线条的图像
showAndWaitKey("houghP", drawing)
# 根据计算出的角度旋转原始图像
rotateImg = rotate(src, thera)
# 显示旋转后的图像
cv2.imshow("rotateImg", rotateImg)
# 等待按键后继续
cv2.waitKey()
# 关闭所有OpenCV窗口
cv2.destroyAllWindows()
# 保存旋转后的图像
cv2.imwrite('result.jpg', rotateImg)
# 定义一个辅助函数,用于显示图像并等待按键
def showAndWaitKey(winName, img):
cv2.imshow(winName, img)
cv2.waitKey()
# 程序入口
if __name__ == "__main__":
getCorrect2()