透视变换(Perspective Transformation)是将成像投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。如图1,通过透视变换ABC变换到A'B'C'。
图1 透视变换示意图
透视变换的通用公式为:
变换后的坐标x,y分别为:。
展开之后即:
其中,称为透视变换矩阵:表示线性变换,如scaling,shearing和ratotion等;
用于平移,因此此前介绍的仿射变换可以视作一种特殊的透视变换。
如图2,我们想将倾斜视角拍摄到的道路图像转换成鸟瞰图,即将摄像机的视角转换到和道路平行。
图2 倾斜视角
首先,我们需要获得此次透视变换的变换矩阵,opencv2和opencv3中用于计算透视变换矩阵的函数是cv::getPerspectiveTransform(),C++接口其调用形式如下:
-
cv::Mat cv::getPerspectiveTransform(
// 返回3x3透视变换矩阵
-
const cv::Point2f* src,
// 源图像四个顶点坐标(点数组)
-
const cv::Point2f* dst
// 目标图像上四个顶点的坐标(点数组)
-
);
如图3 ,我们选取道路上的两条平行分界线上的四个点A(165, 270)、C(360, 125)、D(615, 125)、B(835, 270),对应于鸟瞰图上的点则分别为A(165, 270)、C'(165, 30)、D'(835, 30)、B(835, 270)。
图3 透视变换端点
通过这四对点我们即可计算出透视变换矩阵M。
C++代码如下:
-
cv::
Mat get_perspective_mat()
-
{
-
cv::Point2f src_points[] = {
-
cv::Point2f(
165,
270),
-
cv::Point2f(
835,
270),
-
cv::Point2f(
360,
125),
-
cv::Point2f(
615,
125) };
-
-
cv::Point2f dst_points[] = {
-
cv::Point2f(
165,
270),
-
cv::Point2f(
835,
270),
-
cv::Point2f(
165,
30),
-
cv::Point2f(
835,
30) };
-
-
cv::Mat M = cv::getPerspectiveTransform(src_points, dst_points);
-
-
return M;
-
-
}
Python代码如下:
-
def get_perspective_mat():
-
-
src_points = np.array([[
165.,
270.], [
835.,
270.], [
360.,
125.], [
615.,
125.]], dtype =
"float32")
-
dst_points = np.array([[
165.,
270.], [
835.,
270.], [
165.,
30.], [
835.,
30.]], dtype =
"float32")
-
-
M = cv2.getPerspectiveTransform(src_points, dst_points)
-
-
return M
计算结果如下:
在获得透视变换矩阵后,即可使用与cv::warpPerspective()进行透视变换,其调用形式如下:
-
void cv::warpPerspective(
-
cv::InputArray src,
// 输入图像
-
cv::OutputArray dst,
// 输出图像
-
cv::InputArray M,
// 3x3 变换矩阵
-
cv::Size dsize,
// 目标图像大小
-
int flags = cv::INTER_LINEAR,
// 插值方法
-
int borderMode = cv::BORDER_CONSTANT,
// 外推方法
-
const cv::Scalar& borderValue = cv::Scalar()
//常量边界时使用
-
);
C++代码如下:
-
cv::Mat perspective;
-
cv::warpPerspective(image, perspective, M, cv::Size(
960,
270), cv::INTER_LINEAR);
Python代码如下:
perspective = cv2.warpPerspective(image, M, (960, 270), cv2.INTER_LINEAR)
变换结果如下:
2017.05.19