OpenCV 图像旋转和平移 数学和代码原理详解

文章目录

在OpenCV中进行图像旋转涉及到一些基本的几何变换和图像处理操作。

数学原理

在图像旋转中,背后的数学原理主要涉及二维欧几里得空间中的几何变换。具体来说,图像旋转可以通过二维旋转矩阵来实现。

旋转矩阵

对于一个二维平面上的点 (x, y),绕原点逆时针旋转角度 θ 后的新坐标 (x', y') 可以通过以下旋转矩阵计算得到:

平移和旋转

在实际应用中,图像通常不会绕原点旋转,而是绕图像的某个中心点 (cx, cy) 进行旋转。要实现绕任意点旋转,我们需要先将该点平移到原点,进行旋转,然后再平移回原来的位置。

具体步骤如下:

  1. 平移中心点到原点 :将中心点 (cx, cy) 平移到原点 (0, 0)
  2. 旋转:在原点进行旋转。
  3. 平移回原位置 :将旋转后的点再平移回 (cx, cy)

合成变换矩阵

综合上述步骤,绕任意点 (cx, cy) 逆时针旋转角度 θ 的变换矩阵可以表示为:

M = T ⋅ R ⋅ T − 1 M = T \cdot R \cdot T^{-1} M=T⋅R⋅T−1

其中:

  • ( T ) 是平移矩阵,用于将中心点平移到原点。
  • ( R ) 是旋转矩阵,用于在原点进行旋转。
  • ( T^{-1} ) 是逆平移矩阵,用于将旋转后的点平移回原位置。

具体形式为:

T = [ 1 0 − c x 0 1 − c y 0 0 1 ] T = \begin{bmatrix} 1 & 0 & -cx \\ 0 & 1 & -cy \\ 0 & 0 & 1 \end{bmatrix} T= 100010−cx−cy1

R = [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] R = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} R= cosθsinθ0−sinθcosθ0001

T − 1 = [ 1 0 c x 0 1 c y 0 0 1 ] T^{-1} = \begin{bmatrix} 1 & 0 & cx \\ 0 & 1 & cy \\ 0 & 0 & 1 \end{bmatrix} T−1= 100010cxcy1

所以综合后的旋转矩阵 ( M ) 为:

M = [ cos ⁡ θ − sin ⁡ θ c x ( 1 − cos ⁡ θ ) + c y sin ⁡ θ sin ⁡ θ cos ⁡ θ c y ( 1 − cos ⁡ θ ) − c x sin ⁡ θ 0 0 1 ] M = \begin{bmatrix} \cos\theta & -\sin\theta & cx(1-\cos\theta) + cy\sin\theta \\ \sin\theta & \cos\theta & cy(1-\cos\theta) - cx\sin\theta \\ 0 & 0 & 1 \end{bmatrix} M= cosθsinθ0−sinθcosθ0cx(1−cosθ)+cysinθcy(1−cosθ)−cxsinθ1

由于图像坐标是二维的,我们只需要前三列中的前两行:

M = [ cos ⁡ θ − sin ⁡ θ c x ( 1 − cos ⁡ θ ) + c y sin ⁡ θ sin ⁡ θ cos ⁡ θ c y ( 1 − cos ⁡ θ ) − c x sin ⁡ θ ] M = \begin{bmatrix} \cos\theta & -\sin\theta & cx(1-\cos\theta) + cy\sin\theta \\ \sin\theta & \cos\theta & cy(1-\cos\theta) - cx\sin\theta \end{bmatrix} M=[cosθsinθ−sinθcosθcx(1−cosθ)+cysinθcy(1−cosθ)−cxsinθ]

应用在OpenCV中的实现

在OpenCV中,函数 cv::getRotationMatrix2D 就是用来计算这个旋转矩阵的:

cpp 复制代码
cv::Mat cv::getRotationMatrix2D(cv::Point2f center, double angle, double scale);

center 参数表示旋转中心 (cx, cy)angle 表示旋转角度 θ,scale 表示缩放比例。

然后通过 cv::warpAffine 函数应用这个旋转矩阵来实现图像的旋转:

cpp 复制代码
cv::warpAffine(src, dst, M, cv::Size(width, height));

其中 M 就是通过 getRotationMatrix2D 计算得到的旋转矩阵。

总结一下,图像旋转的数学原理是通过平移和旋转组合的方式,利用二维旋转矩阵实现绕任意点的旋转。OpenCV中提供的函数封装了这些数学计算,使得图像旋转操作变得简单直观。

代码关键点解读

以胖虎为例

C++代码:

void rotate_demo(Mat &image){
    int width = image.cols;
    int height = image.rows;
    //计算旋转中心坐标
    Point2f center(width/2.0,height/2.0);
    double angle =180;
    Mat rotation_matrix = getRotationMatrix2D(center,angle,1.0);
    Mat rotate_image ;
    warpAffine(image,rotate_image,rotation_matrix,Size(width,height));
    imshow("Rotate Image",rotate_image);
}

调用上述代码就会发现一个问题: 图像是旋转了,但是旋转后的图像尺寸不对了 , 向右旋转之后,图像宽度不正常。宽度应该是原来的高度,原来的高度应该是宽度才对

warpAffine(image,rotate_image,rotation_matrix,Size(width,height));中尝试调换一下宽度和高度试试。

warpAffine(image,rotate_image,rotation_matrix,Size(height,width));

尝试运行之后,发现虽然窗口尺寸对了但是图像右上角都是黑边,原来的图像现了缺失

