Opencv 几何变换
python API
计算仿射矩阵
1、方程法
如果(0,0)、(200,0)、(0,200)三个坐标通过某仿射变换矩阵A转换为(0,0)、(100,0)、(0,100),通过三组坐标构造出六个方程,求解A
import cv2
import numpy as np
src = np.array([[0,0],[200,0],[0,200]],np.float32)#数据类型必须为浮点型,否则出错
dst = np.array([[0,0],[100,0],[0,100]],np.float32)
A = cv2.getAffineTransform(src,dst)
print(A)
[[0.5 0. 0. ]
[0. 0.5 0. ]]
2、知道基本变换步骤后可以使用矩阵法计算仿射矩阵
例如空间坐标先等比例缩放两倍,然后水平方向平移100,垂直方向平移200
s = np.array([[0.5,0,0],[0,0.5,0],[0,0,1]])#缩放矩阵
t = np.array([[1,0,100],[0,1,200],[0,0,1]])#平移矩阵
A = np.dot(t,s)#矩阵相乘
print(A)
[[ 0.5 0. 100. ]
[ 0. 0.5 200. ]
[ 0. 0. 1. ]]
3、等比例缩放的仿射运算Opencv提供了cv2.getRotationMatrix2D(center, angle, scale)
例子:计算坐标点(40, 50)为中心逆时针旋转30度的仿射变换矩阵。
A = cv2.getRotationMatrix2D((40,50),30,0.5)
print(A)
[[ 0.4330127 0.25 10.17949192]
[-0.25 0.4330127 38.34936491]]
插值算法
最近邻插值:采用离(x,y)点最近的整数坐标点的值来代替该点的数值
双线性插值:通过(x,y)附件4个点的值来估计该点的数值
实现仿射变换
opencv提供了cv2.warpAffine函数
import numpy as np
import cv2
import sys
import math
if __name__ == "__main__":
image = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
cv2.imwrite("img.jpg",image)
#原图的高、宽
h,w = image.shape[:2]
# 仿射变换矩阵,缩小两倍
A1 = np.array([[0.5,0,0],[0,0.5,0]],np.float32)
d1 = cv2.warpAffine(image,A1,(w,h),borderValue = 125)
#先缩小两倍,再平移
A2 = np.array([[0.5,0,w/4],[0,0.5,h/4]],np.float32)
d2 = cv2.warpAffine(image,A2,(w,h),borderValue = 125)
#在d2的基础上,绕图像的中心点旋转
A3 = cv2.getRotationMatrix2D((w/2.0,h/2.0),30,1)
d3 = cv2.warpAffine(d2,A3,(w,h),borderValue = 125)
cv2.imshow("image",image)
cv2.imshow("d1",d1)
cv2.imshow("d2",d2)
cv2.imshow("d3",d3)
cv2.waitKey(0)
cv2.destroyAllWindows()
旋转函数rotate
opencv3.X的新特性
这个函数不是通过仿射变换来完成图像的旋转,而是通过行列的互换,类似于矩阵的转置操作
import cv2
image = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
#显示原图
cv2.imshow("image",image)
#图像旋转:
rImg = cv2.rotate(image,cv2.ROTATE_90_CLOCKWISE)
#显示旋转结果
cv2.imshow("rImg",rImg)
cv2.waitKey(0)
cv2.destroyAllWindows()
投影变换
使用方程法计算投影变换的投影变换的仿射矩阵需要4个点对
假设(0,0)(200,0)(0,200)(200,200)是原坐标,通过投影变换转换为(100,20)(200,20)(50,70)(250,70)
import cv2
import numpy as np
src = np.array([[0,0],[200,0],[0,200],[200,200]],np.float32)
dst = np.array([[100,20],[200,20],[50,70],[250,70]],np.float32)
p = cv2.getPerspectiveTransform(src,dst)#返回投影矩阵p的类型是float64
print(p)
[[ 5.00e-01 -3.75e-01 1.00e+02]
[ 0.00e+00 7.50e-02 2.00e+01]
[-0.00e+00 -2.50e-03 1.00e+00]]
实现投影变换
import numpy as np
import cv2
image = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
# 原图的宽和高
h,w = image.shape
src = np.array([[0,0],[w-1,0],[0,h-1],[w-1,h-1]],np.float32)
dst = np.array([[50,50],[w/3,50],[50,h-1],[w-1,h-1]],np.float32)
#计算投影变换矩阵
p = cv2.getPerspectiveTransform(src,dst)
#利用计算出的投影变换矩阵进行投影变化
r = cv2.warpPerspective(image,p,(w,h),borderValue = 125)
#显示
cv2.imshow("image",image)
cv2.imshow("warpperspective",r)
cv2.waitKey(0)
cv2.destroyAllWindows()
极坐标变换
笛卡尔坐标系转化为极坐标
以变换中心为圆心的同一个圆上的点,在极坐标系中显示为一条直线
(11,13)以(3,5)为中心进行极坐标变换
import math
r = math.sqrt(math.pow(11-3,2)+math.pow(13-5,2));
theta = math.atan2(13-5,11-3)/math.pi*180#转换为角度
r,theta
(11.313708498984761, 45.0)
opencv提供了函数:cartToPolar
举例:(0,0)(1,0)(2,0)(0,1)(1,1)(2,1)(0,2)(1,2)(2,2)这九 个点以(1,1)为中心进行极坐标变换
import cv2
import numpy as np
x = np.array([[0,1,2],[0,1,2],[0,1,2]],np.float64)-1
y = np.array([[0,0,0],[1,1,1],[2,2,2]],np.float64)-1
r,theta = cv2.cartToPolar(x,y,angleInDegrees = True)
r,theta
(array([[1.41421356, 1. , 1.41421356],
[1. , 0. , 1. ],
[1.41421356, 1. , 1.41421356]]),
array([[224.990448 , 270. , 315.009552 ],
[180. , 0. , 0. ],
[135.009552 , 90. , 44.99045563]]))
极坐标转化为笛卡尔坐标
Opencv提供了函数:cv2.polarToCart
举例:已知极坐标系θor中的(30,10)(31,10)(30,11)(31,11),其中θ是用角度表示的,问笛卡尔坐标系中哪四个坐标以(-12,15)为中心经过极坐标变换后得到这四个坐标。
import cv2
import numpy as np
angle = np.array([[30,31],[30,31]],np.float32)
r = np.array([[10,10],[11,11]],np.float32)
x,y = cv2.polarToCart(r,angle,angleInDegrees = True)
print(x,y)#这里得到的(x,y)以(0,0)为变换中心
x+=-12
y+=15
x,y#以(-12,15)为变换中心
[[8.660255 8.571674]
[9.52628 9.428843]] [[5.0000005 5.150382 ]
[5.5000005 5.66542 ]]
(array([[-3.3397446, -3.4283257],
[-2.4737196, -2.5711575]], dtype=float32),
array([[20. , 20.150383],
[20.5 , 20.66542 ]], dtype=float32))
实现极坐标变换
Numpy中的tile(a,(m,n))函数
该函数返回的矩阵是由m*n个a平铺而成的与MATLAB中的remat函数功能相同
import numpy as np
a = np.array([[1,2],[3,4]])
print(a)
b = np.tile(a,(2,3))#将a分别在垂直方向和水平方向复制2次和3次
b
[[1 2]
[3 4]]
array([[1, 2, 1, 2, 1, 2],
[3, 4, 3, 4, 3, 4],
[1, 2, 1, 2, 1, 2],
[3, 4, 3, 4, 3, 4]])
def polar(I,center,r,theta=(0,360),rstep=1.0,thetastep=360.0/(180*8)):
#得到距离的最小、最大范围
minr,maxr = r;
#角度的最小返回
mintheta,maxtheta = theta
#输出图像的高宽
H = int((maxr - minr) / rstep) + 1
W = int((maxtheta - mintheta)/thetastep) + 1
O = 125 * np.ones((H,W),I.dtype)
#极坐标变换
r = np.linspace(minr, maxr,H)
r = np.tile(r,(W,1))
r = np.transpose(r)
theta = np.linspace(mintheta, maxtheta, W)
theta = np.tile(theta,(H,1))
x,y = cv2.polarToCart(r,theta,angleInDegrees = 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 py <= h-1)):
O[i][j] = I[py][px]
return 0
import cv2
import numpy as np
I = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
h,w = I.shape[:2]
# 极坐标变换中心
cx, cy = 508,503
cv2.circle(I,(int(cx),int(cy)),10,(255.0,0,0),3)
# 距离的最小和最大半径 # 200 550 270 340
O = polar(I,(cx,cy),(200,550))
# 旋转
O = cv2.flip(0,0)
# 显示原图和输出图像
cv2.imshow("I",I)
cv2.imshow("O",O)
cv2.waitKey(0)
cv2.destroyAllWindows()
线性极坐标函数LinearPolar
import cv2
import sys
src = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
#显示原图
cv2.imshow("src",src)
# 图像的极坐标变换
dst = cv2.linearPolar(src,(508,503),550,cv2.INTER_LINEAR)
# 显示结果
cv2.imshow("dst",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
对数极坐标函数logPolar
import cv2
src = cv2.imread("c:/users/76973/desktop/output_image1.jpg",cv2.IMREAD_GRAYSCALE)
#显示原图
cv2.imshow("src",src)
# 图像的极坐标变换
M = 100
dst = cv2.logPolar(src,(508,503),M,cv2.WARP_FILL_OUTLIERS)
# 显示结果
cv2.imshow("dst",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
C++ API
计算仿射矩阵
# include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
template<class T>
void printMat(Mat matrix)
{
for (int r = 0; r < matrix.rows; r++)
{
for (int c = 0; c < matrix.cols; c++)
{
if (typeid(T) == typeid(uchar)) {
cout << (int)(matrix.at<T>(r, c)) << ",";
}
else
{
cout << (matrix.at<T>(r, c)) << ",";
}
}
cout << endl;
}
cout << endl;
}
template<class T>
void printMatT(Mat matrix)
{
for (int r = 0; r < matrix.rows; r++)
{
for (int c = 0; c < matrix.cols; c++)
{
cout << (matrix.at<T>(r, c)) << ",";
}
cout << endl;
}
cout << endl;
}
int main()
{
//# 方程法计算仿射矩阵
//## 第一种方式:将原坐标点和经过仿射变换后的坐标点放在Point2f数组中
//原位置坐标
Point2f src[] = { Point2f(0,0),Point2f(200,0),Point2f(0,200) };
//经过某仿射变换后对应的坐标
Point2f dst[] = { Point2f(0,0),Point2f(100,0),Point2f(0,100) };
//计算仿射矩阵
Mat A = getAffineTransform(src, dst);//注意:计算结果返回的数据类型是CV_64F
printMat<double>(A);
//## 第二种方式:将原坐标点和经过仿射变换后的坐标点保存在Mat中;每一行代表一个坐标,数据类型必须为CV32F
Mat src1 = (Mat_<float>(3, 2) << 0, 0, 200, 0, 0, 200);
Mat dst1 = (Mat_<float>(3, 2) << 0, 0, 100, 0, 0, 100);
//计算仿射矩阵
Mat A1 = getAffineTransform(src1, dst1);//注意:计算结果返回的数据类型是CV_64F
printMat<double>(A1);
//# 矩阵法计算仿射矩阵
Mat s = (Mat_<float>(3, 3) << 0.5, 0, 0, 0, 0.5, 0, 0, 0, 1);
Mat t = (Mat_<float>(3,3) << 1, 0, 100, 0, 1, 200, 0, 0, 1);
Mat A2 = t * s;//使用操作符“*”
printMat<float>(A2);
Mat A3;
gemm(t, s, 1.0, Mat(), 0, A3, 0);//矩阵相乘
printMat<float>(A3);
//计算等比例缩放仿射变换矩阵
Mat A4 = getRotationMatrix2D(Point2f(40, 50), 30, 0.5);//返回的数据类型是CV_64F
printMat<double>(A4);
//# 实现仿射变换 为了方便对图像的缩放Opencv提供了resize函数
Mat I = imread("c:/users/76973/desktop/output_image1.jpg", IMREAD_GRAYSCALE);
if (!I.data)
return -1;
/*第一种方式:利用warpAffine进行缩放*/
//构造缩放仿射矩阵,等比例缩小2倍
Mat s1 = (Mat_<float>(2, 3) << 0.5, 0, 0, 0, 0.5, 0);
Mat dst2;
warpAffine(I, dst2, s1, Size(I.cols /1, I.rows / 2));//图像缩放
/*第二种方式:利用resize等比例缩小两倍*/
Mat dst3;
resize(I, dst3, Size(I.cols/2 , I.rows/2 ), 0.5, 0.5);
//显示效果
imshow("I", I);
imshow("warpAffine", dst2);
imshow("resize", dst3);
//# rotate 函数
Mat rImg;
rotate(I, rImg, ROTATE_90_CLOCKWISE);
imshow("旋转", rImg);
//waitKey(0);
//# 投影变换
//原坐标
Point2f sc1[] = { Point2f(0,0),Point2f(200.0,0.0),Point2f(0,200.0),Point2f(200,200) };
//经过某投影变换后的坐标
Point2f dt1[] = { Point2f(100,20),Point2f(200,20),Point2f(50,70),Point2f(250,70) };
//计算投影变换矩阵
Mat P = getPerspectiveTransform(sc1, dt1);//返回类型为CV_64F
printMatT<double>(P);
//构建Mat
Mat sc2 = (Mat_<float>(4, 2) << 0, 0, 200, 0, 0, 200, 200, 200);
Mat dt2 = (Mat_<float>(4, 2) << 100, 20, 200, 20, 50, 70, 250, 70);
Mat P1 = getPerspectiveTransform(sc2, dt2);//返回类型为CV_64F
printMatT<double>(P1);
// # 笛卡尔坐标系转化为极坐标
Mat x = (Mat_<float>(3, 3) << 0, 1, 2, 0, 1, 2, 0, 1, 2) - 1;
Mat y = (Mat_<float>(3, 3) << 0, 0, 0, 1, 1, 1, 2, 2, 2) - 1;
Mat r, theta;
cartToPolar(x, y, r, theta, true);
printMat<float>(r);
printMat<float>(theta);
// # 极坐标转化为笛卡尔坐标
Mat angle1 = (Mat_<float>(2, 2) << 30, 31, 30, 31);
Mat r1 = (Mat_<float>(2, 2) << 10, 10, 11, 11);
Mat x1, y1;
polarToCart(r1, angle1, x1, y1, true);
x1 += -12.0;
y1 += 15.0;
printMatT<float>(x1);
printMatT<float>(y1);
return 0;
}
实现极坐标变换
# include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat polar(Mat I, Point2f center, Size size, float minr = 0, float mintheta = 0, float thetaStep = 1.0 / 4, float rStep = 1.0)
{
//构建r
Mat ri = Mat::zeros(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
Mat thetaj = Mat::zeros(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 nearestx = int(round(xij));
int nearesty = int(round(yij));
if (0 <= nearestx && nearestx < I.cols && (0 <= nearesty && nearesty < I.rows))
{
dst.at<uchar>(i, j) = I.at<uchar>(nearesty, nearestx);
}
}
}
return dst;
}
int _main()
{
Mat I = imread("c:/users/76973/desktop/output_image1.jpg");
if (!I.data)
return -1;
//图像的极坐标变换
float thetaStep = 1.0 / 4;
float minr = 270;
Size size(int(360 / thetaStep), 70);
Mat dst = polar(I, Point2f(508, 503), size, minr);
//沿水平方向的镜像处理
flip(dst, dst, 0);
//显示原图和变换后的结果
imshow("I", I);
imshow("极坐标变换", dst);
waitKey(0);
return 0;
}
线性极坐标函数
# include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//输入图像
Mat src = imread("c:/users/76973/desktop/output_image1.jpg", 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;
}
对数极坐标函数
# include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//读入图像
Mat src = imread("c:/users/76973/desktop/output_image1.jpg", 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;
}
Jupyder 笔记下载
https://gitee.com/pythonFCGa/Opencv/tree/master/