☰
scikit-image 几何变换完全指南:裁剪、缩放、单应变换与参数估计实战
2026/10/6 7:32:23 网站建设 项目流程
  • 计算机视觉
  • 图像处理
  • 科学计算

【免费下载链接】scikit-image

Image processing in Python

项目地址:https://gitcode.com/gh_mirrors/sc/scikit-image
点击查看免费下载

几何变换是 scikit-image(skimage)图像处理的核心能力之一,覆盖从最基础的裁剪、缩放、重采样,到基于 3×3 矩阵的欧氏/相似/仿射/射影(单应)变换,再到通过对应点估计变换参数、借助 RANSAC 抗离群点的完整实战链路。本文以官方用户指南 geometrical_transform.rst 为主体骨架,结合skimage.transform、skimage.registration的真实源码与示例,为你逐层拆解每种变换的数学含义、构造方式、调用方法与失败处理模式,读完即可直接在自己的图像配准、矫正、拼接任务中落地使用。

图像几何变换的起点:裁剪、缩放与重采样

裁剪:基于 NumPy 数组的切片操作

在 scikit-image 中,图像本质上就是 NumPy 数组(参见 numpy_images.rst),因此裁剪一张图像就是一次普通的切片操作。下面的代码从 astronaut 图像的左上角裁出 100×100 的区域,且对所有颜色通道同时生效(颜色维度是最后一个维度):

import skimage as ski img = ski.data.astronaut() top_left = img[:100, :100]

由于颜色通道位于第三维,img[:100, :100]会保留全部 3 个通道,得到形状为(100, 100, 3)的彩色图块。切片只改变数组的视图(view),不复制底层数据,因此裁剪操作几乎零开销。

缩放(rescale)、调整尺寸(resize)与局部均值降采样(downscale)

要改变图像的形状,skimage.transform提供了三种语义不同的函数,完整的对比演示见 plot_rescale.py:

函数语义关键参数
rescale按缩放因子改变尺寸,因子可以是单个浮点数,也可以是每个轴各一个浮点数scale、anti_aliasing、anti_aliasing_sigma
resize与rescale目的一致,但直接指定输出形状而非缩放因子output_shape、anti_aliasing
downscale_local_mean按整数因子对 n 维图像降采样,使用局部均值聚合每个块内的元素factors(各轴的整数因子元组)

一个把三种方式放在一起的典型用法:

