几何变换
-
-
- [2.1 仿射变换](#2.1 仿射变换)
- [2.2 投影变换](#2.2 投影变换)
- [2.3 极坐标变换](#2.3 极坐标变换)
-
- [2.3.1 笛卡尔坐标转极坐标](#2.3.1 笛卡尔坐标转极坐标)
- [2.3.2 极坐标转换为笛卡儿坐标](#2.3.2 极坐标转换为笛卡儿坐标)
- [2.3.3 图像极坐标变换](#2.3.3 图像极坐标变换)
- [2.3.4 线性极坐标函数](#2.3.4 线性极坐标函数)
- [2.3.5 对数极坐标函数 logPolar](#2.3.5 对数极坐标函数 logPolar)
-
几何变换:
- 仿射变换:平移、放大缩小、旋转、计算仿射矩阵(方程法、矩阵法)、插值(最近邻、双线性插值)、
- 投影变换
- 极坐标变换
2.1 仿射变换
平移
c++
x_ = 1*x + 0*y+ tx
y_ = 0*x + 1*y+ ty
放大缩小
c++
/*
x_ = sx*x + 0*y+ 0*tx
y_ = 0*x + sy*y+ 0*ty
void resize(InputArray scr,outputArray dst,Size dsize,double fx=0,double fy=0,int interpolation=INTER_LINEAR)
scr: 输入图像
dst: 输出图像
dsize:(宽高)
fx: 水平方向上的缩放比例
fy: 垂直方向上的缩放比例
interpolation: 插值法 INTER_NEAREST、INTER_LINEAR
*/
#include<opencv/core/core.hpp>
#include<opencv/highgui/highgui.hpp>
#include<opencv/imgproc/imgproc.hpp>
using namespace cv;
int main(int argv,char*argv[])
{
Mat I = imread(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
if(!I.data)
return -1;
Mat s = (Mat_<float>(2,2)<<0.5,0,0,0,0.5,0);
Mat dst;
warpAffine(I,dst,s,Size(I.cols/2,I.rows/2));
Mat dst1;
resize(I,dst1,Size(I.cols/2,I.rows/2),0.5,0.5);
}
旋转
c++
/*
x_ = cosa*x + sina*y+ 0*tx
y_ = -sina*x + cosa*y+ 0*ty
void rotate(InputArray scr,outputArray dst,int rotateCode)
rotateCode: ROTATE_90_CLOCKWISE,ROTATE_180,ROTATE_90_COUNTERCLOCKWISE,270度
*/
#include<opencv/core/core.hpp>
#include<opencv/highgui/highgui.hpp>
using namespace cv;
int main(int argv,char*argv[])
{
Mat I = imread(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
if(!I.data)
return -1;
Mat dst;
rotate(I,dst,ROTATE_90_CLOCKWISE);
}
计算仿射矩阵
方程法求仿射矩阵
c++
// 仿射矩阵有6个自由度,因此需要src,dst3对不共线的点求解
// 把对应点存在数组中
Point2f src[]={Point2f(0,0),Point2f(10,24),Point2f(60,120)};
Point2f dst[]={Point2f(0,0),Point2f(30,64),Point2f(50,98)};
Mat A = getAffineTransform(src,dst)
// 把对应点存在矩阵中
Mat src = (Mat_<float>(3,2)<<0,0,10,24,60,120);
Mat dst = (Mat_<float>(3,2)<<0,0,30,64,50,98);
Mat A = getAffineTransform(src,dst)
计算仿射矩阵
矩阵法:
c++
// 获取旋转矩阵
Mat A = getRotationMatrixD(Point2f(x,y),angle,scale);
2.2 投影变换
c++
/*
Point2f src[]={Point2f(0,0),Point2f(100,0),Point2f(0,100),Point2f(100,100)};
Point2f dst[]={Point2f(100,30),Point2f(200,30),Point2f(70,80),Point2f(230,90)};
Mat P=getPerspectiveTransform(src,dst);
Point2f src=(Mat_<float>(4,2)<<0,0,20,10,40,89,100,100);
Point2f dst=(Mat_<float>(4,2)<<10,20,30,80,80,90,200,150);
Mat P=getPerspectiveTransform(src,dst);
*/
2.3 极坐标变换
2.3.1 笛卡尔坐标转极坐标
c++
/*
笛卡尔坐标转极坐标
已知任意一点(x,y),中心(x',y'),(a,r)
r = ((x-x')**2+(y-y')**2)**0.5
a = 2pi+arctan2(y-y',x-x') 当y-y' <=0 or arctan2(y-y',x-x') 当y-y' >0
cartToPolar(x,y magnitude,angle,angleInDegrees)
angleInDegrees: ture 返回角度,反之,返回弧度。
*/
// 笛卡儿坐标转换为极坐标
Mat x = (Mat_<float>(1,8)<<0,1,2,0,2,0,1,2)-1;
Mat y = (Mat_<float>(1,8)<<0,0,0,1,1,2,2,2)-1;
Mat r,theta;
cartToPolar(x,y,r,theta,True);
/* 结果
1.41 3.93
1.0 4.71
1.41 5.5
1.0 3.14
1.0 0.0
1.41 2.36
1.0 1.57
1.41 0.79
*/
2.3.2 极坐标转换为笛卡儿坐标
c++
/*
x = x'+r*cosa
y = y'+r*sina
polarTocart(magnitude,angle,x,y,angleInDegrees));
返回的是以(0,0)为中心的笛卡尔坐标,已知(r,theta)和(x',y')得到(x-x',y-y')
*/
Mat angle = (Mat_<float>(2,2)<<30,31,30,31);
Mat r = (Mat_<float>(2,2)<<10,10,11,11);
Mat x,y;
polarTocart(r,angle,x,y,true);
2.3.3 图像极坐标变换
c++
/*
O(i,j) = I(x'+(r_min+r_step*i)*cos(a_min+a_step*j), y'+(r_min+r_step*i)*sin(a_min+a_step*j))
*/
python
import cv2
import numpy as np
import matplotlib.pyplot as plt
def polar(img, center, r, theta=(0, 360), rstep=1.0, thetastep=360.0 / (180 * 8)):
cx, cy = center
r_min, r_max = r
theta_min, theta_max = theta
H = int((r_max - r_min) / rstep) + 1
W = int((theta_max - theta_min) / thetastep) + 1
out = np.ones((H, W), img.dtype) * 125
r = np.linspace(r_min, r_max, H)
r = np.tile(r, (W, 1)).T # (H,W)
theta = np.linspace(theta_min, theta_max, W)
theta = np.tile(theta, (H, 1)) # (H,W)
x, y = cv2.polarToCart(r, theta, True)
for i in range(H):
for j in range(W):
px = int(round(x[i, j]) + cx)
py = int(round(y[i, j]) + cy)
if (px >= 0 and px <= W - 1) and (py >= 0 and px <= H - 1):
out[i, j] = img[px, py]
return out
if __name__ == "__main__":
img = np.zeros([300, 300, ], np.uint8)
img = cv2.circle(img, (150, 150), 80, (255), -1)
plt.imshow(img, 'gray')
o = polar(img, center=(150, 150), r=(0, 100), theta=(0, 360), rstep=0.25)
plt.imshow(o, 'gray')
c++
/*
根据中心(cx,cy)、最小半径、最小角度、半径步长、角度步长获取最大半径和最大步长,把r、theta转换成x,y。把(x,y)对应的像素值赋值给对应坐标(r、theta)。
*/
Mat polar(Mat I,Point2f center,Size size,float minr = 0,float mintheta=0,float thetaStep = 1.0/4,float rStep;=1.0)
{
// 构建 r[size.height,size.width]
Mat ri = Mat::zeroes(Size(1,size.height),CV_32FC1);
for (int i = 0;i <size.height;++i)
{
ri.at<float>(i,0) = minr+i*rStep;
}
Mat r = repeat(ri,1,size.width);
// 构建 theta[size.height,size.width]
Mat thetaj = Mat::zeroes(Size(size.width,1),CV_32FC1);
for (int j = 0;j <size.width;++j)
{
thetaj.at<float>(0,j) = mintheta+j*thetaStep;
}
Mat theta = repeat(thetaj,size.height,1
// 把极坐标转换为笛卡尔坐标
Mat x,y;
polarToCart(r,theta,x,y,true);
x += center.x;
y += center.y;
// 最近邻插值
Mat dst = 125*Mat::ones(size,CV_8UC1);
for (int i=0;i<size.height,++i)
{
for (int j=0;j<size.width,++j)
{
float xij = x.at<float>(i,j);
float yij = y.at<float>(i,j);
int x_near = int(round(xij));
int y_near = int(round(yij));
}
if((x_near>=0&&x_near<=I.cols)&&(y_near>=0&&y_near<=I.rows))
{
dst.at<uchar>(i,j)=I.at<uchar>(y_near,x_near)
}
}
return dst;
}
int main(int argc,char*argv[])
{
Mat I = imread(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
if(!I.data)
return -1;
float thetaStep=1.0/4;
float minr = 270;
Size size(int(360/thetaStep),80);
Mat dst = polar(I,Point2f(500,508),size,minr);
flip(dst,dst,0);
}
2.3.4 线性极坐标函数
c++
/*
void linearPolar( InputArray src, OutputArray dst,Point2f center, double
maxRadius, int flags );
src :输入图像矩阵(单、多通道矩阵都可以)
dst :输出图像矩阵,其尺寸和 src 是相同的
center :极坐标变换中心
maxRadius :极坐标变换的最大距离
flags :插值算法,同函数 resize、warpAffine 的插值算法
角度 𝜃 的变换步长大约为 36/0𝐻
,𝑟 的变换步长大约为 maxRadius/𝑊
;
*/
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
using namespace cv;
int main(int argc, char*argv[])
{
//输入图像
Mat src = imread(argv[1], IMREAD_ANYCOLOR);
if (!src.data)
return -1;
//极坐标变换
Mat dst;
linearPolar(src, dst, Point2f(508, 503), 550, CV_INTER_LINEAR);
//显示原图和极坐标变换图
imshow("原图", src);
imshow("极坐标变换图", dst);
waitKey(0);
return 0;
}
2.3.5 对数极坐标函数 logPolar
c++
/*
void logPolar(InputArray src,OutputArray dst,Point2f center, double M, int
flags )
src: 输入图像矩阵(单、多通道矩阵都可以)
dst: 输出图像矩阵,其尺寸和 src 是相同的
center: 极坐标变换中心
M: 系数,该值大一点效果会好一些
flags:
WARP_FILL_OUTLIERS:笛卡儿坐标向对数极坐标变换
WARP_INVERSE_MAP:对数极坐标向笛卡儿坐标变换
笛卡儿坐标转换为对数极坐
标:
r = M*log(((x-cx)**2+(y-cy)**2)**0.5)
theta = actan2(y-cy,x-cx); if y>cy,theta=theta else theta+=2*pi
将对数极坐标转换为笛卡儿坐标:
x = cx+exp(r/M)cosa;y = cy+exp(r/M)sina
*/
int main(int argc, char*argv[])
{
//读入图像
Mat src = imread(argv[1], IMREAD_ANYCOLOR);
//对数极坐标变换
Mat dst;
Point2f center(508, 503);
float M = 100;
logPolar(src, dst, center, M, WARP_FILL_OUTLIERS);
//显示对数极坐标变换的结果
imshow("对数极坐标变换", dst);
imshow("原图", src);
waitKey(0);
return 0;
}