做图像配准的时候,发现图像进行旋转的情况下的配准有一些特殊。于是想到可以用极坐标进行配准。查了一下资料,发现大家用的更多的是对数极坐标Log Polar。

笛卡尔坐标系和极坐标系

先来说一下我们常用的笛卡尔坐标。X轴水平向右是正方向,y轴垂直于x轴,竖直向上是正方向。但是在计算机中图像的原点在左上方,所以如果是在笛卡尔坐标中进行旋转,需要三个矩阵相乘,分别实现从计算机坐标到笛卡尔坐标,旋转角度theta,笛卡尔坐标转换为计算机坐标系。

如果将笛卡尔坐标系换为极坐标系,两个坐标轴分别是角度theta和长轴,点的位置的表示方式用与原点水平方向的夹角和到原点的距离表示。而对数极坐标,是在极坐标的基础上,对长轴的长度取对数,这样把到原点距离的线性变换变成非线性变换,当距离成倍增长时对数极坐标下的距离小幅变化,起到了数据压缩的作用,减少了计算量。就像是通信脉冲采样中的谬率和a率,还有信道中的信噪比。参考资料中提到的极坐标和对数坐标都是对人眼的仿生模拟,因为人眼的中心凸起有聚焦的作用,极坐标下的像素都环绕在原点周围,但考虑到半径越来越大,越远离中心点的地方空间分辨率越低。所以可以将这么一次变换看作是图像通过了如下的滤波器:

极坐标在OpenCV中的函数

OpenCV中集成了对数极坐标的实现。参数也都很简单:

cvLogPolar(

const CvArr* src, CvArr* dst,

CvPoint2D32f center, //极坐标变换的原点

double M,//缩放系数

int flagsCV_DEFAULT(CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS)//插值方式

);

在计算机图像处理中,看似目标图像是原图像经过某种变换直接得到的,其实我们不能一次性得到目标图像,而是在已知目标图像大小和位深的基础上,根据变换关系将目标图像的像素一个个映射回原图像,由原图像的已知像素求出目标图像的未知像素。而具体求解的算法就是插值法。这里cvLogPolar中默认的插值算法是双线性插值,策略是当目标图像的像素映射到原图边界之外时置0.具体关于函数的细节可以参考opencv手册https://docs.opencv.org/2.4/index.html

除了cvLogPolar函数,opencv还有一个函数cvLinerPolar,这个函数没有对距离取对数,所以叫线性极坐标。有的博客说是对半径做了log变换模拟人眼看到中间分辨率高,边缘分辨率低的效果,在机器学习中线性极坐标变换更加常用。下面是使用线性极坐标变换的例子,可以看到与对数极坐标相比,差距不大,都达到了人眼的效果,只是取了对数之后效果更加突出。

对极坐标变换结果的解释

因为在对数极坐标下两个坐标轴分别是角度和到中心点的距离的对数,所以原图中以中心点为圆心的圆在极坐标下映射为一条直线,通过中心点的直线被映射为平行于极轴坐标的直线(角度不变),原图边界处的点到中心点的距离在一个闭区间内有规律地波动,角度分量则在0~360度均匀分布。

