【OpenCV】第四章 图像几何变换

news/2025/1/11 21:08:19 标签: opencv, 计算机视觉, 人工智能

4.1 仿射变换

图像的几何变换在计算机视觉和图像处理中扮演着至关重要的角色。仿射变换(Affine Transformation)是一种线性变换,它能够保持直线和平行线的特性,但不一定保持角度和长度。仿射变换广泛应用于图像旋转、缩放、剪切、平移以及图像对齐等操作中。

仿射变换的数学基础

仿射变换可以由一个2x3的矩阵表示,形式如下:

这个矩阵可以应用于图像的每个像素点 (x, y) 来得到新的位置 (x', y'):

仿射变换包括以下几种基本操作:

  1. 平移(Translation):将图像整体向某个方向移动。
  2. 缩放(Scaling):调整图像的尺寸。
  3. 旋转(Rotation):将图像绕某个中心点旋转一定的角度。
  4. 剪切(Shearing):沿水平或垂直方向倾斜图像。
使用OpenCV进行仿射变换

OpenCV提供了cv2.getAffineTransform()cv2.warpAffine()两个主要函数来实现仿射变换。

  • cv2.getAffineTransform(src, dst):计算仿射变换矩阵。
    • src:源图像中的三个点。
    • dst:目标图像中对应的三个点。
  • cv2.warpAffine(src, M, dsize):应用仿射变换矩阵M到源图像。
    • src:源图像。
    • M:仿射变换矩阵。
    • dsize:输出图像的尺寸,格式为 (宽度, 高度)
示例一:图像的平移
import cv2
import numpy as np

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    rows, cols = image.shape[:2]

    # 定义平移矩阵,向右移动100像素,向下移动50像素
    M = np.float32([[1, 0, 100],
                    [0, 1, 50]])

    # 应用平移变换
    translated = cv2.warpAffine(image, M, (cols, rows))

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Translated Image', translated)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('translated.jpg', translated)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 平移矩阵中的10050分别表示图像将向右移动100像素,向下移动50像素。
  • cv2.warpAffine()函数根据平移矩阵将图像平移。
示例二:图像的缩放与旋转
import cv2
import numpy as np

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    rows, cols = image.shape[:2]

    # 缩放因子
    scale_factor = 0.5

    # 定义缩放矩阵
    M_scale = np.float32([[scale_factor, 0, 0],
                          [0, scale_factor, 0]])

    # 应用缩放变换
    scaled = cv2.warpAffine(image, M_scale, (int(cols * scale_factor), int(rows * scale_factor)))

    # 定义旋转中心,通常为图像中心
    center = (cols / 2, rows / 2)

    # 定义旋转角度和缩放因子
    angle = 45  # 旋转45度
    scale = 1.0  # 不缩放

    # 获取旋转矩阵
    M_rotate = cv2.getRotationMatrix2D(center, angle, scale)

    # 计算旋转后图像的尺寸,以防止图像内容被裁剪
    abs_cos = abs(M_rotate[0, 0])
    abs_sin = abs(M_rotate[0, 1])
    bound_w = int(rows * abs_sin + cols * abs_cos)
    bound_h = int(rows * abs_cos + cols * abs_sin)

    # 调整旋转矩阵的平移部分
    M_rotate[0, 2] += bound_w / 2 - center[0]
    M_rotate[1, 2] += bound_h / 2 - center[1]

    # 应用旋转变换
    rotated = cv2.warpAffine(image, M_rotate, (bound_w, bound_h))

    # 显示结果
    cv2.imshow('Scaled Image', scaled)
    cv2.imshow('Rotated Image', rotated)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('scaled.jpg', scaled)
    cv2.imwrite('rotated.jpg', rotated)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 缩放:使用仿射矩阵进行图像缩放,将图像尺寸缩小至原来的50%。
  • 旋转
    • 使用cv2.getRotationMatrix2D()获取旋转矩阵,指定旋转中心、角度和缩放因子。
    • 计算旋转后图像的边界尺寸,调整旋转矩阵的平移部分,确保整个图像内容不被裁剪。
    • 应用旋转矩阵进行图像旋转。
示例三:图像的剪切(Shearing)

剪切变换通过倾斜图像来改变图像的形状。常见的剪切分为水平剪切和垂直剪切。

import cv2
import numpy as np

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    rows, cols = image.shape[:2]

    # 定义水平剪切矩阵,shx为剪切因子
    shx = 0.3
    M_shear = np.float32([[1, shx, 0],
                          [0,    1, 0]])

    # 应用剪切变换
    sheared = cv2.warpAffine(image, M_shear, (int(cols + shx * rows), rows))

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Sheared Image', sheared)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('sheared.jpg', sheared)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 水平剪切因子shx = 0.3表示图像将沿水平轴倾斜30%的比例。
  • 新的图像宽度通过cols + shx * rows计算,确保剪切后的图像内容完整显示。
