OpenCV计算机视觉开发入门与实践<十四>:基础几何图形绘制

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);
}
相关推荐
Elastic 中国社区官方博客1 小时前
跳过 mapping 爆炸:ES|QL 无需动态 mapping 即可查询无 schema JSON key
大数据·人工智能·sql·elasticsearch·搜索引擎·json·全文检索
IT_陈寒1 小时前
SpringBoot自动配置的坑,把我整不会了
前端·人工智能·后端
阿沐沐,1 小时前
PowerShell 里改了 config.toml,Codex CLI 仍用旧值:先分清该改哪一层
java·服务器·数据库·人工智能·ai·ai编程
YOLO数据集集合1 小时前
火情监测新方案:无人机红外-可见光多模态火点烟雾数据集(含YOLOv11实战)
人工智能·yolo·计算机外设·无人机·无人机数据集·可见光多模态
Sky1987star1 小时前
华玺云科最新产品体系更新:从华玺人人播到 Growth Agent OS、Sales Agent OS,产品正在从能力框架走向可落地系统
人工智能
学无止步_穷其一生1 小时前
把 DeepSeek Harness 智能体嵌进 IntelliJ IDEA —— 类 Qoder 的 AI 编程插件
人工智能·intellij-idea·idea插件·deepseek
PhotonixBay1 小时前
从粗糙度到三维形貌:激光共聚焦显微镜实现微米级表面分析
图像处理·人工智能·测试工具·算法
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践<十六>:图像边框和图像轮廓
c++·人工智能·opencv·计算机视觉