相关代码

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
IplImage* rotateImage1(IplImage* img, int degree);int main()
{IplImage *src = cvLoadImage("beaver.png");// , -1);cvLogPolar要求必须是三通道的IplImage *dst = cvCreateImage(cvGetSize(src), 8, 3);IplImage *dstLiner = cvCreateImage(cvGetSize(src), 8, 3);IplImage* src2 = cvCreateImage(cvGetSize(src), 8, 3);IplImage* src2Liner = cvCreateImage(cvGetSize(src), 8, 3);CvPoint2D32f center = cvPoint2D32f(src->width/2, src->height/2);//旋转中心为图像中心  //CvPoint2D32f center;//center.x = float(img->width / 2.0 + 0.5);//center.y = float(img->height / 2.0 + 0.5);这里为什么要加0.5?double m = 50.0;cvLogPolar(src, dst, center, m, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS);cvLogPolar(dst, src2, center, m, CV_INTER_LINEAR + CV_WARP_INVERSE_MAP);//CV_WARP_INVERSE_MAP - 表示矩阵由输出图像到输入图像的逆变换,并且因此可以直接用于像素插值。否则,函数从map_matrix中寻找逆变换。cvLogPolar(src, dstLiner, center, m, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS);cvLogPolar(dstLiner, src2Liner, center, m, CV_INTER_LINEAR + CV_WARP_INVERSE_MAP);//对原图旋转IplImage *RotateImage = rotateImage1(src, 20);cvNamedWindow("旋转图", CV_WINDOW_AUTOSIZE);cvShowImage("旋转图", RotateImage);//对旋转图极坐标变换IplImage *dstR = cvCreateImage(cvGetSize(src), 8, 3);cvLogPolar(RotateImage, dstR, center, m, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS);cvNamedWindow("旋转图极坐标", CV_WINDOW_AUTOSIZE);cvShowImage("旋转图极坐标", dstR);cvNamedWindow("原图", CV_WINDOW_AUTOSIZE);cvShowImage("原图", src);cvNamedWindow("变换后的图", CV_WINDOW_AUTOSIZE);cvShowImage("变换后的图", dst);cvNamedWindow("线性极坐标变换后的图", CV_WINDOW_AUTOSIZE);cvShowImage("线性极坐标变换后的图", dstLiner);cvNamedWindow("反变换后的图", CV_WINDOW_AUTOSIZE);cvShowImage("反变换后的图", src2);cvNamedWindow("线性反变换后的图", CV_WINDOW_AUTOSIZE);cvShowImage("线性反变换后的图", src2Liner);cvWaitKey();cvReleaseImage(&src);cvReleaseImage(&dst);cvReleaseImage(&dstLiner);cvReleaseImage(&src2Liner);cvReleaseImage(&RotateImage);cvReleaseImage(&dstR);cvDestroyWindow("原图");cvDestroyWindow("变换后的图");cvDestroyWindow("线性极坐标变换后的图");cvDestroyWindow("线性反变换后的图");cvDestroyWindow("旋转图");cvDestroyWindow("旋转图极坐标");return 0;
}
//旋转图像内容不变,尺寸相应变大  https://blog.csdn.net/qingzai_/article/details/51095297
IplImage* rotateImage1(IplImage* img, int degree){double angle = degree  * CV_PI / 180.; // 弧度    double a = sin(angle), b = cos(angle);int width = img->width;int height = img->height;int width_rotate = int(height * fabs(a) + width * fabs(b));int height_rotate = int(width * fabs(a) + height * fabs(b));//旋转数组map  // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  float map[6];CvMat map_matrix = cvMat(2, 3, CV_32F, map);// 旋转中心  CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);cv2DRotationMatrix(center, degree, 1.0, &map_matrix);map[2] += (width_rotate - width) / 2;map[5] += (height_rotate - height) / 2;IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);//对图像做仿射变换  //CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。  //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.  //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换,  cvWarpAffine(img, img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));return img_rotate;
}

需要注意的几个问题

1.Cvloadimage问题

在参考的代码中,因为要返回指向CvArr的指针,所以使用cvLoadImage读入图像,但是使用的是CV_LOAD_IMAGE_UNCHANGED参数,会中断。一个解释是:如果输入有冲突的标志,将采用较小的数字值。CV_LOAD_IMAGE_ANYCOLOR有着可以和CV_LOAD_IMAGE_ANYDEPTH同时使用的优点,CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR可以载入最真实的图像,所以CV_LOAD_IMAGE_UNCHANGED不再使用了。但是将cvLoadImage的参数换成CV_LOAD_IMAGE_ANYDEPTH| CV_LOAD_IMAGE_ANYCOLOR也依然中断。后来经过调试发现,中断发生在cvLogPolar阶段,源图像只要是单通道即灰度图就会中断。而我选择的原图就是单通道的,所以按照原图载入就会发生错误。三通道的图可以被读取成单通道的,而之前我不知道的是单通道的图也可以读取成三通道的。。

cvLoadImage和imread都调用的是imread_函数,只不过参数一个是LOAD_IMAGE,一个是LOAD_MAT。

enum
{
/* 8bit, color or not */CV_LOAD_IMAGE_UNCHANGED  =-1,
/* 8bit, gray */CV_LOAD_IMAGE_GRAYSCALE  =0,
/* ?, color 缺省值,三通道*/CV_LOAD_IMAGE_COLOR      =1,
/* any depth, 任意深度,保持不变? */CV_LOAD_IMAGE_ANYDEPTH   =2,
/* ?, any color 任意颜色,保持不变?*/CV_LOAD_IMAGE_ANYCOLOR   =4
};

2.反变换问题

cvLogPolar的CV_WARP_INVERSE_MAP参数可以实现反变换,但是链接5的代码中把反变换的输入图像写成笛卡尔坐标系下的原图了。

Reference:

1.      https://blog.csdn.net/liyuan02/article/details/6750828

2.      https://blog.csdn.net/xieyan0811/article/details/71106496

3.      https://blog.csdn.net/jjrfjyfjyfjdfjrujdjd/article/details/40268573

4.      Opencv代码cvloadimage:https://blog.csdn.net/w12345_ww/article/details/45362843

5.      https://blog.csdn.net/hitwengqi/article/details/6895215

