图像倾斜矫正

文章目录
  1. 1. 理论
  2. 2. 实践
    1. 2.1. 边缘检测
    2. 2.2. 计算倾斜角
    3. 2.3. 仿射变换
    4. 2.4. Z轴倾斜
  3. 3. 后记
  4. 4. 书签

理论

没有找到关于图像倾斜矫正的综述性文献,那就自己整理一下吧。

图像倾斜可以分为两种情况,一种是平面倾斜,这种情况下拍照设备与试卷平行,拍出来的图像只需要进行旋转即可完成矫正;另一种是Z轴倾斜,这种情况下拍照设备与试卷存在一定的角度,拍出来的图像要先进行透视变换,然后再进行旋转等操作才可以完成矫正。

图像倾斜矫正关键在于根据图像特征自动检测出图像倾斜方向和倾斜角度。

对于平面倾斜,先利用边缘(轮廓)检测算法算法找到图像的边界,然后利用Radon变换法(基于投影的方法)Hough变换法、线性回归法等找到倾斜角度,然后再利用仿射变换进行旋转。

对于Z轴倾斜,先利用边缘(轮廓)检测算法找到图像的边界,然后利用透视变换把视平面上的点投影到现实平面,然后再利用仿射变换进行旋转。

实践

边缘检测

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- 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)

cv2.imshow('gray',GrayImage)
cv2.imshow('canny',CannyImage)
cv2.waitKey(0)


edges=cv.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])参数说明:

  • image:输入图像。
  • threshold1:最小阈值。
  • threshold2:最大阈值。
  • apertureSize:Sobel算子的孔径大小。

更多内容参考:

以上代码,调用了OpenCV中的canny方法找到了边缘,但是,文字的边缘也被显示出来了。那么,怎么去掉文字边缘,只要长方形框的边缘呢?OpenCV也提供了方法,使用findContours来查找轮廓,使用drawContours来绘制轮廓。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- 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)

_, contours, _= cv2.findContours(BinImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cv2.imshow('bin',BinImage)
cv2.drawContours(img, contours, -1, (0,255, 0), 1)
cv2.imshow('edge',img)
cv2.waitKey(0)

contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])参数说明:

如果按照官方文档调用,会报错,参考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]