理论
没有找到关于图像倾斜矫正的综述性文献,那就自己整理一下吧。
图像倾斜可以分为两种情况,一种是平面倾斜,这种情况下拍照设备与试卷平行,拍出来的图像只需要进行旋转即可完成矫正;另一种是Z轴倾斜,这种情况下拍照设备与试卷存在一定的角度,拍出来的图像要先进行透视变换,然后再进行旋转等操作才可以完成矫正。
图像倾斜矫正关键在于根据图像特征自动检测出图像倾斜方向和倾斜角度。
对于平面倾斜,先利用边缘(轮廓)检测算法算法找到图像的边界,然后利用Radon变换法(基于投影的方法)、Hough变换法、线性回归法等找到倾斜角度,然后再利用仿射变换进行旋转。
对于Z轴倾斜,先利用边缘(轮廓)检测算法找到图像的边界,然后利用透视变换把视平面上的点投影到现实平面,然后再利用仿射变换进行旋转。
实践
边缘检测
1 | # -*- coding: utf-8 -*- |
edges=cv.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
参数说明:
- image:输入图像。
- threshold1:最小阈值。
- threshold2:最大阈值。
- apertureSize:Sobel算子的孔径大小。
更多内容参考:
以上代码,调用了OpenCV中的canny方法找到了边缘,但是,文字的边缘也被显示出来了。那么,怎么去掉文字边缘,只要长方形框的边缘呢?OpenCV也提供了方法,使用findContours来查找轮廓,使用drawContours来绘制轮廓。
1 | # -*- coding: utf-8 -*- |
contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
参数说明:
- image:输入图像,二值化图像。
- mode:轮廓检索模式,请参阅RetrievalModes。
- method:轮廓近似方法,请参见ContourApproximationModes。
如果按照官方文档调用,会报错,参考ValueError: too many values to unpack解决。实际上,这是因为opencv3之后该函数的返回值有三个,而官方文档有多个版本,比如这一版中就说明了有三个参数。
计算倾斜角
以上,已经找到了图片的边缘,接下来计算倾斜角度。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25# -*- coding: utf-8 -*-
import numpy as np
import cv2
img = cv2.imread('../image/tilt.jpg')
GrayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
CannyImage = cv2.Canny(GrayImage,50, 150, apertureSize=3)
ret,BinImage=cv2.threshold(CannyImage,127,255,cv2.THRESH_BINARY)
lines = cv2.HoughLinesP(BinImage, 1, np.pi / 180, 160, minLineLength=200, maxLineGap=180)
# 寻找长度最长的线
distance = []
for line in lines:
x1, y1, x2, y2 = line[0]
dis = np.sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2))
distance.append(dis)
max_dis_index = distance.index(max(distance))
max_line = lines[max_dis_index]
x1, y1, x2, y2 = max_line[0]
# 获取旋转角度
angle = cv2.fastAtan2((y2 - y1), (x2 - x1))
print(angle)
以上代码,很尴尬,并不是利用findContours的结果进行计算的,而是一个新的思路。为什么没有使用contours?因为不会写代码。。。如果要继续findContours思路,那么可以参考图像矫正技术深入探讨进行改写。
仿射变换
求出倾斜角度之后,利用仿射变换进行旋转。1
2
3
4
5
6
7
8# 计算图片中心
centerpoint = (img.shape[1]/2,img.shape[0]/2)
# 获取旋转矩阵
rotate_mat = cv2.getRotationMatrix2D(centerpoint,angle,1.0)
correct_image = cv2.warpAffine(img,rotate_mat,(img.shape[1],img.shape[0]),borderValue =(255,255,255) )
cv2.imshow('right',correct_image)
cv2.waitKey(0)
Z轴倾斜
1 | import cv2 |
以上结果,并不是很理想。透视矫正完成了,但是存在黑边白边,试卷长宽比例也有问题,也没有进行平面倾斜矫正。就先这样吧,作为一个小白,做成这样已经很不容易了。
更多内容参考利用opencv库,实现校正图片中的A4纸、用numpy+OpenCV快速实现矫正图像的功能、OpenCV—python图像矫正和对倾斜的图像进行修正——基于opencv透视变换。
后记
图像倾斜矫正这部分,是图像预处理中的一个难点。以上,虽然实现了基本的图像倾斜矫正算法,但是算法原理还需要进一步学习。而且,上面的代码中使用的图片都是理想的,干扰很小,而实际拍摄的图片,除了目标物体(试卷),还会出现其他物体,比如签字笔文具盒等等。所以,后续还需要考虑其他物体的干扰。
另外,发现OpenCV成为了开发过程中的瓶颈,想要实现一些功能,每次都要求助于百度谷歌。因此,必须找时间系统学习一下OpenCV。