OpenCV C++開發 第二節:圖像處理(四、圖像疊加、調整圖像亮度與對比度、繪製形狀與文字)

一、圖像疊加

關門放代碼

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc, char** argv) {
	Mat src1, src2, dst;
	src1 = imread("C:\\Users\\Administrator\\Desktop\\test.jpg");
	src2 = imread("C:\\Users\\Administrator\\Desktop\\test2.jpg");
	if (!src1.data) {
		cout << "could not load image Linux Logo..." << endl;
		return -1;
	}
	if (!src2.data) {
		cout << "could not load image WIN7 Logo..." << endl;
		return -1;
	}
	double alpha = 0.5;
	if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type()) {
		addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);//將src2設置透明度1.0-alpha後疊加在src1上。
		// multiply(src1, src2, dst, 1.0);

		imshow("linuxlogo", src1);
		imshow("win7logo", src2);
		namedWindow("blend demo", CV_WINDOW_AUTOSIZE);
		imshow("blend demo", dst);
	}
	else {
		printf("could not blend images , the size of images is not same...\n");
		return -1;
	}

	waitKey(0);
	return 0;
}

以上代碼中主要的幾個知識點解釋下:

這裏要求兩個圖像的長寬需要一樣。

1.if (!src1.data) {}

這裏用於判斷圖片是否加載成功,如果加載失敗則停止運行。

2.addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);

公式:

其中α的取值範圍爲0~1之間

參數1:輸入圖像src1

參數2:輸入圖像src1alpha

參數3:輸入圖像src2

參數4:輸入圖像src2alpha

參數5gamma

參數6:輸出混合圖像

這句代碼的意思爲:將src2設置透明度1.0-alpha後疊加在src1上存於dst。

看效果

二、調整圖像亮度與對比度

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc, char** argv) {
	Mat src, dst;
	src = imread("C:\\Users\\Administrator\\Desktop\\test.png");
	//src2 = imread("C:\\Users\\Administrator\\Desktop\\test1.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}
	char input_win[] = "input image";
	//cvtColor(src, src, CV_BGR2GRAY);
	namedWindow(input_win, CV_WINDOW_AUTOSIZE);
	imshow(input_win, src);

	// contrast and brigthtness changes 
	int height = src.rows;
	int width = src.cols;
	dst = Mat::zeros(src.size(), src.type());//創建一個空白的圖像
	float alpha = 1.2;
	float beta = 30;

	Mat m1;
	src.convertTo(m1, CV_32F);//默認是CV_8UC轉換到CV32F
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			if (src.channels() == 3) {
				float b = m1.at<Vec3f>(row, col)[0];// blue
				float g = m1.at<Vec3f>(row, col)[1]; // green
				float r = m1.at<Vec3f>(row, col)[2]; // red

				// output
				dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
				dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
				dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
			}
			else if (src.channels() == 1) {
				float v = src.at<uchar>(row, col);
				dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);
			}
		}
	}

	char output_title[] = "contrast and brightness change demo";
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow(output_title, dst);

	waitKey(0);
	return 0;
}

以上代碼中主要的幾個知識點解釋下:

1.dst = Mat::zeros(src.size(), src.type());//返回指定大小和類型的零數組。即創建一個空的圖像。

2.src.convertTo(m1, CV_32F);

默認Mat是CV_8UC的Vec3b。
這句的意思是把CV_8UC轉換到CV32F的Vec3f。
Vec3b對應三通道的順序是blue、green、red的uchar類型數據。
Vec3f對應三通道的float類型數據,要比Vec3b更精確。

3.三通道dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);

公式:

Mat.at<Vec3b>(y,x)[index]=value 給每個像素點每個通道賦值,x與y是像素點。index是通道,rgb通道就是對應012。如果沒有賦值則是取值。

4.單通道dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);

與第3點類似,單通道的寫法。

5.alpha=1.2;

alpha大於1則更亮,若小於1則偏暗。

如下圖,調整後是不是亮瞎了。

三、繪製形狀與文字

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;
Mat bgImage;
const char* drawdemo_win = "draw shapes and text demo";
void MyLines();
void MyRectangle();
void MyEllipse();
void MyCircle();
void MyPolygon();
void MyText();
void RandomLineDemo();
int main(int argc, char** argv) {
	bgImage = imread("C:\\Users\\Administrator\\Desktop\\test.png");
	if (!bgImage.data) {
		printf("could not load image...\n");
		return -1;
	}
	MyLines();
	MyRectangle();
	MyEllipse();
	MyCircle();
	MyPolygon();
	MyText();

	namedWindow("random line demo", CV_WINDOW_AUTOSIZE);
	imshow("random line demo", bgImage);

	RandomLineDemo();
	waitKey(0);
	return 0;
}
/*
畫一條線條
*/
void MyLines() {
	Point p1 = Point(20, 30);
	Point p2;
	p2.x = 400;
	p2.y = 400;
	Scalar color = Scalar(0, 0, 255);
	line(bgImage, p1, p2, color, 1, LINE_AA);
}
/*
畫一個方形框框
*/
void MyRectangle() {
	Rect rect = Rect(200, 100, 300, 300);
	Scalar color = Scalar(255, 0, 0);
	rectangle(bgImage, rect, color, 2, LINE_8);
}
/*
畫一個橢圓框框
*/
void MyEllipse() {
	Scalar color = Scalar(0, 255, 0);
	ellipse(bgImage, Point(bgImage.cols / 2, bgImage.rows / 2), Size(bgImage.cols / 4, bgImage.rows / 8), 0, 0, 360, color, 2, LINE_8);
}
/*
畫一個圓形框框
*/
void MyCircle() {
	Scalar color = Scalar(0, 255, 255);
	Point center = Point(bgImage.cols / 2, bgImage.rows / 2);
	circle(bgImage, center, 150, color, 2, 8);
}

