Python OpenCV 图像矫正的原理与实现

题目描述

目录hw1下的图像是一些胶片的照片,请将其进行度量矫正。

推荐流程:采用Canny算子,检测边缘点;采用Hough直线检测,根据边缘点检测胶片边缘对应的4条直线;4条直线在图像平面中的交点为胶片图像的4个顶点。根据4个顶点与真实世界中胶片的位置(假设胶片图像长宽比为4:3),得到两个平面之间的单应变换矩阵,并根据单应变换矩阵实现图像矫正。

基本思路

  • 使用Canny算子,检测边缘点;
  • 以边缘点作为输入,采用Hough直线检测,检测出最多点共线的四条直线,这四条直线的交点就是照片中屏幕的四个顶点;
  • 假设胶片图像长宽比为4:3,那么此时已知四个匹配的点,可以求解出两个平面之间的单应变换矩阵;
  • 从而可以使用原图像、单应变换矩阵,对原图像进行变换,即可实现图像矫正。

实现日志

Canny边缘检测:Python OpenCV Canny边缘检测算法的原理与实现_乔卿的博客-CSDN博客

Hough直线检测:Python OpenCV Hough直线检测算法的原理与实现_乔卿的博客-CSDN博客

在具体实现时,发现对于给定的图像,几乎不可能通过调整阈值的方式,使得Hough检测到的直线刚好是屏幕边框。经过多轮调整,在下界为180、上界为260时取得了较为理想的结果,如下图所示。

对于三张图像,经过实验,最终选择的最佳阈值为:

correct('images/1.jpeg', 180, 260)
correct('images/2.jpeg', 30, 100)
correct('images/3.jpeg', 100, 160)

但即便是最佳阈值,也无法做到仅检测出四条线。思考过后,决定加入一步人工筛选。有两种可行的技术方案:

  • 人工筛选直线
  • 人工筛选交点

考虑到如果筛选交点的话,工作量明显比筛选直线更大,所以选择人工筛选直线。后面有时间的话考虑加入图形化界面,目前因时间原因,选择专注于算法本身,暂不考虑可视化编程。直接显示出下图用于筛选:

这里符合条件的直线id为2、3、6、7。求解得到的交点:

我们假设目标图像是4:3的,也就是其大小为(800, 600),从而我们可以确定目标图像中四个关键点位置为[0, 0], [800, 0], [0, 600], [800, 600]。为了保证交点与目标点一一对应,最为高效的解决方案是,我们筛选图像的时候,按照上、左、下、右的顺序即可。

核心代码

def correct(image_path, threshold1, threshold2):
    # 读取图像并转换为灰度图像
    image = cv2.imread(image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 使用Canny算子检测边缘
    edges = canny_detect(gray, threshold1, threshold2, show=False)

    # 使用Hough检测直线
    lines = hough_detect(image, edges, show=False)

    # 手动筛选
    for id, line in enumerate(lines):
        rho, theta = line[0]
        x1, y1, x2, y2 = convert_polar_to_two_points(rho, theta)
        temp_image = image.copy()
        cv2.line(temp_image, (x1, y1), (x2, y2), (255, 0, 0), 7)
        plt.subplot(5, 5, id + 1)
        plt.imshow(temp_image)
        plt.title('{}'.format(id))
        plt.xticks([])
        plt.yticks([])
    plt.show()
    choose = input('请输入您选择的直线的id,以空格分隔:').split(' ')

    # 求解交点
    crossover_points = []
    assert len(choose) == 4
    for i in range(4):
        for j in range(i+1, 4):
            rho1, theta1 = lines[int(choose[i])][0]
            rho2, theta2 = lines[int(choose[j])][0]
            # 如果角度差太小,认为它们是平行线
            if abs(theta2 - theta1) > np.pi / 8 and abs(theta2 - theta1) < np.pi * 7 / 8:
                crossover_points.append(cal_crossover(rho1, theta1, rho2, theta2))

    # 确定变换前后的坐标
    before = np.float32(crossover_points)
    after = np.float32([[0, 0], [800, 0], [0, 600], [800, 600]])

    # 单应变换
    h = cv2.getPerspectiveTransform(before, after)
    result = cv2.warpPerspective(image, h, (800, 600))

    cv2.imwrite(image_path.split('.')[0] + '_correct.jpeg', result)
    return result

完整代码

import cv2
import numpy as np
from matplotlib import pyplot as plt


def canny_detect(gray, threshold1=100, threshold2=200, show=True):
    # 获取边缘检测结果
    edges = cv2.Canny(gray, threshold1, threshold2, apertureSize=3)

    if show:
        # 绘制原图
        plt.subplot(121)
        plt.imshow(gray, cmap='gray')
        plt.title('Original Image')
        plt.xticks([])
        plt.yticks([])

        # 绘制边缘图
        plt.subplot(122)
        plt.imshow(edges, cmap='gray')
        plt.title('Edge Image')
        plt.xticks([])
        plt.yticks([])

        plt.show()

    # 返回的是一个二值图像
    return edges


def hough_detect(image, edges, show=True):
    # 使用Hough检测直线
    lines = cv2.HoughLines(edges, 1, np.pi/180, 200)

    if show:
        # 绘制直线
        for line in lines:
            rho, theta = line[0]
            x1, y1, x2, y2 = convert_polar_to_two_points(rho, theta)
            cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)

        cv2.imshow('line,jpg', image)
        cv2.waitKey()

    return lines


