1. 点的表示
在 OpenCV 中,点分为 2D 平面中的点和 3D 平面中的点,区别是 3D 平面中的点多了一个 z坐标。我们首先介绍 2D 平面中的点,坐标为整数的点可以用类 cv::Point 来表示,它实际上由模板类 Point_重新定义而得:
c
typedef Point_<int> Point2i;
typedef Point2i Point;
Point 表示由图像坐标 x 和 y 指定的 2D 点,坐标 x 和 y 都是整数。比如这样定义一个坐标为(10,8)的点:
cpp
Point pt; //先定义一个 Point 变量
pt.x = 10;//对 x 坐标赋值
pt.y = 8; //对 y 坐标赋值
或者定义时直接赋值:
c
Point pt = Point(10, 8);
点和点之间还可以进行加减运算和逻辑判断,比如:
cpp
pt1 = pt2 + pt3;
pt1 = pt2 - pt3;
pt1 += pt2;
pt1 -= pt2;
if(pt1 == pt2) ...
if(pt1 != pt2) ...
除此之外,OpenCV 还支持坐标为 float 和 double 类型的点,分别用 Point2f 和 Point2d 来表示,它们也是由模板类 Point_重新定义而得的:
cpp
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
比如我们定义两个坐标为浮点数类型的点:
cpp
Point2f a(0.3f, 0.f), b(0.f, 0.4f);
Point pt = (a + b)*10.f;
cout << pt.x << ", " << pt.y << endl;
3D 空间中的点也有类似定义,比如 x、y、z 均是整数的点,可以用类 cv::Point3i 来定义,它是由模板类 Point3_重新定义的:
cpp
typedef Point3_<int> cv::Point3i;
同样,也有坐标为浮点数的 3D 空间的点,用 Point3f 来定义,比如:
cpp
//1.默认构造函数
Point3f p2;//数据为[0,0,0]
//2.赋值构造函数
Point3f p3(1, 2,3);//数据为[1,2,3]
Point3f p4(p3);//数据为[1,2,3]
cout << p3 << "\n" << p4 << endl;
//3.带参数的构造函数
Point3f p6(1.1, 1.2, 1.3);//数据为[1.1,1.2,1.3]
2. 画矩形
全局函数 rectangle 通过对角线上的两个顶点绘制矩形,函数声明如下:
cpp
void cv::rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar
& color, int thickness = 1, int lineType = LINE_8, int shift = 0 );
void cv::rectangle (InputOutputArray img, Rect rec, const Scalar & color,
int thickness=1, int lineType = LINE_8, int shift = 0)
其中参数 img 表示矩形所在的图像;pt1 表示矩形的一个顶点;pt2 表示矩形对角线上的另一个顶点;color 表示线条颜色(RGB)或亮度(灰度图像);thickness 表示组成矩形的线条的粗细程度,取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形;line_type 表示线条的类型;shift表示坐标点的小数点位数。
下面我们来看一个例子,绘制一个医院的红十字。
【例 】绘制医院的红十字
cpp
#include <iostream>
#include <opencv2\imgproc\types_c.h> //for CV_RGB2GRAY
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace std;
using namespace cv;
int main()
{
Mat image3 = Mat::zeros(200, 320, CV_8UC3);//生成全零矩阵
Rect rec1 = Rect(10, 30, 60, 20); //定义矩形对象,左上角坐标是(10,30),宽度是 60,高度是 20
Rect rec2 = Rect(30, 10, 20, 60); //定义矩形对象,左上角坐标是(30,10),宽度60,高度是 20
rectangle(image3, rec1, Scalar(0, 0, 255), -1, 8, 0);//在 image3 上画横矩形
rectangle(image3, rec2, Scalar(0, 0, 255), -1, 8, 0);//在 image3 上画竖矩形
rectangle(image3, Point(10, 30), Point(70, 50), Scalar(0, 255, 255),2, 8, 0);//黄色矩形镶边
rectangle(image3, Point(30, 10), Point(50, 70), Scalar(0, 255, 255), 2,8, 0);//黄色矩形镶边
rectangle(image3, Point(30, 30), Point(50, 50), Scalar(0, 0, 255), 3, 8);//红色正方形覆盖(中央)
imshow("红十字", image3);
waitKey();
return 0;
}

