(一)基础知识
极坐标变换及其反变换的关键在于,根据极坐标变换前的图像(我们称为“圆图”)确定极坐标变换后的图像(我们称为“方图”)上每个像素点的像素值。也即是找到“圆图”和“方图”间几何坐标的对应关系。
1、极坐标变换
原理:
如下图所示,实现极坐标变换的关键即在于找到圆图上任一点P(i,j),在方图上对应的点p(m,n),然后通过插值算法实现圆图上所有像素点的赋值。
方图上,其行列数分别为M、N,方图上的每一列对应为圆图上的每条半径,半径方向存在着一个长度缩放因子delta_r = M/R,圆周方向被分为N等分,即角度因子为delta_t = 2π/N;
圆图上,图像坐标(i,j)和世界坐标(x,y)有着如下变换关系:x = j - R, y = R - i;
那么,图中P点半径长度为r = sqrt(xx + yy),角度theta = arctan(y/x);
圆图上点P在方图上对应行数为r/delta_r;
圆图上点P在方图上对应的列数n = thata/delta_t。
图像变换过程可以参考
(二)软件设计
因为这个代码时应用于嵌入式平台必须考虑到计算速度的问题,所以这里我们自己实现三角函数的近似计算缺点在于,边界点可能会出现失真。
/**
* @fn float fastSin(float x)
*
* @brief 自定义快速变换 sin cos 函数 定义 而外精度 更高拟合效果 参考链接
* https://www.cnblogs.com/sun11086/archive/2009/03/20/1417944.html
*
* @author IRIS_Chen
* @date 2019/6/17
*
* @param x The x coordinate
*
* @return A float
*/
//本质上是利用了一个已经拟合好的二次函数近似三角函数
float fastSin(float x)
{
float y;
// 限定 x 在 -Pi 到 pi
while (x < - PI)
{
x += (float)(2 * PI);
}
while (x > PI)
{
x -= (float)(2 * PI);
}
const float B = 1.2732; // 4 / CV_PI;
const float C = -0.4053; // -4 / (CV_PI*CV_PI);
if(x>0)
{
y = B * x + C * x * x;
}
else
{
y = -1*B * x + C * x * x;
}
return y;
}
/**
* @fn float fastCos(float x)
*
* @brief Fast cosine
*
* @author IRIS_Chen
* @date 2019/6/17
*
* @param x The x coordinate
*
* @return A float
*/
float fastCos(float x)
{
return fastSin(x + 1.5707);
}
Mat creatMapMat(Mat src,
int rows_c,
int cols_c,
double startdelta)
{
Mat dst;
int i,j;
u8* inaddr;
u8* outaddr;
int polar_d =src.width;
double polar_r = polar_d / 2.0;
printf("1");
dst=create("..\\picture\\test.bmp",cols_c,rows_c,3);
double delta_r = polar_r / rows_c; //半径因子
double delta_t = 2.0*PI / cols_c; //角度因子
double center_polar_x = (polar_d - 1) / 2.0;
double center_polar_y = (polar_d - 1) / 2.0;
printf("2");
for (i = 0; i < cols_c; i++)
{
double theta_p = i * delta_t+startdelta; //方图第i列在圆图对应线的角度
double sin_theta = fastSin(theta_p);
double cos_theta = fastCos(theta_p);
for (int j = 0; j < rows_c; j++)
{
double temp_r = j * delta_r; //方图第j行在圆图上对应的半径长度
int polar_x = (int)(center_polar_x + temp_r * cos_theta);
int polar_y = (int)(center_polar_y - temp_r * sin_theta);
if(i<dst.width&&j<dst.highth&&polar_x<src.width&&polar_y<=src.highth)
{
inaddr=at(&dst,i,j);
outaddr=at(&src,polar_x,polar_y);
*inaddr=*outaddr;
*(inaddr+1)=*(outaddr+1);
*(inaddr+2)=*(outaddr+2);
}
}
}
return dst;
}
(三)应用举例
/*************************************************
Copyright © Yueyang Co. Ltd. 2019-2029. All rights reserved.
File name: cv.h
Author: Yueyang
Version: V1.0
Description: LiteCV运行主函数
Others:
Log: 11.3 Yueyang
*************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "bmp.h"
#include "cv.h"
#include "GeoTrans.h"
#include "PointOr.h"
#include "BasicGui.h"
int main()
{
Mat src;
Mat_Init();
src=load("..\\picture\\hole.bmp");
Mat dst=dst=creatMapMat(src, 500,500,PI/4);
save("..\\picture\\test.bmp",&dst);
show(&dst);
destory(&src);
destory(&dst);
return 0;
}
原图:
这是一张医学中使用的人的血管内壁照片:
变换后:
(四)写在后面
因为LiteCV项目才刚刚写了一个开头,代码中有错误的地方还望指出。我已经将项目同步到了github,我会实时更新这个代码仓库。
项目github地址:
LITECV