出现这个原因是我们一开始是按照中心点进行旋转,并不是从左上角开始旋转的,因此会出现图片缺失问题,因此我们需要把图像再平移回去 。平移回去的第一大问题就是旋转之后的图像宽度和高度发生了变化,我们需要重新计算旋转后的图像尺寸

OpenCV提供了计算旋转后的的图像边界尺寸的工具,bbox就是以某个角度旋转之后的矩阵,为什么需要这个函数?举个栗子,旋转九十度,图像的宽度是原来图像的高度,图像的高度是原来的宽度,但如果是旋转45度?旋转后的图像并不是原来图像的高度,需要重新计算。

 Rect bbox = RotatedRect(Point2f(), image.size(), angle).boundingRect();

旋转矩阵 rotation_matrix 是一个 2x3 的矩阵,其形式为:
[ a b t x c d t y ] \begin{bmatrix} a & b & tx \\ c & d & ty \end{bmatrix} [acbdtxty]

其中 txty 是平移部分。

因此我们需要调整tx和ty的值

  • 旋转后的图像尺寸 bbox.size() 的中心点坐标为 (bbox.width / 2.0, bbox.height / 2.0)
  • 原始旋转中心点为 center,坐标为 (center.x, center.y)
  • 调整 txty 的目的是将旋转中心点平移到新图像中心,使图像内容在旋转后居中。

调整后的 txty 计算如下:

  • tx:将图像沿 x 轴平移 bbox.width / 2.0 - center.x
  • ty:将图像沿 y 轴平移 bbox.height / 2.0 - center.y

对应的代码为:

rotation_matrix.at<double>(0, 2) += bbox.width / 2.0 - center.x;
rotation_matrix.at<double>(1, 2) += bbox.height / 2.0 - center.y;

at<double>(0, 2) 访问矩阵中的 tx 值,at<double>(1, 2) 访问矩阵中的 ty 值。

再来运行一下代码,可以看到此时图像就正常了。

试试45度旋转,修改angle的值为45,效果如下,注意图像的尺寸已经发生了改变,重新计算尺寸后不会造成像素丢失的问题

完整代码

C++代码:

void rotate_demo(Mat &image){
    int width = image.cols;
    int height = image.rows;
    //计算旋转中心坐标
    Point2f center(width/2.0,height/2.0);
    double angle =45;
    Mat rotation_matrix = getRotationMatrix2D(center,angle,1.0);
    //因为涉及到旋转,图像的高度和宽度其实发生了变化
    // 计算旋转后的图像边界尺寸
    Rect bbox = RotatedRect(Point2f(), image.size(), angle).boundingRect();
    rotation_matrix.at<double>(0, 2) += bbox.width / 2.0 - center.x;
    rotation_matrix.at<double>(1, 2) += bbox.height / 2.0 - center.y;

    Mat rotate_image ;
    warpAffine(image,rotate_image,rotation_matrix,bbox.size());
    imshow("Rotate Image",rotate_image);
}

Python代码:

import cv2
import numpy as np

def rotate_demo(image_path, angle=45):
    # 读取图像
    image = cv2.imread(image_path)
    if image is None:
        print("Could not read the image.")
        return

    height, width = image.shape[:2]

    # 计算旋转中心坐标
    center = (width / 2.0, height / 2.0)

    # 计算旋转矩阵
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)

    # 计算旋转后的图像边界尺寸
    corners = np.array([
        [0, 0],
        [width, 0],
        [width, height],
        [0, height]
    ])

    corners = np.hstack((corners, np.ones((4, 1))))
    rotated_corners = rotation_matrix.dot(corners.T).T

    x_coords = rotated_corners[:, 0]
    y_coords = rotated_corners[:, 1]

    bbox_width = int(np.ceil(x_coords.max() - x_coords.min()))
    bbox_height = int(np.ceil(y_coords.max() - y_coords.min()))

    # 调整旋转矩阵的平移部分
    rotation_matrix[0, 2] += bbox_width / 2.0 - center[0]
    rotation_matrix[1, 2] += bbox_height / 2.0 - center[1]

    # 执行旋转操作
    rotate_image = cv2.warpAffine(image, rotation_matrix, (bbox_width, bbox_height))

    # 显示旋转后的图像
    cv2.imshow("Rotate Image", rotate_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 调用旋转函数
rotate_demo("path_to_your_image.jpg", 45)
相关推荐
shiming887916 小时前
MATLAB图像处理
图像处理·计算机视觉·matlab
点PY18 小时前
基于Sparse Optical Flow 的Homography estimation
人工智能·opencv·计算机视觉
越甲八千18 小时前
opencv滤波算法总结
opencv
越甲八千18 小时前
opencv对比度增强方法算法汇总
人工智能·opencv·算法
独木三绝18 小时前
OpenCV第八章——腐蚀与膨胀
人工智能·opencv·计算机视觉
柠檬少少开发20 小时前
图像拼接算法及实现(一)
人工智能·算法·计算机视觉
红米煮粥20 小时前
OpenCV-直方图
人工智能·opencv·计算机视觉
隔窗听雨眠20 小时前
计算机视觉学习路线
计算机视觉
美狐美颜sdk20 小时前
探索视频美颜SDK与直播美颜工具的开发实践方案
人工智能·计算机视觉·音视频·直播美颜sdk·视频美颜sdk
极术社区21 小时前
ResNeXt学习
开发语言·学习·计算机视觉·php