写点什么

番外 3. Python OpenCV 中如何绘制各种图形?

发布于: 4 小时前

本系列专栏写作方式

本系列专栏写作将采用首创的问答式写作形式,快速让你学习到 OpenCV 的初级、中级、高级知识。

3. Python OpenCV 中如何绘制各种图形?

本篇博客主要分享一下在 Python OpenCV 中绘制不同的几何图形。


这里的几何图形,主要涉及的是平面图形,例如直线,圆形,矩形,椭圆形,当然在图像上绘制文字也属于图形范畴


涉及到的具体函数包括 <kbd>cv2.line</kbd>、 <kbd>cv2.circle</kbd>、 <kbd>cv2.rectangle</kbd>、 <kbd>cv2.ellipse</kbd>、 <kbd>cv2.putText</kbd>


接下来我们将针对这些函数进行说明,重点会为你解释这些函数使用过程中存在的各种问题


上述函数虽然数量较多,但是函数内部的参数大致一样,都包括如下内容,可以提前记忆一下


  • <kbd>image</kbd>:要操作的图像,就是你要在上面绘制图形的原图;

  • <kbd>color</kbd>:图形的颜色,以 BGR 为例,参数的数据类型是一个元组,所以参数格式为 (0,0,255)红色,如果是灰度图,只需要传入一个灰度值即可;

  • <kbd>thickness</kbd>:图形的线条粗细,默认值是 1,如果希望图形内部全部填充,即闭合图形,需要设置为 -1

  • <kbd>linetype</kbd>:线条的类型,8 连通线,4 连通线,抗锯齿,默认是 8 连通线,如果开启抗锯齿,效率有影响,但是线条会变的平滑,一般情况下,省略即可,非必填项。

绘制直线 <kbd>cv2.line</kbd>

函数的基本使用和效果可以参考下述代码:


import numpy as npimport cv2
# 创建一个 400 * 400 的 3 通道黑色图片img = np.zeros((400, 400, 3), np.uint8)
# 画一条线cv2.line(img, (0, 0), (250, 250), (0, 0, 255), 3)

cv2.namedWindow("img", cv2.WINDOW_AUTOSIZE)cv2.imshow("img", img)cv2.waitKey()
复制代码


代码运行结果是从坐标(0,0)点到 (250,250)点绘制一条红色的、线条粗细为 3 的直线。



上述代码基本使用是没有问题的,也很容易看懂,常见的问题如下,在代码执行的时候,会因为坐标点元组的数据类型报错,错误提示如下:


TypeError: integer argument expected, got float
复制代码


该问题是由于下述代码样例造成的。


# 画一条线,注意第二个坐标点元组中值的类型为 floatcv2.line(img, (0, 0), (250.0, 250.0), (0, 0, 255), 3)
复制代码


既然是类型错误,直接修改数据类型即可。


第二点需要注意的问题是,该类型函数在原图像上进行图形绘制,不需要额外通过变量接收返回值,所以下述是正确的,但是很少出现。


# 画一条线new_img = cv2.line(img, (0, 0), (250, 250), (0, 0, 255), 3)
复制代码


备注一下该函数的原型:


line(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) -> img
复制代码

绘制矩形 <kbd>cv2.rectangle</kbd>

矩形绘制只需要知道待绘制矩形左上角顶点坐标和右下角顶点坐标即可实现。


例如下述代码:


cv2.rectangle(img, (100, 100), (200, 200), (0, 255, 0), 3, cv2.LINE_4)
复制代码


函数的基本使用依旧不在本文范围内,你需要注意一点,两个坐标点严格说并没有先后顺序。


只要是矩形对角线上的两个点就可以,核心是依据元组的值进行运算的,例如下面两行代码得到的矩形一致


cv2.rectangle(img, (100, 100), (200, 200), (0, 255, 0), 3, cv2.LINE_4)cv2.rectangle(img, (200, 200), (100, 100), (0, 255, 0), 3, cv2.LINE_4)
复制代码


绘制矩形也会出现下述代码异常,即数据类型问题,例如:


TypeError: argument for rectangle() given by name ('thickness') and position (4)
复制代码


该异常如果出现,原因是 <kbd>thickness</kbd>参数提供了非整数类型的值,例如 thickness = 4.0


其它更多内容,可以参看文档

绘制圆形与椭圆形 <kbd>cv2.circle</kbd> 和 <kbd>cv2.ellipse</kbd>

在 OpenCV 中绘制圆形与椭圆形方法基本一致,使用 help 函数可以得到相关用法。


circle(img, center, radius, color[, thickness[, lineType[, shift]]]) -> img
复制代码


该函数与上面两个的差异是多出 <kbd>center</kbd> 与 <kbd>radius</kbd> 参数,含义分别是中心点与半径。


测试案例如下:


cv2.circle(img, (200, 200), 100, (255, 0, 0))
cv2.namedWindow("img", cv2.WINDOW_AUTOSIZE)cv2.imshow("img", img)cv2.waitKey()
复制代码


运行效果如下图所示:



椭圆形绘制的函数原型如下:


ellipse(img, center, axes, angle, start_angle, end_angle, color, thickness=1, lineType=8, shift=0)
复制代码


新增参数是 <kbd>center</kbd>,<kbd>axes</kbd>,<kbd>angle</kbd>,<kbd>start_angle</kbd>,<kbd>end_angle</kbd>。


先测试一下相关函数,然后在解决常见的问题。


cv2.ellipse(img, (200, 200), (100, 50), 0, 0, 180, (0, 0, 255))
复制代码


该代码运行之后,会形成如下图形,以上参数解释在图片后面。



  • <kbd>center</kbd>:椭圆的中心点;

  • <kbd>axes</kbd>:椭圆长轴与短轴,使用元组表示,例如(100,50)

  • <kbd>angle</kbd>:绘制的椭圆旋转的角度,你可以尝试 0 度,90 度,该角度为顺时针旋转;

  • <kbd>start_angle</kbd>,<kbd>end_angle</kbd>:椭圆弧起始角度与结束角度。


测试 <kbd>angle</kbd> 参数,可以使用下面代码查阅。


cv2.ellipse(img, (200, 200), (100, 50), 0, 0, 180, (0, 255, 0))cv2.ellipse(img, (200, 200), (100, 50), 60, 0, 180, (0, 0, 255))cv2.namedWindow("img", cv2.WINDOW_AUTOSIZE)cv2.imshow("img", img)cv2.waitKey()
复制代码


运行结果如下所示




按照函数简介,我们可以知道绘制整个椭圆是 0,360,绘制下半椭圆就是 0,180。


在绘制椭圆形的时候,如果想要获得一个实心的椭圆,按照如下代码进行设置,当然下面的代码绘制的依旧是一个半椭圆,你可以进行修改,实现循环绘制。


cv2.ellipse(img, (200, 200), (100, 50), 0, 0, 45, (0, 255, 0),-1)cv2.ellipse(img, (200, 200), (100, 50), 0, 45, 90, (0, 0, 255),-1)cv2.ellipse(img, (200, 200), (100, 50), 0, 90, 135, (255, 0, 0),-1)cv2.ellipse(img, (200, 200), (100, 50), 0, 135, 180, (255, 255, 0),-1)
复制代码


图像绘制文字 <kbd>cv2.putText</kbd>

在图像上绘制问题最大的问题是中文问题,并且该问题无法使用 OPenCV 自行解决,网上提供的策略也是采用 PIL 绘制文字,大家可以直接参考即可。


核心原因是因为 <kbd>cv2.putText</kbd> 不支持完整 ASCII 字符,只支持一部分,更不用说 unicode 字符了,所以只能用 PIL 进行绘制。


该函数的原型如下:


putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]]) -> img
复制代码


其中差异化的参数分别是: <kbd>text</kbd> 、 <kbd>org</kbd> 、 <kbd>fontFace</kbd> 、 <kbd>fontScale</kbd>


先实现案例,在对其进行说明:


cv2.putText(img, 'OpenCV', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
复制代码


关于 fontFace 参数,我直接查阅了 OPenCV 的 C++源码,提供给你作为参考:


//! Only a subset of Hershey fontsenum HersheyFonts {    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_TRIPLEX        = 4, //!< normal size serif font (more complex than FONT_HERSHEY_COMPLEX)    FONT_HERSHEY_COMPLEX_SMALL  = 5, //!< smaller version of 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};
复制代码


其它的参数,最重要的就是 <kbd>fontScale</kbd> 字体大小了。


学了这个函数之后,我们可以操作视频文件,然后添加英文字体水印了,修改上篇博客对应位置,最终结果如下所示



写在最后:网上的很多资料都会告诉你,这些图形函数没有返回值,一般给你的函数原型如下:


cv.Circle(img, center, radius, color, thickness=1, lineType=8, shift=0) → None
复制代码


视频文件,然后添加英文字体水印了,修改上篇博客对应位置,最终结果如下所示


[外链图片转存中...(img-PYfSwjTL-1628339598386)]


写在最后:网上的很多资料都会告诉你,这些图形函数没有返回值,一般给你的函数原型如下:


cv.Circle(img, center, radius, color, thickness=1, lineType=8, shift=0) → None
复制代码


注意,这个是错误的,3.0 以上版本,本文涉及的函数都是有返回值的,不再为空值,学习的时候,请进行实际测试。


发布于: 4 小时前阅读数: 4
用户头像

爬虫 100 例作者,蓝桥签约作者,博客专家 2021.02.06 加入

6 年产品经理+教学经验,3 年互联网项目管理经验; 互联网资深爱好者; 沉迷各种技术无法自拔,导致年龄被困在 25 岁; CSDN 爬虫 100 例作者。 个人公众号“梦想橡皮擦”。

评论

发布
暂无评论
番外3. Python OpenCV 中如何绘制各种图形?