Day 3-OpenCV图片变换

原文:https://courses.learnopencv.com/courses/227056/lectures/3797166
代码:https://courses.learnopencv.com/courses/227056/lectures/3804006

OpenCV图片变换

仿射变换

一张图片能够变换成不同的形状。如下所示就是几种不同的图片变形。
图片变换
欧几里得变换(Euclidean Transform) 是指只包含移动(translation)和旋转(rotation)的变换。欧几里得变换保证了正交性(Orthogonality),原本垂直的线,在转换后也保持垂直。

仿射变换(Affine Transfrom)是指包含移动、旋转、放缩(x,y方向),以及一个叫做shear(剪切)的参数
上面的变换都是线性变换(Linear Transform),即所有的直线在变换后依旧是直线。

1. OpenCV中的仿射变换

在OpenCV中,仿射变换被存储在一个2x3大小的矩阵当中。前两列编码了旋转、缩放、剪切信息,最后一列编码了位移信息。

平移、旋转和欧几里得变换都是仿射变换的特例,在欧几里得变换中,缩放和剪切参数都为

WarpAffine example [ C++ ]

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cmath>
#include <vector>
using namespace cv;
using namespace std;
//In this program, we will use OpenCV warpaffine function to transform 
//a triangle from source image to another triangle as shown in the 
//above figure.
int main(void)
{
// Read image
Mat source = imread("sample.jpg",1);
// Create 2 warp matrices for different transformations
Mat warpMat = (Mat_<double>(2,3) << 1.2, 0.2, 2, -0.3, 1.3, 1 );
// Another mask
Mat warpMat2 = (Mat_<double>(2,3) << 1.2, 0.3, 2, 0.2, 1.3, 1);
Mat result,result2;
// Use warp affine
cv::warpAffine(source, result, warpMat, 
Size(1.5*source.rows,1.4*source.cols), INTER_LINEAR, BORDER_REFLECT_101);
cv::warpAffine(source, result2, warpMat2, 
Size(1.5*source.rows, 1.4*source.cols), INTER_LINEAR, BORDER_REFLECT_101);
// Display images
imshow("Original", source);
imshow("Result", result);
imshow("Result2", result2);
waitKey(0);
destroyAllWindows();
}

2. 逆问题

C++ [ getAffineTransform example ] [ getAffine.cpp ]

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cmath>
#include <vector>
using namespace cv;
using namespace std;
int main(void)
{
/*In this program, we will use OpenCV getAffineTransform function 
to obtain the warping matrix using two triangles, 
one being the input triangle and the other being the output triangle
*/
// Input triangle
vector <Point2f> tri1;
tri1.push_back(Point2f(50, 50));
tri1.push_back(Point2f(180, 140));
tri1.push_back(Point2f(150, 200));

// Output triangle
vector <Point2f> tri2;
tri2.push_back(Point2f(72, 51));
tri2.push_back(Point2f(246, 129));
tri2.push_back(Point2f(222, 216));
// Another output triangle
vector <Point2f> tri3;
tri3.push_back(Point2f(77, 76));
tri3.push_back(Point2f(260, 219));
tri3.push_back(Point2f(242, 291));
// Get the transformation matrices
Mat warp = cv::getAffineTransform(tri1,tri2);
Mat warp2 = cv::getAffineTransform(tri1,tri3);
// Display the matrices
cout << "Warp Matrix 1 : \n\n " << warp 
<< "\n\n" << "Warp Matrix 2 : \n" << warp2<< endl;
}

Output

Output
[1.19, 0.20, 2;

-0.30, 1.3, 1]|
|[1.2, 0.30, 2;
0.19, 1.3, 1]|

转换矩阵相应的的点通过下面的公式进行计算:

单应矩阵和透视变换(Homography & Perspective Transform)

1. 什么是单应矩阵Homography?

Homography是一个3x3的转换矩阵,将一张图片里的点映射到另一张图片里相应的点上。如下图:
单应矩阵
图 1: 一个3D平面(即指书的封面所在平面)上的两张图片被单应矩阵关联起来了

我们可以把3x3的单应矩阵写作:
H=h00h10h20h01h11h21h02h12h22 H = [ h 00 h 01 h 02 h 10 h 11 h 12 h 20 h 21 h 22 ]
左图中的(x1,y1)和右图中的(x2,y2)由下面的方式来映射:
x1y11=Hx2y21=h00h10h20h01h11h21h02h12h22x2y21 [ x 1 y 1 1 ] = H [ x 2 y 2 1 ] = [ h 00 h 01 h 02 h 10 h 11 h 12 h 20 h 21 h 22 ] [ x 2 y 2 1 ]

2. 使用单应矩阵来做图片对齐

上面的公式适用于所有对应点落在同一平面上的情况。
这就意味着,我们可以拿上面图一中的左图进行对齐操作,如图2所示:

图片对齐
图2:能够使用单应矩阵把一个3D平面上的图片对齐到同一平面的另一张图片上

不在同一个平面上的点不会进行对齐,如上图所示。
假如在图片里包含两个平面,那么你将需要两个单应矩阵来进行操作。