仿射变换的应用场景
  1. 图像对齐:将多幅图像对齐到同一坐标系,常用于图像拼接、全景图生成等。
  2. 图像增强:通过缩放、旋转等操作改善图像质量,适用于图像预处理。
  3. 目标跟踪与识别:在实时视频中对目标进行位姿调整,提升识别准确性。
  4. 图像翻译:实现图像的移动和平移,用于数据增强等。
常见问题及解决方案
  1. 图像部分内容被裁剪

    • 原因:变换矩阵未考虑图像尺寸变化,导致部分内容超出边界。
    • 解决方案:计算变换后的图像尺寸,并相应调整变换矩阵的平移部分,确保整个图像内容完整显示。
  2. 变换后的图像失真

    • 原因:变换矩阵参数设置不当,或使用的插值方法不合适。
    • 解决方案:合理设置仿射变换矩阵参数,选择适当的插值方法(如cv2.INTER_LINEARcv2.INTER_CUBIC)以减少失真。
  3. 图像变换速度慢

    • 原因:处理高分辨率图像,或在循环中重复计算变换矩阵。
    • 解决方案:优化代码,预计算不变的变换矩阵,或调整图像分辨率以加快处理速度。
  4. 颜色空间问题

    • 原因:在处理过程中颜色空间发生变化,导致变换后的图像颜色异常。
    • 解决方案:确保在仿射变换前后保持一致的颜色空间,必要时进行色彩空间转换。
总结

仿射变换是图像几何变换中的基础操作,通过线性的仿射矩阵,可以实现图像的平移、缩放、旋转和剪切等多种变换。OpenCV提供了简洁高效的函数接口,使得仿射变换的应用变得简单直观。理解仿射变换的数学原理和OpenCV的实现方法,有助于开发者在计算机视觉和图像处理项目中灵活应用这些技术,解决实际问题。


4.2 透视变换

透视变换(Perspective Transformation)是一种更为复杂的几何变换,相比仿射变换,透视变换能够处理图像的非线性变形,保留投影的特性。它在图像校正、视角变换、图像拼接及增强现实等应用中具有重要作用。

使用OpenCV进行透视变换

OpenCV提供了cv2.getPerspectiveTransform()cv2.warpPerspective()函数来实现透视变换。

  • cv2.getPerspectiveTransform(src, dst):计算透视变换矩阵。
    • src:源图像中的四个点。
    • dst:目标图像中对应的四个点。
  • cv2.warpPerspective(src, M, dsize):应用透视变换矩阵M到源图像。
    • src:源图像。
    • M:透视变换矩阵。
    • dsize:输出图像的尺寸,格式为 (宽度, 高度)
示例一:图像的透视校正

假设我们有一个拍摄角度有偏差的文档图像,通过透视变换可以将其校正为正视图。

import cv2
import numpy as np

# 读取图像
image = cv2.imread('document.jpg')

if image is not None:
    # 定义源点(文档的四个角)
    src_points = np.float32([[100, 150], [400, 130],
                             [120, 400], [420, 390]])

    # 定义目标点(校正后的四个角)
    dst_points = np.float32([[0, 0], [300, 0],
                             [0, 400], [300, 400]])

    # 计算透视变换矩阵
    M = cv2.getPerspectiveTransform(src_points, dst_points)

    # 应用透视变换
    warped = cv2.warpPerspective(image, M, (300, 400))

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Warped Image', warped)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('warped_document.jpg', warped)
else:
    print("Error: 无法读取图像文件。")

说明:

  • src_points:文档图像中四个角的坐标。
  • dst_points:目标透视图像中四个角的坐标,通常为一个矩形区域。
  • 通过计算透视变换矩阵并应用cv2.warpPerspective(),实现图像的透视校正,使文档图像呈现正视效果。
示例二:实现鸟瞰图(Top-Down View)转换

鸟瞰图广泛应用于地图制作、交通监控等领域。通过透视变换,可以将斜视图转换为俯视图。

import cv2
import numpy as np

# 读取图像
image = cv2.imread('road_scene.jpg')

if image is not None:
    # 定义源点(地面上的四个关键点)
    src_points = np.float32([[200, 720], [1100, 720],
                             [595, 450], [685, 450]])

    # 定义目标点(俯视图的四个角)
    dst_points = np.float32([[300, 720],
                             [1000, 720],
                             [300, 0],
                             [1000, 0]])

    # 计算透视变换矩阵
    M = cv2.getPerspectiveTransform(src_points, dst_points)

    # 应用透视变换
    bird_eye_view = cv2.warpPerspective(image, M, (image.shape[1], image.shape[0]))

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Bird Eye View', bird_eye_view)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('bird_eye_view.jpg', bird_eye_view)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 通过选择地面上的四个关键点作为源点,将其映射到目标点,转换为俯视图。
  • 俯视图尺寸通过原始图像尺寸决定,确保转换后的图像保持适当的比例和视角。
手动选择源点

在实际应用中,用户可能需要手动选择图像中的源点,以实现更灵活的透视变换。以下是一个示例,展示如何通过鼠标点击选择源点。

