11 仿射变换矩阵与图像变换#
一句话总结
HALCON 把二维几何变换统一抽象成一个齐次变换矩阵 HomMat2D(本质就是 6 个实数组成的元组):用 hom_mat2d_identity 起手得到单位矩阵,再用 hom_mat2d_rotate / scale / translate / slant 依次往里"加"操作,最后交给 affine_trans_image 去真正变换图像。
全篇最该背下来的一句:行坐标传给 x、列坐标传给 y —— 也就是照常按 (Row, Column) 的顺序写就对。
源文件(本轮新增): -
note/仿射变换/01旋转平移缩放斜切.hdev-note/仿射变换/02刚性矩阵.hdev-note/仿射变换/03获取矩阵的参数.hdev素材: HALCON 自带示例图
printer_chip/printer_chip_01(read_image直接写名字即可)
一、仿射变换能做什么#
仿射变换(Affine Transformation)= 平移 + 旋转 + 缩放 + 斜切,它的特点是直线还是直线、平行线还是平行线。按能力从弱到强分四级,HomMat2D 能表达前三级的全部:
| 级别 | 包含的操作 | 自由度 | HALCON 里怎么来 |
|---|---|---|---|
| 刚体变换 Rigid | 旋转 + 平移(长度、角度都不变) | 3 | vector_angle_to_rigid、vector_to_rigid |
| 相似变换 Similarity | 刚体 + 等比缩放 | 4 | vector_to_similarity |
| 仿射变换 Affine | 相似 + 各向异性缩放 + 斜切 | 6 | hom_mat2d_* 手工拼、vector_to_hom_mat2d |
| 投影变换 Projective | 仿射 + 透视(平行线会相交) | 8 | vector_to_proj_hom_mat2d(不是 HomMat2D,是 HomMat3D) |
为什么要学它
① 把倾斜摆放的零件转正,后面的测量/打标才能用固定坐标;
② 把目标搬到画面中心,多张图之间可以直接逐像素比较;
③ 做模板匹配时把模板轮廓按匹配位姿"贴"到搜索图上(下一阶段 find_shape_model 的核心用法);
④ 做抠图,把 ROI 裁出来当模板。
二、核心算子表#
2.1 怎么得到矩阵#
| 算子 | 说明 |
|---|---|
hom_mat2d_identity (HomMat2DIdentity) |
生成单位矩阵(什么都不做),一切手工拼装的起点 |
hom_mat2d_rotate (HomMat2D, Phi, Px, Py, Out) |
逆时针旋转 Phi 弧度,固定点 (Px, Py) 不动 |
hom_mat2d_scale (HomMat2D, Sx, Sy, Px, Py, Out) |
绕固定点缩放,Sx 作用于 x 轴(=行方向)、Sy 作用于 y 轴(=列方向) |
hom_mat2d_translate (HomMat2D, Tx, Ty, Out) |
平移 Tx 行、Ty 列(无固定点参数) |
hom_mat2d_slant (HomMat2D, Theta, Axis, Px, Py, Out) |
斜切:Axis = 'x' 斜切 x 轴(y 轴不动),'y' 斜切 y 轴(x 轴不动) |
hom_mat2d_reflect (HomMat2D, Px, Py, Out) |
关于直线 (Px,Py)-(Px+1,Py) 镜像 |
vector_angle_to_rigid (Row1, Col1, Ang1, Row2, Col2, Ang2, Out) |
一个点 + 一个角度的对应关系 → 刚体矩阵(最常用) |
vector_to_rigid / vector_to_similarity / vector_to_aniso / vector_to_hom_mat2d |
由多组对应点反求矩阵,能力依次变强 |
hom_mat2d_invert (HomMat2D, HomMat2DInvert) |
求逆矩阵(正变换 ↔ 反变换) |
hom_mat2d_to_affine_par (HomMat2D, Sx, Sy, Phi, Theta, Tx, Ty) |
反解出 6 个变换参数 |
_local 后缀版本
hom_mat2d_rotate_local / hom_mat2d_scale_local / hom_mat2d_translate_local 是把操作加在局部坐标系(即矩阵自带的坐标系)里,和默认的"全局坐标系"版本方向相反。平时用不到,但官方那个 0.5 像素补偿就要用到 hom_mat2d_translate_local —— 见 [[12 仿射变换实战 区域轮廓与抠图]]。
2.2 怎么把矩阵用出去#
| 算子 | 变换对象 | 关键控制参数 |
|---|---|---|
affine_trans_image (Image, Out : HomMat2D, Interpolation, AdaptImageSize) |
Image(灰度重采样) | Interpolation 五档、AdaptImageSize |
affine_trans_region (Region, Out : HomMat2D, Interpolate) |
Region(二值像素集合) | 'nearest_neighbor' / 'constant' |
affine_trans_contour_xld (Contours, Out : HomMat2D) |
XLD 轮廓 | 无控制参数(只搬点) |
affine_trans_polygon_xld (Polygons, Out : HomMat2D) |
XLD 多边形 | 无 |
affine_trans_pixel (HomMat2D, Row, Col, RowT, ColT) |
单个点(像素坐标系) | 传参顺序 (Row, Col) |
affine_trans_point_2d (HomMat2D, Px, Py, Qx, Qy) |
单个点(标准坐标系) | 传参顺序 (Px, Py) = (Col, Row) |
流程图:矩阵的两条来路
flowchart TD
A["要变换 图像 / 区域 / 轮廓"] --> B{"矩阵从哪来?"}
B -->|"手工拼装"| C["hom_mat2d_identity<br/>起手:单位矩阵"]
C --> D["hom_mat2d_rotate / scale / translate / slant<br/>依次往矩阵里'加'操作"]
B -->|"已知两个位姿"| E["vector_angle_to_rigid<br/>(R1,C1,A1) → (R2,C2,A2)"]
B -->|"已知多组对应点"| F["vector_to_rigid / similarity / aniso / hom_mat2d"]
D --> G["HomMat2D(6 元组)"]
E --> G
F --> G
G --> H["affine_trans_image / region / contour_xld"]
G --> I["hom_mat2d_to_affine_par<br/>反解 Sx Sy Phi Theta Tx Ty"]
三、齐次矩阵长什么样(本节是重点)#
3.1 存储形式:6 个实数#
HomMat2D 在 HALCON 里不是一个 3×3 数组,而是一个长度为 6 的实数元组(最后一行恒为 0 0 1,所以省略不存),按行排列:
作用在一个点上:
参数名为什么是 ra / rb / tc / rd / re / tf
r 是 rotation(左上 2×2 的旋转+缩放+斜切部分),t 是 translation(第 3 列的平移)。a,b,d,e 是 2×2 块的四个元素,c,f 是两个平移分量。在 HDevelop 里可以直接把 HomMat2D 拖到变量窗口看这 6 个数。
3.2 坐标约定:Row 当 x,Column 当 y#
这是整个仿射变换最容易翻车的地方,所以直接抄官方原文:
官方原文(hom_mat2d_rotate / hom_mat2d_slant / affine_trans_image 都有这段话)
It should be noted that homogeneous transformation matrices refer to a general right-handed mathematical coordinate system. If a homogeneous transformation matrix is used to transform images, regions, XLD contours, or any other data that has been extracted from images, the row coordinates of the transformation must be passed in the x coordinates, while the column coordinates must be passed in the y coordinates. Consequently, the order of passing row and column coordinates follows the usual order (Row, Column). This convention is essential to obtain a right-handed coordinate system for the transformation of iconic data, and consequently to ensure in particular that rotations are performed in the correct mathematical direction.
翻译成人话:
| 数学坐标 | 对应图像坐标 |
|---|---|
| \(x\) | Row(行,向下) |
| \(y\) | Column(列,向右) |
Px |
传 Row |
Py |
传 Column |
Tx |
平移的行方向分量 |
Ty |
平移的列方向分量 |
Sx |
沿行方向的缩放 |
Sy |
沿列方向的缩放 |
记忆口诀
不用翻译,照平时写。 因为约定就是 Px ← Row、Py ← Column,所以写 hom_mat2d_rotate (M, rad(45), Row, Column, M2) 就是对的 —— 和 area_center 输出 (Row, Column)、disp_arrow 收 (Row, Column) 是同一个顺序,全程都是 (Row, Column)。
千万别自作聪明地"翻译成 x,y"再交换一次,那反而错了。
3.3 正角度到底往哪边转#
答案是 正角度 = 屏幕上逆时针。可以推导一遍来验证:
把 \(x=\text{Row}\)、\(y=\text{Column}\) 代进旋转公式 \(x'=x\cos\theta-y\sin\theta,\;y'=x\sin\theta+y\cos\theta\):
取一个指向"右"的向量 \((\text{Row}=0,\ \text{Column}=L)\),在 \(\theta=90^\circ\) 时得到 \((\text{Row}'=-L,\ \text{Column}'=0)\),即指向上方。屏幕上"向右"转到"向上"就是逆时针。
和 [[09 实战 曲别针计数与角度]] 对上号了
画主轴箭头用的是 HALCON 官方写法:
终点方向向量是 \((-L\sin\Phi,\ +L\cos\Phi)\) —— 和上面把旋转矩阵代进去算出来的结果一字不差。这说明:hom_mat2d_rotate 的正方向、orientation_region 的正方向、disp_arrow 画箭头的方向,三者是同一个约定,全部统一在"x=Row、y=Column"之下。
所以别记"Row 往下所以是顺时针"—— 只有加了这条约定,旋转方向才对。
3.4 多次操作的叠加顺序:先写的先作用#
hom_mat2d_rotate / scale / translate / slant 都是把新操作加到矩阵的最外层(在这之前用"全局坐标系"表达),等价于左乘:
也就是说:代码里先写的操作,作用在对象上时先发生。 官方示例就是按这个顺序读的:
* Reduction of an image (512 x 512 Pixels) by 50%, rotation
* by 180 degrees and translation to the upper-left corner:
hom_mat2d_identity (Matrix1)
hom_mat2d_scale (Matrix1, 0.5, 0.5, 256.0, 256.0, Matrix2) * ① 先缩小一半
hom_mat2d_rotate (Matrix2, 3.14, 256.0, 256.0, Matrix3) * ② 再绕中心转 180°
hom_mat2d_translate (Matrix3, -128.0, -128.0, Matrix4) * ③ 最后平移到左上角
affine_trans_image (Image, TransImage, Matrix4, 'constant', 'true')
这条推论很实用:固定点的坐标是\"当前(全局)坐标系\"下的点
既然后面的操作作用在"已经被前面的操作搬走过的"对象上,那么后面操作里的 (Px, Py) 必须写成"搬走之后的"位置。
典型错误:先 vector_angle_to_rigid 把目标质心搬到画面中心,接着 hom_mat2d_scale (M, 0.5, 0.5, Row, Column, ...) —— 此时的 (Row, Column) 是原来的质心坐标,不是画面中心,结果会缩偏。想"原地缩小"应该写 512/2, 512/2。详见 [[12 仿射变换实战 区域轮廓与抠图]] 里对 05对轮廓进行仿射变换.hdev 的推算。
四、逐文件解读#
4.1 01旋转平移缩放斜切.hdev#
dev_close_window ()
read_image (Image, 'printer_chip/printer_chip_01')
dev_open_window (0, 0, 512, 512, 'black', WindowHandle)
dev_display (Image)
area_center (Image, Area, X, Y) * 拿到"整幅图"的面积与中心
hom_mat2d_identity (HomMat2DIdentity) * ① 单位矩阵起手
*hom_mat2d_rotate (HomMat2DIdentity,rad(90), X, Y, HomMat2DRotate)
*hom_mat2d_translate (HomMat2DIdentity, 100, 200, HomMat2DTranslate)
*hom_mat2d_scale (HomMat2DIdentity, 0.5, 0.8, Y, X, HomMat2DScale)
hom_mat2d_slant (HomMat2DIdentity, rad(-20), 'y', 0, 0, HomMat2DSlant) * ② 斜切
*hom_mat2d_translate (HomMat2DIdentity, 200, 200, HomMat2DTranslate)
*hom_mat2d_rotate (HomMat2DIdentity, rad(20), X, Y, HomMat2DRotate)
*hom_mat2d_scale (HomMat2DRotate, 0.5, 0.5, X, Y, HomMat2DScale)
affine_trans_image (Image, ImageAffineTrans, HomMat2DSlant, 'constant', 'false') * ③ 变换
这文件就是"四种变换的试验台":每次只放开一行注释,看效果。四行对应的分别是:
| 放开哪一行 | 效果 |
|---|---|
hom_mat2d_rotate (…, rad(90), X, Y, …) |
以图像中心为轴逆时针转 90° |
hom_mat2d_translate (…, 100, 200, …) |
向下移 100 行、向右移 200 列 |
hom_mat2d_scale (…, 0.5, 0.8, Y, X, …) |
行方向压 0.5、列方向压 0.8 |
hom_mat2d_slant (…, rad(-20), 'y', 0, 0, …) |
y 轴(列方向)倾斜 20°,x 轴(行方向)不动 → 剪切变形 |
这份练习里有一处坐标顺序写反了
作者给变量起名 X, Y,但 area_center 的实际输出顺序是 Area, Row, Column,所以 X 里装的是 Row、Y 里装的是 Column。以此为基准逐行检查:
| 行 | 写法 | 实际传入 | 对不对 |
|---|---|---|---|
hom_mat2d_rotate (…, X, Y, …) |
Px=X=Row、Py=Y=Column |
(Row, Column) |
✅ 正确 |
hom_mat2d_scale (…, Y, X, …) |
Px=Y=Column、Py=X=Row |
(Column, Row) |
❌ 反了 |
hom_mat2d_rotate (…, X, Y, …) |
同第一行 | (Row, Column) |
✅ 正确 |
hom_mat2d_scale (…, X, Y, …) |
同第二行的反向 | (Row, Column) |
✅ 正确 |
也就是说只有那一行 scale 是错的(两处固定点参数被交换)。写成 (Row, Column) 就永远是对的,根本不需要绕 X/Y 这个弯。
建议把变量名直接改成 Row, Column,让顺序问题在命名阶段就消失。
hom_mat2d_slant 的参数含义
Axis = 'y' 表示斜切 y 轴:y 轴(图像里就是列方向)逆时针转过 Theta,x 轴(行方向)保持不动;'x' 则相反。固定点 (0, 0) 是左上角。所以 rad(-20), 'y' 的效果是图形沿着行方向被"推歪"。
4.2 02刚性矩阵.hdev#
dev_close_window ()
read_image (Image, 'printer_chip/printer_chip_01')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width/3, Height/3, 'black', WindowHandle) * 开个 1/3 大小的窗口
dev_display (Image)
area_center (Image, Area, Row, Column) * 整幅图的中心
orientation_region (Image, Phi) * 整幅图的方向
vector_angle_to_rigid (Row, Column, -rad(360), Row+100, Column+100, -rad(350), HomMat2D)
affine_trans_image (Image, ImageAffineTrans, HomMat2D, 'constant', 'false')
dev_clear_window ()
dev_display (ImageAffineTrans)
这一节的主角是 vector_angle_to_rigid —— 最常用的"一步得到矩阵"的算子。
它的语义是:构造一个刚体变换,把"源位姿"映射到"目标位姿",其中"位姿"=一个点 + 一个角度。官方给它的定位就是模板匹配的标准搭档:
* 官方示例(find_shape_model 之后必然写这两行)
vector_angle_to_rigid (0, 0, 0, Row, Column, Angle, HomMat2DObject)
affine_trans_contour_xld (ModelXLD, ObjectXLD, HomMat2DObject)
回到本练习,逐项折算一下:
| 参数 | 值 | 含义 |
|---|---|---|
Row1, Column1 |
Row, Column |
整幅图的中心(变换前) |
Angle1 |
-rad(360) |
= -360° ≡ 0°,也就是"没转"(写成这样纯粹是为了和下一项对称好看) |
Row2, Column2 |
Row+100, Column+100 |
中心往右下各移 100 像素 |
Angle2 |
-rad(350) |
≡ +10° |
净效果 = 先原地逆时针转 \((-350°)-(-360°) = +10°\),再整体向右下平移 100 像素。
为什么这个算子比手拼矩阵省事
手拼"绕点 \(A\) 转 \(10°\) 再平移到 \(B\)"要写三行(hom_mat2d_rotate 绕 A + hom_mat2d_translate),而且固定点、平移量的正负号很容易算错。vector_angle_to_rigid 只要把"从哪来、到哪去"两组数填进去就行 —— 能说清起点终点,就别自己算矩阵。
dev_open_window (0, 0, Width/3, Height/3, …) 的一个小现象
窗口只有图像的 1/3 大,而 dev_display 是按 1:1 画的,所以这时窗口里只能看到图像的左上角一块。真想在小窗口里看全图,要显式改"显示区域":
dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) 让窗口自动适配。
area_center 传图像是允许的
area_center 官方签名写的是 area_center(Regions : : : Area, Row, Column),输入类型声明为 region。但 HALCON 也接受直接传 Image(此时按图像的 domain,默认就是整幅矩形来算),所以这两行能跑通,含义就是"整幅图的面积与中心"。
规范一点的写法是显式取出来,语义更清楚:
4.3 03获取矩阵的参数.hdev#
dev_clear_window ()
dev_get_window (WindowHandle)
read_image (Image, 'printer_chip/printer_chip_01')
hom_mat2d_identity (HomMat2DIdentity) * ① 单位矩阵
hom_mat2d_rotate (HomMat2DIdentity, rad(-45), 0, 0, HomMat2DRotate) * ② 绕左上角转 -45°
hom_mat2d_scale (HomMat2DRotate, 0.8, 0.8, 0, 0, HomMat2DScale) * ③ 绕左上角缩 0.8
hom_mat2d_translate (HomMat2DScale, 64, 64, HomMat2DTranslate) * ④ 平移 (64, 64)
affine_trans_image (Image, ImageAffineTrans, HomMat2DTranslate, 'constant', 'false')
hom_mat2d_to_affine_par (HomMat2DTranslate, Sx, Sy, Phi, Theta, Tx, Ty) * ⑤ 反解参数
前面 ①~④ 是正向拼矩阵,⑤ 是反向拆矩阵,这一节就学这一拆一装。
hom_mat2d_to_affine_par 的输出含义(顺序不能记错):
| 输出 | 含义 |
|---|---|
Sx |
沿 x(行) 方向的缩放,恒为正 |
Sy |
沿 y(列) 方向的缩放,恒为正 |
Phi |
变换后 x 轴相对原 x 轴的旋转角(弧度) |
Theta |
斜切角;Theta = 0 表示两坐标轴仍正交。若含镜像,该项会带负值 |
Tx |
沿 x(行) 方向的平移 |
Ty |
沿 y(列) 方向的平移 |
本例反解回来就是 Sx = Sy = 0.8、Phi = rad(-45)、Theta = 0、Tx = Ty = 64。
反解有一个隐藏前提:它只认一种\"标准拼法\"
官方文档写明,这 6 个参数是按固定顺序拼出来的矩阵才能被反解回去:
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_scale (HomMat2DIdentity, Sx, Sy, 0, 0, HomMat2DScale) * ① 缩放
hom_mat2d_slant (HomMat2DScale, Theta, 'y', 0, 0, HomMat2DSlant) * ② 斜切
hom_mat2d_rotate (HomMat2DSlant, Phi, 0, 0, HomMat2DRotate) * ③ 旋转
hom_mat2d_translate (HomMat2DRotate, Tx, Ty, HomMat2D) * ④ 平移
之所以还能反解出正确的 0.8 / -45° / 64,是因为这里用了等比缩放(Sx = Sy = 0.8),等比缩放矩阵和旋转矩阵可交换(\(S\cdot R = R\cdot S\)),所以 \(T\cdot S\cdot R = T\cdot R\cdot S\),恰好等于标准形式。
如果换成各向异性缩放(比如 0.5, 0.8),两种顺序就不相等了,hom_mat2d_to_affine_par 解出来的 Sx / Sy / Phi / Theta 就不再是你填进去的那几个数。
结论:想反解,就老老实实按官方那四行的顺序拼;或者反过来说,hom_mat2d_to_affine_par 的结果只在"按官方顺序拼"的前提下有明确物理意义。
这一对算子的实用场景
- 正着用:手工设计一个变换(比如"零件转正 + 缩到固定尺寸 + 移到画面中心"),存成矩阵。
- 反着用:拿到别人给的/匹配算法算出的矩阵,想知道它到底转了多远、缩了多少、移了多少 —— 直接
hom_mat2d_to_affine_par打印出来。 - 反变换:测量做完后要把结果换算回原图尺度,就用
hom_mat2d_invert把矩阵取反再作用一次。
五、affine_trans_image 的参数详解#
5.1 Interpolation:灰度重采样方式(五档)#
变换后的点通常落在像素之间,必须插值。慢→快 / 差→好的大致排序:
| 取值 | 算法 | 说明 |
|---|---|---|
'nearest_neighbor' |
最近邻 | 取最近像素的灰度。最快、质量最低(会有锯齿) |
'bilinear' |
双线性 | 由周围 4 像素双线性插值。放大时小于 1 倍会不抗锯齿(有混叠) |
'constant' |
双线性 + 抗混叠 | 双线性基础上,缩小(倍率 < 1)时改用均值滤波防混叠。默认值,性价比最高 |
'weighted' |
双线性 + 高斯 | 缩小时用高斯滤波防混叠,质量最好,较慢 |
'bicubic' |
双三次 | 高精度放大效果好,但同样不抗缩小混叠,最慢 |
怎么选
不确定就用默认的 'constant'(它自带"缩小防混叠",最不容易出错)。
追求速度(大批量、粗定位)用 'nearest_neighbor';追求放大质量用 'bicubic';追求缩小质量用 'weighted'。
练习文件里全部写 'constant' 是稳妥的选择。
5.2 AdaptImageSize:输出图像多大#
| 取值 | 效果 |
|---|---|
'false'(默认) |
输出和输入等大,超出部分被裁掉 |
'true' |
自动放大输出尺寸,保证右下方向的内容不被裁 |
上下左右不对称:永远裁左上
官方原文:无论 AdaptImageSize 取什么值,图像总会被裁掉左边界和上边界,即变换后落在负坐标的部分一律丢弃。
所以"把图往左、往上移"一定会丢内容 —— 这也是为什么 'true' 只解决"右下方装不下"的问题。
5.3 两个容易被忽略的官方说明#
输入的 domain 被忽略
affine_trans_image 把输入图像当作整幅矩形处理(The region of the input image is ignored);输出图像的 domain 是"变换后的矩形"与"输出图像矩形"的交集。
也就是说:先用 reduce_domain 限定范围、再 affine_trans_image,那个限定是无效的 —— 想缩小处理范围得先 crop_domain 或 crop_part。
0.5 像素的坐标系差异(官方专门写了这段)
affine_trans_image / affine_trans_region 不使用 HALCON 标准坐标系(原点在左上角像素的中心),而是用 affine_trans_pixel 那套坐标系(原点在左上角像素的外角)。
后果:如果你用 area_center 之类的算子算出某个中心、再绕这个点做变换,结果不会精确地压在原位置上,会差 0.5 像素。官方给的补偿写法:
六、坑位清单#
仿射变换十坑
- 坐标顺序:
Px ← Row、Py ← Column,也就是照常写(Row, Column)。别"翻译"。 - 正方向:正角度 = 屏幕上逆时针(不是顺时针)。
- 单位:角度参数一律是弧度。要写
rad(90),直接写90就是 90 弧度(≈5157°)。 - 固定点必须给对:
hom_mat2d_rotate / scale / slant的(Px, Py)是"不动的那个点"。想绕图像中心转就传图像中心,想绕左上角转就传0, 0—— 传0,0和传中心视觉效果差别巨大。 - 叠加顺序:先写的先作用;后面操作里的固定点要写"前一步搬完之后"的位置。
- 反解只在标准顺序下成立:
hom_mat2d_to_affine_par只认scale → slant → rotate → translate的拼装顺序。 identity别忘了:手工拼矩阵必须用hom_mat2d_identity起手,否则HomMat2D变量里是空的/上一次的残留。AdaptImageSize='false'时左、上一定被裁,往左上平移会丢内容。affine_trans_image忽略输入 domain:reduce_domain挡不住它。- 整数除法:
512/2得 256 没问题,但Width/3在Width = 512时得 170(截断);想要小数就写Width/3.0。窗口大小这种参数用整数没问题,坐标计算时务必用2.0、3.0这种写法。
七、自检#
Question
HomMat2D在 HALCON 里是一个变量还是一个元组?里面存了几个数、按什么顺序?- 为什么官方规定"Row 传给 x、Column 传给 y"?如果不这么干,旋转方向会怎样?
hom_mat2d_rotate (M, rad(30), 0, 0, M2)和hom_mat2d_rotate (M, rad(30), 256, 256, M2)有什么区别?- 已知
Row1, Col1, Angle1和Row2, Col2, Angle2,用一个算子直接得到矩阵,用哪个? hom_mat2d_to_affine_par的 6 个输出分别是什么?为什么练习里rotate → scale → translate的顺序还能反解出正确参数?affine_trans_image的Interpolation五档里,哪一档是默认值?它和'bilinear'的关键区别是什么?- 想把图整体向左上移动 50 像素,用
AdaptImageSize='false'会看到什么现象?为什么?
上一步:算子速查表 · 下一步:仿射变换实战 区域轮廓与抠图