opencv学习笔记十:常见的图像变换

拉伸,收缩,扭曲和旋转

均匀调整

最简单的调整大小的方法,就是调用cv::resize()函数,函数原型如下:

	cv::resize(
		cv::InputArray	src,	输入图像
		cv::OutputArray	dst,	输出图像
		cv::Size		dsize,	图像变换后的大小
		double			fx = 0,	当dsize = (0,0)时,fx为x轴比例因子
		double			fy = 0,
		int				interpolation = CV::INTER_LINEAR    插值方法,默认为线性插值
	);

cv::resize()函数的插值选项:

		INTER_NEAREST		最近邻插值
		INTER_LINEAR		双线性插值
		INTER_AREA			像素区域重采样
		INTER_CUBIC			双三次插值
		INTER_LANCZOS4		插值(超过8*8个邻域)

这个函数有两个使用方法

  1. 使用前三个参数,dsize参数就是你想让图像变成的大小
  2. 使用前两个和fx,fy参数,将dsize换设为(0,0),fx与fy为各轴的比例因子
    下面看个程序:
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
	Mat src,dst1,dst2;
	src = imread("1.jpg", 1);
	imshow("原图", src);
	Size2i sz(200, 150);   //设置大小

	resize(src, dst1, sz);
	resize(src, dst2, Size(0, 0), 2, 2);
	
	imshow("变换1", dst1);
	imshow("变换2", dst2);
	waitKey(0);
	return 0;
}

在这里插入图片描述

仿射变换与透视变换

虽然目前,我不知道这两个变换在哪些方面会有妙用与奇效,但既然有这个功能,就先整理一下。目前就知道,当你知道多个图像是同一个图像的不同角度的视图时,可能需要计算不同试图相关的实际变换。
对于平面图像,应用23矩阵进行变换,称为“仿射变换”,而基于33矩阵进行变换,称为“透视变换”。仿射变换可以将矩形转换为平行四边形,而透视变换提供更多的灵活性,可以将矩形变换为任意四边形。

首先看仿射变换
函数原型:

	warpAffine(
		InputArray	src,		输入图像
		OutputArray	dst,		输出图像
		InputArray	M,			仿射计算矩阵
		Size		dsize,		输出图像大小
		int			flags = INIET_LINEAR,   插值方法
		int			borderMode = BORDER_CONSTANT,   
		const Scalar&  borderValue = Scalar()	
	);

其中比较难搞的就是第三个参数仿射计算矩阵,这个矩阵需要我们自己计算,但opencv提供了计算该矩阵的函数:

	getAffineTransform(
		const Point2f* src,
		const Point2f* dst
	);

下面提供一下对一幅图片进行仿射操作的常规步骤:

  1. 首先定义两组二维点数组,数组长度为3
  2. 定义2*3大小的仿射矩阵
  3. 设置源图像和目标图像上的三组点以计算仿射变换
  4. 求得仿射变换矩阵
  5. 对源图像应用刚刚求得的仿射变换
    下面贴代码来看一看:
int main()
{
	//【1】参数准备
	Mat srcImage, dstImage_warp, dst2;
	srcImage = imread("2.jpg", 1);
	if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }
	imshow("原图", srcImage);

    //定义两组点,代表两个三角形
	Point2f srcTriangle[3];
	Point2f dstTriangle[3];
	//定义Mat类仿射矩阵
	Mat warpMat(2, 3, CV_32FC1);
	//【2】设置目标图像的大小和类型与源图像一致
	dstImage_warp = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
	//【3】设置源图像和目标图像上的三组点以计算仿射变换
	srcTriangle[0] = Point2f(0, 0);
	srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols - 1), 0);
	srcTriangle[2] = Point2f(0, static_cast<float>(srcImage.rows - 1));

	dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));
	dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));
	dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));
	//【4】求得仿射变换
	warpMat = getAffineTransform(srcTriangle, dstTriangle);

	//【5】对源图像应用刚刚求得的仿射变换
	warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());

	imshow("仿射变换", dstImage_warp);

	waitKey(0);
	return 0;
}

在这里插入图片描述
对于透视变换,有着类似的操作,这里就不多说了,就贴两张函数截图好了。
WarpPerspective()透视变换函数:
在这里插入图片描述
在这里插入图片描述

图像修复

图像修复是涉及一个cv::inpaint()函数,函数原型:

cv::inpaint(
	InputArray	src,		输入图像
	InputArray	inpaintMast,图像腌膜
	OutputArray dst,		输出图像
	double		inpaintRadius,  每个已渲染像素周围的区域大小
	int			flags		图像修复方法INPAINT_NS   INPAINT_TELEA
);
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

#define WINDOW_NAME0 "【原始图参考】"        //为窗口标题定义的宏 
#define WINDOW_NAME1 "【原始图】"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "【修补后的效果图】"        //为窗口标题定义的宏 

//-----------------------------------【全局变量声明部分】--------------------------------------
//          描述:全局变量声明
//-----------------------------------------------------------------------------------------------
Mat srcImage0, srcImage1, inpaintMask;
Point previousPoint(-1, -1);//原来的点座标

//-----------------------------------【ShowHelpText( )函数】----------------------------------
//          描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
	//输出欢迎信息和OpenCV版本
	printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
	printf("\n\n\t\t\t此为本书OpenCV3版的第78个配套示例程序\n");
	printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION);
	printf("\n\n  ----------------------------------------------------------------------------\n");

	//输出一些帮助信息
	printf("\n\n\n\t欢迎来到【图像修复】示例程序~\n");
	printf("\n\t请在进行图像修复操作之前,在【原始图】窗口中进行适量的绘制"
		"\n\n\t按键操作说明: \n\n"
		"\t\t【鼠标左键】-在图像上绘制白色线条\n\n"
		"\t\t键盘按键【ESC】- 退出程序\n\n"
		"\t\t键盘按键【1】或【SPACE】-进行图像修复操作 \n\n");
}


//-----------------------------------【On_Mouse( )函数】--------------------------------
//          描述:响应鼠标消息的回调函数
//----------------------------------------------------------------------------------------------
static void On_Mouse(int event, int x, int y, int flags, void*)
{
	//鼠标左键弹起消息
	if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
		previousPoint = Point(-1, -1);
	//鼠标左键按下消息
	else if (event == EVENT_LBUTTONDOWN)
		previousPoint = Point(x, y);
	//鼠标按下并移动,进行绘制
	else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
	{
		Point pt(x, y);
		if (previousPoint.x < 0)
			previousPoint = pt;
		//绘制白色线条
		line(inpaintMask, previousPoint, pt, Scalar::all(255), 5, 8, 0);
		line(srcImage1, previousPoint, pt, Scalar::all(255), 5, 8, 0);
		previousPoint = pt;
		imshow(WINDOW_NAME1, srcImage1);
	}
}


//--------------------------------------【main( )函数】-----------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main(int argc, char** argv)
{
	//改变console字体颜色
	system("color 2F");

	//显示帮助文字
	ShowHelpText();

	//载入原始图并进行掩膜的初始化
	Mat srcImage = imread("1.jpg", -1);
	if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }
	srcImage0 = srcImage.clone();
	srcImage1 = srcImage.clone();
	inpaintMask = Mat::zeros(srcImage1.size(), CV_8U);

	//显示原始图参考
	imshow(WINDOW_NAME0, srcImage0);
	//显示原始图
	imshow(WINDOW_NAME1, srcImage1);
	//设置鼠标回调消息
	setMouseCallback(WINDOW_NAME1, On_Mouse, 0);

	//轮询按键,根据不同的按键进行处理
	while (1)
	{
		//获取按键键值
		char c = (char)waitKey();

		//键值为ESC,程序退出
		if (c == 27)
			break;

		//键值为2,恢复成原始图像
		if (c == '2')
		{
			inpaintMask = Scalar::all(0);
			srcImage.copyTo(srcImage1);
			imshow(WINDOW_NAME1, srcImage1);
		}

		//键值为1或者空格,进行图像修补操作
		if (c == '1' || c == ' ')
		{
			Mat inpaintedImage;
			inpaint(srcImage1, inpaintMask, inpaintedImage, 3, INPAINT_TELEA);
			imshow(WINDOW_NAME2, inpaintedImage);
		}
	}

	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章