基于OPENCV的图像透视矫正

这段代码的主要功能是对输入的图像进行透视矫正。它会读取一张图像,检测图像中最大的四边形轮廓,然后对该四边形区域进行透视变换,将其矫正为正视图,最后保存矫正后的图像。

模块导入说明

python

import cv2
import numpy as np
import os

  • cv2:OpenCV 库,用于图像处理和计算机视觉任务,如读取图像、边缘检测、轮廓查找、透视变换等。
  • numpy:用于数值计算,在处理图像数据和坐标计算时非常有用。
  • os:提供了与操作系统进行交互的功能,例如文件路径操作和目录创建。

函数 detect_and_correct_perspective 说明

python

def detect_and_correct_perspective(input_path, output_path):

  • 功能:检测输入图像中的四边形轮廓,并对其进行透视矫正,最后将结果保存到指定路径。
  • 参数
    • input_path:输入图像的文件路径。
    • output_path:矫正后图像的保存路径。
函数内部步骤及代码解释
  1. 读取图像

python

image = cv2.imread(input_path)
if image is None:
    print(f"无法加载图像,请检查路径: {os.path.abspath(input_path)}")
    return

尝试使用 cv2.imread 读取输入图像。如果图像读取失败,会打印错误信息并终止函数。

  1. 转换为灰度图

python

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

将彩色图像转换为灰度图像,便于后续的边缘检测。

  1. 高斯模糊降噪

python

blurred = cv2.GaussianBlur(gray, (5, 5), 0)

使用高斯模糊对灰度图像进行降噪处理,减少边缘检测时的噪声干扰。

  1. Canny 边缘检测

python

edges = cv2.Canny(blurred, 50, 150)

使用 Canny 边缘检测算法检测图像中的边缘。

  1. 查找轮廓

python

contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

查找图像中的所有外部轮廓。

  1. 找到最大的轮廓

python

largest_contour = max(contours, key=cv2.contourArea)

找到所有轮廓中面积最大的轮廓,假设这个轮廓就是需要矫正的对象。

  1. 计算轮廓的近似多边形

python

epsilon = 0.02 * cv2.arcLength(largest_contour, True)
approx = cv2.approxPolyDP(largest_contour, epsilon, True)

使用 cv2.approxPolyDP 对最大轮廓进行多边形近似,简化轮廓。

  1. 绘制轮廓和近似多边形

python

cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)
cv2.drawContours(image, [approx], -1, (0, 0, 255), 2)

在原始图像上绘制最大轮廓(绿色)和近似多边形(红色),用于可视化。

  1. 确保找到四边形

python

if len(approx) == 4:

检查近似多边形是否有 4 个顶点,如果是,则进行透视变换;否则,打印错误信息。

  1. 对四个顶点进行排序

python

pts = approx.reshape(4, 2)
rect = np.zeros((4, 2), dtype="float32")

s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]  # 左上
rect[2] = pts[np.argmax(s)]  # 右下

diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]  # 右上
rect[3] = pts[np.argmax(diff)]  # 左下

对四个顶点进行排序,确定左上、右上、右下、左下的顺序。

  1. 计算目标矩形的宽度和高度

python

(tl, tr, br, bl) = rect
widthA = np.sqrt(((br[0]-bl[0])**2)+((br[1]-bl[1])**2))
widthB = np.sqrt(((tr[0]-tl[0])**2)+((tr[1]-tl[1])**2))
maxWidth = max(int(widthA), int(widthB))

heightA = np.sqrt(((tr[0] - br[0])**2) + ((tr[1] - br[1])**2))
heightB = np.sqrt(((tl[0] - bl[0])**2) + ((tl[1] - bl[1])**2))
maxHeight = max(int(heightA), int(heightB))

根据四个顶点的坐标计算目标矩形的宽度和高度。

  1. 定义目标矩形的四个顶点

python

dst = np.array([
    [0, 0],
    [maxWidth - 1, 0],
    [maxWidth - 1, maxHeight - 1],
    [0, maxHeight - 1]], dtype="float32")

定义目标矩形的四个顶点坐标。

  1. 计算透视变换矩阵并应用

python

M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

使用 cv2.getPerspectiveTransform 计算透视变换矩阵,然后使用 cv2.warpPerspective 应用该矩阵进行透视变换。

  1. 确保输出目录存在

python

os.makedirs(os.path.dirname(output_path), exist_ok=True)

确保输出目录存在,如果不存在则创建。

  1. 保存结果

python

cv2.imwrite(output_path, warped)
print(f"结果已保存到: {os.path.abspath(output_path)}")

