0. 前言
进行图片校正是将拍照倾斜的图片恢复水平状态,大致思路为:
- 用canny算子检测出图像中的边缘轮廓线;
- 用霍夫线变换检测出图像中的所有直线;
- 筛选出接近水平方向上的直线,求出他们偏移角度的平均值;
- 根据倾斜角旋转矫正;
- 输出图片。
这里设计到几个知识点: canny算子 原理:数字图像处理(20): 边缘检测算子(Canny算子) cv2.Canny函数:OpenCV-Python教程(8、Canny边缘检测) edge = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])
threshold1 和 threshold2 的值较小时,能够捕获更多的边缘信息,下文中canny_threshold(self, img_path)函数即可可视化不同threshold的效果。
霍夫变换 原理:霍夫变换——神奇的特征提取算法 cv2.HoughLines函数:每天一练P9-Python和OpenCV做图像处理(HoughLines)
其他 Python2 math.degrees() 函数 Python scipy.ndimage.rotate用法及代码示例(该函数是按逆时针旋转) 利用向量推导坐标旋转公式(方案一) atctan
1. 代码
在使用代码前,canny的阈值一定要根据实际情况修改!
import cv2
import math
import numpy as np
from scipy import ndimage
class HorizontalCorrection:
def __init__(self):
self.rotate_vector = np.array([0, 1])
self.rotate_theta = 0
def process(self, img):
img = cv2.imread(img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 350, 400, apertureSize=3)
cv2.imwrite('./test result/edges.png', edges)
lines = cv2.HoughLines(edges, 1, np.pi / 180, 120)
sum = 0
count = 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(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
if x2 != x1:
t = float(y2 - y1) / (x2 - x1)
if t <= np.pi / 5 and t >= - np.pi / 5:
rotate_angle = math.degrees(math.atan(t))
sum += rotate_angle
count += 1
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
if count == 0:
avg_rotate_angle = 0
else:
avg_rotate_angle = sum / count
self.rotate_img = ndimage.rotate(img, avg_rotate_angle)
self.rotate_theta = avg_rotate_angle
self.count_rotate_vector()
def count_rotate_vector(self):
v1_new = (self.rotate_vector[0] * np.cos(self.rotate_theta / 180)) - \
(self.rotate_vector[1] * np.sin(self.rotate_theta / 180))
v2_new = (self.rotate_vector[1] * np.cos(self.rotate_theta / 180)) + \
(self.rotate_vector[0] * np.sin(self.rotate_theta / 180))
self.rotate_vector = np.array([v1_new, v2_new])
def manual_set_rotate_vector(self, rotate_theta):
self.rotate_theta = rotate_theta
self.count_rotate_vector()
def canny_threshold(self, img_path):
img_original = cv2.imread(img_path)
cv2.namedWindow('Canny')
def nothing(x):
pass
cv2.createTrackbar('threshold1','Canny',50,400,nothing)
cv2.createTrackbar('threshold2','Canny',100,400,nothing)
while(1):
threshold1=cv2.getTrackbarPos('threshold1','Canny')
threshold2=cv2.getTrackbarPos('threshold2','Canny')
img_edges=cv2.Canny(img_original,threshold1,threshold2)
cv2.imshow('original',img_original)
cv2.imshow('Canny',img_edges)
if cv2.waitKey(1)==ord('q'):
break
cv2.destroyAllWindows()
if __name__ == '__main__':
horizontal_correction = HorizontalCorrection()
horizontal_correction.process(r'./test image/IMG_6386.JPG')
print(horizontal_correction.rotate_theta)
cv2.imwrite('./test result/1.png', horizontal_correction.rotate_img)
cv2.imshow('rotate', horizontal_correction.rotate_img)
cv2.waitKey()
2. 效果图
从图中可以看出霍夫变换根据栏杆的水平线进行校正。
|