import cv2
import numpy as np

# 全局变量用于存储点击的点
src_points = []
def select_points(event, x, y, flags, param):
    global src_points, image_copy
    if event == cv2.EVENT_LBUTTONDOWN and len(src_points) < 4:
        src_points.append([x, y])
        cv2.circle(image_copy, (x, y), 5, (0, 255, 0), -1)
        cv2.imshow('Select Points', image_copy)

# 读取图像
image = cv2.imread('source.jpg')
image_copy = image.copy()

if image is not None:
    cv2.namedWindow('Select Points')
    cv2.setMouseCallback('Select Points', select_points)

    print("请点击图像中的四个点(左上、右上、左下、右下)...")

    while True:
        cv2.imshow('Select Points', image_copy)
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q') or len(src_points) == 4:
            break

    if len(src_points) == 4:
        src_pts = np.float32(src_points)
        # 定义目标点(根据需要调整)
        dst_pts = np.float32([[0, 0], [300, 0],
                              [0, 400], [300, 400]])

        # 计算透视变换矩阵
        M = cv2.getPerspectiveTransform(src_pts, dst_pts)

        # 应用透视变换
        warped = cv2.warpPerspective(image, M, (300, 400))

        # 显示结果
        cv2.imshow('Warped Image', warped)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        # 保存结果
        cv2.imwrite('warped_selected.jpg', warped)
    else:
        print("未选择足够的点,无法进行透视变换。")
else:
    print("Error: 无法读取图像文件。")

说明:

  • 通过设置鼠标回调函数,用户可以在图像窗口中逐一点击四个点。
  • 点击的四个点将被保存并用于计算透视变换矩阵。
  • 适用于需要灵活选择源点的应用场景,如标定地图、校正文档等。
透视变换的应用场景
  1. 图像校正:修正由于拍摄角度导致的图像扭曲,恢复图像的真实比例和视角。
  2. 鸟瞰图生成:将斜视图转换为俯视图,便于进行地理信息分析和规划。
  3. 增强现实:将虚拟物体正确地叠加到实际场景中,需要透视变换来匹配视角。
  4. 图像拼接:将多张图像对齐以生成全景图,需要透视变换来进行图像对齐和配准。
  5. 车道检测:在自动驾驶系统中,通过透视变换将车道线转换为平行线,便于后续处理和识别。
常见问题及解决方案
  1. 透视变换后的图像失真

    • 原因:源点或目标点选择不准确,导致变换不符合预期。
    • 解决方案:确保源点与目标点对应准确,尽量选择图像中易于识别且明确的点。使用鼠标手动选择点时,尽量精确点击。
  2. 透视变换矩阵计算失败

    • 原因:源点或目标点数量不足,或点的排列不符合透视变换的要求。
    • 解决方案:确保源点和目标点各有四个,且点的排列符合原图和目标图的对应关系。
  3. 裁剪后的图像黑边

    • 原因:变换后的图像尺寸设置不合理,导致部分区域未被填充。
    • 解决方案:根据透视变换后的图像内容,合理设置dsize参数,调整变换矩阵的平移部分,确保图像内容完整显示。
  4. 颜色空间问题

    • 原因:在透视变换前后颜色空间发生变化,导致图像颜色异常。
    • 解决方案:确保在透视变换的整个过程中保持一致的颜色空间,必要时在处理前后进行颜色转换。
总结

透视变换是一种强大的几何变换工具,能够处理图像的非线性变形,应用于广泛的计算机视觉和图像处理任务中。通过OpenCV提供的函数接口,开发者可以轻松实现图像的透视校正、视角变换和图像对齐等功能。理解透视变换的数学原理和实际应用方法,有助于在项目中灵活应用这些技术,解决实际问题,提高图像处理的精准度和效果。


4.3 图像配准

图像配准(Image Registration)是指将两幅或多幅图像对齐到同一坐标系中的过程,以便进行比较、融合或进一步分析。这在医学影像处理、遥感图像分析、图像拼接和增强现实等领域具有重要应用。配准过程通常包括特征检测、特征匹配、变换估计和图像变换等步骤。

图像配准的流程
  1. 特征检测与描述:在图像中检测关键特征点,并为其生成描述符。
  2. 特征匹配:将不同图像中的特征点进行配对,找到对应关系。
  3. 变换估计:根据匹配的特征点,估计图像之间的几何变换关系。
  4. 图像变换与对齐:应用估计的变换,将一幅图像对齐到另一幅图像。
常用的配准方法
  1. 基于特征的配准

    • SIFT(Scale-Invariant Feature Transform):检测和描述图像中的局部特征,具有尺度不变性和旋转不变性。
    • SURF(Speeded-Up Robust Features):基于SIFT,速度更快,适用于实时应用。
    • ORB(Oriented FAST and Rotated BRIEF):快速且高效的特征检测与描述方法,适用于资源受限的环境。
  2. 基于区域的配准

    • 直接比较图像区域的相似性,如互信息(Mutual Information)、相关系数等,适用于医学图像配准。