6.      Unchanged:https://blog.csdn.net/smf0504/article/details/51384023

图像极坐标变换的研究相关推荐

  1. 【OpenCV 4开发详解】图像极坐标变换

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  2. 【算法+OpenCV】图像极坐标变换及基于OpenCV的实现

    在医学图像处理,尤其是在处理血管断层扫描类(如OCT.IVUS等)图像的过程中,不可避免的会使用到极坐标变换,也即是我们通常所说的"方转圆".同样,我们可以使用极坐标变换的反变换实 ...

  3. 图像极坐标变换及在OCR中的应用

    极坐标变换定义 我们知道在二维坐标系中,有直角坐标系,也有极坐标系,二者的转换关系是: 如下图: 如图,直角坐标系的圆心与极坐标系的圆心一一对应,且圆弧BA可以通过极坐标变换到极坐标系ρ=r\rho= ...

  4. matlab 图像极坐标变换,如何在Matlab中将图像从笛卡尔坐标变换为极坐标?

    这不是很清楚你正在尝试做的,这就是为什么我在做...... 所以给出的图像我自己的例子,我使用CART2POL将像素x/y坐标从笛卡尔坐标转换为极坐标. 在第一个图中,我显示了点的位置,第二个图中我绘 ...

  5. 教程 | OpenCV4中的极坐标变换

    极坐标变换就是将图像在直角坐标系与极坐标系中互相变换,形式如图3-26所示,它可以将一圆形图像变换成一个矩形图像,常用于处理钟表.圆盘等图像.圆形图案边缘上的文字经过及坐标变换后可以垂直的排列在新图像 ...

  6. 【OpenCV 4开发详解】图像距离变换

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  7. 图像检测技术的研究现状

    图像检测技术的研究现状 技术检测 图像处理知识库 · 2016-01-08 19:59 图像检测技术的研究现状 所谓图像检测,就是通过图像对感兴趣的特征区域(检测目标)进行提取的过程,其中图像是承载检 ...

  8. halcon 圆环类缺陷检测的一种方法(极坐标变换法)

    目录 简介 极坐标变换 定义 原理 Halcon中的极坐标变换 1.polar_trans_image_ext算子用法与参数剖析 2.polar_trans_region_inv算子用法与参数剖析 圆 ...

  9. 深度-图像风格变换【二】

    深度卷积神经网络图像风格变换 Deep Photo Style Transfer Taylor Guo, 2017年4月23日 星期日 - 4月27日星期四 摘要 本文介绍了深度学习方法的图像风格转换 ...

最新文章

  1. linux3.0 nand分区,OK6410(256MRAM2Gnandflash) Linux3.0.1内核移植
  2. Socket编程获取服务器时间
  3. CSS中clear属性的both、left和right浅析
  4. Android --- Retrofit 之 Okhttp3 网络请求总是调用 onFailure 方法,而不调用 onResponse,报错 timeout。
  5. java整合mybatis,springboot集成mybatis
  6. 使用jsp实现word、excel格式报表打印-JSP教程,Jsp/Servlet
  7. 【论文解读】何恺明团队最新力作SimSiam:消除表征学习“崩溃解”,探寻对比表达学习成功之根源...
  8. mount:在/dev/sr0上找不到媒体
  9. 8. Geometric problems
  10. Laravel核心技术解析(1)—— Composer 组件管理与自动加载
  11. C++11 并发指南三(Lock 详解)(转载)
  12. python教程视频哪个好-Python 基础视频教程那个好?
  13. flask-uploads
  14. java导出图片到excel_POI:将图片导出到Excel
  15. 做外贸如何防止邮箱被封?已解决!
  16. Android页面跳转(Intent)
  17. 【多线程】Lock和ReentrantLock使用和源码分析
  18. 小米米家零冷水燃气热水器 S1 18L 评测
  19. 【2018,中国智能+】新智元10万+热文排行,AI爆发没有看客
  20. 比 B 站好用,堪称二次元福音!GitHub 这款「动漫搜番」神器我爱了!

热门文章

  1. Python小工具:批量给视频加水印
  2. 安卓视频播放器(TV)
  3. 用python的嵌套if结构开发一个输入(input)计税计算器
  4. 任意长度的python列表元组_【任】 任怎么读|组词|读音|拼音|多音字|意思 - 辞海之家...
  5. 基于双语数据集搭建seq2seq模型
  6. iOS Xcode提交IPA时收到苹果邮件ITMS-90338: Non-public API usage
  7. 小米10 MIUI11 安卓10安装面具获取root权限
  8. 《MySQL必知必会》SQL文件
  9. 【机器学习】逻辑回归案例二:鸢尾花数据分类,决策边界绘制逐步代码讲解
  10. 网页中漂浮的图片代码