3. 单应矩阵应用案例:全景图(Panarama)

根据两张水平移动拍摄的图片共享的一些区域,将绿化图片对齐并连接起来,这样就得到了一个简单的“全景图”(Panarama)。

4. 怎样计算单应矩阵

计算两个图像之间的单应矩阵,你需要了解至少4个两幅图像之间的对应点。如果超过4个对应点更好。OpenCV将鲁棒地估计一个最适合所有对应点的单应矩阵。通常来说,这些点可以通过例如SIFT或SURF这样的算法,自动找到图像之间的匹配特征点,但在这篇文章里,我们只是手动地点击你需要的点。

// pts_src and pts_dst are vectors of points in source 
// and destination images. They are of type vector<Point2f>. 
// We need at least 4 corresponding points. 

Mat h = findHomography(pts_src, pts_dst);

// The calculated homography can be used to warp 
// the source image to destination. im_src and im_dst are
// of type Mat. Size is the size (width,height) of im_dst. 
warpPerspective(im_src, im_dst, h, size);
4.1 OpenCV单应矩阵案例

图片2 中的效果能够使用下面的C++代码实现,下面的代码展示了怎样使用4个对应点来进行变换。

#include "opencv2/opencv.hpp" 

using namespace cv;
using namespace std;

int main( int argc, char** argv)
{
    // Read source image.
    Mat im_src = imread("book2.jpg");
    // Four corners of the book in source image
    vector<Point2f> pts_src;
    pts_src.push_back(Point2f(141, 131));
    pts_src.push_back(Point2f(480, 159));
    pts_src.push_back(Point2f(493, 630));
    pts_src.push_back(Point2f(64, 601));


    // Read destination image.
    Mat im_dst = imread("book1.jpg");
    // Four corners of the book in destination image.
    vector<Point2f> pts_dst;
    pts_dst.push_back(Point2f(318, 256));
    pts_dst.push_back(Point2f(534, 372));
    pts_dst.push_back(Point2f(316, 670));
    pts_dst.push_back(Point2f(73, 473));

    // Calculate Homography
    Mat h = findHomography(pts_src, pts_dst);

    // Output image
    Mat im_out;
    // Warp source image to destination based on homography
    warpPerspective(im_src, im_out, h, im_dst.size());

    // Display images
    imshow("Source Image", im_src);
    imshow("Destination Image", im_dst);
    imshow("Warped Source Image", im_out);

    waitKey(0);
}

下面是Python版本

#!/usr/bin/env python

import cv2
import numpy as np

if __name__ == '__main__' :

    # Read source image.
    im_src = cv2.imread('book2.jpg')
    # Four corners of the book in source image
    pts_src = np.array([[141, 131], [480, 159], [493, 630],[64, 601]])


    # Read destination image.
    im_dst = cv2.imread('book1.jpg')
    # Four corners of the book in destination image.
    pts_dst = np.array([[318, 256],[534, 372],[316, 670],[73, 473]])

    # Calculate Homography
    h, status = cv2.findHomography(pts_src, pts_dst)

    # Warp source image to destination based on homography
    im_out = cv2.warpPerspective(im_src, h, (im_dst.shape[1],im_dst.shape[0]))

    # Display images
    cv2.imshow("Source Image", im_src)
    cv2.imshow("Destination Image", im_dst)
    cv2.imshow("Warped Source Image", im_out)

    cv2.waitKey(0)

5. 单应矩阵的应用

5.1 透视校正

将上面图一的照片校正成如图3所示
透视校正
图3. 透视校正

下面是步骤。
1. 写一个用户界面来收集书的四个角的坐标,命名为pts_src.
2. 我们需要知道书本的宽高比,这本书大小为300x400,宽高比为3/4,因此,我们设定变换后的终点坐标pts_dst为(0,0),(299,0),(299,399)和(0,399)
3. 使用pts_srcpts_dst来获取单应矩阵
4. 对原图片应用单应矩阵来获得图3中的图片

Project Idea
透视校正的技术已经被大量文档扫描类app所使用,比如有着5千万下载量的CamScanner,这款应用允许你从任意角度拍摄你的文档或黑板、书籍等等,之后就可以通过透视转换来修正角度,从而达到扫描的效果。快试着用如此简单但又充满影响力以及实用的技术来做点什么吧!

5.2 虚拟公告板
我们将替换掉图5中的一块公告牌为我们的图4
互联网上第一张图片
图4. 上传到互联网上的第一张图片

时代广场
图5. 时代广场

操作步骤:
1. 编写一个用户界面来收集公告牌中的四个角,命名这些点为pts_dst.
2. 测量你想放上公告板的图片的w×h,那么原图片的四点(pts_src)为(0,0),(w-1,0),(w-1,h-1) 和(0,h-1)
3. 使用pts_srcpts_det获得单应矩阵
4. 在原图片上应用单应矩阵并把它和目标图片混合起来得到图6
虚拟公告牌
图6. 虚拟公告牌。左边的一块公告牌被替换了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值