【OpenCV:從零到一】07:繪製圖像與文字

前言
這是我《OpenCV:從零到一》專欄的第七篇博客,想看跟多請戳
本文概要

  • 使用cv::Point與cv::Scalar
  • 繪製線、矩形、園、橢圓等基本幾何形狀
    • 畫線 cv::line (LINE_4\LINE_8\LINE_AA)
    • 畫橢圓cv::ellipse
    • 畫矩形cv::rectangle
    • 畫圓cv::circle
    • 畫填充cv::fillPoly
  • 隨機生成與繪製文本
    • RNG類
    • 生成高斯隨機數RNG.gaussian (double sigma)
    • 生成正態分佈隨機數RNG.uniform (int a, int b)

案例代碼
大概內容: 繪製線、矩形、園、橢圓、文字、隨機線 。

#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 RandomLineDemo();
int main(int argc, char** argv) {
	bgImage = imread("D:/vcprojects/images/test1.png");
	if (!bgImage.data) {
		printf("could not load image...\n");
		return -1;
	}
	MyLines();
	MyRectangle();
	MyEllipse();
	MyCircle();
	MyPolygon();

	putText(bgImage, "Hello OpenCV", Point(300, 300), FONT_HERSHEY_COMPLEX, 1.0, Scalar(12, 23, 200), 3, 8);
	//opencv4中一般取消了cv前綴;應該用FONT_HERSHEY_COMPLEX_SMALL和下面的用WINDOW_AUTOSIZE
	namedWindow(drawdemo_win, WINDOW_AUTOSIZE);
	imshow(drawdemo_win, 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), 90, 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 RandomLineDemo() {
	RNG rng(12345);
	Point pt1;
	Point pt2;
	Mat bg = Mat::zeros(bgImage.size(), bgImage.type());
	namedWindow("random line demo");
	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(bg, pt1, pt2, color, 1, 8);
		imshow("random line demo", bg);
	}
}



運行效果:
在這裏插入圖片描述在這裏插入圖片描述

解析及注意事項

  • 之前第三篇裏面提到了scalar、point、size是三個很常見的圖像處理數據結構。
  • 之前在圖像操作那一篇提到改變像素值的操作函數的參數都差多,都有那幾個固定的項,而繪製圖像類也是這樣的。第一個參數一般是背景圖像InputOutputArray img 後面四個const Scalar & color,int thickness = 1,int lineType = LINE_8,int shift = 0 分別是顏色、線的粗細、線的類型(鋸齒)、點座標的小數位數。
  • 幾個圖形之中橢圓和矩形的一個重載有點難理解,矩形傳的也是兩個點,分別是左上角和右下角,所以畫出來的矩形永遠都是正的
  • 橢圓外接矩形的重載不難理解,難理解的是三個角度的重載。angle是整個橢圓的旋轉角度,如果旋轉角度爲0的話,橢圓永遠都是正的,而endAngle-startAngle就是這個橢圓要從哪裏畫到哪裏(不一定畫一整個,可以之畫一部分線段)axes要包括寬和高,寬和高決定橢圓的形狀,如果一樣就是圓。
  • 參照下面官方給的這個圖。紅色爲橢圓,藍色爲實際畫出來的曲線段,其中左右的橢圓形態不同是因爲AXES.WIDTH和AXES.HEIGHT的大小關係不同。看左邊的圖不要把它看成三維的圖,他就是一個實實在在的二維圖,注意黑色的座標軸。這下配合圖片就應該能很清晰的理解參數了。
    在這裏插入圖片描述
  • C和C++中產生隨機數的方法如rand()、srand()等在OpenCV中仍可以用。也可以使用opencv裏的RNG類,可以得到高斯分佈(正態)的、和均勻分佈的隨機數。
  • 之前使用一些宏的時候經常會報錯“未定義”,看了別的博客才知道是版本不一樣引起的,我們平時看的教程大多是opencv2,或者早起的opencv3,所以一些宏不一樣很正常。他們的區別大概是,前帶cv的都是C裏的寫法,不帶cv的是C++裏的寫法,比如FONT_HERSHEY_COMPLEX和CV_FONT_HERSHEY_COMPLEX,其本質都是一樣的,只不過後者要加一個c的頭文件才能識別出來,也就不難解釋我們加的頭文件都是****c.h 。總言而之,如果使用宏的時候飄紅表示未定義可以試一下把CV或者CV(以此類推)刪掉試試,屢試不爽。

