TensorFlow图像旋转增强函数的逆变换逻辑与像素坐标设置原理问询
核心实现逻辑
这类图像几何增强采用逆映射采样思路:不直接把原图像素搬运到目标位置,而是遍历目标图的每一个像素点,反向计算该点在原图上对应的位置,取对应像素值填充到目标位置,完全避免目标图出现空洞。
目标像素索引段的坐标设计逻辑
坐标原点偏移的必要性
常规数组的像素坐标以左上角为(0,0),如果直接用这个坐标系做旋转、剪切、缩放运算,所有变换都会绕左上角执行,旋转后的图像会直接移出画布边界,丢失大半内容。因此所有几何变换的数学计算,都会把原点平移到图像正中心,此时绕原点执行的旋转、缩放、剪切,才是符合日常认知的“以图像中心为轴”的增强效果。
取DIM=224为例,逐行拆解坐标生成的实际数值:
DIM//2等于112,整个图像的中心坐标点为(0,0),x、y的取值范围均覆盖-112到111的对称区间,刚好匹配224的图像尺寸。x = tf.repeat( tf.range(DIM//2,-DIM//2,-1), DIM )
首先生成序列[112, 111, ..., 0, ..., -111],再把每个元素重复224次,最终得到长度为224*224的x向量。这段向量的前224个值全是112,对应目标图最左侧的一列像素;最后224个值全是-111,对应目标图最右侧的一列像素。y = tf.tile( tf.range(-DIM//2,DIM//2),[DIM] )
首先生成序列[-112, -111, ..., 0, ..., 111],再把这个序列整体重复224次,最终得到长度为224*224的y向量。这段向量的第一组224个值是[-112, -111, ..., 111],对应目标图最上面的一行像素;最后一组224个值也是[-112, -111, ..., 111],对应目标图最下面的一行像素。z = tf.ones([DIM*DIM],dtype='int32')是齐次坐标的固定补位,用来适配3×3变换矩阵的乘法运算。
为什么不用(0,0)为起点、DIM//2做偏移的写法
两种写法的数学本质完全等价,当前代码的写法只是把“原点移到中心”的步骤提前到了坐标生成阶段,在TensorFlow图执行时减少了一步张量减法运算,效率更高:
- 如果按常规思路,会先生成从(0,0)开始的全图像素数组坐标,再减去
(DIM//2, DIM//2)把原点移到中心,最终得到的中心坐标系坐标和当前代码生成的结果没有任何区别。 - 另外当前代码特意把x轴方向取反、y轴方向对齐数组行的存储顺序,后续转回数组索引、调用
tf.gather_nd取像素值的时候,不需要额外做行列转置,进一步减少了张量运算开销。
后续坐标转换的补偿逻辑
乘完变换矩阵m得到的idx2,是每个目标像素在原图中心坐标系下对应的位置。最后一段idx3 = tf.stack( [DIM//2-idx2[0,], DIM//2-1+idx2[1,]] ),就是把中心坐标系的坐标再转回数组识别的左上角原点索引:
- 中心坐标系x=112对应数组第0行(最顶部的行),代入公式
112 - 112 = 0,完全匹配;x=-111对应数组第223行(最底部的行),代入公式112 - (-111) = 223,计算正确。 - 加的
-1项是为了补偿偶数尺寸图像的半像素偏移:224的偶数尺寸图像,真实几何中心在第111.5个像素的位置,整数数组索引存在半格偏差,加-1修正后能避免中心旋转时出现像素错位。配套的tf.clip操作会把超出范围的坐标截断到合法数组边界,防止gather_nd取像素时出现越界错误。
内容的提问来源于stack exchange,提问作者Infrared1029
相关产品推荐
相关产品推荐

