持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
本文是我之前在微信公众号上的一篇文章记录。原链接为:# OpenCV学习笔记:绘制几何图形
本篇文章,将学习如何在图像上使用OpenCV函数绘制几何图形,如画线、圆、矩形、椭圆等,另外还学习在图像中增加文本信息。主要学习函数line()、circle()、rectangle()、ellipse()、putText()等函数的使用。
直线
函数原型:
void 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:线型
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
void testDrawLine() {
std::string img_path = "car.png";
cv::Mat image = cv::imread(img_path, cv::IMREAD_COLOR);
cv::Point start(100, 50);
cv::Point end(400, 50);
cv::line(image, start, end, cv::Scalar(0, 255, 0), 5, cv::LINE_8);
cv::Point start1(100, 200);
cv::Point end1(400, 240);
cv::line(image, start1, end1, cv::Scalar(0, 0, 255), 10, cv::LINE_4);
cv::namedWindow("draw line", cv::WINDOW_AUTOSIZE);
cv::imshow("draw line", image);
cv::waitKey(0);
}
int main() {
testDrawLine();
return 0;
}
复制代码
圆形
函数原型:
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(红)
- thickness: 如果是正数,表示组成圆的线条的粗细程度。如果是负数,则绘制一个实心圆
- line_type: 线条的类型。默认是8
- shift: 圆心坐标点和半径值的小数点位数
void testDrawCircle() {
std::string img_path = "car.png";
cv::Mat image = cv::imread(img_path, cv::IMREAD_COLOR);
// unfilled circle - draws a circle.
cv::Point centerCircle1(250, 100);
int radiusCircle = 50;
cv::Scalar colorCircle1(0, 255, 0); // (B, G, R)
int thicknessCircle1 = 5;
cv::circle(image, centerCircle1, radiusCircle, colorCircle1,
thicknessCircle1);
// filled circle - draws a circle.
cv::Point centerCircle2(400, 100);
cv::Scalar colorCircle2(255, 0, 0); // (B, G, R)
// FILLED = -1
cv::circle(image, centerCircle2, radiusCircle, colorCircle2, cv::FILLED);
cv::namedWindow("draw circle", cv::WINDOW_AUTOSIZE);
cv::imshow("draw circle", image);
cv::waitKey(0);
}
复制代码
输出:
椭圆
函数原型:
void ellipse(Mat& img, Point center, Size axes, double angle, double startAngle,
double endAngle, const Scalar& color, int thickness = 1,
int lineType = 8, int shift = 0)
复制代码
- img:图像。
- center:椭圆圆心坐标。
- axes:主轴的长度:横轴长与纵轴长。
- angle:偏转的角度。
- start_angle:圆弧起始角的角度。
- end_angle:圆弧终结角的角度。
- color:线条的颜色。
- thickness:线条的粗细程度。
- line_type:线条的类型。
- shift:圆心坐标点和数轴的精度。
void testDrawEllipse() {
std::string img_path = "car.png";
cv::Mat image = cv::imread(img_path, cv::IMREAD_COLOR);
int thickness = 3;
double angle = 30; // 椭圆旋转角度
// 第三个参数Size中的两个参数分别是横轴长、纵轴长。
// 同理,thickness若是小于0,表示实心
cv::ellipse(image, cv::Point(100, 100), cv::Size(90, 60), angle, 0, 360,
cv::Scalar(255, 0, 0), thickness, cv::LINE_8);
double angle1 = 0; // 椭圆旋转角度
cv::ellipse(image, cv::Point(300, 100), cv::Size(90, 60), angle1, 0, 300,
cv::Scalar(0, 255, 0), cv::FILLED, cv::LINE_8);
cv::namedWindow("draw Ellipse", cv::WINDOW_AUTOSIZE);
cv::imshow("draw Ellipse", image);
cv::waitKey(0);
}
复制代码
输出:
矩形
函数原型:
// 根据两个对角顶点画矩形
void rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar &color,
int thickness = 1, int lineType = LINE_8, int shift = 0);
// 根据左上角点和长宽画矩形
void rectangle(InputOutputArray img, Rect rec, const Scalar &color,
int thickness = 1, int lineType = LINE_8, int shift = 0);
复制代码
- img:图像。
- rec:表征矩形的位置和长宽, (pt1 和 pt2是矩形对角的两个点)。
- color:线条颜色 (RGB) 或亮度(灰度图像 )(grayscale image)。
- thickness:组成矩形的线条的粗细程度。取负值时(如CV_FILLED)函数绘制填充了色彩的矩形。
- line_type:线条的类型。见cvLine的描述
- shift:坐标点的小数点位数。
void testDrawRectangle() {
std::string img_path = "car.png";
cv::Mat image = cv::imread(img_path, cv::IMREAD_COLOR);
// unfilled
cv::Point p1(50, 50), p2(200, 100);
cv::Scalar colorRectangle1(0, 255, 0);
cv::rectangle(image, p1, p2, colorRectangle1, 3);
// filled
cv::Point p3(250, 50), p4(400, 100);
cv::Scalar colorRectangle2(255, 0, 0);
cv::rectangle(image, p3, p4, colorRectangle2, cv::FILLED);
// draw with Rect
cv::Rect r(100, 150, 300, 50);
cv::Scalar colorRectangle3(0, 255, 0);
cv::rectangle(image, r, colorRectangle3, cv::FILLED);
cv::namedWindow("draw rectangle", cv::WINDOW_AUTOSIZE);
cv::imshow("draw rectangle", image);
cv::waitKey(0);
}
复制代码
输出:
多边形
填充多边形
函数原型:
void fillPoly(Mat& img, const Point** pts, const int* npts, int ncontours,
const Scalar& color, int lineType = 8, int shift = 0,
Point offset = Point());
复制代码
- img:在img表示的图像上绘制
- pts:是指向多边形数组的指针,必须是const修饰的
- npts:是多边形顶点个数的数组名
- ncontours:是绘制多边形的个数
void testDrawFillpoly() {
std::string img_path = "car.png";
cv::Mat image = cv::imread(img_path, cv::IMREAD_COLOR);
// 创建一个二维数组,每一行存储一个多边形的点,总共存储了两个多边形的点
cv::Point p[2][5] = {
{cv::Point(50, 50), cv::Point(150, 50), cv::Point(180, 90),
cv::Point(10, 190), cv::Point(200, 267)},
{cv::Point(360, 60), cv::Point(420, 70), cv::Point(480, 120),
cv::Point(370, 200), cv::Point(300, 90)}};
// 这个指针数组存储每一个多边形的定点所在的一维数组的地址
const cv::Point *pp[] = {p[0], p[1]};
// 这个数组存储每一个多边形的定点个数
int n[] = {5, 5};
// 绘制两个个填充多边形
cv::fillPoly(image, pp, n, 2, cv::Scalar(0, 255, 0));
cv::namedWindow("draw fillPoly", cv::WINDOW_AUTOSIZE);
cv::imshow("draw fillPoly", image);
cv::waitKey(0);
}
复制代码
输出:
非填充多边形
函数原型:
void polylines(Mat& img, const Point** pts, const int* npts, int ncontours,
bool isClosed, const Scalar& color, int thickness = 1,
int lineType = 8, int shift = 0);
复制代码
- img:在img表示的图像上绘制
- pts:是指向多边形数组的指针,必须是const修饰的
- npts:是多边形顶点个数的数组名
- ncontours:绘制多边形的个数
- isClosed:表示多边形是否闭合,1表示闭合,0表示不闭合
- color:是填充的颜色
void testDrawPolylines() {
std::string img_path = "car.png";
cv::Mat image = cv::imread(img_path, cv::IMREAD_COLOR);
// 创建一个二维数组,每一行存储一个多边形的点,总共存储了两个多边形的点
cv::Point p[2][5] = {
{cv::Point(50, 50), cv::Point(150, 50), cv::Point(180, 90),
cv::Point(10, 190), cv::Point(200, 267)},
{cv::Point(360, 60), cv::Point(420, 70), cv::Point(480, 120),
cv::Point(370, 200), cv::Point(300, 90)}};
// 这个指针数组存储每一个多边形的定点所在的一维数组的地址
const cv::Point *pp[] = {p[0], p[1]};
// 这个数组存储每一个多边形的定点个数
int n[] = {5, 5};
// 绘制两个个填充多边形
cv::polylines(image, pp, n, 2, false, cv::Scalar(0, 255, 0), 3);
cv::namedWindow("draw polylines", cv::WINDOW_AUTOSIZE);
cv::imshow("draw polylines", image);
cv::waitKey(0);
}
复制代码
非闭合输出:
闭合输出:
文字
函数原型:
void cv::putText(cv::Mat& img, // 待绘制的图像
const string& text, // 待绘制的文字
cv::Point origin, // 文本框的左下角
int fontFace, // 字体 (如cv::FONT_HERSHEY_PLAIN)
double fontScale, // 尺寸因子,值越大文字越大
cv::Scalar color, // 线条的颜色(RGB)
int thickness = 1, // 线条宽度
int lineType = 8, // 线型(4邻域或8邻域,默认8邻域)
bool bottomLeftOrigin = false // true='origin at lower left'
);
复制代码
这个函数是OpenCV的一个主要文字绘制方法,它可以简单地在图像上绘制一些文字,由text指定地文字将在以左上角为原点地文字框中以color指定地颜色绘制出来,除非bottomLeftOrigin标志设置为真,这种情况以左下角为原点,使用的字体由fontFace参数决定,支持的字体如下表格所示:
标识符 | 描述 |
---|---|
FONT_HERSHEY_SIMPLEX = 0 | !< normal size sans-serif font 普通大小无衬线字体 |
FONT_HERSHEY_PLAIN = 1 | !< small size sans-serif font 小号无衬线字体 |
FONT_HERSHEY_DUPLEX = 2 | !< normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX) 普通大小无衬线字体 |
FONT_HERSHEY_COMPLEX = 3 | !< normal size serif font 普通大小无衬线字体比 FONT_HERSHEY_DUPLEX 更复杂 |
FONT_HERSHEY_TRIPLEX = 4 | !< normal size serif font (more complex than FONT_HERSHEY_COMPLEX 普通大小无衬字体,比 FONT_HERSHEY_COMPLEX 更复杂 |
FONT_HERSHEY_COMPLEX_SMALL = 5 | !< smaller version of FONT_HERSHEY_COMPLEX-小号版本的 FONT_HERSHEY_COMPLEX |
FONT_HERSHEY_SCRIPT_SIMPLEX = 6 | !< hand-writing style font 手写字体 |
FONT_HERSHEY_SCRIPT_COMPLEX = 7 | !< more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX |
FONT_ITALIC = 16 | !< flag for italic font |
另外,在实际绘制文字之前,还可以使用cv::getTextSize()接口先获取待绘制文本框的大小,以方便放置文本框。具体调用形式如下:
cv::Size cv::getTextSize(const string& text, cv::Point origin, int fontFace,
double fontScale, int thickness, int* baseLine);
复制代码
- const string& text: 输入的文本文字
- int fontFace: 文字字体类型
- double fontScale: 字体缩放系数
- int thickness: 字体笔画线宽
- CV_OUT int* baseLine: 文字最底部y坐标
其中前四个参数都好理解:text为文本,fontFace为文本的字体类型,fontScale为文本大小的倍数(以字体库中的大小为基准而放大的倍数),thickness为文本的粗细。最后一个参数baseLine是指距离文本最低点对应的y坐标,乍听起来很乱,具体如图所示
我们对于四线本应该很熟悉,在小学英语时会经常用这种本子写英文,借助其中的三条线可以帮助我们了解该函数。函数返回的Size中的height其实是图中两个红线的距离,而baseLine即下面红线与蓝线直接的距离。
程序如下:
void testDrawText() {
std::string img_path = "mm.jpeg";
cv::Mat src = cv::imread(img_path, cv::IMREAD_COLOR);
cv::Mat image;
cv::resize(src, image, cv::Size(src.cols / 2, src.rows / 2));
const std::string text = "Hello World!";
cv::Scalar color(0, 255, 0);
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
cv::putText(image, text, cv::Point2i(10, 50), fontFace, 2, color, 1, 8,
false);
fontFace = cv::FONT_HERSHEY_PLAIN;
cv::putText(image, text, cv::Point2i(10, 100), fontFace, 2, color, 1, 8,
false);
fontFace = cv::FONT_HERSHEY_DUPLEX;
cv::putText(image, text, cv::Point2i(10, 150), fontFace, 2, color, 1, 8,
false);
fontFace = cv::FONT_HERSHEY_COMPLEX;
cv::putText(image, text, cv::Point2i(10, 200), fontFace, 2, color, 1, 8,
false);
fontFace = cv::FONT_HERSHEY_TRIPLEX;
// 左下角为原点,字体倒立显示
cv::putText(image, text, cv::Point2i(10, 250), fontFace, 2, color, 1, 8,
true);
cv::Scalar color2(255, 0, 0);
fontFace = cv::FONT_HERSHEY_COMPLEX_SMALL;
cv::putText(image, text, cv::Point2i(10, 500), fontFace, 2, color2, 1, 8,
false);
fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;
cv::putText(image, text, cv::Point2i(10, 560), fontFace, 2, color2, 1, 8,
false);
fontFace = cv::FONT_HERSHEY_SCRIPT_COMPLEX;
cv::putText(image, text, cv::Point2i(10, 620), fontFace, 2, color2, 1, 8,
false);
fontFace = cv::FONT_ITALIC | cv::FONT_HERSHEY_PLAIN;
// 左下角为原点,字体倒立显示
cv::putText(image, text, cv::Point2i(10, 680), fontFace, 2, color2, 1, 8,
true);
fontFace = cv::FONT_HERSHEY_SIMPLEX; // 字体样式
double fontScale = 2.0; // 字体大小
int thickness = 3;//字体线条粗细
int baseline = 0;
cv::Size textSize = cv::getTextSize(text, fontFace, fontScale, thickness,
&baseline); // 居中显示
cv::Scalar color3(0, 0, 255);
cv::Point2i orgin(image.cols / 2 - textSize.width / 2,
image.rows / 2 - textSize.height / 2);
cv::putText(image, text, orgin, fontFace, fontScale, color3, thickness, 8,
false);
cv::namedWindow("draw text", cv::WINDOW_AUTOSIZE);
cv::imshow("draw text", image);
cv::waitKey(0);
}
复制代码
输出: