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