0. 前言

进行图片校正是将拍照倾斜的图片恢复水平状态,大致思路为:

  1. 用canny算子检测出图像中的边缘轮廓线;
  2. 用霍夫线变换检测出图像中的所有直线;
  3. 筛选出接近水平方向上的直线,求出他们偏移角度的平均值;
  4. 根据倾斜角旋转矫正;
  5. 输出图片。

这里设计到几个知识点:
canny算子 edge = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])

变量

内容

image

要检测的图像

threshold1 和 threshold2 的值较小时,能够捕获更多的边缘信息,下文中canny_threshold(self, img_path)函数即可可视化不同threshold的效果。

霍夫变换

其他

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)  # 二值化
        # 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)  # 逆时针旋转
        # cv2.imwrite('./test result/1.png', self.rotate_img)
        # cv2.imshow('rotate', rotate_img)
        # cv2.waitKey()

        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
        #创建两个滑动条,分别控制threshold1,threshold2
        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')
            #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. 效果图

python 镜头矫正 python图像倾斜校正_计算机视觉

python 镜头矫正 python图像倾斜校正_计算机视觉_02

从图中可以看出霍夫变换根据栏杆的水平线进行校正。
彩蛋:乾元的朋友,让我看见你们的双手。

补充——用滑动条调整canny阈值

之前在一个博客看到的,但是现在找不到了,先把代码放上。

def canny_threshold(self, img_path):
        img_original = cv2.imread(img_path)
        # 设置窗口
        cv2.namedWindow('Canny')
        # 定义回调函数
        def nothing(x):
            pass
        # 创建两个滑动条,分别控制threshold1,threshold2
        cv2.createTrackbar('threshold1', 'Canny', 50, 400, nothing)
        cv2.createTrackbar('threshold2', 'Canny', 100, 400, nothing)
        while True:
            # 返回滑动条所在位置的值
            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返回值的处理方式进行了修改。

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)  # 二值化
        # 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)  # 逆时针旋转
        # cv2.imwrite('./test result/1.png', self.rotate_img)
        # cv2.imshow('rotate', rotate_img)
        # cv2.waitKey()

        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
        #创建两个滑动条,分别控制threshold1,threshold2
        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')
            #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()