You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从两组点对获取无剪切的2D变换矩阵?求简化实现方案

问题描述

OpenCV提供以下函数:

  • getRotationMatrix2D:通过center、angle和scale获取包含旋转、缩放、平移的2x3变换矩阵
  • getAffineTransform:通过三组点对获取包含旋转、缩放、平移、剪切的2x3变换矩阵

我需要从两组点对中获取仅包含旋转、缩放和平移(即无剪切)的变换矩阵。

以下是我当前的实现代码,可正常运行但过于复杂:

from typing import Tuple, List

import cv2
import numpy as np
import numpy.typing


def _third_triangle_point(p1: Tuple[float, float], p2: Tuple[float, float]) -> Tuple[float, float]:
    """Calculate the third point of an isosceles right-angled triangle."""
    p1_arr = np.array(p1, dtype=np.float32)
    p2_arr = np.array(p2, dtype=np.float32)
    diff = p2_arr - p1_arr
    perpendicular = np.array((diff[1], -diff[0]), dtype=np.float32)
    result = p1_arr + perpendicular
    return result[0], result[1]


def _stack_points(points: List[Tuple[float, float]]) -> np.typing.NDArray[np.float32]:
    return np.vstack([np.array(p, dtype=np.float32) for p in points])


def get_transformation_between_two_point_pairs(
        src: Tuple[Tuple[float, float], Tuple[float, float]],
        dst: Tuple[Tuple[float, float], Tuple[float, float]]
) -> np.typing.NDArray[np.float32]:
    # cv2.getAffineTransform takes three point pairs.
    # It supports rotation, translation, scaling, and shearing.
    # We don't need the shearing,
    # so we invent a third point with a stable relation to the given two.
    return cv2.getAffineTransform(  # type: ignore
        _stack_points([src[0], src[1], _third_triangle_point(src[0], src[1])]),
        _stack_points([dst[0], dst[1], _third_triangle_point(dst[0], dst[1])])
    )


print(get_transformation_between_two_point_pairs(((10, 10), (17, 23)), ((30, 30), (70, 30))))

运行输出:

[[ 1.28440367  2.3853211  -6.69724771]
 [-2.3853211   1.28440367 41.00917431]]

请问是否有更简单的实现方式?


简化实现方案

可以直接通过计算向量的缩放、旋转参数推导平移量,无需构造额外点对。核心思路是利用两组点对的向量关系,直接构建无剪切的2x3变换矩阵:

import numpy as np
import cv2
from typing import Tuple

def get_transformation_between_two_point_pairs(
    src: Tuple[Tuple[float, float], Tuple[float, float]],
    dst: Tuple[Tuple[float, float], Tuple[float, float]]
) -> np.ndarray:
    # 转换为numpy数组
    src1, src2 = np.array(src[0], dtype=np.float32), np.array(src[1], dtype=np.float32)
    dst1, dst2 = np.array(dst[0], dtype=np.float32), np.array(dst[1], dtype=np.float32)
    
    # 计算源向量和目标向量
    src_vec = src2 - src1
    dst_vec = dst2 - dst1
    
    # 计算缩放因子
    scale = np.linalg.norm(dst_vec) / np.linalg.norm(src_vec)
    
    # 计算旋转角度(弧度转角度)
    src_angle = np.arctan2(src_vec[1], src_vec[0])
    dst_angle = np.arctan2(dst_vec[1], dst_vec[0])
    angle = np.degrees(dst_angle - src_angle)
    
    # 构造旋转缩放矩阵(以src1为中心)
    rot_mat = cv2.getRotationMatrix2D(tuple(src1), angle, scale)
    
    # 计算实际平移量:目标点dst1减去src1经过旋转缩放后的位置
    src1_rotated = rot_mat[:, :2] @ src1 + rot_mat[:, 2]
    translation = dst1 - src1_rotated
    
    # 调整矩阵的平移分量
    rot_mat[:, 2] += translation
    
    return rot_mat

# 测试
print(get_transformation_between_two_point_pairs(((10, 10), (17, 23)), ((30, 30), (70, 30))))

实现说明

  1. 向量计算:通过两组点对的向量差直接推导缩放因子和旋转角度,避免构造额外辅助点。
  2. 旋转缩放矩阵:用getRotationMatrix2D生成以第一个源点为中心的旋转缩放矩阵,再修正平移分量,确保变换后第一个源点精准对应第一个目标点。
  3. 无剪切保证:整个变换仅包含旋转、缩放和平移,完全符合需求,且代码结构更简洁,无需额外辅助函数。

运行该代码会得到与原实现一致的输出结果。


内容的提问来源于stack exchange,提问作者Tobias Hermann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:13:22