在代码中,我们首先创建了一个 Mat 矩阵对象 image3,并且是全黑的。然后定义了两个矩形对象 rec1 和 rec2,分别表示红十字的横矩形和竖矩形,然后调用画矩形函数 rectangle 进行绘制,这两个矩形使用颜色填充,因为倒数第三个参数是-1。接着我们又画了两个矩形(这次没有填充,是画的矩形边),黄色镶边。
3.画圆
全局函数 circle 用来绘制或填充一个给定圆心和半径的圆,函数声明如下:
cpp
void cv::circle (InputOutputArray img, Point center,int radius,const
Scalar & color, int thickness = 1, int lineType = LINE_8, int shift = 0);
其中参数 img 表示输入的图像(圆画在这个图像上);center 表示圆心坐标;radius 表示圆的半径;color 表示圆的颜色,规则根据 B(蓝)G(绿)R(红),例如蓝色为Scalar(255,0,0);thickness如果是正数,就表示组成圆的线条的粗细程度,否则表示圆是否被填充;lineType 表示线条的类型;shift 表示圆心坐标点和半径值的小数点位数。
下面我们来看一个实例,绘制奥迪车标。我们知道奥迪车的车标是 4 个圆圈,所以使用 circle函数来实现。
【例 】绘制奥迪车标
cpp
#include <iostream>
#include <opencv2\imgproc\types_c.h> //for CV_RGB2GRAY
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat image2 = Mat::zeros(500, 850, CV_8UC3);//生成一个 500x850 的 Mat 矩阵
circle(image2, Point(447, 63), 63, (0, 0, 255), -1); //演示画一个实心圆
//开始绘制奥迪车标
//绘制第一个圆,半径为 100,圆心为(200,300),颜色为紫色
circle(image2, Point(200, 300), 100, Scalar(225, 0, 225), 7, 8);
//绘制第一个圆,半径为 100,圆心为(350,300),线宽为 7
circle(image2, Point(350, 300), 100, Scalar(225, 0, 225), 7, 8);
//绘制第一个圆,半径为 100,圆心为(500,300)
circle(image2, Point(500, 300), 100, Scalar(225, 0, 225), 7, 8);
//绘制第一个圆,半径为 100,圆心为(650,300)
circle(image2, Point(650, 300), 100, Scalar(225, 0, 225), 7, 8);
imshow("奥迪车标", image2);
waitKey();
return 0;
}

4.画椭圆
函数 ellipse 用来绘制或者填充一个简单的椭圆弧或椭圆扇形,该函数声明如下:
cpp
void cv::ellipse ( InputOutputArray img, Point center, Size axes, double
angle,double startAngle, double endAngle, const Scalar & color, int thickness =
1, int lineType = LINE_8, int shift = 0 );
其中参数 img 表示输入的图像(圆画在这个图像上);center 表示椭圆圆心坐标;axes 表示轴的长度;angle 表示偏转的角度;startAngle 表示圆弧起始角的角度;endAngle 表示圆弧终结角的角度;color 表示线条的颜色;thickness 表示线条的粗细程度;line_type 表示线条的类型;shift 表示圆心坐标点和数轴的精度。
下面我们来看一个实例,绘制丰田车标。我们知道丰田车标是由 3 个椭圆组成的,两个横着,一个竖着。
【例】绘制丰田车标
cpp
#include <iostream>
#include <opencv2\imgproc\types_c.h> //for CV_RGB2GRAY
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
//绘制丰田车标
Mat image1 = Mat::zeros(900, 900, CV_8UC3);//900×900 的窗口
//绘制第一个椭圆,大椭圆,颜色为红色
ellipse(image1, Point(450, 450), Size(400, 250), 0, 0, 360, Scalar(0, 0,225), 5, 8);
//绘制第二个椭圆,竖椭圆
ellipse(image1, Point(450, 450), Size(250, 110), 90, 0, 360, Scalar(0, 0,225), 5, 8);
//绘制第三个椭圆,小椭圆(横)
ellipse(image1, Point(450, 320), Size(280, 120), 0, 0, 360, Scalar(0, 0,225), 5, 8);
imshow("丰田", image1);
waitKey();
return 0;
}

【例】画圆和画椭圆的联合作战
cpp
#include <iostream>
#include <opencv2\imgproc\types_c.h> //for CV_RGB2GRAY
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace std;
using namespace cv;
#define WINDOW_NAME1 "绘制图 1"
#define WINDOW_NAME2 "绘制图 2"
#define WINDOW_WIDTH 600 //定义窗口大小
void DrawEllipse(Mat img, double angle);
void DrawFi1ledCirc1e(Mat img, Point center);
int main()
{
Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
//绘制椭圆
DrawEllipse(atomImage, 90);
DrawEllipse(atomImage, 0);
DrawEllipse(atomImage, 45);
DrawEllipse(atomImage, -45);
//绘制圆心
DrawFi1ledCirc1e(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
imshow(WINDOW_NAME1, atomImage);
waitKey(0);
return 0;
}
void DrawEllipse(Mat img, double angle) {
int thickness = 2;
int lineType = 8;
ellipse(img, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2), Size(WINDOW_WIDTH
/ 4, WINDOW_WIDTH / 16), angle, 0, 360, Scalar(255, 129, 0), thickness, lineType);
}
void DrawFi1ledCirc1e(Mat img, Point center) {
int thickness = -1;
int lineType = 8;
circle(img, center, WINDOW_WIDTH / 32, Scalar(0, 0, 255), thickness, lineType);
}