全註釋代碼

#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 RandomLineDemo();

int main(int argc, char** argv) {
	bgImage = imread("D:\\86186\\Documents\\opencv\\white.png");
	if (!bgImage.data) {
		printf("could not load image...\n");
		return -1;
	}
	MyLines();
	MyRectangle();
	MyEllipse();
	MyCircle();
	MyPolygon();

	putText(bgImage, "Hello OpenCV", Point(300, 300), FONT_HERSHEY_COMPLEX, 1.0, Scalar(12, 23, 200), 3, 8);
	/*
	InputOutputArray 	img,
	const String & 	text,//	Text string to be drawn.
	Point 	org,//Bottom-left corner of the text string in the image.
	int 	fontFace,//Font type, see HersheyFonts.
	double 	fontScale,
	Scalar 	color,
	int 	thickness = 1,
	int 	lineType = LINE_8,
	bool 	bottomLeftOrigin = false

	HersheyFonts:
	FONT_HERSHEY_SIMPLEX		normal size sans-serif font
	FONT_HERSHEY_PLAIN			small size sans-serif font
	FONT_HERSHEY_DUPLEX			normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX)
	FONT_HERSHEY_COMPLEX		normal size serif font
	FONT_HERSHEY_TRIPLEX		normal size serif font (more complex than FONT_HERSHEY_COMPLEX)
	FONT_HERSHEY_COMPLEX_SMALL	smaller version of FONT_HERSHEY_COMPLEX
	FONT_HERSHEY_SCRIPT_SIMPLEX hand-writing style font
	FONT_HERSHEY_SCRIPT_COMPLEX more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX
	FONT_ITALIC					flag for italic font
	*/
	//opencv4中一般取消了cv前綴;應該用FONT_HERSHEY_COMPLEX_SMALL和下面的用WINDOW_AUTOSIZE
	namedWindow(drawdemo_win, WINDOW_AUTOSIZE);
	imshow(drawdemo_win, bgImage);
	RandomLineDemo();
	waitKey(0);
	return 0;
}

void MyLines() {
	//兩種創建point的方法
	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);//Draws a line segment connecting two points.
	/*
	For non-antialiased lines with integer coordinates, the 8-connected or 4-connected Bresenham algorithm is used.
	對於具有整數座標的非抗鋸齒線,採用8連通或4連通Bresenham算法。
	Thick lines are drawn with rounding endings. Antialiased lines are drawn using Gaussian filtering.
	繪製粗線以四捨五入結尾。使用高斯濾波繪製抗鋸齒線。
	參數:
		InputOutputArray 	img,
		Point pt1,
		Point pt2,
		const Scalar & color,
		int thickness = 1,
		int lineType = LINE_8,
		int shift = 0 ,//Number of fractional bits in the point coordinates.點座標中的小數位數。
	lineType有下列四種:
		FILLED
		LINE_4  4-connected line
		LINE_8  8-connected line
		LINE_AA  antialiased line
	*/
}

void MyRectangle() {
	Rect rect = Rect(200, 100, 300, 300);
	Scalar color = Scalar(255, 0, 0);
	rectangle(bgImage, rect, color, 2, LINE_8);//Draws a simple, thick, or filled up-right rectangle.
	/*
	參數
		InputOutputArray 	img,
		Rect rec{//use rec parameter as alternative specification of the drawn rectangle: r.tl() and r.br()-Point(1,1) are opposite corners
			Point 	pt1,//Vertex of the rectangle.//根據rec的出此爲矩形左上角
			Point 	pt2, //Vertex of the rectangle opposite to pt1 .//右下角
		}//意思是rect和兩個point是等價的,要麼填前者,要麼填後者
		const Scalar & 	color,
		int 	thickness = 1,
		int 	lineType = LINE_8,
		int 	shift = 0 
	*/
}

