几何变换
本章节内容是博主网上收集的。当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
不同编程语言对应的OpenCv版本以及开发环境信息如下:
语言 | OpenCv版本 | IDE |
C# | OpenCvSharp4.4.8.0.20230708 | Visual Studio 2022 |
C++ | OpenCv-4.5.5-vc14_vc15 | Visual Studio 2022 |
Python | OpenCv-Python (4.6.0.66) | PyCharm Community Edition 2022.1.3 |
图像的几何变换,主要涉及到的内容包括:图像的平移、图像的旋转、图像透视变换、图像的仿射变换。这四种操作。
OpenCV 提供了两个变换函数虽cv2.warpAffine 和 cv2.warpPerspective虽使用䦈两个函数你可以实现所有类型的变换。cv2.warpAffine 接收的参数是2 × 3 的变换矩俤,而 cv2.warpPerspective 接收的参数是 3 × 3 的变换矩俤。
测试原图:

demo1:图像的平移操作

demo2:旋转图像操作

demo3:仿射变换的基本操作(左:原图;右:效果图)

demo4:透视变换的基本操作(左:原图;右:效果图)

C#版本运行代码如下:
using OpenCvSharp;
namespace demo
{
internal class Program
{
static void Main(string[] args)
{
demo1();
//demo2();
//demo3();
//demo4();
}
#region 图像的平移操作
public static void demo1()
{
// 移动了100,50 个像素。
Mat img = Cv2.ImRead("../../../images/messi5.jpg", 0);
// Define the transformation matrix
Mat M = new Mat(2, 3, MatType.CV_32FC1);
M.Set<float>(0, 0, 1);
M.Set<float>(0, 1, 0);
M.Set<float>(0, 2, 100);
M.Set<float>(1, 0, 0);
M.Set<float>(1, 1, 1);
M.Set<float>(1, 2, 50);
// 应用平移变换
Mat dst = new Mat();
Cv2.WarpAffine(img, dst, M, img.Size());
Cv2.ImShow("img", dst);
Cv2.WaitKey(0);
}
#endregion
#region 旋转图像操作
public static void demo2()
{
Mat img = Cv2.ImRead("../../../images/messi5.jpg", 0);
Mat dst = new Mat();
Mat rotMat = Cv2.GetRotationMatrix2D(new Point2f(img.Cols / 2, img.Rows / 2), 45, 0.5);
Cv2.WarpAffine(img, dst, rotMat, new Size(img.Cols, img.Rows));
Cv2.ImShow("img", dst);
Cv2.WaitKey(0);
}
#endregion
#region 仿射变换的基本操作
public static void demo3()
{
Mat img = Cv2.ImRead("../../../images/drawing.png");
Mat pts1 = new Mat(3, 2, MatType.CV_32FC1);
pts1.Set<float>(0, 0, 50);
pts1.Set<float>(0, 1, 50);
pts1.Set<float>(1, 0, 200);
pts1.Set<float>(1, 1, 50);
pts1.Set<float>(2, 0, 50);
pts1.Set<float>(2, 1, 200);
Mat pts2 = new Mat(3, 2, MatType.CV_32FC1);
pts2.Set<float>(0, 0, 10);
pts2.Set<float>(0, 1, 100);
pts2.Set<float>(1, 0, 200);
pts2.Set<float>(1, 1, 50);
pts2.Set<float>(2, 0, 100);
pts2.Set<float>(2, 1, 250);
Mat M = Cv2.GetAffineTransform(pts1, pts2);
Mat dst = new Mat();
Cv2.WarpAffine(img, dst, M, img.Size());
Cv2.ImShow("Input", img);
Cv2.ImShow("Output", dst);
Cv2.WaitKey(0);
}
#endregion
#region 透视变换的基本操作
public static void demo4()
{
Mat img = Cv2.ImRead("../../../images/sudoku.jpg");
Mat pts1 = new Mat(4, 2, MatType.CV_32FC1);
pts1.Set<float>(0, 0, 56);
pts1.Set<float>(0, 1, 65);
pts1.Set<float>(1, 0, 368);
pts1.Set<float>(1, 1, 52);
pts1.Set<float>(2, 0, 28);
pts1.Set<float>(2, 1, 387);
pts1.Set<float>(3, 0, 389);
pts1.Set<float>(3, 1, 390);
Mat pts2 = new Mat(4, 2, MatType.CV_32FC1);
pts2.Set<float>(0, 0, 0);
pts2.Set<float>(0, 1, 0);
pts2.Set<float>(1, 0, 300);
pts2.Set<float>(1, 1, 0);
pts2.Set<float>(2, 0, 0);
pts2.Set<float>(2, 1, 300);
pts2.Set<float>(3, 0, 300);
pts2.Set<float>(3, 1, 300);
Mat M = Cv2.GetPerspectiveTransform(pts1, pts2);
Mat dst = new Mat();
Cv2.WarpPerspective(img, dst, M, new Size(300, 300));
Cv2.ImShow("Input", img);
Cv2.ImShow("Output2", dst);
Cv2.WaitKey(0);
}
#endregion
}
}
C++版本运行代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/cvconfig.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <io.h>
#include <string>
#include <iostream>
using namespace cv;
using namespace std;
// 图像的平移操作
void demo1()
{
// 移动了100,50 个像素。
Mat img = cv::imread("../images/messi5.jpg", 0);
// Define the transformation matrix
Mat M(2, 3, CV_32FC1);
M.at<float>(0, 0) = 1;
M.at<float>(0, 1) = 0;
M.at<float>(0, 2) = 100;
M.at<float>(1, 0) = 0;
M.at<float>(1, 1) = 1;
M.at<float>(1, 2) = 50;
// 应用平移变换
Mat dst;
cv::warpAffine(img, dst, M, img.size());
cv::imshow("img", dst);
cv::waitKey(0);
}
// 旋转图像操作
void demo2()
{
Mat img = cv::imread("../images/messi5.jpg", 0);
Mat dst;
Mat rotMat = cv::getRotationMatrix2D(Point2f(img.cols / 2, img.rows / 2), 45, 0.6);
cv::warpAffine(img, dst, rotMat, Size(img.cols * 2, img.rows * 2));
cv::imshow("img", dst);
cv::waitKey(0);
}
// 仿射变换的基本操作
void demo3()
{
Mat img = cv::imread("../images/drawing.png");
Mat pts1(3, 2, CV_32FC1);
pts1.at<float>(0, 0) = 50;
pts1.at<float>(0, 1) = 50;
pts1.at<float>(1, 0) = 200;
pts1.at<float>(1, 1) = 50;
pts1.at<float>(2, 0) = 50;
pts1.at<float>(2, 1) = 200;
Mat pts2(3, 2, CV_32FC1);
pts2.at<float>(0, 0) = 10;
pts2.at<float>(0, 1) = 100;
pts2.at<float>(1, 0) = 200;
pts2.at<float>(1, 1) = 50;
pts2.at<float>(2, 0) = 100;
pts2.at<float>(2, 1) = 250;
Mat M = cv::getAffineTransform(pts1, pts2);
Mat dst;
cv::warpAffine(img, dst, M, img.size());
cv::imshow("Input", img);
cv::imshow("Output", dst);
cv::waitKey(0);
}
// 透视变换的基本操作
void demo4()
{
Mat img = cv::imread("../images/sudoku.jpg");
Mat pts1(4, 2, CV_32FC1);
pts1.at<float>(0, 0) = 56;
pts1.at<float>(0, 1) = 65;
pts1.at<float>(1, 0) = 368;
pts1.at<float>(1, 1) = 52;
pts1.at<float>(2, 0) = 28;
pts1.at<float>(2, 1) = 387;
pts1.at<float>(3, 0) = 389;
pts1.at<float>(3, 1) = 390;
Mat pts2(4, 2, CV_32FC1);
pts2.at<float>(0, 0) = 0;
pts2.at<float>(0, 1) = 0;
pts2.at<float>(1, 0) = 300;
pts2.at<float>(1, 1) = 0;
pts2.at<float>(2, 0) = 0;
pts2.at<float>(2, 1) = 300;
pts2.at<float>(3, 0) = 300;
pts2.at<float>(3, 1) = 300;
Mat M = cv::getPerspectiveTransform(pts1, pts2);
Mat dst;
cv::warpPerspective(img, dst, M, Size(300, 300));
cv::imshow("Input", img);
cv::imshow("Output", dst);
cv::waitKey(0);
}
int main()
{
demo1();
//demo2();
//demo3();
//demo4();
}
Python版本运行代码如下:
demo1.py:图像的平移操作:
"""
http://docs.opencv.org/3.2.0/da/d6e/tutorial_py_geometric_transformations.html
函数 cv2.warpAffine() 的第三个参数的是 出图像的大小 ,它的格式应是图像的(宽,高) 。
图像的宽对应的是列数, 高对应的是行数。
"""
import cv2
import numpy as np
# 移动了100,50 个像素。
img = cv2.imread('../images/messi5.jpg', 0)
rows, cols = img.shape
M = np.float32([[1, 0, 100], [0, 1, 50]])
dst = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow('img', dst)
cv2.imwrite('img.jpg', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
demo2.py:旋转图像操作:
# -*- coding: utf-8 -*-
# 旋转
import cv2
import numpy as np
img = cv2.imread('../images/messi5.jpg', 0)
rows, cols = img.shape
# 的第一个参数为旋转中心 第二个为旋转角度
# 第三个为旋转后的缩放因子
# 可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 0.6)
# 第三个参数是输出图像的尺寸中心
dst = cv2.warpAffine(img, M, (2 * cols, 2 * rows))
cv2.imshow('img', dst)
cv2.imwrite('img2.jpg', dst)
cv2.waitKey(0) # & 0xFF
cv2.destroyAllWindows()
demo3.py:仿射变换的基本操作:
'''
仿射变换
在仿射变换中 原图中所有的平行线在结果图像中同样平行。
为了创建 这个矩阵,我们需要从原图像中找到三个点以及他们在 出图像中的位置。
然后 cv2.getAffineTransform 会创建一个 2x3 的矩 最后 个矩 会 传给 函数 cv2.warpAffine。
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('../images/drawing.png')
rows, cols, ch = img.shape
print(img.shape)
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (cols, rows))
# plt.subplot(121, plt.imshow(img), plt.title('Input'))
# plt.subplot(122, plt.imshow(dst), plt.title('Output'))
plt.figure(figsize=(8, 7), dpi=98)
p1 = plt.subplot(211)
#p1.show(img) # 官方代码采用的是这个操作,运行会报错
plt.imshow(img)
p1.set_title('Input')
p2 = plt.subplot(212)
#p2.show(dst)# 官方代码采用的是这个操作,运行会报错
plt.imshow(dst)
p2.set_title('Output')
plt.show()
demo4.py:透视变换的基本操作:
# -*- coding: utf-8 -*-
'''
透视变换
对于透视变换 ,我们需要一个 3x3 变换矩 。
在变换前后直线 是直线。
构建 个变换矩 你需要在输入图像上找 4 个点, 以及他们在输出图 像上对应的位置。
四个点中的任意三个都不能共线。这个变换矩阵可以用函数 cv2.getPerspectiveTransform() 构建。
然后把这个矩阵传给函数 cv2.warpPerspective。
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('../images/sudoku.jpg')
rows, cols, ch = img.shape
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (300, 300))
plt.figure(figsize=(8, 7), dpi=98)
p1 = plt.subplot(211)
#p1.show(img) # 官方代码采用的是这个操作,运行会报错
plt.imshow(img)
p1.set_title('Input')
p2 = plt.subplot(212)
#p2.show(dst)# 官方代码采用的是这个操作,运行会报错
plt.imshow(dst)
p2.set_title('Output')
plt.show()