利用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