使用OpenCV进行图像配准

以下示例将演示如何使用ORB特征检测器和基于特征的配准方法,将两幅图像对齐。

示例一:基于ORB的图像配准
import cv2
import numpy as np

def image_registration(img1, img2, max_features=500, good_match_percent=0.15):
    # 初始化ORB特征检测器
    orb = cv2.ORB_create(max_features)

    # 检测ORB特征并计算描述符
    keypoints1, descriptors1 = orb.detectAndCompute(img1, None)
    keypoints2, descriptors2 = orb.detectAndCompute(img2, None)

    # 创建Brute-Force匹配器并进行匹配
    matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = matcher.match(descriptors1, descriptors2, None)

    # 按照距离排序匹配点
    matches = sorted(matches, key=lambda x: x.distance)

    # 保留前指定比例的匹配点
    num_good_matches = int(len(matches) * good_match_percent)
    matches = matches[:num_good_matches]

    # 提取匹配点的坐标
    points1 = np.zeros((len(matches), 2), dtype=np.float32)
    points2 = np.zeros((len(matches), 2), dtype=np.float32)

    for i, match in enumerate(matches):
        points1[i, :] = keypoints1[match.queryIdx].pt
        points2[i, :] = keypoints2[match.trainIdx].pt

    # 计算变换矩阵
    H, mask = cv2.findHomography(points1, points2, cv2.RANSAC)

    # 使用变换矩阵将img1对齐到img2
    height, width, channels = img2.shape
    img1_reg = cv2.warpPerspective(img1, H, (width, height))

    return img1_reg, H, matches

# 读取图像
img1 = cv2.imread('image1.jpg')  # 待配准图像
img2 = cv2.imread('image2.jpg')  # 参考图像

