在图像处理领域中,经常通过极坐标与笛卡尔直角坐标的互转来实现图像中圆形转为方形,或者通过极坐标反变换实现方形转圆形。例如钟表的表盘,人眼虹膜,医学血管断层都需要用到极坐标变换来实现圆转方。

本文所有代码见:

1 基础数学知识

这一部分是高中数学知识,可以不看。

1.1 极坐标

关于极坐标的详细介绍见极坐标基本概念。在平面上,取一点O称为极点,从O出发的水平射线OX称为极轴,然后我们就可以确定了一个极坐标系。简单来说极坐标就是通过长度和角度来表示点的位置的坐标系。这个长度一般用

ρ

\rho

ρ表示,角度一般用

θ

\theta

θ表示(都是希腊字母,有些地方长度会用r来表示)。在程序语言中长度用变量rho来表示,角度用变量theta来表示(也就是它们的读音)。在极坐标系中任何一点的坐标都可以用

(

ρ

,

θ

)

(\rho,\theta)

(ρ,θ)来表示。如下图所示我们将极坐标和直角坐标叠在一起,根据三角相关公式,就能够得到相应的极坐标转换为直角坐标的公式。

详细的转换公式如下所示,其中

θ

[

0

,

2

π

]

\theta \in [0,2\pi ]

θ∈[0,2π]。

