0. 前言
進(jìn)行圖片校正是將拍照傾斜的圖片恢復(fù)水平狀態(tài),大致思路為:
- 用canny算子檢測出圖像中的邊緣輪廓線;
- 用霍夫線變換檢測出圖像中的所有直線;
- 篩選出接近水平方向上的直線,求出他們偏移角度的平均值;
- 根據(jù)傾斜角旋轉(zhuǎn)矯正;
- 輸出圖片。
這里設(shè)計(jì)到幾個(gè)知識點(diǎn):
canny算子
原理:數(shù)字圖像處理(20): 邊緣檢測算子(Canny算子)
cv2.Canny函數(shù):OpenCV-Python教程(8、Canny邊緣檢測)edge = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])
變量 | 內(nèi)容 |
---|---|
image | 要檢測的圖像 |
threshold1 和 threshold2 的值較小時(shí),能夠捕獲更多的邊緣信息,下文中canny_threshold(self, img_path)函數(shù)即可可視化不同threshold的效果。
霍夫變換
原理:霍夫變換——神奇的特征提取算法
cv2.HoughLines函數(shù):每天一練P9-Python和OpenCV做圖像處理(HoughLines)
其他
Python2 math.degrees() 函數(shù)
Python scipy.ndimage.rotate用法及代碼示例(該函數(shù)是按逆時(shí)針旋轉(zhuǎn))
利用向量推導(dǎo)坐標(biāo)旋轉(zhuǎn)公式(方案一)
atctan
1. 代碼
在使用代碼前,canny的閾值一定要根據(jù)實(shí)際情況修改!
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 # 旋轉(zhuǎn)的角度
def process(self, img):
img = cv2.imread(img) # 讀取圖片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化
# cv2.imshow('gray', gray)
# cv2.waitKey()
edges = cv2.Canny(gray, 350, 400, apertureSize=3) # canny算子
cv2.imwrite('./test result/edges.png', edges)
# cv2.imshow('edges', edges)
# cv2.waitKey()
# 霍夫變換
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) # 逆時(shí)針旋轉(zhuǎn)
# cv2.imwrite('./test result/1.png', self.rotate_img)
# cv2.imshow('rotate', rotate_img)
# cv2.waitKey()
self.rotate_theta = avg_rotate_angle # 向量順時(shí)針旋轉(zhuǎn)公式
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)
#設(shè)置窗口
cv2.namedWindow('Canny')
#定義回調(diào)函數(shù)
def nothing(x):
pass
#創(chuàng)建兩個(gè)滑動(dòng)條,分別控制threshold1,threshold2
cv2.createTrackbar('threshold1','Canny',50,400,nothing)
cv2.createTrackbar('threshold2','Canny',100,400,nothing)
while(1):
#返回滑動(dòng)條所在位置的值
threshold1=cv2.getTrackbarPos('threshold1','Canny')
threshold2=cv2.getTrackbarPos('threshold2','Canny')
#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.canny_threshold(r'./test image/IMG_6386.JPG')
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. 效果圖
從圖中可以看出霍夫變換根據(jù)欄桿的水平線進(jìn)行校正。
彩蛋:乾元的朋友,讓我看見你們的雙手。
補(bǔ)充——用滑動(dòng)條調(diào)整canny閾值
之前在一個(gè)博客看到的,但是現(xiàn)在找不到了,先把代碼放上。文章來源:http://www.zghlxwxcb.cn/news/detail-443992.html
def canny_threshold(self, img_path):
img_original = cv2.imread(img_path)
# 設(shè)置窗口
cv2.namedWindow('Canny')
# 定義回調(diào)函數(shù)
def nothing(x):
pass
# 創(chuàng)建兩個(gè)滑動(dòng)條,分別控制threshold1,threshold2
cv2.createTrackbar('threshold1', 'Canny', 50, 400, nothing)
cv2.createTrackbar('threshold2', 'Canny', 100, 400, nothing)
while True:
# 返回滑動(dòng)條所在位置的值
threshold1 = cv2.getTrackbarPos('threshold1', 'Canny')
threshold2 = cv2.getTrackbarPos('threshold2', 'Canny')
# 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()
其他
對cv2.HoughLines
返回值的處理方式進(jìn)行了修改。文章來源地址http://www.zghlxwxcb.cn/news/detail-443992.html
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 # 旋轉(zhuǎn)的角度
def process(self, img):
img = cv2.imread(img) # 讀取圖片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化
# cv2.imshow('gray', gray)
# cv2.waitKey()
edges = cv2.Canny(gray, 350, 400, apertureSize=3) # canny算子
cv2.imwrite('./test result/edges.png', edges)
# cv2.imshow('edges', edges)
# cv2.waitKey()
# 霍夫變換
lines = cv2.HoughLines(edges, 1, np.pi / 180, 120)
sum = 0
count = 0
for r_theta in lines:
arr = np.array(r_theta[0], dtype=np.float64)
rho, theta = arr
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) # 逆時(shí)針旋轉(zhuǎn)
# cv2.imwrite('./test result/1.png', self.rotate_img)
# cv2.imshow('rotate', rotate_img)
# cv2.waitKey()
self.rotate_theta = avg_rotate_angle # 向量順時(shí)針旋轉(zhuǎn)公式
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)
#設(shè)置窗口
cv2.namedWindow('Canny')
#定義回調(diào)函數(shù)
def nothing(x):
pass
#創(chuàng)建兩個(gè)滑動(dòng)條,分別控制threshold1,threshold2
cv2.createTrackbar('threshold1','Canny',50,400,nothing)
cv2.createTrackbar('threshold2','Canny',100,400,nothing)
while(1):
#返回滑動(dòng)條所在位置的值
threshold1=cv2.getTrackbarPos('threshold1','Canny')
threshold2=cv2.getTrackbarPos('threshold2','Canny')
#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.canny_threshold(r'./test image/IMG_6386.JPG')
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()
到了這里,關(guān)于python 圖片傾斜校正的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!