if img1 is not None and img2 is not None:
    # 配准
    registered_img, homography, matches = image_registration(img1, img2)

    # 显示匹配结果
    img_matches = cv2.drawMatches(img1, None, img2, None, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    cv2.imshow('Matches', img_matches)

    # 显示配准后的图像
    cv2.imshow('Registered Image', registered_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('registered_image.jpg', registered_img)
    cv2.imwrite('matches.jpg', img_matches)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 特征检测:使用ORB检测图像中的关键点,并计算描述符。
  • 特征匹配:使用Brute-Force匹配器进行特征匹配,选择距离较近的匹配点。
  • 变换矩阵估计:使用RANSAC算法估计单应性矩阵H,消除误匹配点。
  • 图像配准:应用变换矩阵将待配准图像对齐到参考图像。
示例二:基于SIFT的图像配准

虽然SIFT由于专利问题在某些OpenCV版本中不可用,但在支持的环境中,SIFT提供了更强大的特征检测与描述能力。

import cv2
import numpy as np

def image_registration_sift(img1, img2, max_features=500, good_match_percent=0.15):
    # 初始化SIFT特征检测器
    sift = cv2.SIFT_create(max_features)

    # 检测SIFT特征并计算描述符
    keypoints1, descriptors1 = sift.detectAndCompute(img1, None)
    keypoints2, descriptors2 = sift.detectAndCompute(img2, None)

    # 创建FLANN匹配器
    FLANN_INDEX_KDTREE = 1
    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params = dict(checks=50)
    flann = cv2.FlannBasedMatcher(index_params, search_params)

    # 进行KNN匹配
    matches = flann.knnMatch(descriptors1, descriptors2, k=2)

    # Lowe's ratio test
    good_matches = []
    for m, n in matches:
        if m.distance < 0.7 * n.distance:
            good_matches.append(m)

    # 提取匹配点的坐标
    points1 = np.zeros((len(good_matches), 2), dtype=np.float32)
    points2 = np.zeros((len(good_matches), 2), dtype=np.float32)

    for i, match in enumerate(good_matches):
        points1[i, :] = keypoints1[match.queryIdx].pt
        points2[i, :] = keypoints2[match.trainIdx].pt

    # 计算变换矩阵
    H, mask = cv2.findHomography(points1, points2, cv2.RANSAC)

    # 使用变换矩阵将img1对齐到img2
    height, width, channels = img2.shape
    img1_reg = cv2.warpPerspective(img1, H, (width, height))

    return img1_reg, H, good_matches

# 读取图像
img1 = cv2.imread('image1.jpg')  # 待配准图像
img2 = cv2.imread('image2.jpg')  # 参考图像

if img1 is not None and img2 is not None:
    # 配准
    registered_img, homography, matches = image_registration_sift(img1, img2)

    # 显示匹配结果
    img_matches = cv2.drawMatches(img1, None, img2, None, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    cv2.imshow('SIFT Matches', img_matches)

    # 显示配准后的图像
    cv2.imshow('SIFT Registered Image', registered_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('sift_registered_image.jpg', registered_img)
    cv2.imwrite('sift_matches.jpg', img_matches)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 特征检测:使用SIFT检测图像中的关键点,并计算描述符。
  • 特征匹配:使用FLANN匹配器进行KNN匹配,并通过Lowe's比率测试筛选出优秀匹配点。
  • 变换矩阵估计:使用RANSAC算法估计单应性矩阵H,消除误匹配点。
  • 图像配准:应用变换矩阵将待配准图像对齐到参考图像。
图像配准的高级应用
  1. 图像拼接与全景图生成

    • 通过配准多幅重叠图像,将其无缝拼接成一幅全景图。
    • 应用于旅游摄影、虚拟现实等领域。
  2. 医学影像配准

    • 将不同模态(如CT、MRI)的医学图像对齐,以辅助诊断和治疗规划。
    • 需要高精度的配准,以保证医疗数据的准确性。
  3. 变化检测

    • 比较同一区域在不同时期拍摄的图像,检测环境变化、建筑物的迁移等。
    • 应用于遥感监测、城市规划等领域。
  4. 增强现实

    • 实时配准虚拟物体与现实场景,实现虚拟与现实的无缝融合。
    • 需要高效的实时配准技术,以保证用户体验。
常见问题及解决方案
  1. 特征点不足或分布不均

    • 原因:图像中缺乏明显的特征点,或者特征点分布集中,导致变换矩阵估计不准确。
    • 解决方案:调整特征检测器的参数,如增加特征点数量,选择更适合的特征检测算法。或者对图像进行预处理,如增强对比度、锐化等,以增加特征点数量和质量。
  2. 变换矩阵估计失败

    • 原因:匹配点中存在大量误匹配,或匹配点不足。
    • 解决方案:采用更严格的匹配条件,如降低匹配点的最大距离,增加Lowe's比率测试的严格性。使用更稳健的变换矩阵估计算法,如RANSAC,以减小误匹配的影响。
  3. 配准后的图像出现透视扭曲

    • 原因:变换矩阵估计不准确,或源点与目标点选择不当。
    • 解决方案:重新选择更加准确的源点和目标点,确保点的对应关系正确。验证变换矩阵的正确性,必要时手动调整。
  4. 配准速度慢

    • 原因:处理高分辨率图像或使用复杂的特征检测与匹配算法。
    • 解决方案:降低图像分辨率,提高算法效率。选择更快的特征检测与匹配方法,如ORB替代SIFT/SURF。
性能优化
  1. 多线程处理

    • 利用多线程并行进行特征检测、匹配和变换矩阵估计,加快配准速度。
  2. 降采样处理

    • 先对图像进行降采样处理,减少计算量,然后在高分辨率图像上进行精细配准。
  3. 硬件加速

    • 使用GPU加速特征检测与匹配过程,提升处理效率。
综合示例:图像拼接生成全景图

以下示例展示如何使用ORB特征检测器和基于特征的配准方法,结合多张图像生成一幅全景图。

import cv2
import numpy as np

def stitch_images(images, max_features=500, good_match_percent=0.15):
    # 初始化ORB特征检测器
    orb = cv2.ORB_create(max_features)

    # 读取第一张图像作为基准
    stitched_image = images[0]

    for i in range(1, len(images)):
        img1 = stitched_image
        img2 = images[i]

        # 检测ORB特征并计算描述符
        keypoints1, descriptors1 = orb.detectAndCompute(img1, None)
        keypoints2, descriptors2 = orb.detectAndCompute(img2, None)

        # 创建Brute-Force匹配器并进行匹配
        matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
        matches = matcher.match(descriptors1, descriptors2, None)

        # 按照距离排序匹配点
        matches = sorted(matches, key=lambda x: x.distance)

        # 保留前指定比例的匹配点
        num_good_matches = int(len(matches) * good_match_percent)
        matches = matches[:num_good_matches]

        # 提取匹配点的坐标
        points1 = np.zeros((len(matches), 2), dtype=np.float32)
        points2 = np.zeros((len(matches), 2), dtype=np.float32)

        for j, match in enumerate(matches):
            points1[j, :] = keypoints1[match.queryIdx].pt
            points2[j, :] = keypoints2[match.trainIdx].pt

        # 计算变换矩阵
        H, mask = cv2.findHomography(points2, points1, cv2.RANSAC)

        # 获取尺寸
        height1, width1 = img1.shape[:2]
        height2, width2 = img2.shape[:2]

        # 获取四个角点
        corners_img2 = np.float32([[0,0], [0, height2],
                                   [width2, height2], [width2,0]]).reshape(-1,1,2)
        transformed_corners_img2 = cv2.perspectiveTransform(corners_img2, H)

        # 获取新图像的边界
        corners_img1 = np.float32([[0,0], [0, height1],
                                   [width1, height1], [width1,0]]).reshape(-1,1,2)
        all_corners = np.concatenate((corners_img1, transformed_corners_img2), axis=0)
        [xmin, ymin] = np.int32(all_corners.min(axis=0).ravel() - 0.5)
        [xmax, ymax] = np.int32(all_corners.max(axis=0).ravel() + 0.5)

        # 计算平移矩阵
        translation = [-xmin, -ymin]
        H_translation = np.array([[1, 0, translation[0]],
                                  [0, 1, translation[1]],
                                  [0, 0, 1]])

        # 拼接图像
        stitched_image = cv2.warpPerspective(img2, H_translation.dot(H), (xmax - xmin, ymax - ymin))
        stitched_image[translation[1]:height1+translation[1],
                      translation[0]:width1+translation[0]] = img1

    return stitched_image

# 读取多张图像
image_filenames = ['image1.jpg', 'image2.jpg', 'image3.jpg']
images = []
for filename in image_filenames:
    img = cv2.imread(filename)
    if img is not None:
        images.append(img)
    else:
        print(f"Error: 无法读取图像文件 {filename}。")

if len(images) >= 2:
    # 拼接图像
    panorama = stitch_images(images)

    # 显示结果
    cv2.imshow('Panorama', panorama)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('panorama.jpg', panorama)
else:
    print("Error: 至少需要两张图像进行拼接。")

说明:

  • 图像读取:读取多张重叠图像,确保它们具有部分相同的特征点。
  • ORB特征检测与匹配:检测每对相邻图像中的ORB特征点,并进行匹配。
  • 变换矩阵计算:通过RANSAC算法估计单应性矩阵,消除误匹配点。
  • 图像拼接:应用透视变换,将图像对齐,并拼接成一幅全景图。
  • 边界处理:计算所有图像的角点,确定拼接后图像的边界,并进行适当的平移和缓冲,避免图像内容溢出。
总结

图像配准是实现多图像对齐和融合的基础技术,通过特征检测、匹配和变换估计,可以将不同视角或时刻拍摄的图像对齐到同一坐标系下。OpenCV提供了丰富的工具和函数,使得图像配准过程简便高效。掌握图像配准的基本原理和实现方法,有助于开发者在医学影像、遥感分析、全景图生成等领域应用这些技术,解决实际问题。


4.4 缩放与裁剪

缩放与裁剪是图像几何变换中最常用且基础的操作,广泛应用于图像预处理、增强、特征提取和数据增强等领域。缩放用于调整图像的尺寸,而裁剪用于提取图像的特定区域。通过OpenCV,开发者可以高效地实现这些操作,并结合其他图像处理技术,满足多样化的应用需求。

图像缩放

图像缩放是指调整图像的宽度和高度,可以是放大(增大尺寸)或缩小(减小尺寸)。缩放操作不仅影响图像的视觉尺寸,还影响存储和处理的计算量。

使用cv2.resize()进行图像缩放
import cv2

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    # 定义新的尺寸
    new_width, new_height = 800, 600

    # 使用双线性插值进行缩放
    resized_linear = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)

    # 使用最近邻插值进行缩放
    resized_nearest = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_NEAREST)

    # 使用立方插值进行缩放
    resized_cubic = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_CUBIC)

    # 使用基于区域关系的插值进行缩放
    resized_area = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_AREA)

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Resized Linear', resized_linear)
    cv2.imshow('Resized Nearest', resized_nearest)
    cv2.imshow('Resized Cubic', resized_cubic)
    cv2.imshow('Resized Area', resized_area)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('resized_linear.jpg', resized_linear)
    cv2.imwrite('resized_nearest.jpg', resized_nearest)
    cv2.imwrite('resized_cubic.jpg', resized_cubic)
    cv2.imwrite('resized_area.jpg', resized_area)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 插值方法
    • cv2.INTER_LINEAR:双线性插值,适用于大多数缩放需求。
    • cv2.INTER_NEAREST:最近邻插值,速度快,但质量较低,适用于需要快速处理的场景。
    • cv2.INTER_CUBIC:四次插值,适用于放大图像,提供更高的图像质量。
    • cv2.INTER_AREA:基于区域关系的重采样方法,适用于图像缩小,能够减少混叠现象。
