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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章