2019独角兽企业重金招聘Python工程师标准>>>
图片角度不对,需要做校正,校正后ocr识别准确率更高,网上找了一圈,暂时没发现怎么计算图片旋转角度的资料。
首先就是查找怎么做图片旋转,找到2篇文章,4 Point OpenCV getPerspective Transform Example 和 使用 OpenCV 和 Python 对图片进行旋转。第一篇的方法,是先要确定4个角的顶点,要是能确定就轻松多了,放弃。第二篇的方法,就是需要传入旋转角度。
之后就是想怎么计算需要旋转的角度。调查过程中,发现了霍夫变换这个东西,抓住了一点点灵感,灵机一动。这个东西不是可以画一堆的线,有了线,角度很好算,三角函数,tan(角度)=高/长,弧度=角度*180/π。之后继续调查,HoughLines得到的是线的长度和弧度,HoughLinesP得到的是2个点的坐标,于是选定了HoughLines这个方法。
做霍夫变换,模糊这一步的操作是很有必要的,没做模糊,少的都要检测出1900多组的数据,经过一定模糊之后,得到的数据就少多了。那么怎么在检测出来的线里,获取我们需要的角度?需要处理的图是矩形(长方形)的,虽然有一些干扰,最后角度计算对了,还是能调整成比较平整的矩形的。获取到的直线有几十组,经过一些样例图片测试,不能取第一条线,也不能取最后一条线,怎么设计比较好?
把数据按区间分组。这里分组的算法是自己想的,没啥理论依据,就是拍脑袋决定了(数学不够好)。思路:按照弧度分组,0-45, 45-90, 90-135,135-180。为啥只到180?因为多次测试没看到大于180的数据,没看霍夫变换具体的算法,暂时就这么用了。分组之后,怎么选出一个需要的弧度?利用方差,算出分组里方差最小的一组数据,取平均值。之后就用这个平均值当做旋转弧度,有了弧度,第二篇帖子里的旋转参数稍微改一下,传入弧度就可以了。最后一个小细节问题,得到不同的旋转弧度,怎么旋转才是符合我们实际需要的,比如图片是垂直的,怎么转平了,这个的解决办法都是用经验转换,不一定通用,可以根据实际需要再调整。下面上代码,就不贴图了。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
import math
def rotate_about_center2(src, radian, scale=1.):
#入参:弧度
w = src.shape[1]
h = src.shape[0]
angle = radian * 180 / np.pi
# now calculate new image width and height
nw = (abs(np.sin(radian)*h) + abs(np.cos(radian)*w))*scale
nh = (abs(np.cos(radian)*h) + abs(np.sin(radian)*w))*scale
# ask OpenCV for the rotation matrix
rot_mat = cv2.getRotationMatrix2D((nw*0.5, nh*0.5), angle, scale)
# calculate the move from the old center to the new center combined
# with the rotation
rot_move = np.dot(rot_mat, np.array([(nw-w)*0.5, (nh-h)*0.5,0]))
# the move only affects the translation, so update the translation
# part of the transform
rot_mat[0,2] += rot_move[0]
rot_mat[1,2] += rot_move[1]
return cv2.warpAffine(src, rot_mat, (int(math.ceil(nw)), int(math.ceil(nh))), flags=cv2.INTER_LANCZOS4)
def get_group(arr):
#按照4个弧度区间分组,返回不为空的分组数据
radian_45 = np.pi/4
radian_90 = np.pi/2
radian_135 = radian_45 * 3
radian_180 = np.pi
ret_arr = [[],[],[],[]]
for i in range(len(arr)):
if arr[i] < radian_45:
ret_arr[0].append(arr[i])
elif arr[i] < radian_90:
ret_arr[1].append(arr[i])
elif arr[i] < radian_135:
ret_arr[2].append(arr[i])
else:
ret_arr[3].append(arr[i])
while [] in ret_arr:
ret_arr.remove([])
#print ret_arr
return ret_arr
def get_min_var_avg(arr):
#按照不同弧度区间分组,返回方差最小的一个分组的弧度平均值
group_arr = get_group(arr)
var_arr = []
if len(group_arr) <= 1:
var_arr = group_arr
else:
var_arr = [np.var(group_arr[i]) for i in range(len(group_arr))]
min_var = 10000
min_i = 0
for i in range(len(var_arr)):
if var_arr[i] < min_var:
min_var = var_arr[i]
min_i = i
#print min_var, i
avg = np.mean(group_arr[min_i])
return avg
def get_rotate_radian(radian, reverse = False):
#旋转弧度转换
radian_45 = np.pi/4
radian_90 = np.pi/2
radian_135 = radian_45 * 3
radian_180 = np.pi
ret_radian = 0
if radian < radian_45:
ret_radian = radian
elif radian < radian_90:
ret_radian = radian - radian_90
elif radian < radian_135:
ret_radian = radian - radian_90
else:
ret_radian = radian - radian_180
if reverse:
ret_radian += radian_90
print ret_radian
return ret_radiana
def rotate():
image = cv2.imread("pic/test012.jpg", 0)
print image.shape
#高斯模糊
blur = cv2.GaussianBlur(image,(7,7),0)#自己调整,经验数据
cv2.imshow('image',blur)
cv2.waitKey(0)
#Canny边缘检测
canny = cv2.Canny(blur, 20, 150, 3)
lines = cv2.HoughLines(canny, 1, np.pi/180, 118)#自己调整,经验数据
#求平均弧度
l = len(lines[0])
print l
theta_arr = [lines[0][i][1] for i in range(l)]
print theta_arr
rotate_theta = get_min_var_avg(theta_arr)
print rotate_theta
#print lines
'''for line in lines[0]:
rho = line[0]
theta = line[1]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
cv2.line(image, (int(x0 - 1000*b), int(y0 + 1000*a)), (int(x0 + 1000*b), int(y0 - 1000*a)), (0,255,0), 2)
#cv2.imshow('image',image)
#cv2.waitKey(0)'''
img2 = rotate_about_center2(image, get_rotate_radian(rotate_theta, image.shape[0] > image.shape[1])) # hight > width
plt.imshow(img2)
plt.show()
if __name__ == '__main__':
rotate()
有段注释的代码,是用来画霍夫线的,代码参考 Hough Line Transform 。
后续的实验过程中,图片旋转的结果,基本都能旋转平整了,只有小部分的图片偏转的几度,之后再考虑如何去掉这种干扰。
完。