from skimage import data, color from skimage.transform import rescale, resize, downscale_local_mean image = color.rgb2gray(data.astronaut()) image_rescaled = rescale(image, 0.25, anti_aliasing=False) image_resized = resize( image, (image.shape[0] // 4, image.shape[1] // 4), anti_aliasing=True ) image_downscaled = downscale_local_mean(image, (4, 3))

需要注意的工程要点:

  • 降采样必须防混叠:对图像做缩小(down-sampling)时,resize和rescale应当执行高斯平滑以避免混叠伪影(aliasing artifacts),这正是anti_aliasing与anti_aliasing_sigma参数存在的意义。示例中rescale(..., anti_aliasing=False)会得到有明显锯齿的结果,而开启anti_aliasing=True的resize结果更平滑。
  • 局部均值降采样无混叠:downscale_local_mean通过在每个factors大小的块上取均值来实现降采样,本身就不会引入混叠,因此不需要anti_aliasing参数。
  • 这些函数在 src/skimage/transform/_warps.py 中统一导出(rescale、resize、downscale_local_mean、rotate、warp等),并注明HOMOGRAPHY_TRANSFORMS = (SimilarityTransform, AffineTransform, ProjectiveTransform),即warp对内支持的单应变换类型。

单应变换(Homographies)家族:从欧氏到射影

什么是单应变换

单应(Homography)是保持点共线性的欧氏空间变换。不同的单应子类型会额外保持更多性质,scikit-image 中按约束数量从多到少排列如下,完整可视化见 plot_transform_types.py:

变换类型保持的性质自由度构造参数
EuclideanTransform(欧氏/刚体)点对间欧氏距离、形状3(旋转+平移)rotation、translation
SimilarityTransform(相似)形状4(旋转+平移+各向同性缩放)scale、rotation、translation
AffineTransform(仿射)直线共线性与平行性6(可分解为相似+剪切)scale、rotation、translation、shear
ProjectiveTransform(射影/单应)直线共线性,不保持平行8matrix

所有单应变换在二维空间(即二维灰度或多通道图像)中都可以用一个3×3 矩阵定义。这些类的继承关系在源码中体现为:EuclideanTransform继承自ProjectiveTransform,SimilarityTransform又继承自EuclideanTransform,AffineTransform直接继承自ProjectiveTransform(见 src/skimage/transform/_geometric.py 与 src/_skimage2/transform/_geometric.py 中对应的类定义)。

两种构造方式:显式参数或完整矩阵

任何变换都可以用两种等价方式构造。方式一,使用显式几何参数(如 scale、shear、rotation、translation):

import numpy as np import skimage as ski tform = ski.transform.EuclideanTransform( rotation=np.pi / 12., translation=(100, -20) )

方式二,直接传入完整变换矩阵。以下矩阵与上面的参数完全等价(旋转 15° + 平移 (100, -20)):

matrix = np.array([[np.cos(np.pi/12), -np.sin(np.pi/12), 100], [np.sin(np.pi/12), np.cos(np.pi/12), -20], [0, 0, 1]]) tform = ski.transform.EuclideanTransform(matrix)

其他类型同理,例如用SimilarityTransform(scale=0.5, rotation=np.pi/12, translation=(100, 50))构造相似变换,用AffineTransform(shear=np.pi/6)构造纯剪切仿射变换,或用任意 3×3 矩阵构造射影变换(示例中给出了matrix = np.array([[1, -0.5, 100], [0.1, 0.9, 50], [0.0015, 0.0015, 1]])的射影例子)。

齐次坐标与矩阵运算

变换矩阵使用齐次坐标(Homogeneous coordinates)表示:它是笛卡尔坐标在射影几何中的推广,其关键优势是——即使无穷远处的点也能用有限坐标表示,使得投影几何中的运算封闭、统一。

在使用上,你需要记住三个基本操作:

  1. 读取矩阵:变换矩阵保存在tform.params属性中。
  2. 组合变换:用@矩阵乘法运算符将多个变换相乘。例如围绕图像中心旋转,需要"先平移到中心 → 旋转 → 平移回去"三步组合:
rotation = ski.transform.EuclideanTransform(rotation=np.pi / 3) shift = ski.transform.EuclideanTransform( translation=-np.array(img.shape[:2]) / 2 ) # 组合变换:把原点移到图像中心,旋转,再移回 matrix = np.linalg.inv(shift.params) @ rotation.params @ shift.params tform = ski.transform.EuclideanTransform(matrix)
  1. 直接调用:变换对象本身可调用,tform(coord)返回正向变换后的坐标;tform.inverse则是一个返回逆变换对象的方法/属性。

源码层面,ProjectiveTransform还支持用+运算符组合同类变换(相同类型组合得到同类,否则得到通用射影变换),该行为在 shim 层 src/skimage/transform/_geometric.py 中被重新定义以适配新包结构。

用 warp 把变换应用到图像上

变换坐标之后,真正把图像"掰弯"的核心函数是skimage.transform.warp。一个容易踩坑的关键点是:warp 需要传入变换的逆(tform.inverse),因为算法是为输出图像中的每个像素,反查它在输入图像中的来源坐标:

img = ski.util.img_as_float(ski.data.chelsea()) tf_img = ski.transform.warp(img, tform.inverse)

也就是说,我们关心的不是"输入图像中的某个坐标跑到输出的哪里"(这由正向变换给出),而是"输出图像的每个像素应该取输入图像的哪个位置"(这由逆变换给出),这样才能正确重建变换后的图像。

warp还支持指定输出形状,例如在矫正文本图像时强制输出 50×300:

text = ski.data.text() warped = ski.transform.warp(text, tform3, output_shape=(50, 300))

从对应点估计变换参数:from_estimate

最小二乘估计

实际项目中常常并不知道变换参数,只有两幅图像之间的一组对应点(源点与目标点)。skimage.transform中的每个变换类都提供了from_estimate类方法,用最小二乘优化最小化源点与目标点之间的距离来估计参数。详细教程见 plot_geometric.py。

一个经典的"矫正倾斜拍摄文本"场景:假设照片不是正对纸面拍摄的,字母发生了射影畸变。给定纸张四个角在照片中的位置(dst)与它们在实际纸面上的坐标(src),即可估计射影变换并矫正图像:

text = ski.data.text() src = np.array([[0, 0], [0, 50], [300, 50], [300, 0]]) dst = np.array([[155, 15], [65, 40], [260, 130], [360, 95]]) tform3 = ski.transform.ProjectiveTransform.from_estimate(src, dst) warped = ski.transform.warp(text, tform3, output_shape=(50, 300))

源码中 src/_skimage2/transform/_geometric.py 给出了射影估计的数学形式:

X = (a0*x + a1*y + a2) / (c0*x + c1*y + 1) Y = (b0*x + b1*y + b2) / (c0*x + c1*y + 1)

并对过定(over-determined)、适定(well-determined)、欠定(under-determined)三种情况统一使用总体最小二乘求解。若点位置本身存在不确定性,from_estimate还支持传入weights参数,让高权重点在拟合中优先被精确匹配。

特征点自动匹配 + RANSAC 抗离群

from_estimate需要的是可靠的对应点,这些点可以手工指定,也可以通过skimage.feature中的特征检测方法自动获得,例如:

  • 角点检测:plot_corner.py
  • ORB 特征:plot_orb.py
  • BRIEF 描述子:plot_brief.py

得到特征点后用skimage.feature.match_descriptors匹配,再估计变换参数。但自动匹配经常产生错误匹配(outliers),此时直接做最小二乘会被离群点严重带偏。推荐的做法是用 RANSAC 算法替代朴素最小二乘来提升鲁棒性,完整流程见 plot_matching.py:

from skimage.feature import corner_harris, corner_peaks, corner_subpix from skimage.transform import AffineTransform, warp from skimage.measure import ransac # 1) 检测角点(含亚像素精化) coords_orig = corner_peaks(corner_harris(img_orig_gray), threshold_rel=0.001, min_distance=5) coords_orig_subpix = corner_subpix(img_orig_gray, coords_orig, window_size=9) # ... 对第二幅图做同样处理,并用加权 SSD 建立对应关系 ... # 2) 全部点参与估计(会被离群点污染) model = AffineTransform.from_estimate(src, dst) # 3) RANSAC 稳健估计:只保留内点 model_robust, inliers = ransac( (src, dst), AffineTransform, min_samples=3, residual_threshold=2, max_trials=100, ) outliers = inliers == False

示例中用ransac返回的model_robust估计出的尺度、平移、旋转参数明显更接近真实值,而全点最小二乘的model则被错误匹配污染。这正是"稳健匹配"标题的含义:先用高斯加权 SSD 找候选对应,再用 RANSAC 筛掉错误对应。

类似的变换估计应用还包括:

  • 立体匹配:估计两视图间的基础矩阵/本质矩阵,见 plot_fundamental_matrix.py(对应FundamentalMatrixTransform、EssentialMatrixTransform,源码中使用 8 点算法)。
  • 图像矫正(rectification):见 plot_geometric.py。

全像素域方法:相位互相关估计平移与旋转

from_estimate是基于点的方法,只用源/目标图像中的一组点。如果只需要估计平移(位移),scikit-image 还提供全场(full-field)方法——利用所有像素、基于傅里叶空间互相关来估计,由skimage.registration.phase_cross_correlation实现,教程见 plot_register_translation.py:

from skimage import data from skimage.registration import phase_cross_correlation image = data.camera() shift = (-22.4, 13.32) # 像素级精度 shift, error, diffphase = phase_cross_correlation(image, offset_image) print(f'Detected pixel offset (y, x): {shift}') # 亚像素精度:通过上采样矩阵乘法 DFT 实现任意亚像素精度 shift, error, diffphase = phase_cross_correlation( image, offset_image, upsample_factor=100 )

upsample_factor参数借助上采样的矩阵乘法 DFT,可以达到任意亚像素精度。而在 plot_register_rotation.py 教程中,还演示了该全场方法的旋转估计变体:先做对数极坐标(log-polar)变换,把旋转问题转化为平移问题,再复用相位互相关求解。

估计失败的处理:FailedEstimation 与真值测试

需要特别留意:对于许多变换类型(包括ProjectiveTransform),估计可能失败。此时from_estimate返回的不是有效变换,而是一个特殊的FailedEstimation对象(定义于 src/_skimage2/_shared/utils.py),该对象携带失败原因字符串。

判定成功与否的标准模式是真值测试(truthiness):成功的估计是 truthy,失败的估计是 falsey。

bool(tform3) # True —— 估计成功,是有效变换 bool(bad_tform) # False —— 估计失败 # 例行检查的推荐写法 if not tform3: raise RuntimeError(f'Failed estimation: {tform3}')

一个必然失败的例子:把所有源点都设为同一个点(四份相同坐标),射影估计无法求解:

bad_src = np.tile(src[-1, :], (4, 1)) bad_tform = ski.transform.ProjectiveTransform.from_estimate(bad_src, dst) str(bad_tform) # 取出失败原因字符串

如果忘记检查 truthiness 就直接使用失败对象,会抛出FailedEstimationAccessError——它是AttributeError的自定义子类(error_cls = FailedEstimationAccessError),提示"不能访问失败估计对象的属性/不能将其作为可调用对象使用"。因此官方建议:每次都例行检查估计结果的真值。

变换能力总览与进一步阅读

skimage.transform中的完整几何变换家族还包括:PiecewiseAffineTransform(用控制点定义的分片仿射变换)、PolynomialTransform(2D 多项式变换,支持order参数)、EssentialMatrixTransform、FundamentalMatrixTransform,以及便捷函数estimate_transform(按类型名分发到各变换类的from_estimate)和matrix_transform(直接对坐标应用 2D 矩阵变换)。全部导出项可见 src/skimage/transform/_geometric.py。

如果想继续深入,本仓库还提供了这些直接相关的示例与源码:

  • plot_rescale.py:三种缩放方式的完整对比与可视化;
  • plot_transform_types.py:四类单应变换的数学属性与视觉差异;
  • plot_geometric.py:变换组合、坐标正逆变换、参数估计与失败处理全流程;
  • plot_matching.py:Harris 角点 + 加权 SSD + RANSAC 的稳健匹配管线;
  • plot_register_translation.py 与 plot_register_rotation.py:相位互相关平移/旋转估计;
  • src/skimage/transform/_geometric.py 与 src/_skimage2/transform/_geometric.py:变换类、from_estimate与齐次坐标的底层实现;
  • src/_skimage2/_shared/utils.py:FailedEstimation与FailedEstimationAccessError的定义。

从切片裁剪到 3×3 单应矩阵,从最小二乘参数估计到 RANSAC 稳健匹配,再到全场相位互相关——这套几何变换工具箱足以支撑图像矫正、配准、拼接等绝大多数实战需求。关键记忆点只有三条:warp记得传逆变换、降采样记得开anti_aliasing、参数估计记得做真值检查。

  • 计算机视觉
  • 图像处理
  • 科学计算

【免费下载链接】scikit-image

Image processing in Python

项目地址:https://gitcode.com/gh_mirrors/sc/scikit-image
点击查看免费下载

相关推荐

上一篇:M/o/Vfuscator性能优化工作坊:工业软件专场
下一篇:3B参数撬动企业级AI:IBM Granite-4.0-Micro-Base如何重新定义轻量化部署

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询