La segmentación de la anotación del conjunto de datos de coco no es una máscara binaria, sino un formato de polígono,
consulte una anotación.
{
"segmentation": [[510.66,423.01,511.72,420.03,510.45......]], #两两组成(x,y)坐标,polygon格式
"area": 702.1057499999998, #面积
"iscrowd": 0, #是不是一群物体,为0是seg是polygon格式,否则是RLE格式
"image_id": 289343, #对应的image id
"bbox": [473.07,395.93,38.65,28.67], #(x,y,w,h)
"category_id": 18, #分类label
"id": 1768 #当前annotation的id,每一个图像有不止一个对象,所以要对每一个对象编号(每个对象的ID是唯一的)
},
La segmentación es en realidad un punto de contorno de una máscara binaria.
Si desea convertir la máscara binaria a este formato, debe extraer el contorno.
1. Formato de polígono
Encontré una imagen de una máscara binaria en Internet.
extraer su contorno
mask_img = cv2.imread("mask.png",cv2.IMREAD_GRAYSCALE)
contours, _ = cv2.findContours(mask_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
polygons = []
for object in contours:
coords = []
for point in object:
coords.append(int(point[0][0]))
coords.append(int(point[0][1]))
polygons.append(coords)
print(polygons)
#[[131, 48, 130, 49, 129, 50, 128, 51, ...]]
¿Se puede usar este polígono? ¿Es lo mismo que el formato de anotación de coco?
Vamos a verificarlo.
Presente la biblioteca coco que viene con python, use annToMask
las funciones que contiene, convierta el polígono en una máscara
y luego muéstrelo para ver si es igual a la imagen original.
Veamos annToMask
primero la función, primero necesita convertir el formato de polígono al formato RLE.
def annToMask(self, ann):
"""
Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask.
:return: binary mask (numpy 2D array)
"""
rle = self.annToRLE(ann)
m = maskUtils.decode(rle)
return m
def annToRLE(self, ann):
"""
Convert annotation which can be polygons, uncompressed RLE to RLE.
:return: binary mask (numpy 2D array)
"""
t = self.imgs[ann['image_id']]
h, w = t['height'], t['width']
segm = ann['segmentation']
if type(segm) == list:
# polygon -- a single object might consist of multiple parts
# we merge all parts into one mask rle code
rles = maskUtils.frPyObjects(segm, h, w)
rle = maskUtils.merge(rles)
elif type(segm['counts']) == list:
# uncompressed RLE
rle = maskUtils.frPyObjects(segm, h, w)
else:
# rle
rle = ann['segmentation']
return rle
En el proceso de conversión a RLE, se necesita el image_id y la segmentación en la anotación.
Ahora solo tenemos una segmentación, y no hay image_id,
y el image_id solo se usa para obtener el w, h de la imagen,
así que importe el coco instance_val2017.json
y encuentre un image_id para usar una vez.
from pycocotools.coco import COCO
coco_api = COCO('coco/annotations/instances_val2017.json')
ann = dict()
ann['image_id'] = 37777
ann['segmentation'] = polygons
mask = coco_api.annToMask(ann)
mask = np.clip(mask*255,0,255)
Muestra que se puede usar el contorno encontrado (el image_id aleatorio hará que h, w sean inconsistentes con la imagen original, pero no afectará la máscara). (Habrá algunos errores
en la continuidad de la curva).
Todavía se prefiere el formato RLE a continuación.
2. Formato RLE
Si se usa en el segmento de coco, esta es la primera opción, habrá errores en el contorno superior y el formato de polígono en la función coco tiene muchas restricciones.
El código para convertir la máscara a RLE proviene de las utilidades del segmento cualquier cosa ,
donde la máscara es una máscara binaria en forma de tensor y el valor es int.
def mask_to_rle(tensor: torch.Tensor) -> List[Dict[str, Any]]:
"""
Encodes masks to an uncompressed RLE, in the format expected by
pycoco tools.
"""
# Put in fortran order and flatten h,w
h, w, b = tensor.shape #需要根据tensor的shape修改
tensor = tensor.permute(2, 1, 0).flatten(1) #需要根据tensor的shape修改
# Compute change indices
diff = tensor[:, 1:] ^ tensor[:, :-1] #要求值是int型
change_indices = diff.nonzero()
# Encode run length
out = []
for i in range(b):
cur_idxs = change_indices[change_indices[:, 0] == i, 1]
cur_idxs = torch.cat(
[
torch.tensor([0], dtype=cur_idxs.dtype, device=cur_idxs.device),
cur_idxs + 1,
torch.tensor([h * w], dtype=cur_idxs.dtype, device=cur_idxs.device),
]
)
btw_idxs = cur_idxs[1:] - cur_idxs[:-1]
counts = [] if tensor[i, 0] == 0 else [0]
counts.extend(btw_idxs.detach().cpu().tolist())
out.append({
"size": [h, w], "counts": counts})
return out