透视变换 任意角度 三维透视 python c++ opencv两种语言
效果图:键盘控制各个x,y,z,还有fov数值
一开始的w, h = img.shape[0:2]这里宽、高错乱,原因未知,只能这么用
python版本的:
# -*- coding:utf-8 -*-
import cv2
import numpy as np
def rad(x):
return x * np.pi / 180
img = cv2.imread("./dog.jpeg")
cv2.namedWindow("original",0)
cv2.imshow("original", img)
# 扩展图像,保证内容不超出可视范围
img = cv2.copyMakeBorder(img, 200, 200, 200, 200, cv2.BORDER_CONSTANT, 0)
w, h = img.shape[0:2]
anglex = 0
angley = 30
anglez = 0 #是旋转
fov = 42
while 1:
# 镜头与图像间的距离,21为半可视角,算z的距离是为了保证在此可视角度下恰好显示整幅图像
z = np.sqrt(w ** 2 + h ** 2) / 2 / np.tan(rad(fov / 2))
# 齐次变换矩阵
rx = np.array([[1, 0, 0, 0],
[0, np.cos(rad(anglex)), -np.sin(rad(anglex)), 0],
[0, -np.sin(rad(anglex)), np.cos(rad(anglex)), 0, ],
[0, 0, 0, 1]], np.float32)
ry = np.array([[np.cos(rad(angley)), 0, np.sin(rad(angley)), 0],
[0, 1, 0, 0],
[-np.sin(rad(angley)), 0, np.cos(rad(angley)), 0, ],
[0, 0, 0, 1]], np.float32)
rz = np.array([[np.cos(rad(anglez)), np.sin(rad(anglez)), 0, 0],
[-np.sin(rad(anglez)), np.cos(rad(anglez)), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]], np.float32)
r = rx.dot(ry).dot(rz)
# 四对点的生成
pcenter = np.array([h / 2, w / 2, 0, 0], np.float32)
p1 = np.array([0, 0, 0, 0], np.float32) - pcenter
p2 = np.array([w, 0, 0, 0], np.float32) - pcenter
p3 = np.array([0, h, 0, 0], np.float32) - pcenter
p4 = np.array([w, h, 0, 0], np.float32) - pcenter
dst1 = r.dot(p1)
dst2 = r.dot(p2)
dst3 = r.dot(p3)
dst4 = r.dot(p4)
list_dst = [dst1, dst2, dst3, dst4]
org = np.array([[0, 0],
[w, 0],
[0, h],
[w, h]], np.float32)
dst = np.zeros((4, 2), np.float32)
# 投影至成像平面
for i in range(4):
dst[i, 0] = list_dst[i][0] * z / (z - list_dst[i][2]) + pcenter[0]
dst[i, 1] = list_dst[i][1] * z / (z - list_dst[i][2]) + pcenter[1]
warpR = cv2.getPerspectiveTransform(org, dst)
result = cv2.warpPerspective(img, warpR, (h, w))
cv2.namedWindow("result",0)
cv2.imshow("result", result)
c = cv2.waitKey(30)
# anglex += 3 #auto rotate
# anglez += 1 #auto rotate
# angley += 2 #auto rotate
# 键盘控制
if 27 == c: # Esc quit
break;
if c == ord('w'):
anglex += 1
if c == ord('s'):
anglex -= 1
if c == ord('a'):
angley += 1
# dx=0
if c == ord('d'):
angley -= 1
if c == ord('u'):
anglez += 1
if c == ord('p'):
anglez -= 1
if c == ord('t'):
fov += 1
if c == ord('r'):
fov -= 1
if c == ord(' '):
anglex = angley = anglez = 0
if c == ord('q'):
print("======================================")
print('旋转矩阵:\n', r)
print("angle alpha: ", anglex, 'angle beta: ', angley, "dz: ", anglez, ": ", z)
cv2.destroyAllWindows()
c++ 版本:
#include <iostream>
#include <math.h>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
double rad(double x)
{
return x * CV_PI / 180.0;
}
int main(int argc, char *argv[])
{
Mat img = imread("dog.jpeg");
namedWindow("original",0);
imshow("original",img);
copyMakeBorder(img,img,200,200,200,200,BORDER_CONSTANT,0);
// int w = img.cols;
// int h = img.rows;
int h = img.cols;
int w = img.rows;
double anglex = 0;
double angley = -62.72;
double anglez = 32;
double fov = 56;
while(1)
{
double z = sqrt(w*w + h*h)/2.0/tan(rad(fov/2.0));
double arr_x[4][4] = {1,0,0,0,
0,cos(rad(anglex)),-sin(rad(anglex)),0,
0,-sin(rad(anglex)),cos(rad(anglex)),0,
0,0,0,1};
Mat rx(4,4,CV_64F,arr_x);
double arr_y[4][4] = {cos(rad(angley)),0,sin(rad(angley)),0,
0,1,0,0,
-sin(rad(angley)),0,cos(rad(angley)),0,
0,0,0,1};
Mat ry(4,4,CV_64F,arr_y);
double arr_z[4][4] = {cos(rad(anglez)),sin(rad(anglez)),0,0,
-sin(rad(anglez)),cos(rad(anglez)),0,0,
0,0,1,0,
0,0,0,1};
Mat rz(4,4,CV_64F,arr_z);
Mat r = rx*ry*rz;
double arr_center[4] = {h/2.0,w/2.0,0,0};
Mat pcenter(1,4,CV_64F,arr_center);
double arr_t1[4] = {0,0,0,0},arr_t2[4] = {w,0,0,0}, arr_t3[4] = {0,h,0,0}, arr_t4[4] = {w,h,0,0};
Mat m_t1(1,4,CV_64F,arr_t1),m_t2(1,4,CV_64F,arr_t2),m_t3(1,4,CV_64F,arr_t3),m_t4(1,4,CV_64F,arr_t4);
Mat p1 = m_t1 - pcenter;
Mat p2 = m_t2 - pcenter;
Mat p3 = m_t3 - pcenter;
Mat p4 = m_t4 - pcenter;
Mat r_transpose;
transpose(r,r_transpose);
Mat dst1 = p1 * r_transpose;
Mat dst2 = p2 * r_transpose;
Mat dst3 = p3 * r_transpose;
Mat dst4 = p4 * r_transpose;
vector<Mat> list_dst = {dst1,dst2,dst3,dst4};
Point2f org[4] = {Point2f(0,0),Point2f(w,0),Point2f(0,h),Point2f(w,h)};
Point2f dst[4];
for(int i=0;i<4;i++)
{
dst[i].x = list_dst[i].at<double>(0,0) * z / (z - list_dst[i].at<double>(0,2)) + pcenter.at<double>(0,0);
dst[i].y = list_dst[i].at<double>(0,1) * z / (z - list_dst[i].at<double>(0,2)) + pcenter.at<double>(0,1);
}
Mat result;
cv::Mat warpMatrix = cv::getPerspectiveTransform(org, dst);
cv::warpPerspective(img, result, warpMatrix, Size(h,w));
namedWindow("result",0);
imshow("result",result);
char c = waitKey(0);
if(27 == c)
{
break;
}
if('w' == c)
{
anglex += 1;
}
if('s' == c)
{
anglex -= 1;
}
if('a' == c)
{
angley += 1;
}
if('d' == c)
{
angley -= 1;
}
if('u' == c)
{
anglez += 1;
}
if('p' == c)
{
anglez -= 1;
}
if('t' == c)
{
fov += 1;
}
if('r' == c)
{
fov -= 1;
}
if(' ' == c)
{
anglex = 0;
angley = 0;
anglez = 0;
}
if('q' == c)
{
cout<<"angle alpha(anglex): "<<anglex<<" angle beta(angley): "<<angley<<" dz(anglez): "<<anglez<<" z="<<z<<" fov="<<fov<<endl;
}
}
return 0;
}
好记性不如烂键盘---点滴、积累、进步!