Segmentaion标签的三种表示:poly、mask、rle

Segmentaion标签的三种表示:poly、mask、rle

不同于图像分类这样比较简单直接的计算机视觉任务,图像分割任务(又分为语义分割、实例分割、全景分割)的标签形式稍为复杂。在分割任务中,我们需要在像素级上表达的是一张图的哪些区域是哪个类别。

多边形坐标Polygon

第一感下,要表达图像中某个区域是什么类别,只要这个区域“圈起来”,并给它一个标签就好了。的确,用多边形来将目标圈出来确实是最符合我们视觉上对图像的感知的方法。并且在很多数据集的标注过程中,来自人类的手工标注也是通过给出一个一个点的坐标,从而形成一个闭合的多边形区域,从而实现对图像中目标物体的分割。

我们通过 OpenCV 的 polylines 函数来将这种做法画出来看一下:

import numpy as np
import cv2
cat_poly = [[390.56410256410254, 1134.179487179487], # ...[407.2307692307692, 1158.5384615384614]]dog_poly = [[794.4102564102564, 635.4615384615385], # ...[780.3076923076923, 531.6153846153846]]img = cv2.imread("cat-dog.jpeg")cat_points = np.array(cat_poly, dtype=np.int32)
cv2.polylines(img, [cat_points], True, (255, 0, 0), 3)
dog_points  = np.array(dog_poly, dtype=np.int32)
cv2.polylines(img, [dog_points], True, (0, 0, 255), 3)cv2.imshow("window", img)
cv2.waitKey(0)

这里的数据 cat_poly 是一个 n×2n\times 2n×2 的二维数组,表示多边形框的 nnn 个坐标,即 [[x1,y1],[x2,y2],...[xn,yn]][[x_1,y_1],[x_2,y_2],...[x_n,y_n]][[x1,y1],[x2,y2],...[xn,yn]]。画出来大概就是下面这样子:

在这里插入图片描述

这样的确可以划分出我们想要的区域,但是没有体现出“区域”的概念,即在整个多边形框内,都是猫/狗区域。

掩膜区域Mask

为了体现出区域的概念,我们可以将整个区域展示出来,这里用到 fillPoly 函数,就是下面这样大家常常见到的样子:

img = cv2.imread("cat-dog.jpeg")dog_poly = [# ...
]
cat_poly = [# ...
]cat_points = np.array(cat_poly, dtype=np.int32)
dog_points = np.array(dog_poly, dtype=np.int32)zeros = np.zeros((img.shape), dtype=np.uint8)
mask = cv2.fillPoly(zeros, [cat_points], color=(255, 0, 0))
mask = cv2.fillPoly(zeros, [dog_points], color=(0, 0, 255))
mask_img = 0.5 * mask + imgcv2.imshow("window", mask_img)
cv2.waitKey(0)

在这里插入图片描述

在模型的设计与训练中,我们有时最后输出的就是与原图尺寸相同二值的 mask 图,其中 1 的地方表示该位置有某一类物体,0 表示没有该类物体。因此我们通常要将上面的多边形标注转为二值的 mask 图来作为直接用来计算损失的标签。由多边形标签转为掩膜标签的代码如下:

def poly2mask(points, width, height):mask = np.zeros((width, height), dtype=np.int32)obj = np.array([points], dtype=np.int32)cv2.fillPoly(mask, obj, 1)return mask

这里的 points 就是上面我们的 cat_poly 这样的二维数组的多边形数据。

就是将有该类物体的地方置为1,其他为0,有些区别会在语义分割和实例分割中有所不同,可能是某一类有一个mask,也可能是每一个实例一个 mask。大家按需调整即可。

将上述猫狗的例子转换后可视化如下:

width, height = img.shape[: 2]
cat_mask = poly2mask(cat_poly, width, height)
dog_mask = poly2mask(dog_poly, width, height)

注意,在做可视化时建议将上面的 poly2mask 函数中的 1 改为 255。因为灰度值为 1 也基本是黑的,但是在训练中为 1 即可。

在这里插入图片描述

在这里插入图片描述

从掩膜 mask 转换回多边形 poly 的函数会比较复杂,在这个过程中可能会有标签精度的损失。我们用越多的坐标点来表示掩膜自然也就越精确,极端情况下,将掩膜边缘处的每一个像素都连接起来,这时不会有精度的损失。但我们通常不会这样做。

这里给出转换的函数,该函数会返回一个数组,数组的长度就是 mask 中闭合区域的个数,数组的每个元素是一组坐标:[x1,y1,x2,y2,...,xn,yn][x_1,y_1,x_2,y_2,...,x_n,y_n][x1,y1,x2,y2,...,xn,yn] ,注意这里的坐标并不是成对的,与我们上面的数据输入略有不同,因此在下面的实验中,笔者用 get_paired_coord 函数统一了一下接口规范。

其中 tolerance 参数(中文意为容忍度)表示的就是输出的多边形的每个坐标点之间的最大距离,可想而知,该值越大,可能的精度损失越大。

from skimage import measuredef close_contour(contour):if not np.array_equal(contour[0], contour[-1]):contour = np.vstack((contour, contour[0]))return contourdef binary_mask_to_polygon(binary_mask, tolerance=0):"""Converts a binary mask to COCO polygon representationArgs:binary_mask: a 2D binary numpy array where '1's represent the objecttolerance: Maximum distance from original points of polygon to approximatedpolygonal chain. If tolerance is 0, the original coordinate array is returned."""polygons = []# pad mask to close contours of shapes which start and end at an edgepadded_binary_mask = np.pad(binary_mask, pad_width=1, mode='constant', constant_values=0)contours = measure.find_contours(padded_binary_mask, 0.5)contours = np.subtract(contours, 1)for contour in contours:contour = close_contour(contour)contour = measure.approximate_polygon(contour, tolerance)if len(contour) < 3:continuecontour = np.flip(contour, axis=1)segmentation = contour.ravel().tolist()# after padding and subtracting 1 we may get -0.5 points in our segmentationsegmentation = [0 if i < 0 else i for i in segmentation]polygons.append(segmentation)return polygons

下面看一下本例中的小狗在 tolerance 为 0 和 100 下的区别。

def get_paired_coord(coord):points = Nonefor i in range(0, len(coord), 2):point = np.array(coord[i: i+2], dtype=np.int32).reshape(1, 2)if (points is None): points = pointelse: points = np.concatenate([points, point], axis=0)return pointspoly_0 = binary_mask_to_polygon(cat_mask+dog_mask, tolerance=0)
poly_100 = binary_mask_to_polygon(cat_mask+dog_mask, tolerance=100)poly0_0 = get_paired_coord(poly_0[0])		# poly_0[0]是小狗,poly[1]是小猫
poly100_0 = get_paired_coord(poly_100[0])p0_img = img
p0_points = np.array(poly0_0, dtype=np.int32)
cv2.polylines(p0_img, [p0_points], True, (255, 0, 0), 3)
cv2.imwrite("poly_dog_0.jpeg", p0_img)p100_img = cv2.imread("cat-dog.jpeg")
p100_points = np.array(poly100_0, dtype=np.int32)
cv2.polylines(p100_img, [p100_points], True, (255, 0, 0), 3)
cv2.imwrite("poly_dog_100.jpeg", p100_img)

在这里插入图片描述

tolerance=0

在这里插入图片描述

tolerance=100

与我们的预期相符,tolerance=0 时不会有精度损失,而当 tolerance=100 时可以看到进度损失已经比较大了。

RLE编码

mask 大概是这种形式:

mask=np.array([[0, 0, 0, 0, 0, 0, 0, 0],[0, 0, 1, 1, 0, 0, 1, 0],[0, 0, 1, 1, 1, 1, 1, 0],[0, 0, 1, 1, 1, 1, 1, 0],[0, 0, 1, 1, 1, 1, 1, 0],[0, 0, 1, 0, 0, 0, 1, 0],[0, 0, 1, 0, 0, 0, 1, 0],[0, 0, 0, 0, 0, 0, 0, 0]])

可以看到其实是有很多信息冗余的,因为只有0,1两种元素,RLE编码就是将相同的数据进行压缩计数,同时记录当前数据出现的初始为位置和对应的长度,例如:[0,1,1,1,0,1,1,0,1,0] 编码之后为1,3,5,2,8,1。其中的奇数位表示数字1出现的对应的index,而偶数位表示它对应的前面的坐标位开始数字1重复的个数。

RLE全称(run-length encoding),翻译为游程编码,又译行程长度编码,又称变动长度编码法(run coding),在控制论中对于二值图像而言是一种编码方法,对连续的黑、白像素数(游程)以不同的码字进行编码。游程编码是一种简单的非破坏性资料压缩法,其好处是加压缩和解压缩都非常快。其方法是计算连续出现的资料长度压缩之。

RLE是COCO数据集的规范格式之一,也是许多图像分割比赛指定提交结果的格式。

mask转rle编码,这里我们借助 pycocotools 工具包:

def singleMask2rle(mask):rle = mask_util.encode(np.array(mask[:, :, None], order='F', dtype="uint8"))[0]rle["counts"] = rle["counts"].decode("utf-8")return rle

该函数的返回值 rle 是一个字典,有两个字段 sizecounts ,该字典通常直接作为 COCO 数据集的 segmentation 字段。

RLE编码的理解推荐:https://blog.csdn.net/wuda19920215/article/details/113865418

Ref:

https://wall.alphacoders.com/big.php?i=324547&lang=Chinese

https://blog.csdn.net/wuda19920215/article/details/113865418

https://www.cnblogs.com/aimhabo/p/9935815.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/532517.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu PPA 使用指南

Ubuntu PPA 使用指南 转自&#xff1a;https://zhuanlan.zhihu.com/p/55250294 一篇涵盖了在 Ubuntu 和其他 Linux 发行版中使用 PPA 的几乎所有问题的深入的文章。 如果你一直在使用 Ubuntu 或基于 Ubuntu 的其他 Linux 发行版&#xff0c;例如 Linux Mint、Linux Lite、Zorin…

杨宏宇:腾讯多模态内容理解技术及应用

杨宏宇&#xff1a;腾讯多模态内容理解技术及应用 分享嘉宾&#xff1a;杨宇鸿 腾讯 内容理解高级工程师 编辑整理&#xff1a;吴祺尧 出品平台&#xff1a;DataFunTalk 导读&#xff1a; 搜索内容的理解贯穿了整个搜索系统。我们需要从多个粒度理解搜索内容&#xff0c;包括语…

CUDA环境详解

CUDA环境详解 本文主要介绍 CUDA 环境&#xff0c;这一堆东西网上有很多博客介绍过了&#xff0c;我再来一篇:)&#xff0c;参考前辈们的文章&#xff0c;看能不能写的更清楚一点。读后仍有问题&#xff0c;欢迎留言交流。 CUDA APIs CUDA是由NVIDIA推出的通用并行计算架构&…

对Docker镜像layer的理解

对Docker镜像layer的理解 转自&#xff1a;https://blog.csdn.net/u011069294/article/details/105583522 FROM python:3.6.1-alpine RUN pip install flask CMD [“python”,“app.py”] COPY app.py /app.py上面是一个Dockerfile的例子&#xff0c;每一行都会生成一个新的l…

机器学习系统:设计与实现 计算图

机器学习系统:设计与实现 计算图 转自&#xff1a;https://openmlsys.github.io/chapter_computational_graph/index.html 在上一章节中&#xff0c;我们展示了用户利用机器学习框架所编写的程序。这些用户程序包含了对于训练数据&#xff0c;模型和训练过程的定义。然而为了…

常见浮点数格式梳理

常见浮点数格式梳理 IEEE 754 标准 浮点数转换网站&#xff1a;https://www.h-schmidt.net/FloatConverter/IEEE754.html IEEE二进制浮点数算术标准&#xff0c;为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式&#xff08;包括负零-0&#xff09;与反常值&am…

混合精度训练

混合精度训练 转自&#xff1a;https://zhuanlan.zhihu.com/p/441591808 通常我们训练神经网络模型的时候默认使用的数据类型为单精度FP32。近年来&#xff0c;为了加快训练时间、减少网络训练时候所占用的内存&#xff0c;并且保存训练出来的模型精度持平的条件下&#xff0…

C++面试常考题——编译内存相关

C面试常考题——编译内存相关 转自&#xff1a;https://leetcode-cn.com/leetbook/read/cpp-interview-highlights/e4ns5g/ C程序编译过程 编译过程分为四个过程&#xff1a;编译&#xff08;编译预处理、编译、优化&#xff09;&#xff0c;汇编&#xff0c;链接。 编译预处…

关键字库函数

关键字库函数 转自&#xff1a;https://leetcode-cn.com/leetbook/read/cpp-interview-highlights/ej3mx1/ sizeof和strlen的区别 strlen 是头文件<cstring> 中的函数&#xff0c;sizeof 是 C 中的运算符。 strlen 测量的是字符串的实际长度&#xff08;其源代码如下&…

memcpy和memmove的区别以及内存重叠问题

memcpy和memmove的区别以及内存重叠问题 转自&#xff1a;https://www.codecomeon.com/posts/89/ 区别 memcpy() 和 memmove() 都是C语言中的库函数&#xff0c;在头文件 string.h 中&#xff0c;作用是拷贝一定长度的内存的内容&#xff0c;原型分别如下&#xff1a; void…

从头搭建一个深度学习框架

从头搭建一个深度学习框架 转自&#xff1a;Build a Deep Learning Framework From Scratch 代码&#xff1a;https://github.com/borgwang/tinynn 当前深度学习框架越来越成熟&#xff0c;对于使用者而言封装程度越来越高&#xff0c;好处就是现在可以非常快速地将这些框架作为…

Docker概念理解

Docker概念理解 本文非Docker命令大全&#xff0c;而是对Docker的概念、原理等作说明&#xff0c;适合有一定实操经验后来加深理解。 转自&#xff1a;docker从入门到实践 Docker简介 本章将带领你进入 Docker 的世界。 什么是 Docker&#xff1f; 用它会带来什么样的好处&a…

Linux内存背后的那些神秘往事

Linux内存背后的那些神秘往事 作者&#xff1a;大白斯基&#xff08;公众号&#xff1a;后端研究所&#xff09; 转自&#xff1a;https://mp.weixin.qq.com/s/l_YdpyHht5Ayvrc7LFZNIA 前言 大家好&#xff0c;我的朋友们&#xff01; CPU、IO、磁盘、内存可以说是影响计算机…

精简CUDA教程——CUDA Driver API

精简CUDA教程——CUDA Driver API tensorRT从零起步迈向高性能工业级部署&#xff08;就业导向&#xff09; 课程笔记&#xff0c;讲师讲的不错&#xff0c;可以去看原视频支持下。 Driver API概述 CUDA 的多级 API CUDA 的 API 有多级&#xff08;下图&#xff09;&#xff…

CUDA编程入门极简教程

CUDA编程入门极简教程 转自&#xff1a;CUDA编程入门极简教程 作者&#xff1a;小小将 前言 2006年&#xff0c;NVIDIA公司发布了CUDA&#xff0c;CUDA是建立在NVIDIA的CPUs上的一个通用并行计算平台和编程模型&#xff0c;基于CUDA编程可以利用GPUs的并行计算引擎来更加高效地…

精简CUDA教程——CUDA Runtime API

精简CUDA教程——CUDA Runtime API tensorRT从零起步迈向高性能工业级部署&#xff08;就业导向&#xff09; 课程笔记&#xff0c;讲师讲的不错&#xff0c;可以去看原视频支持下。 Runtime API 概述 环境 图中可以看到&#xff0c;Runtime API 是基于 Driver API 之上开发的…

TensorRT ONNX 基础

TensorRT ONNX 基础 tensorRT从零起步迈向高性能工业级部署&#xff08;就业导向&#xff09; 课程笔记&#xff0c;讲师讲的不错&#xff0c;可以去看原视频支持下。 概述 TensorRT 的核心在于对模型算子的优化&#xff08;合并算子、利用当前 GPU 特性选择特定的核函数等多种…

mmdetection tools工具梳理

mmdetection tools工具梳理 mmdetection 是一个非常好用的开源目标检测框架&#xff0c;我们可以用它方便地训练自己的目标检测模型&#xff0c;mmdetection 项目仓库提供许多实用的工具来实现帮助我们进行各种测试。本篇将梳理以下 mmdetection 项目仓库 tools 目录下的各种实…

TensorRT ONNX 基础(续)

TensorRT ONNX 基础&#xff08;续&#xff09; PyTorch正确导出ONNX 几条推荐的原则&#xff0c;可以减少潜在的错误&#xff1a; 对于任何使用到 shape、size 返回值的参数时&#xff0c;例如 tensor.view(tensor.size(0), -1) 这类操作&#xff0c;避免直接使用 tensor.s…

frp实现内网穿透极简教程

frp实现内网穿透极简教程 本文是内网穿透极简教程&#xff0c;为求简洁&#xff0c;我们不介绍为什么内网穿透也不介绍其原理&#xff0c;这里假设各位读者都已经明确的知道自己的目的&#xff0c;本文仅介绍如何安装配置 frp 实现内网穿透。 简单来说&#xff0c;内网穿透就…