/*
畫一個實心方形
*/
void MyPolygon() {
	Point pts[1][5];
	pts[0][0] = Point(100, 100);
	pts[0][1] = Point(100, 200);
	pts[0][2] = Point(200, 200);
	pts[0][3] = Point(200, 100);
	pts[0][4] = Point(100, 100);

	const Point* ppts[] = { pts[0] };
	int npt[] = { 5 };
	Scalar color = Scalar(255, 12, 255);

	fillPoly(bgImage, ppts, npt, 1, color, 8);
}

/*
畫一段文字
*/
void MyText() {
	putText(bgImage, "Hello OpenCV", Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1.0, Scalar(12, 23, 200), 3, 8);
}
//隨機畫線段
void RandomLineDemo() {
	RNG rng(12345);
	Point pt1;
	Point pt2;
	for (int i = 0; i < 100000; i++) {
		pt1.x = rng.uniform(0, bgImage.cols);
		pt2.x = rng.uniform(0, bgImage.cols);
		pt1.y = rng.uniform(0, bgImage.rows);
		pt2.y = rng.uniform(0, bgImage.rows);
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		if (waitKey(50) > 0) {
			break;
		}
		line(bgImage, pt1, pt2, color, 1, 8);
		imshow("random line demo", bgImage);
	}
}

以上代碼中主要的幾個知識點解釋下:

該段代碼中如果分辨不清畫的圖與對應的代碼,可以分別註釋某一些代碼,一段一段運行。

1.line(bgImage, p1, p2, color, 1, LINE_AA);

畫一條線,bgImage是要繪畫的目的圖像。p1是Point起點。p2是Point終點。color是Scalar類型顏色。1是代表粗細。LINE_AA是線的類型,(LINE_4\LINE_8\LINE_AA),LINE_4與LINE_8都有鋸齒,LINE_AA沒有鋸齒。

2.rectangle(bgImage, rect, color, 2, LINE_8);

畫一個方形框框,bgImage是要繪畫的目的圖像。rect是Rect類型對象,rect內包括起點xy與終點xy。color是Scalar類型顏色。2代表粗細,LINE_8代表有鋸齒的類型。

3.ellipse(bgImage, Point(bgImage.cols / 2, bgImage.rows / 2), Size(bgImage.cols / 4, bgImage.rows / 8), 0, 0, 360, color, 2, LINE_8);

畫一個橢圓框框。首先我們理解一下橢圓【橢圓是平面內到定點F1、F2的距離之和等於常數(大於|F1F2|)的動點P的軌跡,F1、F2稱爲橢圓的兩個焦點。其數學表達式爲:|PF1|+|PF2|=2a(2a>|F1F2|)。橢圓有一個長半軸,就是原點到最遠的頂點的距離。一個短半軸,原點到最近的頂點的距離】。

bgImage是要繪畫的目的圖像。Point(bgImage.cols / 2, bgImage.rows / 2)代表原點即是中心點的位置。Size(bgImage.cols / 4, bgImage.rows / 8)左邊的數是長半軸,右邊是短半軸。第一個0是旋轉角度。第二個0是起點角度。360是終點角度。color是Scalar類型顏色。2代表粗細,LINE_8代表有鋸齒的類型。

4.circle(bgImage, center, 150, color, 2, 8);

畫一個圓形。center代表原點即是中心點的位置。150是半徑。color是Scalar類型顏色。2代表粗細,LINE_8代表有鋸齒的類型。8其實就是LINE_8,可以在Visual Studio裏按住Ctrl+鼠標點擊LINE_8,就會跳轉到LINE_8的定義。

5.fillPoly(bgImage, ppts, npt, 1, color, 8);

畫一個實心方形。bgImage是要繪畫的目的圖像。ppts是一個二維數組。npt是多邊形頂點數目。1是多邊形數量。color是Scalar類型顏色。8代表有鋸齒的類型。

6.rng.uniform(0, bgImage.cols);

產生一個0到bgImage.cols之間的整型隨機數。

7.waitKey(50)

waitKey()與waitKey(0)都是無限等待。

waitKey(50)是等待50毫秒,繼續運行。

來看看效果

這節就這麼多,這些代碼只要記住都是什麼功能就行,不用背下來,等到了使用的時候再查。還有一些原理需要看懂。

發佈了15 篇原創文章 · 獲贊 8 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章