自动保持宽高比的缩放

在实际应用中,为了避免图像变形,通常需要自动保持宽高比进行缩放。可以通过计算缩放因子,根据新的宽度或高度自动调整另一维度。

import cv2

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    # 获取原始尺寸
    original_height, original_width = image.shape[:2]

    # 设置新的宽度
    new_width = 500
    scale_factor = new_width / original_width
    new_height = int(original_height * scale_factor)

    # 缩放图像
    resized_image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Resized Image with Aspect Ratio', resized_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('resized_aspect_ratio.jpg', resized_image)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 通过设定新的宽度,计算相应的高度,保持原始图像的宽高比,避免图像变形。
图像裁剪

图像裁剪(Cropping)是指从原始图像中提取出一个子区域,通常用于关注图像的特定部分或去除不必要的区域。裁剪操作简单高效,广泛应用于图像编辑和预处理。

使用数组切片进行图像裁剪

在OpenCV中,图像被表示为NumPy数组,可以通过数组切片直接实现裁剪。

import cv2

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    # 获取图像尺寸
    height, width = image.shape[:2]

    # 定义裁剪区域(y_start:y_end, x_start:x_end)
    y_start, y_end = 100, 400
    x_start, x_end = 150, 450

    # 确保裁剪区域在图像范围内
    y_start = max(0, y_start)
    y_end = min(height, y_end)
    x_start = max(0, x_start)
    x_end = min(width, x_end)

    # 裁剪图像
    cropped_image = image[y_start:y_end, x_start:x_end]

    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Cropped Image', cropped_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('cropped.jpg', cropped_image)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 通过指定裁剪区域的起始和结束坐标,提取出图像中的特定区域。
  • 使用max()min()函数确保裁剪区域在图像的有效范围内,避免索引错误。
