利用opencv-python绘制多边形框或(半透明)区域填充(可用于分割任务mask可视化)
本文主要就少opencv中两个函数polylines和fillPoly分别用于绘制多边形框或区域填充,并会会以常见用途分割任务mask(还是笔者的猪仔数据集^^)可视化举例示范。
cv2.polylines()
以下是摘自Ref的函数介绍,笔者将在下面结合例子解释其中的参数。
cv2.polylines() method is used to draw a polygon on any image.
Syntax: cv2.polylines(image, [pts], isClosed, color, thickness)>
Parameters:
image: It is the image on which circle is to be drawn.
pts: Array of polygonal curves.
npts: Array of polygon vertex counters.
ncontours: Number of curves.
isClosed: Flag indicating whether the drawn polylines are closed or not. If they are closed, the function draws a line from the last vertex of each curve to its first
vertex.
color: It is the color of polyline to be drawn. For BGR, we pass a tuple.
thickness: It is thickness of the polyline edges.
Return Value: It returns an image.
说几个关键的参数:第一个参数image自然就是要标记多边形框的原图,pts是多边形框的各个坐标点(这里整个pts参数要注意多加一个中括号,一会儿会结合例子说一下),isClosed的参数是是否要将多边形闭合,即最后一个坐标点是否要再连回到第一个坐标点,一会儿会实验给大家看一下差别,color是多边形框的颜色,大家选用自己喜欢的颜色就好,这里都采用笔者的生日 (98,9,11)。
我们先讲猪仔数据集中的一张图片及其标签读入:
import json
import cv2
import numpy as npwith open('labels/mask2bbox_labels_200/20190515142143.json', 'r') as obj:dict = json.load(obj)
img = cv2.imread('images/train_img/20190515142143.jpg')
原图是这样滴:
我们的目标就是根据人工mask标注将小猪仔们用多边形框(不是box矩形框)圈出来,或者用半透明的mask区域表示出来。
开始用一个for循环将所有多边形框读出并画出:
for label in dict['shape']:points = np.array(label['points'], dtype=np.int32)cv2.polylines(img, [points], True, (98, 9, 11), 3)cv2.imshow('rect', img)
cv2.waitKey(0)
cv2.imwrite('rect.jpg', img)
可以看到是我们想要的效果,小猪仔都被人工标注的坐标形成的多边形圈了起来。
这里要注意我们的pts参数要在外面多加一个中括号,笔者一开始也忽视了这个地方:
cv2.polylines(img, points, False, (98, 9, 11), 3)
会导致报错:
cv2.error: OpenCV(4.4.0) /tmp/pip-req-build-99ib2vsi/opencv/modules/imgproc/src/drawing.cpp:2427: error: (-215:Assertion failed) p.checkVector(2, CV_32S) >= 0 in function 'polylines'
我们一般拿到的某个多边形框的坐标点的形状是[n, 2],n是某一多边形框的点的个数,2是每个点的x,y坐标。但这里其实是要pts参数的形状为[1, n, 2],当然也可以用expand_dims将形状调整过来,但这属于舍近求远了。笔者一开始就以为不明晰这其中的状况绕了远路,所以在这里提醒一下大家。
总之把我们的形状为[n, 2]坐标点作为参数在传入函数时加个中括号[pts]即可。
如果isClosed=False参数会怎样呢:
for label in dict['shape']:points = np.array(label['points'], dtype=np.int32)cv2.polylines(img, [points], False, (98, 9, 11), 3)
结果:
可以看到确实每个多边形框都少了一段,即是没有闭合的结果,所以我们一般将isClosed设为True。
cv2.fillPoly()
for label in dict['shape']:points = np.array(label['points'], dtype=np.int32)cv2.fillPoly(img, [points], color=(98, 9, 11))
参数与polylines()类似,就不再赘述了,直接这样做得到的结果:
确实能将mask标注内的区域都做填充,但这显然不是我们要的效果,我们需要的是半透明的mask。
我们只要将mask先放到zero图上,再将mask和原图加和起来就OK了。
zeros = np.zeros((img.shape), dtype=np.uint8)
for label in dict['shape']:points = np.array(label['points'], dtype=np.int32)mask = cv2.fillPoly(zeros, [points], color=(98, 9, 11))mask_img = 0.9 * mask + img
结果:
这才是我们最终想要的mask可视化结果。
读者如有疑惑或异议,欢迎留言讨论。
Ref:
https://www.geeksforgeeks.org/
https://blog.csdn.net/weixin_41735859/article/details/103758249