前言
這是我《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);
}
}