动态裁剪与界面交互

在实际应用中,用户可能需要动态选择裁剪区域,可以通过鼠标事件实现交互式裁剪。

import cv2

# 初始化全局变量
cropping = False
start_point = ()
end_point = ()
cropped_image = None

# 鼠标回调函数
def crop_rectangle(event, x, y, flags, param):
    global cropping, start_point, end_point, cropped_image, image_copy

    if event == cv2.EVENT_LBUTTONDOWN:
        cropping = True
        start_point = (x, y)
        end_point = (x, y)

    elif event == cv2.EVENT_MOUSEMOVE:
        if cropping:
            end_point = (x, y)

    elif event == cv2.EVENT_LBUTTONUP:
        cropping = False
        end_point = (x, y)
        cv2.rectangle(image_copy, start_point, end_point, (0, 255, 0), 2)
        cv2.imshow("Image", image_copy)

        # 裁剪图像
        x1, y1 = start_point
        x2, y2 = end_point
        cropped_image = image[y1:y2, x1:x2]
        cv2.imshow("Cropped Image", cropped_image)

# 读取图像
image = cv2.imread('test.jpg')
image_copy = image.copy()

if image is not None:
    cv2.namedWindow("Image")
    cv2.setMouseCallback("Image", crop_rectangle)

    print("请用鼠标拖动选择裁剪区域,然后松开鼠标按钮。按 'q' 退出。")

    while True:
        cv2.imshow("Image", image_copy)
        key = cv2.waitKey(1) & 0xFF

        if key == ord("q"):
            break

    cv2.destroyAllWindows()

    if cropped_image is not None:
        cv2.imshow("Final Cropped Image", cropped_image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        cv2.imwrite('final_cropped.jpg', cropped_image)
    else:
        print("未进行裁剪操作。")
else:
    print("Error: 无法读取图像文件。")

说明:

  • 设置鼠标回调函数,通过鼠标点击和拖动选择裁剪区域。
  • 实时绘制裁剪框,松开鼠标按钮后显示裁剪后的图像。
  • 适用于需要用户交互选择裁剪区域的应用,如图片编辑软件。
缩放与裁剪的综合应用

在图像预处理阶段,缩放与裁剪常常结合使用,例如在对象检测前将图像调整为统一尺寸并裁剪出感兴趣区域。

import cv2

# 读取图像
image = cv2.imread('test.jpg')

if image is not None:
    # 定义缩放参数
    new_width, new_height = 500, 500
    resized = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)

    # 定义裁剪区域
    y_start, y_end = 100, 400
    x_start, x_end = 100, 400
    cropped = resized[y_start:y_end, x_start:x_end]

    # 显示结果
    cv2.imshow('Resized Image', resized)
    cv2.imshow('Cropped Image', cropped)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存结果
    cv2.imwrite('resized.jpg', resized)
    cv2.imwrite('cropped_resized.jpg', cropped)
else:
    print("Error: 无法读取图像文件。")

说明:

  • 首先将图像缩放到统一尺寸,确保后续处理的一致性。
  • 然后裁剪出感兴趣区域,减少处理的计算量和噪声干扰。
常见问题及解决方案
  1. 缩放后图像失真

    • 原因:缩放因子设置不合理,或未保持宽高比。
    • 解决方案:确保在缩放时保持图像的宽高比,避免非均匀缩放导致图像变形。
  2. 裁剪区域超出图像边界

    • 原因:裁剪坐标设置不正确,超过图像实际尺寸。
    • 解决方案:在裁剪前,检查并调整裁剪坐标,确保在图像的有效范围内。
  3. 性能问题

    • 原因:处理高分辨率图像时,缩放和裁剪操作计算量大。
    • 解决方案:降低图像分辨率,或使用更高效的算法和硬件加速技术。
  4. 颜色空间问题

    • 原因:在缩放和裁剪过程中,颜色空间发生变化,导致图像颜色异常。
    • 解决方案:确保在整个过程中保持一致的颜色空间,必要时进行颜色空间转换。
