OpenCV 图像变换之 —— 通用变换

本文最后更新于:2022年7月4日 上午

本文摘录 OpenCV 中的图像变换相关操作内容,重点介绍 Opencv 中的通用变换操作。

概述

我们目前所看到的仿射变换和透射变换是一些更为一般的处理过程中特殊的例子。本质上,这两种变换有着相似的特性:它们把源图像的像素从一个地方映射到目标图像的另一个地方。事实上,其他一些操作也有着相同的结构。本文学习一些类似的变换,而后学习如何让OpenCV实现自己的映射变换。

极坐标映射

笛卡尔直角坐标系与距离-角度极坐标系之间的转换。

cv2.cartToPolar()

计算二维向量的角度和幅度,笛卡尔坐标转极坐标

  • 函数使用
1
magnitude, angle = cv2.cartToPolar(x, y)
$$ \begin{array}{c} magnitude_{i}=\sqrt{{x}_{i}{ }^{2}+y_{i}{ }^{2}}\\ angle_{i}=\operatorname{atan} 2\left(y_{i}, x_{i}\right) \end{array} $$
  • 输入维度相同的 X, Y 向量 (float32 或 float64),返回相同维度的坐标转换结果
1
2
3
4
5
6
7
8
9
10
11
12
vector = np.array([[1,1,1],[1,1,1]]).astype('float32')
X =vector
Y = vector
mag, ang = cv2.cartToPolar(X, Y)

-->
mag
array([[1.4142135, 1.4142135, 1.4142135],
[1.4142135, 1.4142135, 1.4142135]], dtype=float32)
ang
array([[0.7852316, 0.7852316, 0.7852316],
[0.7852316, 0.7852316, 0.7852316]], dtype=float32)

cv2.polarToCart()

已知角度和幅度,求出对应的二维向量

  • 函数使用
1
cv2.polarToCart(magnitude, angle, angleInDegrees=False) 
$$ \begin{array}{c} x_{i}= magnitude _{i} \cos \left(\right. angle \left._{i}\right) \\ y_{i}= magnitude _{i} \sin \left(\right. angle \left._{i}\right) \end{array} $$

cv2.polarToCart() 从向量场的极坐标中计算笛卡尔坐标(x,y)。输入具有相同尺寸和类型的两个矩阵:幅度和角度,指定每个点处向量的幅度和角度。输出类似的两个矩阵,它们与输入具有相同的尺寸和类型,并且将包含每个点处向量的xy投影。附加标志angleInDegrees将使角度矩阵中的数值以度为单位,而不是弧度。

  • 代码示例
1
2
3
4
5
6
7
8
mag = np.array(2**0.5)
angle = np.array(np.pi/4)
res = cv2.polarToCart(mag, angle)


-->
res
(array([[1.]]), array([[1.]]))

cv2.warpPolar()

图像的极坐标变换函数(包含线性极坐标和对数极坐标变换)

官方文档

  • 函数使用
1
2
3
4
5
6
7
cv2.warpPolar(
src, # 源图像
dsize, # 输出图像尺寸
center, # 原图像中极坐标点中心坐标
maxRadius, # 半径
flags[, # 函数执行标记
dst] ) -> dst
  • 该函数可以实现线性极坐标变换和对数极坐标变换,其中线性极坐标我们比较熟悉,对数极坐标为:

    对于二维图像,对数-极坐标变换是从直角坐标变换为对数极坐标,即$ (x, y) \leftrightarrow r e^{i \theta} $,其中$ r=\sqrt{x{2}+y{2}}, \quad \theta=\operatorname{atan} 2(y, x) $。下图展示了正方形对数极坐标变换后的图像:

  • 函数实现极坐标与笛卡尔坐标之间的转换,以官方图像为例:

  • dsize 为图像输出尺寸$(w, h)$,如果二者均为小于零的输入,则会返回与源图像中指定圆相关尺寸的图像$( r,\pi r)$
$$ \begin{array}{c} dsize. area \leftarrow\left(\operatorname{maxRadius}{ }^{2} \cdot \Pi\right) \\ dsize. width =\mathrm{cvRound}(\operatorname{maxRadius}) \\ dsize. height =\operatorname{cvRound}(\operatorname{maxRadius} \cdot \Pi) \end{array} $$