将矫正后的图像保存到指定路径,并打印保存信息。

  1. 显示结果(可选)

python

cv2.imshow("Original with Contours", image)
cv2.imshow("Perspective Corrected", warped)
cv2.waitKey(0)
cv2.destroyAllWindows()

显示原始图像(带有轮廓)和矫正后的图像,按任意键关闭窗口。

主程序部分说明

python

current_dir = os.path.dirname(os.path.abspath(__file__))  # 获取当前脚本所在目录
input_image = os.path.join(current_dir, "girl.png")  # 输入图像路径
output_image = os.path.join(current_dir, "corrected_girl.png")  # 输出图像路径

# 执行函数
detect_and_correct_perspective(input_image, output_image)

  • 获取当前脚本所在的目录。
  • 构造输入图像和输出图像的路径。
  • 调用 detect_and_correct_perspective 函数进行透视矫正。

完整代码

import cv2
import numpy as np
import os

def detect_and_correct_perspective(input_path, output_path):
    # 读取图像
    image = cv2.imread(input_path)
    if image is None:
        print(f"无法加载图像,请检查路径: {os.path.abspath(input_path)}")
        return
    
    # 转换为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 高斯模糊降噪
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    
    # Canny边缘检测
    edges = cv2.Canny(blurred, 50, 150)
    
    # 查找轮廓
    contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # 找到最大的轮廓(假设这是我们想要矫正的对象)
    largest_contour = max(contours, key=cv2.contourArea)
    
    # 计算轮廓的近似多边形(简化轮廓)
    epsilon = 0.02 * cv2.arcLength(largest_contour, True)
    approx = cv2.approxPolyDP(largest_contour, epsilon, True)
    
    # 绘制轮廓和近似多边形(用于可视化)
    cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)
    cv2.drawContours(image, [approx], -1, (0, 0, 255), 2)
    
    # 确保我们找到了四边形(4个顶点)
    if len(approx) == 4:
        # 对四个顶点进行排序:左上、右上、右下、左下
        pts = approx.reshape(4, 2)
        rect = np.zeros((4, 2), dtype="float32")
        
        # 计算四个点的总和,左上点有最小的x+y,右下点有最大的x+y
        s = pts.sum(axis=1)
        rect[0] = pts[np.argmin(s)]  # 左上
        rect[2] = pts[np.argmax(s)]  # 右下
        
        # 计算点之间的差异,右上点有最小的x-y,左下点有最大的x-y
        diff = np.diff(pts, axis=1)
        rect[1] = pts[np.argmin(diff)]  # 右上
        rect[3] = pts[np.argmax(diff)]  # 左下
        
        # 计算目标矩形的宽度和高度
        (tl, tr, br, bl) = rect
        widthA = np.sqrt(((br[0]-bl[0])**2)+((br[1]-bl[1])**2))
        widthB = np.sqrt(((tr[0]-tl[0])**2)+((tr[1]-tl[1])**2))
        maxWidth = max(int(widthA), int(widthB))
        
        heightA = np.sqrt(((tr[0] - br[0])**2) + ((tr[1] - br[1])**2))
        heightB = np.sqrt(((tl[0] - bl[0])**2) + ((tl[1] - bl[1])**2))
        maxHeight = max(int(heightA), int(heightB))
        
        # 定义目标矩形的四个顶点
        dst = np.array([
            [0, 0],
            [maxWidth - 1, 0],
            [maxWidth - 1, maxHeight - 1],
            [0, maxHeight - 1]], dtype="float32")
        
        # 计算透视变换矩阵并应用
        M = cv2.getPerspectiveTransform(rect, dst)
        warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
        
        # 确保输出目录存在
        os.makedirs(os.path.dirname(output_path), exist_ok=True)
        
        # 保存结果
        cv2.imwrite(output_path, warped)
        print(f"结果已保存到: {os.path.abspath(output_path)}")
        
        # 显示结果(可选)
        cv2.imshow("Original with Contours", image)
        cv2.imshow("Perspective Corrected", warped)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    else:
        print("未检测到四边形轮廓,无法进行透视变换")

# 使用相对路径
current_dir = os.path.dirname(os.path.abspath(__file__))  # 获取当前脚本所在目录
input_image = os.path.join(current_dir, "girl.png")  # 输入图像路径
output_image = os.path.join(current_dir, "corrected_girl.png")  # 输出图像路径

# 执行函数
detect_and_correct_perspective(input_image, output_image)

猜你喜欢

转载自blog.csdn.net/weixin_69327572/article/details/147031592
今日推荐