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/

posted @ 2019-10-07 13:02  消灭猕猴桃  阅读(240)  评论(0编辑  收藏  举报