void MyEllipse() {
	Scalar color = Scalar(0, 255, 0);
	//不建議下面這麼寫參數,可讀性太差了。
	ellipse(bgImage, Point(bgImage.cols / 2, bgImage.rows / 2), Size(bgImage.cols / 4, bgImage.rows / 8), 90, 0, 360, color, 2, LINE_8);
	/*
	參數:
		InputOutputArray 	img,
		Point 	center,
		Size 	axes, //Half of the size of the ellipse main axes.
		double 	angle, //Ellipse rotation angle in degrees.
		double 	startAngle, //Starting angle of the elliptic arc in degrees.
		double 	endAngle, //Ending angle of the elliptic arc in degrees.
		const Scalar & 	color,
		int		thickness = 1,//Thickness of the ellipse arc outline, if positive. Otherwise, this indicates that a filled ellipse sector is to be drawn.
		int 	lineType = LINE_8,
		int 	shift = 0 
	參數和正方形差不多主要是那三個角度
	angle是整個橢圓的旋轉角度,如果旋轉角度爲0的話,橢圓永遠都是正的
	而endAngle-startAngle就是這個橢圓要從哪裏畫到哪裏(不一定畫一整個,可以之畫一部分線段)
	axes要包括寬和高,寬和高決定橢圓的形狀,如果一樣就是圓
	重載參數
		const RotatedRect & box,
		這個參數可以代替center+axes+angle,starAngle和endAngle也不用填了,外切矩形確定唯一一個橢圓(只能畫一整個)
	*/
}

void MyCircle() {
	Scalar color = Scalar(0, 255, 255);
	Point center = Point(bgImage.cols / 2, bgImage.rows / 2);
	circle(bgImage, center, 150, color, 2, 8);
	/*
	InputOutputArray 	img,
	Point 	center,
	int 	radius,//半徑
	const Scalar & 	color,
	int 	thickness = 1,
	int 	lineType = LINE_8,
	int 	shift = 0 
	參數和橢圓差不多
	*/
}

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);
	/*
	InputOutputArray img,
	InputArrayOfArrays 	pts{//	Array of polygons where each polygon is represented as an array of points.
		const Point ** 	pts,//點s
		const int * npts,//點的數量
		int ncontours,//輪廓數量
	}
	const Scalar & 	color,
	int ineType = LINE_8,
	int shift = 0,
	Point offset = Point(),//Optional offset of all points of the contours.可選的輪廓的所有點的偏移。
	*/
}

void RandomLineDemo() {
	RNG rng((int)getTickCount());//裏面的數值一樣的話產生的隨機數也是一樣的,可以用getTickCount()來做到真正的隨機
	Point pt1;
	Point pt2;
	Mat bg = Mat::zeros(bgImage.size(), bgImage.type());
	namedWindow("random line demo");
	for (int i = 0; i < 100000; i++) {
		pt1.x = rng.uniform(0, bgImage.cols);//uniformly-distributed 均勻分佈
		pt2.x = rng.uniform(0, bgImage.cols);//eturns uniformly distributed integer random number from [a,b) range
		pt1.y = rng.uniform(0, bgImage.rows);
		pt2.y = rng.uniform(0, bgImage.rows);
		//double cv::RNG::gaussian(double sigma)//參數:分佈的標準差。
		//Returns the next random number sampled from the Gaussian distribution.
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		line(bg, pt1, pt2, color, 1, 8);
		putText(bg, "Open CV", Point(bg.cols / 2 - 200, bg.rows / 2),
			FONT_HERSHEY_PLAIN, 2.0, Scalar(0, 255, 0), 3, LINE_8);
		if (waitKey(50) > 0) {
			break;
		}
		imshow("random line demo", bg);
	}
}

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