前言:
欢迎来到本博客
本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。
具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。
支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!
文章目录
学习目标一、极坐标变换原理1.1、笛卡儿坐标转换为极坐标1.2、极坐标转换为笛卡儿坐标1.3、利用极坐标变换对图像进行变换
二、常见极坐标变换函数2.1、线性极坐标函数linearPolar()2.2、对数极坐标函数logPolar()
三、 总结
学习目标
熟悉极坐标变换原理 了解常见极坐标变换函数 C++实现极坐标变换案例
一、极坐标变换原理
之前介绍的几种几何变换,如投影变换和仿射变换都是笛卡儿坐标系之间的变换,下面介绍笛卡 儿平面坐标和极坐标之间的空间变换关系,从而完成图像的极坐标变换。
1.1、笛卡儿坐标转换为极坐标
在笛卡尔坐标系xoy平面上的一点(x,y),以(x',y’)为中心,通过以下公式将笛卡儿坐标转换为极坐标:
从上述公式可以看出,以变换中心为圆心的同一个圆上的点,在极坐标系θ & r中显示为一条直线。θ的取值范围用弧度表示为[0,2π],用角度表示为[0,360],反正切函数arctan2返回的角度和笛卡儿坐标点所在的象限有关系。
象限反正切角度范围(y-y',x-x')在第一象限[0,90](y-y',x-x')在第二象限[90,180](y-y',x-x')在第三象限[-180,-90](y-y',x-x')在第四象限[-90,0]
为了使用方便,将第三、四象限情况,即(y-y')≤0时返回的正切角度加上一个周期360 ° ,所以经过极坐标变换后的角度范围为[0,360]。
OpenCV提供了函数:
void cartToPolar(InputArray x,
InputArray y,
OutputArray magnitude,
OutputArray angle,
bool angleInDegrees = false
)
将原点移动到变换中心后的笛卡儿坐标向极坐标的变换,其参数解释如下表所示,返回值magnitude、angle是与参数x和y具有相同尺寸和数据类型的ndarray。
参数注释x输入,单精度或双精度浮点型数组。float_32或float_64y与x一致magnitude输出,极径,单精度或双精度浮点型数组。angle输出,极角,可为角度或弧度。angleInDegrees输入,为True时,angle为角度;为False时,angle为弧度
举例:计算(0,0)、(1,0)、(2,0)、(0,1)、(1,1)、(2,1)、(0,2)、(1,2)、(2,2)这9个点以(1,1)为中心进行的极坐标变换。首先将坐标原点移动到(1,1)处,按照平移仿射矩阵计算出这9个点平移后的新坐标值,然后利用函数cartToPolar进行极坐标变换。代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
Mat x = (Mat_
Mat y = (Mat_
Mat r, theta;
Mat r1, theta1;
cartToPolar(x,y,r, theta, true);//角度
cartToPolar(x, y, r1,theta1, false);//弧度
cout << "极径:"<< r<< endl;
cout << "对应角度信息:" << theta << endl;
cout << "===================================" << endl;
cout << "极径:" << r1 << endl;
cout << "对应弧度信息:" << theta1 << endl;
return 0;
}
1.2、极坐标转换为笛卡儿坐标
极坐标变换也是可逆的,在已知极坐标(θ,r)和笛卡儿坐标的条件下,计算哪个笛卡儿坐标(x,y)以 为中心的极坐标变换是(θ,r),可通过以下公式计算:
OpenCV提供了函数:
void polarToCart (InputArray magnitude,
InputArray angle,
OutputArray x,
OutputArray y,
bool angleInDegrees = false
)
将极坐标转换为笛卡儿坐标,其参数解释与函数cartToPolar类似。注意:返回的是以原点(0,0)为中心的笛卡儿坐标,即已知(θ,r)和(x',y') ,计算出的是(x-x',y-y') 。
参数注释magnitude输入,极径,单精度或双精度浮点型数组。angle输入,极角,可为角度或弧度。x输出,单精度或双精度浮点型数组。float_32或float_64y与x一致angleInDegrees输入,为True时,angle为角度;为False时,angle为弧度
举例:已知极坐标系θ & r中的(30,10)、(31,10)、(30,11)、(31,11),其中θ是以角度表示的,问笛卡儿坐标系xoy中的哪四个坐标是以(-15,15)为中心经过极坐标变换后得到这四个坐标。代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
//极坐标转笛卡尔
Mat angle = (Mat_
Mat r = (Mat_
Mat x, y;
polarToCart(r,angle,x,y,true);
x += -15;
y += 15;
for (int i = 0; i < x.rows; i++)
{
for (int j = 0; j < x.cols; j++)
{
cout << "笛卡尔坐标为:(" <
}
}
return 0;
}
为什么要x+=-15,y+=15呢? 得到的(x,y)是以(0,0)为变换中心的,而这里的变换中心为(-15,15),所以只要进行x+=-15,y+=15操作。
1.3、利用极坐标变换对图像进行变换
假设输入图像矩阵为I, (x',y')代表极坐标空间变换的中心,输出图像矩阵为O,比较简单的方法就是利用极坐标和笛卡儿坐标的一一对应关系得到O的每一个像素值,即:
这里的θ和r都是以1为步长进行离散化的,由于变换步长较大,输出图像矩阵O可能会损失原图的很多信息。
但是,可以进行优化,假设要将与(x',y') 的距离范围为[rmin ,rmax ]、角度范围在[θmin ,θmax]内的点进行极坐标向笛卡儿坐标的变换,当然这个范围内的点也是无穷多的,仍需要离散化;假设r的变换步长为rstep >0;sstep ≤1,θ的变换步长为θstep ,θstep 一般取360/180*N,N≥2。那么,输出的图像矩阵的宽和高为:
图像矩阵O的第i行第j列的值通过以下公式进行计算:
在C++中可以通过先确定输出图像的宽、高,以及r和θ的最小值和变换步长,从而估算出r和θ的最大值。在实现极坐标变换之前,先介绍一下OpenCV中的几个函数:
(1) 该函数实现矩阵的平铺
repeat(const Mat&src,int ny,int nx)
参数注释src输入矩阵。ny将src在垂直方向上重复ny次。nx将src在水平方向上重复nx次。
(2) 实现图像的极坐标变换
Mat polar (Mat I,Point2f center ,Size size, float minr = 0,float mintheta=0,float thetaStep = 0,float rStep =0)
参数注释I输入图像center极坐标变换中心minr与变换中心的最小距离mintheta与变换中心的最小角度thetaStep角度的变换步长rStep距离的变换步长
(3) 实现矩阵的水平镜像、垂直镜像及逆时针旋转180 °
void flip(InputArray src, OutputArray dst, int flipCode)
参数注释src输入图像矩阵dst输出图像矩阵,尺寸与数据类型与src一致flipCode>0:src绕y轴镜像处理; =0:src绕x轴镜像处理 ;<0:逆时针旋转180°(先绕x镜像,再绕y镜像)
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
//flip(src,dst,flipcode)函数
Mat src = imread("D:/VSCodeFile/OpenCV_CSDN/image/logo.jpeg", IMREAD_COLOR);
Mat dst,dst1,dst2;
flip(src,dst,1);//绕y轴镜像处理
flip(src, dst1, 0);//绕x轴镜像处理
flip(src, dst2, -1);//逆时针旋转180
imshow("src原图", src);
namedWindow("src原图", WINDOW_AUTOSIZE);
putTextHusky(dst, "绕y轴镜像处理", Point(30, 30), Scalar(0, 0, 255), 20, "Arial");
imshow("绕y轴镜像处理", dst);
namedWindow("绕y轴镜像处理", WINDOW_AUTOSIZE);
putTextHusky(dst1, "绕x轴镜像处理", Point(30, 30), Scalar(0, 0, 255), 20, "Arial");
imshow("绕x轴镜像处理", dst1);
namedWindow("绕x轴镜像处理", WINDOW_AUTOSIZE);
putTextHusky(dst2, "逆时针旋转180 ", Point(30, 30), Scalar(0, 0, 255),20, "Arial");
imshow("逆时针旋转180", dst2);
namedWindow("逆时针旋转180", WINDOW_AUTOSIZE);
waitKey(0);
return 0;
}
极坐标变换对图像进行变换
极坐标变换是比较耗时的运算,有的时候只需对距离变换中心一定范围内的点进行极坐标变换即可;当然,角度也与之类似,有的时候不需要是整个的0~360 ° 。比如图像中的圆环区域进行极坐标变换,这个圆环区域与中心点(x,y)的距离在某个范围内。
分析:下面进行图像的极坐标变换,处理下列图像为例。下图所示的图像宽为942、高为910,其中有2层圆环,目的是将圆环按照“矩形”展示,通过极坐标变换正好可以实现该功能,圆环的中心大概位置为(466,453),将它作为极坐标变换中心。比如只对图像中"星座"符号圆环区域进行极坐标变换,这个圆环区域与中心点(466,453)的距离在 100~230 范围内,这个区域也是估算获得,那么可以令minr=100、rStep=1,从而输出图像的高大约等于130(230-100),令mintheta=0、thetastep=1/4、因为是整个圆环即需要角度范围是[0,360],则设置输出图像的宽大约等于360*4=1440。代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
Mat src = imread("D:/VSCodeFile/OpenCV_CSDN/image/img1.png", IMREAD_COLOR);
if (!src.data)
{
return -1;
}
//图像极坐标变换
float thetastep = 1.0 / 4;
float minr = 100;
Size size(int(360 / thetastep), 130);
Mat dst = polar(src, Point2f(466, 453), size, minr);
flip(dst, dst, 0); //绕x轴镜像处理
imshow("src", src);
imshow("dst", dst);
waitKey(0);
return 0;
}
注:我opencv3.4好像没有polar()函数,此问题待解决,先把代码呈上。
二、常见极坐标变换函数
2.1、线性极坐标函数linearPolar()
线性极坐标函数linearPolar()如下:
void linearPolar(InputArray src,OutputArray dst,Point2f center,double maxRadius,int flags);
参数注释src输入图像矩阵dst输出图像矩阵,尺寸与数据类型与src一致center极坐标变换中心maxRadius极坐标变换得最大距离flags插值算法,与函数resize、warpAffine得插值算法一致
利用该函数的C++处理上面的那个图像,极坐标变换中心和上面代码中的中心是相同的,取最大距离为230,具体代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
//极坐标对图像进行转换linearPolar函数
Mat src = imread("D:/VSCodeFile/OpenCV_CSDN/image/img1.png", IMREAD_COLOR);
if (!src.data)
{
return -1;
}
//图像极坐标变换
float maxr = 230;
uchar flags = CV_INTER_LINEAR;
//Mat dst = polar(src, Point2f(466, 453), size, minr);
Mat dst;
linearPolar(src, dst, Point2f(466, 453), maxr, flags);
//显示
imshow("src", src);
imshow("dst", dst);
waitKey(0);
return 0;
}
注:上面polar()函数结果与次类似。
函数linearPolar生成的极坐标,θ在垂直方向上,r在水平方向上,前面实现的极坐标变换r在垂直方向上,θ在水平方向上,所以旋转90 ° 得到的结果就会类似。
仔细观察,里面的字好像也是在垂直方向上被压缩了,主要由于θ步长有点大造成的,那么该函数的r和θ的变换步长是多少呢?假设src的尺寸为宽W、高H,因为输出图像的尺寸也为宽W、高H,所以角度θ的变换步长大约为360/H,r的变换步长大约为maxRadius/W,上图图像的高为453、宽为466,所以r的变换步长为230/466,θ的变换步长为360/453,比上面θ的变换步长thetastep=1/4还要大,因此会显得有些扁。
该函数有两个缺点:第一,极坐标变换的步长是不可控制的,导致得到的图可能不是很理想;第二,该函数只能对整个圆内区域,而无法对一个指定的圆环区域进行极坐标变换。除了线性极坐标变换,
OpenCV还实现了另一种极坐标变换——对数极坐标变换,它们在本质上是相同的。
2.2、对数极坐标函数logPolar()
对数极坐标函数logPolar()如下:
void logPolar(InputArray src,OutputArray dst,Point2f center,double M,int flags);
参数注释src输入图像矩阵dst输出图像矩阵,尺寸与数据类型与src一致center极坐标变换中心M系数,大一点效果更好flagsWARP_FILL_OUTLIERS:笛卡儿坐标向对数极坐标变换;WARP_INVERSE_MAP:对数极坐标向笛卡儿坐标变换
本质上,对数极坐标变换和线性极坐标变换是一样的,将笛卡儿坐标转换为对数极坐标的公式如下:
反过来,将对数极坐标转换为笛卡儿坐标的公式如下:
对比标准的线性极坐标变换公式,显然M值越小,得到的r方向上的压缩越大,在图像上的表现就是在r方向上的信息越来越少,所以设置M值大一点效果会好一些。还是以上图为例,代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
//极坐标对图像进行转换logPolar函数
Mat src = imread("D:/VSCodeFile/OpenCV_CSDN/image/img1.png", IMREAD_COLOR);
if (!src.data)
{
return -1;
}
//图像极坐标变换
float maxr = 230;
uchar flags = WARP_FILL_OUTLIERS;
//Mat dst = polar(src, Point2f(466, 453), size, minr);
Mat dst, dst1, dst2;
//linearPolar(src, dst, Point2f(466, 453), maxr, flags);
logPolar(src,dst, Point2f(466, 453),50,flags);
logPolar(src, dst1, Point2f(466, 453),100, flags);
logPolar(src, dst2, Point2f(466, 453), 150, flags);
//显示
imshow("src", src);
imshow("dst-M:50", dst);
imshow("dst-M:100", dst1);
imshow("dst-M:150", dst2);
waitKey(0);
return 0;
}
三、 总结
最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。
今天的文章就到这里啦~
喜欢的话,点赞、收藏⭐️、关注哦 ~
相关文章
发表评论