​ 若 dsize 仅有高度小于零,则高度会被设置为:
$$
dsize. height =\operatorname{cvRound}( dsize. width \cdot \Pi)
$$

  • flags 为三部分组成:

    • 是否反变换:cv2.WARP_INVERSE_MAP(16):不设置表示表示极坐标变换或对数极坐标变换,设置为反变换
    • 变换模式:cv2.WARP_POLAR_LINEAR 表示普通的极坐标变换,cv2.WARP_POLAR_LOG 表示对数极坐标变换
    • 插值方法:参考cv2.resize 的差值配置方法
  • 示例代码

极坐标变换

1
2
3
4
5
polar_img = mt.cv_rgb_imread('polar.jpg')
center = (185, 166)
radius = 161
polar_res = cv2.warpPolar(polar_img, [-1, -1], center, radius, flags=cv2.INTER_CUBIC + cv2.WARP_POLAR_LINEAR)
PIS(polar_res)

极坐标反变换

1
2
3
4
5
6
7
polar_img = mt.cv_rgb_imread('polar.jpg')
center = (185, 166)
radius = 161
polar_res = cv2.warpPolar(polar_img, [-1, -1], center, radius, flags=cv2.INTER_CUBIC + cv2.WARP_POLAR_LINEAR)
inverse = cv2.warpPolar(polar_res, [350, 350], center, radius, flags=cv2.INTER_CUBIC + cv2.WARP_POLAR_LINEAR+ cv2.WARP_INVERSE_MAP)

PIS(inverse)

对数极坐标变换 / 反变换

1
2
3
4
5
6
polar_img = mt.cv_rgb_imread('polar.jpg')
center = (185, 166)
radius = 161
polar_res = cv2.warpPolar(polar_img, [-1, -1], center, radius, flags=cv2.INTER_CUBIC + cv2.WARP_POLAR_LINEAR + cv2.WARP_POLAR_LOG)
inverse = cv2.warpPolar(polar_res, [350, 350], center, radius, flags=cv2.INTER_CUBIC + cv2.WARP_POLAR_LINEAR+ cv2.WARP_INVERSE_MAP + cv2.WARP_POLAR_LOG)
PIS(polar_res, inverse)

任意映射

我们有时想要实现以编程方式插值。这意味着我们要使用一些已知的能够实现映射的算法。另一方面,我们又想自己实现这种映射。在研究这些能为我们计算(并应用)这些映射方法前,我们先看看其他方法依赖的能够实现这种映射的函数。

cv2.remap()

用于常规图像的重绘,应用通用几何变换。

官方文档

  • 函数 remap 使用指定的 map 转换源图像:

$$
\operatorname{dst}(x, y)=\operatorname{src}\left(\operatorname{map}{x}(x, y), \operatorname{map}{y}(x, y)\right)
$$

  • 函数使用
1
2
3
4
5
6
7
8
cv.remap(
src, # 源图像
map1, # 第一个(x,y)点的映射,或者只是 x 值的映射,类型 CV_16SC2 , CV_32FC1, CV_32FC2
map2, # 第二个关于 y 的映射,如果 map1 定义了 y 则为 None
interpolation[, # 差值标记
dst[,
borderMode[, # 边缘外推方法
borderValue]]]) -> dst
  • 其中 map1, map2 均为和src相同尺寸的图像,每个对应位置填入映射后的$x,y $坐标,cv2.remap函数得到映射后的图像
  • 示例代码
1
2
3
4
5
6
7
8
img = mt.cv_rgb_imread('img1.jpg')
img = mt.image_resize(img, [400, 300])
y_map = np.arange(300).astype('float32')
x_map = np.arange(400).astype('float32')
x_map = x_map[::-1]
xmap, ymap = np.meshgrid(x_map, y_map)
res = cv2.remap(img, xmap, ymap, cv2.INTER_LINEAR)
PIS(res)

示例源码

参考资料


OpenCV 图像变换之 —— 通用变换
https://www.zywvvd.com/notes/study/image-processing/opencv/opencv-universe-trans/universe-trans/
作者
Yiwei Zhang
发布于
2022年3月21日
许可协议