# 将极坐标转换为两点
def convert_polar_to_two_points(rho, theta):
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 10000 * (-b))
    y1 = int(y0 + 10000 * (a))
    x2 = int(x0 - 10000 * (-b))
    y2 = int(y0 - 10000 * (a))
    return x1, y1, x2, y2


# 一般式 Ax+By+C=0
def GeneralEquation(x1, y1, x2, y2):
    A = y2 - y1
    B = x1 - x2
    C = x2 * y1 - x1 * y2
    return A, B, C


# 求解交点
def cal_crossover(rho1, theta1, rho2, theta2):
    x11, y11, x12, y12 = convert_polar_to_two_points(rho1, theta1)
    x21, y21, x22, y22 = convert_polar_to_two_points(rho2, theta2)
    A1, B1, C1 = GeneralEquation(x11, y11, x12, y12)
    A2, B2, C2 = GeneralEquation(x21, y21, x22, y22)
    m = A1 * B2 - A2 * B1
    x = (C2 * B1 - C1 * B2) / m
    y = (C1 * A2 - C2 * A1) / m
    return [x, y]


def correct(image_path, threshold1, threshold2):
    # 读取图像并转换为灰度图像
    image = cv2.imread(image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 使用Canny算子检测边缘
    edges = canny_detect(gray, threshold1, threshold2, show=False)

    # 使用Hough检测直线
    lines = hough_detect(image, edges, show=False)

    # 手动筛选,按照上、左、下、右的顺序
    for id, line in enumerate(lines):
        rho, theta = line[0]
        x1, y1, x2, y2 = convert_polar_to_two_points(rho, theta)
        temp_image = image.copy()
        cv2.line(temp_image, (x1, y1), (x2, y2), (255, 0, 0), 7)
        plt.subplot(5, 5, id + 1)
        plt.imshow(temp_image)
        plt.title('{}'.format(id))
        plt.xticks([])
        plt.yticks([])
    plt.show()
    choose = input('请输入您选择的直线的id,以空格分隔:').split(' ')

    # 求解交点
    crossover_points = []
    assert len(choose) == 4
    for i in range(4):
        for j in range(i+1, 4):
            rho1, theta1 = lines[int(choose[i])][0]
            rho2, theta2 = lines[int(choose[j])][0]
            # 如果角度差太小,认为它们是平行线
            if abs(theta2 - theta1) > np.pi / 8 and abs(theta2 - theta1) < np.pi * 7 / 8:
                print(abs(theta2 - theta1) / np.pi)
                print(choose[i], choose[j])
                crossover_points.append(cal_crossover(rho1, theta1, rho2, theta2))
                temp_image = image.copy()
                cv2.circle(temp_image, (int(cal_crossover(rho1, theta1, rho2, theta2)[0]), int(cal_crossover(rho1, theta1, rho2, theta2)[1])), 10, (255, 0, 0), 2)
                cv2.imshow('point', temp_image)
                cv2.waitKey()

    # 确定变换前后的坐标
    before = np.float32(crossover_points)
    after = np.float32([[0, 0], [800, 0], [0, 600], [800, 600]])

    # 单应变换
    h = cv2.getPerspectiveTransform(before, after)
    result = cv2.warpPerspective(image, h, (800, 600))

    cv2.imwrite(image_path.split('.')[0] + '_correct.jpeg', result)
    return result


# correct('images/1.jpeg', 180, 260)
# correct('images/2.jpeg', 30, 100)
correct('images/3.jpeg', 100, 160)

矫正结果

  • 5
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
### 回答1: OpenCVSharp是一款开源的计算机视觉库,它提供了各种图像处理算法和工具,可以实现图像矫正的功能。图像矫正是指将图像变形成另一种形状或方向,以便更好地展示或处理。 在OpenCVSharp中,实现图像矫正的方法有很多种,比如基于图像的角点特征、基于相机的内部参数和误差、基于灰度图像的直线拟合等。这些方法都可以根据不同的场景和需求,选择最适合的方法来实现图像矫正。 例如,如果需要将一组拍摄于不同角度的图像矫正成同一角度和比例,可以先使用基于角点特征的方法来检测每幅图像的四个角点,然后使用仿射变换或透视变换来求得变换矩阵,最后应用该矩阵到每幅图像实现矫正。 总之,在OpenCVSharp中实现图像矫正的方法是十分灵活和丰富的,需要根据不同场景和需求选择不同的方法,才能得到最优的矫正效果。 ### 回答2: OpenCVSharp是一款基于C#的OpenCV图像处理库,可以实现很多常见的图像处理和计算机视觉任务。在OpenCVSharp中,图像矫正是一项常见的任务,它可以将图像对齐到某个特定的角度,使得图像更加清晰、可读性更高,减少图像畸变。 图像矫正可以通过旋转和平移来实现。在OpenCVSharp中,可以使用函数cv2.warpAffine()来实现这个功能。该函数需要输入源图像、矩阵M和输出图像的大小,其中矩阵M可以使用cv2.getRotationMatrix2D()函数获得,该函数可以指定旋转角度和缩放比例。 此外,如果图像存在扭曲或透视变形,可以使用cv2.getPerspectiveTransform()函数来获得透视转换矩阵,并将其传递给cv2.warpPerspective()函数来实现透视校正。 总之,OpenCVSharp提供了许多方便的功能来进行图像处理和计算机视觉任务,包括图像矫正。开发者可以根据实际需求选择合适的函数和参数来进行操作,以获得最佳效果。 ### 回答3: 在使用OpenCVSharp进行图像矫正之前,需要明确矫正的对象是哪一部分图像。通常情况下,矫正的对象是图像中的文字或者图形,通过对其进行旋转或者倾斜校正来使其更加垂直或者水平。 首先,需要确定用于矫正的参考线。这可以是一条已知方向的线条(比如水平或者竖直方向的线条),或者通过计算来得到参考线(比如基于文本行的朝向来确定参考线)。然后,根据参考线的方向来旋转或者倾斜整个图像,使矫正后的对象与参考线对齐。 在OpenCVSharp中,可以使用getRotationMatrix2D函数来获得旋转矩阵,并使用warpAffine函数来执行旋转操作。如果需要进行倾斜矫正,可以使用getAffineTransform函数获得仿射变换矩阵,并使用warpAffine函数执行倾斜变换。 需要注意的是,在进行图像矫正之前,需要对图像进行预处理,例如去噪、二值化、降噪去毛刺等等,以提高矫正的效果。同时,由于图像矫正可能会导致图像的内容被裁剪或者留空,因此需要根据具体情况设置合适的参数,使得矫正后的图像不会损失重要信息。 总的来说,图像矫正图像处理中非常常见的一个操作,使用OpenCVSharp可以方便地实现矫正的功能,同时需要根据具体情况选择适当的处理方法和参数,以达到最佳的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乔卿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值