5. 画线段
在 OpenCV 中,函数 Line 用来实现画线段,函数声明如下:
cpp
void cv::line ( InputOutputArray img, Point pt1, Point pt2,const Scalar &
color, int thickness = 1, int lineType = LINE_8, int shift = 0);
其中参数 img 表示输入的图像(圆画在这个图像上);pt1 表示线段的起始点;pt2 表示线段的结束点;color 表示线段颜色;thickness 表示线段粗细;lineType 表示线段类型;shift 表示点坐标中的小数位数。
6. 填充多边形
在 OpenCV 中,函数 fillPoly 用来填充多边形,函数声明如下:
cpp
void cv::fillPoly (Mat &img, const Point **pts, const int *npts, int ncontours,
const Scalar &color, int lineType=LINE_8, int shift=0, Point offset=Point());
其中参数 img 表示输入的图像(圆画在这个图像上);pts 表示多边形点集;color 表示多变形颜色;lineType 表示线段类型;shift 表示点坐标中的小数位数;offset 表示等高线所有点的偏移。
现在我们来看一个综合的例子,例子中实现画线段、画椭圆、填充多边形等。
【例】画图综合
cpp
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#define w 400
using namespace cv;
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
void MyEllipse(Mat img, double angle);
void MyFilledCircle(Mat img, Point center);
void MyPolygon(Mat img);
void MyLine(Mat img, Point start, Point end);
int main(void)
{
char atom_window[] = "Drawing 1: Atom";
char rook_window[] = "Drawing 2: Rook";
Mat atom_image = Mat::zeros(w, w, CV_8UC3);
Mat rook_image = Mat::zeros(w, w, CV_8UC3);
MyEllipse(atom_image, 90);
MyEllipse(atom_image, 0);
MyEllipse(atom_image, 45);
MyEllipse(atom_image, -45);
MyFilledCircle(atom_image, Point(w / 2, w / 2));
MyPolygon(rook_image);
rectangle(rook_image, Point(0, 7 * w / 8), Point(w, w), Scalar(0, 255,255), FILLED, LINE_8);
MyLine(rook_image, Point(0, 15 * w / 16), Point(w, 15 * w / 16));
MyLine(rook_image, Point(w / 4, 7 * w / 8), Point(w / 4, w));
MyLine(rook_image, Point(w / 2, 7 * w / 8), Point(w / 2, w));
MyLine(rook_image, Point(3 * w / 4, 7 * w / 8), Point(3 * w / 4, w));
imshow(atom_window, atom_image);
moveWindow(atom_window, 0, 200);
imshow(rook_window, rook_image);
moveWindow(rook_window, w, 200);
waitKey(0);
return(0);
}
void MyEllipse(Mat img, double angle)
{
int thickness = 2;
int lineType = 8;
ellipse(img, Point(w / 2, w / 2), Size(w / 4, w / 16), angle, 0, 360, Scalar(255,
0, 0), thickness, lineType);
}
void MyFilledCircle(Mat img, Point center)
{
circle(img, center, w / 32, Scalar(0, 0, 255), FILLED, LINE_8);
}
void MyPolygon(Mat img)
{
int lineType = LINE_8;
Point rook_points[1][20];
rook_points[0][0] = Point(w / 4, 7 * w / 8);
rook_points[0][1] = Point(3 * w / 4, 7 * w / 8);
rook_points[0][2] = Point(3 * w / 4, 13 * w / 16);
rook_points[0][3] = Point(11 * w / 16, 13 * w / 16);
rook_points[0][4] = Point(19 * w / 32, 3 * w / 8);
rook_points[0][5] = Point(3 * w / 4, 3 * w / 8);
rook_points[0][6] = Point(3 * w / 4, w / 8);
rook_points[0][7] = Point(26 * w / 40, w / 8);
rook_points[0][8] = Point(26 * w / 40, w / 4);
rook_points[0][9] = Point(22 * w / 40, w / 4);
rook_points[0][10] = Point(22 * w / 40, w / 8);
rook_points[0][11] = Point(18 * w / 40, w / 8);
rook_points[0][12] = Point(18 * w / 40, w / 4);
rook_points[0][13] = Point(14 * w / 40, w / 4);
rook_points[0][14] = Point(14 * w / 40, w / 8);
rook_points[0][15] = Point(w / 4, w / 8);
rook_points[0][16] = Point(w / 4, 3 * w / 8);
rook_points[0][17] = Point(13 * w / 32, 3 * w / 8);
rook_points[0][18] = Point(5 * w / 16, 13 * w / 16);
rook_points[0][19] = Point(w / 4, 13 * w / 16);
const Point* ppt[1] = { rook_points[0] };
int npt[] = { 20 };
fillPoly(img, ppt, npt, 1, Scalar(255, 255, 255), lineType);
}
void MyLine(Mat img, Point start, Point end)
{
int thickness = 2;
int lineType = LINE_8;
line(img, start, end, Scalar(0, 0, 0), thickness, lineType);
}
