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个邻域)
这个函数有两个使用方法
- 使用前三个参数,dsize参数就是你想让图像变成的大小
- 使用前两个和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
);
下面提供一下对一幅图片进行仿射操作的常规步骤:
- 首先定义两组二维点数组,数组长度为3
- 定义2*3大小的仿射矩阵
- 设置源图像和目标图像上的三组点以计算仿射变换
- 求得仿射变换矩阵
- 对源图像应用刚刚求得的仿射变换
下面贴代码来看一看:
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;
}