缩放与裁剪的最佳实践
  1. 保持图像质量

    • 选择合适的插值方法,如放大时使用cv2.INTER_CUBIC,缩小时使用cv2.INTER_AREA,以保持图像质量。
  2. 自动计算裁剪区域

    • 根据图像内容或特定需求,自动计算裁剪区域的位置和尺寸,实现智能裁剪。
  3. 批量处理

    • 对多张图像进行批量缩放与裁剪,结合循环和自动化脚本,提高处理效率。
  4. 结合其他处理步骤

    • 将缩放与裁剪与其他图像处理步骤(如过滤、增强、特征提取)结合使用,形成完整的图像处理流水线。
总结

缩放与裁剪是图像几何变换中最常用的基本操作,通过调整图像尺寸和提取特定区域,开发者可以实现多种图像预处理和增强功能。OpenCV提供了简单高效的函数接口,使得这些操作变得直观易用。合理选择缩放因子、保持宽高比以及正确设置裁剪区域,是确保图像处理效果的关键。结合实际需求和应用场景,灵活运用缩放与裁剪技术,可以显著提升图像处理的效率和质量。


结语

第四章详细探讨了图像几何变换的关键技术,包括仿射变换、透视变换、图像配准以及缩放与裁剪。这些变换技术在计算机视觉和图像处理中广泛应用,为图像的对齐、校正、增强和分析提供了强大的工具。通过理解每种变换的数学原理和OpenCV的实现方法,开发者能够在各种项目中灵活应用这些技术,解决实际问题,提升图像处理的精准度和效果。

在后续的章节中,我们将继续深入探讨更高级的图像处理技术和应用场景,如图像分割、目标检测、深度学习在图像处理中的应用等,帮助读者全面提升在计算机视觉领域的技能和理解。


http://www.niftyadmin.cn/n/5820181.html

相关文章

python学习笔记—17—数据容器之字符串

1. 字符串 (1) 字符串能通过下标索引来获取其中的元素 (2) 旧字符串无法修改特定下标的元素 (3) index——查找字符串中任意元素在整个字符串中的起始位置(单个字符或字符串都可以) tmp_str "supercarrydoinb" tmp_position1 tmp_str.index("s") tmp_p…

磁盘满造成业务异常问题排查

最近遇到一个因为磁盘满导致的问题&#xff0c;分享一下&#xff0c;希望能够帮助到以后遇到同样问题的朋友。 早上突然收到业务老师反馈说&#xff1a;上传文件不能正常上传了。 想想之前都好好的&#xff0c;最近又没有更新&#xff0c;为什么突然不能使用了呢&#xff1f;…

From Orthogonal Time Frequency Space to Affine Frequency Division Multiplexing

From Orthogonal Time Frequency Space to Affine Frequency Division Multiplexing 《从正交时频空间到仿射频分复用》 下一代无线系统将集成感知与通信&#xff08;ISAC&#xff09;功能&#xff0c;不仅为了实现新的应用&#xff0c;还为了应对高移动性场景以及毫米波&…

HTTPS协议的基础与工作原理

什么是HTTPS&#xff1f; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是HTTP协议的安全版本&#xff0c;它通过SSL/TLS协议对通信数据进行加密&#xff0c;确保数据的安全传输。与HTTP相比&#xff0c;HTTPS能防止数据被窃取、篡改或伪造&#xff0c;广…

Docker compose 使用 --force-recreate --no-recreate 控制重启容器时的行为【后续】

前情&#xff1a;上一篇实际是让AI工具帮我总结了一下讨论的内容&#xff0c;这里把讨论的过程贴出来&#xff0c;这个讨论是为解决实际问题 前文https://blog.csdn.net/wgdzg/article/details/145039446 问题说明&#xff1a; 我使用 docker compose 管理我的容器&#xff0…

openGauss系列 --启动和停止服务器

https://docs.opengauss.org/zh/docs/6.0.0/docs/DatabaseOMGuide/%E5%90%AF%E5%81%9CopenGauss.html 启动服务器 以操作系统用户omm登录数据库主节点。 使用以下命令启动openGauss。 gs_om -t start [ommlab6 ~]$ gs_om -t start Starting cluster. [SUCCESS] lab6 2024-12-…

KGA:AGeneral Machine Unlearning Framework Based on Knowledge Gap Alignment

文章目录 摘要1 引言2 相关工作3 符号与定义4 我们的 KGA 框架4.1 KGA框架知识差距对齐目标 4.2 KGA在自然语言处理任务中的应用文本分类机器翻译响应生成 5 实验设置数据集评估指标参数设置比较方法 6 实验结果6.1 主要比较结果6.2 KGA 的优越性分析降低语言模型概率比较 6.3 …

uniapp 导入uview-plus,使用组件出现,页面出现<up-parse>元素不存在,请检查你的代码

错误截图&#xff1a; 原因&#xff1a; 未按照官网方式进行配置&#xff0c;需要进行以下配置。具体详情 // pages.json {"easycom": {"autoscan": true,// 注意一定要放在custom里&#xff0c;否则无效&#xff0c;https://ask.dcloud.net.cn/question…