(一)基礎知識
極座標變換及其反變換的關鍵在於,根據極座標變換前的圖像(我們稱爲“方圖”)確定極座標變換後的圖像(我們稱爲“圓圖”)上每個像素點的像素值。也即是找到“圓圖”和“方圖”間幾何座標的對應關係。
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