{

x

=

ρ

cos

(

θ

)

y

=

ρ

sin

(

θ

)

\left\{ \begin{array}{l} x = \rho \cos (\theta )\\ y = \rho \sin (\theta ) \end{array} \right.

{x=ρcos(θ)y=ρsin(θ)​

极坐标转为直角坐标的公式逆变换,就能够得到直角坐标转为极坐标的变换公式,如下所示。

{

ρ

=

x

2

+

y

2

θ

=

arctan

y

x

\left\{ \begin{array}{l} \rho = \sqrt {{x^2} + {y^2}} \\ \theta = \arctan \frac{y}{x} \end{array} \right.

{ρ=x2+y2

​θ=arctanxy​​

1.2 二维直角坐标系转换

如下图所示。在坐标系XY上有任一一点P,该点的坐标为(x,y)。如果我们将坐标系XY逆时针旋转A度(逆时针为正)得到坐标系X’Y’,P点在坐标系X’Y’中的坐标为(x’,y’),在坐标系X’Y’中P点与X’轴角度为B度。那么(x,y)与(x’,y’)的转换关系是什么?

转换关系求解也很简单,见直角坐标系转换公式。简单来说,由上节知识我们知道在X’Y’坐标系中P点的坐标如下。

{

x

=

ρ

cos

(

B

)

y

=

ρ

sin

(

B

)

\left\{ \begin{array}{l} x' = \rho \cos (B)\\ y' = \rho \sin (B) \end{array} \right.

{x′=ρcos(B)y′=ρsin(B)​

P点在XY坐标系的坐标如下:

{

x

=

ρ

cos

(

A

+

B

)

=

ρ

cos

A

cos

B

ρ

sin

A

sin

B

y

=

ρ

sin

(

A

+

B

)

=

ρ

sin

A

cos

B

+

ρ

cos

A

sin

B

\left\{ \begin{array}{l} x = \rho \cos (A + B) = \rho \cos A\cos B - \rho \sin A\sin B\\ y = \rho \sin (A + B) = \rho \sin A\cos B + \rho \cos A\sin B \end{array} \right.

{x=ρcos(A+B)=ρcosAcosB−ρsinAsinBy=ρsin(A+B)=ρsinAcosB+ρcosAsinB​

合并以上两个公式,就可以得到(x,y)与(x’,y’)的转换公式了。如下所示:

{

x

=

ρ

cos

(

A

+

B

)

=

x

cos

A

y

sin

A

y

=

ρ

sin

(

A

+

B

)

=

x

sin

A

+

y

cos

A

\left\{ \begin{array}{l} x = \rho \cos (A + B) = x'\cos A - y'\sin A\\ y = \rho \sin (A + B) = x'\sin A + y'\cos A \end{array} \right.

{x=ρcos(A+B)=x′cosA−y′sinAy=ρsin(A+B)=x′sinA+y′cosA​

2 圆形区域转换为矩形区域

2.1 预设值

我们想要将图像中圆环区域展开成矩形长条可以通过第一章的极坐标变换知识来实现。如下图所示是常见的钟表图像。

我们对这种圆盘或者圆环类的区域很难处理,所以需要转为矩形长条,实现这一步骤需要预先设定一系列的值:

  1. 将圆盘区域从图像中提取出来,并统一设置图像尺寸为预先给定的固定大小(通常是正方形)。
  2. 根据预先给定的固定大小设置圆盘区域的半径和圆心坐标。半径一般是圆盘图像高的一半,圆心通常是圆盘图像的中心点。
  3. 设置转换后矩形长条图像摆放方向,矩形长条摆放圆形区域可以是从上到下或者从左至右,本文选择从左至右。
  4. 设置矩形长条图像的尺寸,矩形图像的宽通常是圆形区域的周长。高通常自己按实际任务给定,要么是图像中圆环区域的宽度,或者是圆形区域的半径的一半。

经过以上设定,我们要输入进行转换的圆形区域图像如下图所示。这张图像来自github开源代码cv-warpPolar-example。关于极坐标变换的相关学习也可以见该开源代码仓库。其他一些可以学习的文章见:将图像中圆环区域展开成矩形长条的方法二维向量旋转公式

我们预设值的全局变量代码如下。

C++

  1. // ----- 全局参数
  2. // PAI值
  3. double PI = M_PI;
  4. // 设置输入图像固定尺寸(必要)
  5. double HEIGHT = 300;
  6. double WIDTH = 300;
  7. // 输入图像圆的半径,一般是宽高一半
  8. int CIRCLE_RADIUS = int(HEIGHT / 2);
  9. // 圆心坐标
  10. cv::Point CIRCLE_CENTER = cv::Point(int(WIDTH / 2), int(HEIGHT / 2));
  11. // 极坐标转换后图像的高,可自己设置
  12. int LINE_HEIGHT = int(CIRCLE_RADIUS / 1.5);
  13. // 极坐标转换后图像的宽,一般是原来圆形的周长
  14. int LINE_WIDTH = int(2 * CIRCLE_RADIUS * PI);
  15. // C++ OpenCV Mat读像素值用
  16. typedef Point3_<uint8_t> Pixel;

Python

  1. # ----- 全局参数
  2. # PAI值
  3. PI = math.pi
  4. # 设置输入图像固定尺寸(必要)
  5. HEIGHT, WIDTH = 300, 300
  6. # 输入图像圆的半径,一般是宽高一半
  7. CIRCLE_RADIUS = int(HEIGHT / 2)
  8. # 圆心坐标
  9. CIRCLE_CENTER = [HEIGHT / 2, WIDTH / 2]
  10. # 极坐标转换后图像的高,可自己设置
  11. LINE_HEIGHT = int(CIRCLE_RADIUS / 1.5)
  12. # 极坐标转换后图像的宽,一般是原来圆形的周长
  13. LINE_WIDTH = int(2 * CIRCLE_RADIUS * PI)

2.2 标准圆形转换

所以标准圆形转换,就是上图表针我们从3点钟开始,逆时针将圆形图像展开。具体步骤如下图所示。

详细来说分为三步:

2.2.1 Step1 获得各点的极坐标

首先我们获得圆形区域各个点的极坐标。对于圆形各点的角度,我们从3点钟开始,逆时针旋转计算角度值,对于圆形的半径,先计算外圈半径,然后计算逐渐往内计算内圈半径。其中极坐标系中的极轴为3点钟方向对应的轴。这一部分计算代码如下:

C++

  1. // 最后的-0.2是用于优化结果,可以自行调整
  2. theta = PI * 2 / LINE_WIDTH * (col + 1) - 0.2;
  3. rho = CIRCLE_RADIUS - row - 1;

Python

  1. # 角度,最后的-0.2是用于优化结果,可以自行调整
  2. theta = PI * 2 / LINE_WIDTH * (col + 1) - 0.2
  3. # 半径,减1防止超界
  4. rho = CIRCLE_RADIUS - row - 1

注意,这个转换的效果不同项目不一样,所以在角度半径后加一个优化值,实际自行调整该值(图像处理过度依赖设计者的经验)。

2.2.2 Step2 获得直角坐标

这里的直角坐标系是指以(0,0)点为原点。从原点出发的水平轴为x轴,x轴往右为正向;从原点出发的垂直轴为y轴,y轴往上为正向。这样应用第一节的极坐标转换公式就能获得圆形各个点的直角坐标。

2.2.3 Step3 获得OpenCV图像坐标

OpenCV的图像坐标系和普通的直接坐标系区别就是,OpenCV的图像坐标系中以y轴往下为正向。所以上一步获得的直角坐标中y值要乘上-1。然后我们要将圆形展开为矩形,需要将坐标原点移到圆形中点,这样就是x,y坐标各加上圆形中点的值。代码如下:

C++

  1. int x = int(CIRCLE_CENTER.x + rho * std::cos(theta) + 0);
  2. int y = int(CIRCLE_CENTER.y - rho * std::sin(theta) + 0);

Python

  1. x = int(CIRCLE_CENTER[0] + rho * math.cos(theta) + 0.0)
  2. y = int(CIRCLE_CENTER[1] - rho * math.sin(theta) + 0.0)

2.2.4 示例代码

上一步中,我们对圆形区域图像的每个点进行转换操作,再赋值给展开后的矩形图像就行了。代码如下:

C++

  1. #include <opencv2/opencv.hpp>
  2. #define _USE_MATH_DEFINES
  3. #include <math.h>
  4. #include <iostream>
  5. using namespace std;
  6. using namespace cv;
  7. // ----- 全局参数
  8. // PAI值
  9. double PI = M_PI;
  10. // 设置输入图像固定尺寸(必要)
  11. double HEIGHT = 300;
  12. double WIDTH = 300;
  13. // 输入图像圆的半径,一般是宽高一半
  14. int CIRCLE_RADIUS = int(HEIGHT / 2);
  15. // 圆心坐标
  16. cv::Point CIRCLE_CENTER = cv::Point(int(WIDTH / 2), int(HEIGHT / 2));
  17. // 极坐标转换后图像的高,可自己设置
  18. int LINE_HEIGHT = int(CIRCLE_RADIUS / 1.5);
  19. // 极坐标转换后图像的宽,一般是原来圆形的周长
  20. int LINE_WIDTH = int(2 * CIRCLE_RADIUS * PI);
  21. // Define a pixel
  22. typedef Point3_<uint8_t> Pixel;
  23. cv::Mat create_line_image(cv::Mat img)
  24. {
  25. cv::Mat line_image = cv::Mat::zeros(Size(LINE_WIDTH, LINE_HEIGHT), CV_8UC3);
  26. // 角度
  27. double theta;
  28. // 半径
  29. double rho;
  30. // 按照圆的极坐标赋值
  31. for (int row = 0; row < line_image.rows; row++)
  32. {
  33. for (int col = 0; col < line_image.cols; col++)
  34. {
  35. // 最后的-0.1是用于优化结果,可以自行调整
  36. theta = PI * 2 / LINE_WIDTH * (col + 1) - 0.2;
  37. rho = CIRCLE_RADIUS - row - 1;
  38. int x = int(CIRCLE_CENTER.x + rho * std::cos(theta) + 0);
  39. int y = int(CIRCLE_CENTER.y - rho * std::sin(theta) + 0);
  40. // Obtain pixel at(y,x)直接访问像素数据(效率不高,可以修改)
  41. Pixel pixel = img.at<Pixel>(y, x);
  42. // 赋值
  43. line_image.at<Pixel>(row, col) = pixel;
  44. }
  45. }
  46. // 如果想改变输出图像方向,旋转就行了
  47. // cv::rotate(line_image, line_image, cv::ROTATE_90_CLOCKWISE);
  48. return line_image;
  49. }
  50. // ----- 主程序
  51. int main()
  52. {
  53. // 输入图像路径
  54. String imgpath = "./image/clock.jpg";
  55. // 读取图像
  56. cv::Mat img = cv::imread(imgpath);
  57. if (img.empty())
  58. {
  59. printf("please check image path");
  60. return -1;
  61. }
  62. // 图像重置为固定大小
  63. cv::resize(img, img, Size(WIDTH, HEIGHT));
  64. printf("shape is: %d,%d", img.rows, img.cols);
  65. // 展示原图
  66. cv::imshow("src", img);
  67. cv::Mat output = create_line_image(img);
  68. // 展示结果
  69. cv::imshow("dst", output);
  70. cv::waitKey();
  71. cv::destroyAllWindows();
  72. system("pause");
  73. return 0;
  74. }

Python

  1. import numpy as np
  2. import math
  3. import cv2
  4. # ----- 全局参数
  5. # PAI值
  6. PI = math.pi
  7. # 设置输入图像固定尺寸(必要)
  8. HEIGHT, WIDTH = 300, 300
  9. # 输入图像圆的半径,一般是宽高一半
  10. CIRCLE_RADIUS = int(HEIGHT / 2)
  11. # 圆心坐标
  12. CIRCLE_CENTER = [HEIGHT / 2, WIDTH / 2]
  13. # 极坐标转换后图像的高,可自己设置
  14. LINE_HEIGHT = int(CIRCLE_RADIUS / 1.5)
  15. # 极坐标转换后图像的宽,一般是原来圆形的周长
  16. LINE_WIDTH = int(2 * CIRCLE_RADIUS * PI)
  17. # ----- 将圆环变为矩形
  18. def create_line_image(img):
  19. # 建立展开后的图像
  20. line_image = np.zeros((LINE_HEIGHT, LINE_WIDTH, 3), dtype=np.uint8)
  21. # 按照圆的极坐标赋值
  22. for row in range(line_image.shape[0]):
  23. for col in range(line_image.shape[1]):
  24. # 角度,最后的-0.1是用于优化结果,可以自行调整
  25. theta = PI * 2 / LINE_WIDTH * (col + 1) - 0.2
  26. # 半径,减1防止超界
  27. rho = CIRCLE_RADIUS - row - 1
  28. x = int(CIRCLE_CENTER[0] + rho * math.cos(theta) + 0.0)
  29. y = int(CIRCLE_CENTER[1] - rho * math.sin(theta) + 0.0)
  30. # 赋值
  31. line_image[row, col, :] = img[y, x, :]
  32. # 如果想改变输出图像方向,旋转就行了
  33. # line_image = cv2.rotate(line_image, cv2.ROTATE_90_CLOCKWISE)
  34. return line_image
  35. # ----- 主程序
  36. def main(imgpath):
  37. # 读取图像
  38. img = cv2.imread(imgpath)
  39. if img is None:
  40. print("please check image path")
  41. return
  42. # 图像重置为固定大小
  43. img = cv2.resize(img, (HEIGHT, WIDTH))
  44. print(img.shape)
  45. # 展示原图
  46. cv2.imshow("src", img)
  47. output = create_line_image(img)
  48. # 展示结果
  49. cv2.imshow("dst", output)
  50. cv2.waitKey()
  51. cv2.destroyAllWindows()
  52. if __name__ == '__main__':
  53. # 输入图像路径
  54. imgpath = "./image/clock.jpg"
  55. main(imgpath)

结果如下图所示。展开后的矩形图像从左往右对应圆环区域图像从3点钟开始逆时针旋转,矩形图像从上往下对应圆环区域图像从外往里。

2.3 任意角度圆形转换

如果我们想以任意角度进行圆形转换,比如从7点钟方向开始逆时针将圆形展为方形。如下图所示:

我们需要在以开始转换方向即7点钟轴为极轴建立极坐标系,确定极坐标后,需要建立直角坐标系,然后旋转直角坐标系获得正常直角坐标系的坐标。直接看代码修改create_line_image函数中的转换代码就行。注意7点钟位置相对3点钟位置,角度是240度或者-120度。

C++

  1. cv::Mat create_line_image(cv::Mat img)
  2. {
  3. cv::Mat line_image = cv::Mat::zeros(Size(LINE_WIDTH, LINE_HEIGHT), CV_8UC3);
  4. // 角度
  5. double theta;
  6. // 半径
  7. double rho;
  8. // 按照圆的极坐标赋值
  9. for (int row = 0; row < line_image.rows; row++)
  10. {
  11. for (int col = 0; col < line_image.cols; col++)
  12. {
  13. // 最后的-0.2是用于优化结果,可以自行调整
  14. theta = PI * 2 / LINE_WIDTH * (col + 1) - 0.2;
  15. rho = CIRCLE_RADIUS - row - 1;
  16. // 1 确定极坐标
  17. double x0 = rho * std::cos(theta) + 0;
  18. double y0 = rho * std::sin(theta) + 0;
  19. // 2 确定旋转角度
  20. double angle = PI * 2 * (-120.0) / 360;
  21. // 3 确定直角坐标
  22. double x1 = x0 * std::cos(angle) - y0 * std::sin(angle);
  23. double y1 = x0 * std::sin(angle) + y0 * std::cos(angle);
  24. // 4 切换为OpenCV图像坐标
  25. int x = int(CIRCLE_CENTER.x + x1);
  26. int y = int(CIRCLE_CENTER.y - y1);
  27. // Obtain pixel at(y,x)直接访问像素数据(效率不高,可以修改)
  28. Pixel pixel = img.at<Pixel>(y, x);
  29. // 赋值
  30. line_image.at<Pixel>(row, col) = pixel;
  31. }
  32. }
  33. // 如果想改变输出图像方向,旋转就行了
  34. // cv::rotate(line_image, line_image, cv::ROTATE_90_CLOCKWISE);
  35. return line_image;
  36. }

Python

  1. # ----- 将圆环变为矩形
  2. def create_line_image(img):
  3. # 建立展开后的图像
  4. line_image = np.zeros((LINE_HEIGHT, LINE_WIDTH, 3), dtype=np.uint8)
  5. # 按照圆的极坐标赋值
  6. for row in range(line_image.shape[0]):
  7. for col in range(line_image.shape[1]):
  8. # 角度,最后的-0.2是用于优化结果,可以自行调整
  9. theta = PI * 2 / LINE_WIDTH * (col + 1)-0.2
  10. # 半径,减1防止超界
  11. rho = CIRCLE_RADIUS - row - 1
  12. # 基础变换
  13. # x = int(CIRCLE_CENTER[0] + rho * math.cos(theta) + 0.0)
  14. # y = int(CIRCLE_CENTER[1] - rho * math.sin(theta) + 0.0)
  15. # 1 确定极坐标
  16. x0 = rho * math.cos(theta) + 0.0
  17. y0 = rho * math.sin(theta) + 0.0
  18. # 2 确定旋转角度
  19. angle = math.pi* (-120) / 360 * 2
  20. # 3 确定直角坐标
  21. x1 = x0*math.cos(angle)-y0*math.sin(angle)
  22. y1 = x0*math.sin(angle)+y0*math.cos(angle)
  23. # 4 切换为OpenCV图像坐标
  24. x = int(CIRCLE_CENTER[0] + x1)
  25. y = int(CIRCLE_CENTER[1] - y1)
  26. # 赋值
  27. line_image[row, col, :] = img[y, x, :]
  28. # 如果想改变输出图像方向,旋转就行了
  29. # line_image = cv2.rotate(line_image, cv2.ROTATE_90_CLOCKWISE)
  30. return line_image

结果如下图所示。展开后的矩形图像从左往右对应圆环区域图像从7点钟开始逆时针旋转,矩形图像从上往下对应圆环区域图像从外往里。

2.4 任意角度圆形顺时针转换

如果我们想以任意角度进行圆形顺时针转换,比如从5点钟方向开始顺时针将圆形展为方形。如下图所示:

顺时针旋转和逆时针旋转不同,我们需要重新确定极坐标后,然后建立直角坐标系,在这个直角坐标系中,极轴对应y轴,与顺时针变换正好相反。接下来步骤与逆时针旋转是一样的,再旋转直角坐标系获得正常直角坐标系的坐标。直接看代码修改create_line_image函数中的转换代码就行。注意7点钟位置相对5点钟位置,角度是210度或者-150度。

C++

  1. cv::Mat create_line_image(cv::Mat img)
  2. {
  3. cv::Mat line_image = cv::Mat::zeros(Size(LINE_WIDTH, LINE_HEIGHT), CV_8UC3);
  4. // 角度
  5. double theta;
  6. // 半径
  7. double rho;
  8. // 按照圆的极坐标赋值
  9. for (int row = 0; row < line_image.rows; row++)
  10. {
  11. for (int col = 0; col < line_image.cols; col++)
  12. {
  13. // 最后的-0.2是用于优化结果,可以自行调整
  14. theta = PI * 2 / LINE_WIDTH * (col + 1) - 0.2;
  15. rho = CIRCLE_RADIUS - row - 1;
  16. // ----- 任意起始位置顺时针变换
  17. // 1 确定极坐标
  18. double x0 = rho * std::sin(theta) + 0;
  19. double y0 = rho * std::cos(theta) + 0;
  20. // 2 确定旋转角度
  21. double angle = PI * 2 * (-150.0) / 360;
  22. // 3 确定直角坐标
  23. double x1 = x0 * std::cos(angle) - y0 * std::sin(angle) + 0;
  24. double y1 = x0 * std::sin(angle) + y0 * std::cos(angle) + 0;
  25. // 4 切换为opencv图像坐标
  26. int x = int(CIRCLE_CENTER.x + x1);
  27. int y = int(CIRCLE_CENTER.y - y1);
  28. // Obtain pixel at(y,x)直接访问像素数据(效率不高,可以修改)
  29. Pixel pixel = img.at<Pixel>(y, x);
  30. // 赋值
  31. line_image.at<Pixel>(row, col) = pixel;
  32. }
  33. }
  34. // 如果想改变输出图像方向,旋转就行了
  35. // cv::rotate(line_image, line_image, cv::ROTATE_90_CLOCKWISE);
  36. return line_image;
  37. }

Python

  1. # ----- 将圆环变为矩形
  2. def create_line_image(img):
  3. # 建立展开后的图像
  4. line_image = np.zeros((LINE_HEIGHT, LINE_WIDTH, 3), dtype=np.uint8)
  5. # 按照圆的极坐标赋值
  6. for row in range(line_image.shape[0]):
  7. for col in range(line_image.shape[1]):
  8. # 角度,最后的-0.2是用于优化结果,可以自行调整
  9. theta = PI * 2 / LINE_WIDTH * (col + 1)-0.2
  10. # 半径,减1防止超界
  11. rho = CIRCLE_RADIUS - row - 1
  12. # ----- 任意起始位置顺时针变换
  13. # 1 确定极坐标
  14. x0 = rho * math.sin(theta) + 0.0
  15. y0 = rho * math.cos(theta) + 0.0
  16. # 2 确定旋转角度
  17. angle = math.pi* (-150) / 360 * 2
  18. # 3 确定直角坐标
  19. x1 = x0*math.cos(angle)-y0*math.sin(angle) + 0
  20. y1 = x0*math.sin(angle)+y0*math.cos(angle) + 0
  21. # 4 切换为OpenCV图像坐标
  22. x = int(CIRCLE_CENTER[0] + x1)
  23. y = int(CIRCLE_CENTER[1] - y1)
  24. # 赋值
  25. line_image[row, col, :] = img[y, x, :]
  26. # 如果想改变输出图像方向,旋转就行了
  27. # line_image = cv2.rotate(line_image, cv2.ROTATE_90_CLOCKWISE)
  28. return line_image

结果如下图所示。展开后的矩形图像从左往右对应圆环区域图像从5点钟开始顺时针旋转,矩形图像从上往下对应圆环区域图像从外往里。

3 OpenCV内置函数实现图像极坐标变换与逆变换

第二部分我们描述了如何实现图像极坐标转换为直角坐标。但是相应直角坐标转换为极坐标在本文并不会进行具体描述,因为OpenCV有内置函数来实现逆变换(逆变换真要自己写公式倒过来就行了)。OpenCV中包含内置函数可以实现极坐标变换,极坐标逆变换,半对数极坐标变换以及半对数极坐标逆变换。OpenCV提供的函数仅实现3点钟方向开始,逆时针转换为直角坐标,但是提供的函数稳定性比自己写的要好。

其中对数极坐标其实就是将直角坐标转换成极坐标,然后对极长

ρ

\rho

ρ求取对数,获得的坐标。对数极坐标可以压缩信息,聚焦于关键信息,常用于图像匹配,具体介绍见LogPolar对数极坐标。注意我们常说的对数极坐标一般是半对数极坐标,即只对极长求对数,不对极角求对数。此外还有半对数直角坐标,半对数直角坐标指的是一个轴是分度均匀的普通坐标轴,另一个轴是分度不均匀的对数坐标轴。

在老版本OpenCV中我们通过logPolar和linearPolar来分别实现对数极坐标转换和极坐标转换。但是新版本中用warpPolar函数来代替这两个函数的功能。warpPolar函数的接口如下:

  1. C++:
  2. void cv::warpPolar(
  3. InputArray src,
  4. OutputArray dst,
  5. Size dsize,
  6. Point2f center,
  7. double maxRadius,
  8. int flags)
  9. Python:
  10. dst = cv.warpPolar(src, dsize, center, maxRadius, flags[, dst])

其中src是输入图像,dst是输出图像,dsize为输出图像尺寸,center要变换的边界圆的圆点坐标, maxRadius要变换的边界圆的半径,flags为标志。

对于dsize在C++中可以输入Size(),在Python中可以输入None,表示由OpenCV自行决定输出尺寸。flags为插值方法标志InterpolationFlags和转换方法标志WarpPolarMode的组合。InterpolationFlags具体介绍见官方文档InterpolationFlags。WarpPolarMode表示转换的方法,具体有三种:

  • WARP_POLAR_LINEAR以选择线性极坐标映射(默认)。
  • WARP_POLAR_LOG以选择半对数极坐标映射。
  • WARP_INVERSE_MAP进行反向映射。

这一部分示例代码参考OpenCV自带示例代码polar_transforms。示例代码如下:

C++

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main()
  6. {
  7. // log_polar_img 半对数极坐标变换结果
  8. // lin_polar_img 极坐标变换结果
  9. // recovered_log_polar 半对数极坐标逆变换结果
  10. // recovered_lin_polar_img 极坐标逆变换结果
  11. Mat log_polar_img, lin_polar_img, recovered_log_polar, recovered_lin_polar_img;
  12. // INTER_LINEAR 双线性插值,WARP_FILL_OUTLIERS填充所有目标图像像素
  13. int flags = INTER_LINEAR + WARP_FILL_OUTLIERS;
  14. // 读图
  15. String imagepath = "image/clock.jpg";
  16. Mat src = imread(imagepath);
  17. if (src.empty())
  18. {
  19. fprintf(stderr, "Could not initialize capturing...\n");
  20. return -1;
  21. }
  22. // 圆心坐标
  23. Point2f center((float)src.cols / 2, (float)src.rows / 2);
  24. // 圆的半径
  25. double maxRadius = min(center.y, center.x);
  26. // direct transform
  27. // linear Polar 极坐标变换, Size()表示OpenCV根据输入自行决定输出图像尺寸
  28. warpPolar(src, lin_polar_img, Size(), center, maxRadius, flags);
  29. // semilog Polar 半对数极坐标变换, Size()表示OpenCV根据输入自行决定输出图像尺寸
  30. warpPolar(src, log_polar_img, Size(), center, maxRadius, flags + WARP_POLAR_LOG);
  31. // inverse transform 逆变换
  32. warpPolar(lin_polar_img, recovered_lin_polar_img, src.size(), center, maxRadius, flags + WARP_INVERSE_MAP);
  33. warpPolar(log_polar_img, recovered_log_polar, src.size(), center, maxRadius, flags + WARP_POLAR_LOG + WARP_INVERSE_MAP);
  34. // 改变结果方向
  35. // rotate(lin_polar_img, lin_polar_img, ROTATE_90_CLOCKWISE);
  36. // 展示图片
  37. imshow("Src frame", src);
  38. imshow("Log-Polar", log_polar_img);
  39. imshow("Linear-Polar", lin_polar_img);
  40. imshow("Recovered Linear-Polar", recovered_lin_polar_img);
  41. imshow("Recovered Log-Polar", recovered_log_polar);
  42. waitKey(0);
  43. system("pause");
  44. return 0;

Python

  1. import cv2
  2. # ----- 主函数
  3. def main():
  4. # INTER_LINEAR 双线性插值,WARP_FILL_OUTLIERS填充所有目标图像像素
  5. flags = cv2.INTER_LINEAR | cv2.WARP_FILL_OUTLIERS
  6. # 读图
  7. imagepath = "image/clock.jpg"
  8. src = cv2.imread(imagepath)
  9. if src is None:
  10. print("Could not initialize capturing...\n")
  11. return -1
  12. # 圆心坐标
  13. center = (float(src.shape[0] / 2), float(src.shape[1] / 2))
  14. # 圆的半径
  15. maxRadius = min(center[0], center[1])
  16. # direct transform
  17. # linear Polar 极坐标变换, None表示OpenCV根据输入自行决定输出图像尺寸
  18. lin_polar_img = cv2.warpPolar(src, None, center, maxRadius, flags)
  19. # semilog Polar 半对数极坐标变换, None表示OpenCV根据输入自行决定输出图像尺寸
  20. log_polar_img = cv2.warpPolar(src, None, center, maxRadius, flags | cv2.WARP_POLAR_LOG)
  21. # inverse transform 逆变换
  22. recovered_lin_polar_img = cv2.warpPolar(lin_polar_img, (src.shape[0], src.shape[1]), center, maxRadius,
  23. flags | cv2.WARP_INVERSE_MAP)
  24. recovered_log_polar = cv2.warpPolar(log_polar_img, (src.shape[0], src.shape[1]), center, maxRadius,
  25. flags | cv2.WARP_POLAR_LOG | cv2.WARP_INVERSE_MAP)
  26. # 改变结果方向
  27. # lin_polar_img = cv2.rotate(lin_polar_img, cv2.ROTATE_90_CLOCKWISE)
  28. # 展示图片
  29. cv2.imshow("Src frame", src)
  30. cv2.imshow("Log-Polar", log_polar_img)
  31. cv2.imshow("Linear-Polar", lin_polar_img)
  32. cv2.imshow("Recovered Linear-Polar", recovered_lin_polar_img)
  33. cv2.imshow("Recovered Log-Polar", recovered_log_polar)
  34. cv2.waitKey(0)
  35. return 0
  36. if __name__ == '__main__':
  37. main()

结果如下所示。OpenCV自带转换函数是直接以3点钟方向为起点顺时针变换,只考虑标准直角坐标系。对数坐标会大大压缩信息,所以对数坐标逆变换后的结果图像会模糊,但是圆转方会把原图中面积较大的区域更多的显示出来。此外OpenCV自带转换函数圆转方输出的是从上到小的结果,图像旋转下就可以得到第二节的结果。

方法 图像
对数极坐标变换
极坐标变换
对数极坐标逆变换
极坐标逆变换

4 参考

4.1 基础数学

4.2 代码

[OpenCV实战]51 基于OpenCV实现图像极坐标变换与逆变换的更多相关文章

  1. [OpenCV实战]45 基于OpenCV实现图像哈希算法

    目前有许多算法来衡量两幅图像的相似性,本文主要介绍在工程领域最常用的图像相似性算法评价算法:图像哈希算法(img hash).图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅 ...

  2. [OpenCV实战]48 基于OpenCV实现图像质量评价

    本文主要介绍基于OpenCV contrib中的quality模块实现图像质量评价.图像质量评估Image Quality Analysis简称IQA,主要通过数学度量方法来评价图像质量的好坏. 本文 ...

  3. [OpenCV实战]28 基于OpenCV的GUI库cvui

    目录 1 cvui的使用 1.1 如何在您的应用程序中添加cvui 1.2 基本的"hello world"应用程序 2 更高级的应用 3 代码 4 参考 有很多很棒的GUI库,例 ...

  4. [OpenCV实战]47 基于OpenCV实现视觉显著性检测

    人类具有一种视觉注意机制,即当面对一个场景时,会选择性地忽略不感兴趣的区域,聚焦于感兴趣的区域.这些感兴趣的区域称为显著性区域.视觉显著性检测(Visual Saliency Detection,VS ...

  5. [OpenCV实战]38 基于OpenCV的相机标定

    文章目录 1 什么是相机标定? 2 图像形成几何学 2.1 设定 2.1.1 世界坐标系 2.1.2 相机坐标系 2.1.3 图像坐标系 2.2 图像形成方法总结 3 基于OpenCV的相机标定原理 ...

  6. [OpenCV实战]26 基于OpenCV实现选择性搜索算法

    目录 1 背景 1.1 目标检测与目标识别 1.2 滑动窗口算法 1.3 候选区域选择算法 2 选择性搜索算法 2.1 什么是选择性搜索? 2.2 选择性搜索相似性度量 2.3 结果 3 代码 4 参 ...

  7. [OpenCV实战]11 基于OpenCV的二维码扫描器

    目录 1 二维码(QRCode)扫描 2 结果 3 参考 在这篇文章中,我们将看到如何使用OpenCV扫描二维码.您将需要OpenCV3.4.4或4.0.0及更高版本来运行代码. 1 二维码(QRCo ...

  8. [OpenCV实战]19 使用OpenCV实现基于特征的图像对齐

    目录 1 背景 1.1 什么是图像对齐或图像对准? 1.2 图像对齐的应用 1.3 图像对齐基础理论 1.4 如何找到对应点 2 OpenCV的图像对齐 2.1 基于特征的图像对齐的步骤 2.2 代码 ...

  9. [OpenCV实战]20 使用OpenCV实现基于增强相关系数最大化的图像对齐

    目录 1 背景 1.1 彩色摄影的一个简短而不完整的历史 1.2 OpenCV中的运动模型 2 使用增强相关系数最大化(ECC)的图像对齐 2.1 findTransformECC在OpenCV中的示 ...

随机推荐

  1. PHP Phar反序列化学习

    PHP Phar反序列化学习 Phar Phar是PHP的压缩文档,是PHP中类似于JAR的一种打包文件.它可以把多个文件存放至同一个文件中,无需解压,PHP就可以进行访问并执行内部语句. 默认开启版 ...

  2. 使用ssm框架搭建的图书管理系统

    等下贴代码 学生图书管理系统 实现的功能: 1.图书信息查询(暂时未分页处理) 2.图书信息修改(点击保存按钮后返回修改后的图书信息界面) 3.删除一本图书(删除某一本书籍,在图书管理界面则不存在该条 ...

  3. MIPI-DSI协议

    MIPI联盟,即移动产业处理器接口(Mobile Industry Processor Interface 简称MIPI)联盟.MIPI(移动产业处理器接口)是MIPI联盟发起的为移动应用处理器制定的 ...

  4. Spring中过滤器(Filter)和拦截器(Interceptor)的区别和联系

    在我们日常的开发中,我们经常会用到Filter和Interceptor.有时同一个功能.Filter可以做,Interceptor也可以做.有时就需要考虑使用哪一个比较好.这篇文章主要介绍一下,二者的 ...

  5. 浅谈消息队列 Message Queue

    消息队列:在消息传递的过程中暂时保存消息的容器,充当发送者和接受者的中间人 消息队列的基本操作 using System; using System.Messaging; namespace MQ { ...

  6. Istio(九):istio安全之授权

    目录 一.模块概览 二.系统环境 三.istio授权 3.1 istio授权 3.2 来源 3.3 操作 3.4 条件 四.实战:授权(访问控制) 4.1 访问控制 4.2 清理 一.模块概览 在Ku ...

  7. ML-梯度下降法的详细推导与代码实现

    计算 对于线性回归,梯度下降法的目标就是找到一个足够好的向量\(\theta\),使代价函数\(J(\theta) = \sum_{i=1}^{m}(\hat{y}-y_{i})^{2}\)取得最小值 ...

  8. 云小课|MRS基础原理之MapReduce介绍

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:MapReduce ...

  9. FHQ Treap 详解

    鲜花 一些鲜花放在前面,平衡树学了很久,但是每学一遍都忘,原因就在于我只能 70% 理解 + 30% 背板子,所以每次都忘.这次我采取了截然不同的策略,自己按照自己的理解打一遍,大获成功(?),大概打 ...

  10. Scrapy 之 docker splash

    Scrapy 之 docker splash 1. ubuntu 安装docker 命令 curl -sSL https://get.daocloud.io/docker | sh 或者 curl - ...