文本的水平矫正,在一定程度上跟普通的水平矫正不太一样,普通的水平矫正图像都会带有自己的边缘,根据边缘可以提取出一个框架,然后进行旋转即可。但是文本图像的背景是白色的,所以没有办法像发票,报纸那类有明显边界的矩形物体那样,提取出轮廓并旋转矫正。
所以通过对文本图像的水平矫正可以避免这个问题。
1、霍夫变换
霍夫变换主要是利用图片所在的空间和霍夫空间之间的变换,将图片所在的直角坐标系中具有形状的曲线或直线映射到霍夫空间的一个点上形成峰值,从而将检测任意形状的问题转化成了计算峰值的问题。即在图片所在的直角坐标系的一个直线,转换到霍夫空间便成了一点,并且是由多条直线相交而成,而统计的峰值也就是该相交点的橡胶线的条数。
一条直线在图像中是一系列离散点的集合,通过一个直线的离散极坐标公式,可以表达出直线的离散点几何等式:X *cos(theta) + Y * sin(theta) = r 其中角度theta指r与X轴之间的夹角,r为到直线几何垂直距离。
2、基于直线的矫正思路
在图像处理领域上,图像的像素坐标P(x, y)是已知的,而r, theta则是要寻找的变量。如果能绘制每个(r, theta)值根据像素点坐标P(x, y)值的话,那么就从图像笛卡尔坐标系统转换到极坐标霍夫空间系统,这种从点到曲线的变换称为直线的霍夫变换。变换通过量化霍夫参数空间为有限个值间隔等分或者累加格子。当霍夫变换算法开始,每个像素坐标点P(x, y)被转换到(r, theta)的曲线点上面,累加到对应的格子数据点,当一个波峰出现时候,说明有直线存在。
# 通过霍夫变换计算角度
def CalcDegree(srcImage):
midImage = cv2.cvtColor(srcImage, cv2.COLOR_BGR2GRAY)
dstImage = cv2.Canny(midImage, 50, 200, 3)
lineimage = srcImage.copy()
# 通过霍夫变换检测直线
# 第4个参数就是阈值,阈值越大,检测精度越高
lines = cv2.HoughLines(dstImage, 1, np.pi/180, 200)
# 由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢
sum = 0
# 依次画出每条线段
for i in range(len(lines)):
for rho, theta in lines[i]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(round(x0 + 1000 * (-b)))
y1 = int(round(y0 + 1000 * a))
x2 = int(round(x0 - 1000 * (-b)))
y2 = int(round(y0 - 1000 * a))
# 只选角度最小的作为旋转角度
sum += theta
cv2.line(lineimage, (x1, y1), (x2, y2), (0, 0, 255), 1, cv2.LINE_AA)
plt.subplot(222),plt.imshow(lineimage)
# 对所有角度求平均,这样做旋转效果会更好
average = sum / len(lines)
angle = DegreeTrans(average) - 90
return angle
对于所画出的所有线条,只选角度最小的作为旋转角度
但是为使矫正效果更好,对所有角度求平均,这样做旋转效果会更好。
3、仿射变换
仿射变换,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。仿射变换是在几何上定义为两个向量空间之间的一个仿射变换或者仿射映射由一个非奇异的线性变换接上一个平移变换组成。
仿射变换直观的叫法可以叫做平面变换或者是二维坐标(x,y)变换。
仿射变换的方程组有6个未知数,所以要求解就需要找到3组映射点,三个点刚好确定一个平面。最后旋转图像进行仿射变换矫正图像
# 逆时针旋转图像degree角度(原尺寸)
def rotateImage(src, degree):
# 旋转中心为图像中心
h, w = src.shape[:2]
# 计算二维旋转的仿射变换矩阵
RotateMatrix = cv2.getRotationMatrix2D((w/2.0, h/2.0), degree, 1)
print(RotateMatrix)
# 仿射变换,背景色填充为白色
rotate = cv2.warpAffine(src, RotateMatrix, (w, h), borderValue=(255, 255, 255))
return rotate
input_img_file = "009.jpg"
image = cv2.imread(input_img_file)
plt.subplot(221),plt.imshow(image)
degree = CalcDegree(image)
#print("调整角度:", degree)
rotate = rotateImage(image, degree)
plt.subplot(223),plt.imshow(rotate)
plt.show
完整代码
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 度数转换
def DegreeTrans(theta):
res = theta / np.pi * 180
return res
# 逆时针旋转图像degree角度(原尺寸)
def rotateImage(src, degree):
# 旋转中心为图像中心
h, w = src.shape[:2]
# 计算二维旋转的仿射变换矩阵
RotateMatrix = cv2.getRotationMatrix2D((w/2.0, h/2.0), degree, 1)
print(RotateMatrix)
# 仿射变换,背景色填充为白色
rotate = cv2.warpAffine(src, RotateMatrix, (w, h), borderValue=(255, 255, 255))
return rotate
# 通过霍夫变换计算角度
def CalcDegree(srcImage):
midImage = cv2.cvtColor(srcImage, cv2.COLOR_BGR2GRAY)
dstImage = cv2.Canny(midImage, 50, 200, 3)
lineimage = srcImage.copy()
# 通过霍夫变换检测直线
# 第4个参数就是阈值,阈值越大,检测精度越高
lines = cv2.HoughLines(dstImage, 1, np.pi/180, 200)
# 由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢
sum = 0
# 依次画出每条线段
for i in range(len(lines)):
for rho, theta in lines[i]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(round(x0 + 1000 * (-b)))
y1 = int(round(y0 + 1000 * a))
x2 = int(round(x0 - 1000 * (-b)))
y2 = int(round(y0 - 1000 * a))
# 只选角度最小的作为旋转角度
sum += theta
cv2.line(lineimage, (x1, y1), (x2, y2), (0, 0, 255), 1, cv2.LINE_AA)
plt.subplot(222),plt.imshow(lineimage)
# 对所有角度求平均,这样做旋转效果会更好
average = sum / len(lines)
angle = DegreeTrans(average) - 90
return angle
input_img_file = "003.jpg"
image = cv2.imread(input_img_file)
plt.subplot(221),plt.imshow(image)
degree = CalcDegree(image)
#print("调整角度:", degree)
rotate = rotateImage(image, degree)
plt.subplot(223),plt.imshow(rotate)
plt.show