国外网站国内备案,南京网站seo优化公司,哪个网站做logo好,重庆网站备案公司做图像配准的时候#xff0c;发现图像进行旋转的情况下的配准有一些特殊。于是想到可以用极坐标进行配准。查了一下资料#xff0c;发现大家用的更多的是对数极坐标Log Polar。
笛卡尔坐标系和极坐标系
先来说一下我们常用的笛卡尔坐标。X轴水平向右是正方向#xff0c;y轴垂…做图像配准的时候发现图像进行旋转的情况下的配准有一些特殊。于是想到可以用极坐标进行配准。查了一下资料发现大家用的更多的是对数极坐标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_LINEARCV_WARP_FILL_OUTLIERS)//插值方式);
在计算机图像处理中看似目标图像是原图像经过某种变换直接得到的其实我们不能一次性得到目标图像而是在已知目标图像大小和位深的基础上根据变换关系将目标图像的像素一个个映射回原图像由原图像的已知像素求出目标图像的未知像素。而具体求解的算法就是插值法。这里cvLogPolar中默认的插值算法是双线性插值策略是当目标图像的像素映射到原图边界之外时置0.具体关于函数的细节可以参考opencv手册https://docs.opencv.org/2.4/index.html除了cvLogPolar函数opencv还有一个函数cvLinerPolar这个函数没有对距离取对数所以叫线性极坐标。有的博客说是对半径做了log变换模拟人眼看到中间分辨率高边缘分辨率低的效果在机器学习中线性极坐标变换更加常用。下面是使用线性极坐标变换的例子可以看到与对数极坐标相比差距不大都达到了人眼的效果只是取了对数之后效果更加突出。对极坐标变换结果的解释因为在对数极坐标下两个坐标轴分别是角度和到中心点的距离的对数所以原图中以中心点为圆心的圆在极坐标下映射为一条直线通过中心点的直线被映射为平行于极轴坐标的直线角度不变原图边界处的点到中心点的距离在一个闭区间内有规律地波动角度分量则在0~360度均匀分布。相关代码
#includeiostream
#includeopencv2/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.5double 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代码cvloadimagehttps://blog.csdn.net/w12345_ww/article/details/45362843
5. https://blog.csdn.net/hitwengqi/article/details/6895215
6. Unchangedhttps://blog.csdn.net/smf0504/article/details/51384023