如何从两组点对获取无剪切的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))))
实现说明
- 向量计算:通过两组点对的向量差直接推导缩放因子和旋转角度,避免构造额外辅助点。
- 旋转缩放矩阵:用
getRotationMatrix2D生成以第一个源点为中心的旋转缩放矩阵,再修正平移分量,确保变换后第一个源点精准对应第一个目标点。 - 无剪切保证:整个变换仅包含旋转、缩放和平移,完全符合需求,且代码结构更简洁,无需额外辅助函数。
运行该代码会得到与原实现一致的输出结果。
内容的提问来源于stack exchange,提问作者Tobias Hermann
相关产品推荐
相关产品推荐

