读书笔记-opencv-投影变换
程序员文章站
2023-12-27 09:02:39
...
读书笔记-opencv-投影变换
原理解析
透视变换是将图片投影到一个新的视平面,也称作投影映射.它是二维(x,y)到三维(X,Y,Z),再到另一个二维(x′,y′)空间的映射.相对于仿射变换,它提供了更大的灵活性,将一个四边形区域映射到另一个四边形区域(不一定是平行四边形).它不止是线性变换.但也是通过矩阵乘法实现的,使用的是一个3x3的矩阵,矩阵的前两行与仿射矩阵相(m11,m12,m13,m21,m22,m23),也实现了线性变换和平移,第三行用于实现透视变换
在公式两边同时除以m33,得到一个有关8个未知数的方程组,只需要四个点就可以求出相关系数。
具体的推理过程,可以参见单应性变换,透视变换相关帖子。
代码示例
opencv提供相关的函数:
cv2.getPerspectiveTransform(src, dst)
其中src是变换前坐标,dst是变换后的坐标,返回33的矩阵src和dst是4 * 2的二维ndarry,数据必须为32位浮点型
python实例
src_points = np.array([[165., 270.], [835., 270.], [360., 125.], [615., 125.]], np.float32)
dst_points = np.array([[165., 270.], [835., 270.], [165., 30.], [835., 30.]], dtype = "float32")
M = cv2.getPerspectiveTransform(src_points, dst_points)
print(M)
返回矩阵M是float64的数据类型
对图片进行处理
if __name__ == "__main__":
image_path = "c:\\users\\pictures\\saved pictures\\1.jpg"
image = cv2.imread(image_path)
#原图的宽高
h,w = image.shape[:2]
#原图的四个点与投影变换对应的点
src = np.array([[0.0, 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("warpPersperctive", r)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果处理的图片:
C++实例
//method 1
//原矩阵
Point2f src[] = { Point2f(0,0), Point2f(200.0, 0), Point2f(0,200.0), Point2f(200.0, 200.0) };
//投影变换后的矩阵
Point2f dst[] = { Point2f(100.0, 20.0), Point2f(200.0, 20.0), Point2f(50, 70), Point2f(250.0, 70.0) };
//投影变换矩阵
Mat M = getPerspectiveTransform(src, dst);
//method 2
//原矩阵
Mat src = (Mat_<float>(4, 2) << 0, 0, 200, 0, 0, 200, 200, 200);
//投影变换后的矩阵
Mat dst = (Mat_<float>(4, 2) << 100, 20, 200, 20, 50, 70, 250, 70);
//投影变换矩阵
Mat M = getPerspectiveTransform(src, dst);
对图片进行处理:
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui_c.h>
#include<iostream>
#include<string>
using namespace cv;
/************************************************************************/
/* 注意CV_EVENT_LBUTTONDOWN需要加载头文件 <opencv2/highgui/highgui_c.h>
void circle(Mat & img, Point center, int radius, const Scalar & color, int thickness = 1, int lineType = 8, int shift = 0
该函数在途中用来画圆, img 代表输入图像, center代表圆心,color代表颜色, thickness代表线条的粗细, linetype线的类型,
opencv还提供rectangle, ellipse,line在图上画相应的线段*/
/************************************************************************/
Mat InitImage; //原图
Mat convertImage; //转换后的图像
Point2f IPoint, pIPoint;
int i = 0, j = 0;
Point2f src[4]; //存储原坐标
Point2f dst[4]; //存储对应的变换的坐标
//通过以下鼠标事件,在要原图上面取四个坐标
/**
* @鼠标点击事件
* @return void
*/
void mouse_I(int event, int x, int y, int flags, void *param)
{
switch (event)
{
//记录左键
case CV_EVENT_LBUTTONDOWN:
IPoint = Point2f(x, y); //记录坐标
break;
case CV_EVENT_LBUTTONUP:
src[i] = IPoint;
circle(InitImage, src[i], 7, Scalar(0), 3); //标记
i += 1;
break;
default:
break;
}
}
//通过以下鼠标事件,在要输出的画布上面取四个对应坐标
/**
* @鼠标点击事件,在要输出的画布上面取四个对应坐标
* @return void
*/
void mouse_pI(int event, int x, int y, int flags, void *param)
{
switch (event)
{
case CV_EVENT_LBUTTONDOWN:
pIPoint = Point2f(x, y);
break;
case CV_EVENT_LBUTTONUP:
dst[j] = pIPoint;
circle(convertImage, dst[j], 7, Scalar(0), 3);
j += 1;
break;
default:
break;
}
}
int main()
{
//加载图片,imread(),第二个参加代表以何种方式加载,具体可以看Opencv的imread()函数解析
std::string iamgePath = "C:\\Users\\huangxin\\Pictures\\Saved Pictures\\1.jpg";
InitImage = imread(iamgePath, 1);
if (!InitImage.data)
{
return -1;
}
//输出图像
convertImage = 255 * Mat::ones(InitImage.size(), CV_8UC1);
//在原图定义,鼠标事件
namedWindow("InitImage", 1);
setMouseCallback("InitImage", mouse_I, NULL);
//在输出窗口定义,鼠标事件
namedWindow("ConvertImage", 1);
setMouseCallback("ConvertImage", mouse_pI, NULL);
imshow("InitImage", InitImage);
imshow("ConvertImage", convertImage);
while (!(i == 4 && j == 4))
{
imshow("InitImage", InitImage);
imshow("ConvertImage", convertImage);
if (waitKey(50) == 'q')
{
break;
}
}
imshow("InitImage", InitImage);
imshow("ConvertImage", convertImage);
//移除鼠标事件
setMouseCallback("InitImage", NULL, NULL);
setMouseCallback("ConvertImage", NULL, NULL);
//计算投影变换矩阵
Mat p = getPerspectiveTransform(src, dst);
//投影变化
Mat resultMat;
warpPerspective(InitImage, resultMat, p, convertImage.size());
imshow("result: ", resultMat);
waitKey(0);
return 0;
}
在原图点击四个点,作为输入矩阵,在convertImage上面点击四个点作为输出矩阵,通过两个矩阵建立投影变换矩阵。
处理的结果图片: