本系列专栏写作方式
本系列专栏写作将采用首创的问答式写作形式,快速让你学习到 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 np
import 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
该问题是由于下述代码样例造成的。
# 画一条线,注意第二个坐标点元组中值的类型为 float
cv2.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()
运行效果如下图所示:
bd993f480b53e890a1481502b26cf2e4[1].png椭圆形绘制的函数原型如下:
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))
该代码运行之后,会形成如下图形,以上参数解释在图片后面。
8e7285f923e8ebace838da9ae64e343d[1].png- <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()
运行结果如下所示
e6b647c9183dce913c1ee33a0abf555d[1].png 7d24b37a07a81739d9f52343b129e92d[1].png按照函数简介,我们可以知道绘制整个椭圆是 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)
6b1d4f3ac09b0c6c7bc6554614fb7cb6[1].png
图像绘制文字 <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 fonts
enum 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> 字体大小了。
学了这个函数之后,我们可以操作视频文件,然后添加英文字体水印了,修改上篇博客对应位置,最终结果如下所示
28983c0d3b217e620425793724e2a568[1].png写在最后:网上的很多资料都会告诉你,这些图形函数没有返回值,一般给你的函数原型如下:
cv.Circle(img, center, radius, color, thickness=1, lineType=8, shift=0) → None
视频文件,然后添加英文字体水印了,修改上篇博客对应位置
写在最后:网上的很多资料都会告诉你,这些图形函数没有返回值,一般给你的函数原型如下:
cv.Circle(img, center, radius, color, thickness=1, lineType=8, shift=0) → None
注意,这个是错误的,3.0 以上版本,本文涉及的函数都是有返回值的,不再为空值,学习的时候,请进行实际测试。
网友评论