ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

detectron2入门学习二:实现FruitsNut水果坚果分割任务数据集Mask与coco格式转换处理

2022-02-05 09:58:14  阅读:217  来源: 互联网

标签:掩膜 detectron2 Mask annotation item image coco id


学习目标:

将数据集进行Mask掩膜、coco标注等不同格式的转换


一、单实例的coco标注与Mask掩膜相互转换:

掩膜转换的方式有多种,每个实例生成一张掩膜或者每张图片生成一张掩膜,对于初学者来说,单实例的coco数据集这里采用已有的工具进行转换比较好,这里采用pycococreator进行转化:
pycococreator工程文件地址:
https://github.com/waspinator/pycococreator
由于官方给出的安装方式是在linux上进行,windows可以选择下载工程文件后在当前的工程文件种打开命令提示符,如果运行环境是在虚拟环境中,则激活虚拟环境,之后输入如下命令即可:

python setup.py install

相关的说明可以参考该网址:
https://patrickwasp.com/create-your-own-coco-style-dataset/
检验是否成功安装可以输入如下指令:

from pycococreatortools import pycococreatortools

如果没有报错即可。

1.1 每个实例转换为一张灰度掩模图

根据标注数据进行实例掩膜的生成。
根据pycococreator的示例,掩膜的命名方式主要为:
在这里插入图片描述
这里在掩膜的生成的命名种直接采用其要求的命名格式,这样即可直接进行后续的转换工作。
相关代码与注释如图所示:

def objectSingleMask(annFile, classes_names, savePath):
    """
    从标注文件annFile提取符合类别classes_names的掩膜,并进行保存
    由于每一张图片仅有一张实例,因此默认为灰度图
    :param annFile: json注释文件
    :param classes_names: 需要提取掩膜的类别名
    :param savePath: 保存路径
    :return:
    """
    # 获取COCO_json的数据
    coco = COCO(annFile)
    # 拿到所有需要的图片数据的id - 我需要的类别的categories的id是多少
    classes_ids = coco.getCatIds(catNms=classes_names)
    # 取所有类别的并集的所有图片id
    # 如果想要交集,不需要循环,直接把所有类别作为参数输入,即可得到所有类别都包含的图片
    imgIds_list = []
    # 循环取出每个类别id对应的有哪些图片并获取图片的id号
    for idx in classes_ids:
        imgidx = coco.getImgIds(catIds=idx)  # 将该类别的所有图片id好放入到一个列表中
        imgIds_list += imgidx
        print("搜索id... ", imgidx)
    # 去除重复的图片
    imgIds_list = list(set(imgIds_list))  # 把多种类别对应的相同图片id合并

    # 一次性获取所有图像的信息
    image_info_list = coco.loadImgs(imgIds_list)
    # 对每张图片的每个实例生成一个mask
    annotation_id = 1
    for imageinfo in image_info_list:
        for singleClassIdIndex in range(len(classes_ids)):
            singleClassId = classes_ids[singleClassIdIndex]
            # 找到掩膜的标号id
            singleClassAnnId = coco.getAnnIds(imgIds=imageinfo['id'], catIds=[singleClassId], iscrowd=None)
            # 提取掩膜的分割数据
            singleClassAnnList = coco.loadAnns(singleClassAnnId)
            for singleItem in singleClassAnnList:
                # 寻找每一个实例
                singleItemMask = coco.annToMask(singleItem)
                # 将实例转换为0~255的uint8,进行保存
                singleItemMask = (singleItemMask * 255).astype(np.uint8)
                # <image_id>_<object_class_name>_<annotation_id>.png
                file_name = savePath + '/' + imageinfo['file_name'][:-4] + '_' + \
                            classes_names[singleClassIdIndex] + \
                            '_' + '%d' % annotation_id + '.png'
                cv2.imwrite(file_name, singleItemMask)
                print("已保存mask图片: ", file_name)
                annotation_id = annotation_id + 1

程序运行时直接输入:

    # 标注文件地址
    jsondir = "data/trainval.json"
    # 单掩膜
    singleMaskDir = "mydata/singleMask"
    mkr(singleMaskDir)
    objectSingleMask(jsondir, ['date', 'fig', 'hazelnut'], singleMaskDir)

即可。
文件夹展示:
在这里插入图片描述

1.2 根据单个实例的掩模图重新生成coco标注文件

# 将单幅掩模图转换为json文件
# https://patrickwasp.com/create-your-own-coco-style-dataset/
import datetime
import json
import os
import re
import fnmatch
from PIL import Image
import numpy as np
from pycococreatortools import pycococreatortools


def filter_for_jpeg(root, files):
    """
    提取文件夹中符合相关后缀的图片文件
    :param root: 文件路径
    :param files: 文件路径下的文件列表
    :return: 符合命名条件的文件
    """
    file_types = ['*.jpeg', '*.jpg', '*.png']
    file_types = r'|'.join([fnmatch.translate(x) for x in file_types])
    files = [os.path.join(root, f) for f in files]
    files = [f for f in files if re.match(file_types, f)]
    return files


def filter_for_annotations(root, files, image_filename):
    """
    提取文件夹中符合相关后缀的coco注释文件,
    由于png图片为无损,因此一般采用png格式作为掩膜格式
    :param root: 文件路径
    :param files: 文件路径下的文件列表
    :param image_filename: coco注释文件对应的图片文件
    :return: 
    """
    file_types = ['*.png']
    file_types = r'|'.join([fnmatch.translate(x) for x in file_types])
    basename_no_extension = os.path.splitext(os.path.basename(image_filename))[0]
    file_name_prefix = basename_no_extension + '.*'
    files = [os.path.join(root, f) for f in files]
    files = [f for f in files if re.match(file_types, f)]
    files = [f for f in files if re.match(file_name_prefix, os.path.splitext(os.path.basename(f))[0])]
    return files


if __name__ == "__main__":
    # 路径设置
    ROOT_DIR = 'data'
    IMAGE_DIR = os.path.join(ROOT_DIR, "images")
    ANNOTATION_DIR = "mydata/singleMask"
    SAVE_PATH_DIR = "myjson/singleJson.json"
    # 注释文件的相关信息,可有可无
    INFO = {
        "description": "FruitsNuts Dataset",
        "url": "https://github.com/fenglingbai/FruitsNutsHandle",
        "version": "0.1.0",
        "year": 2022,
        "contributor": "fenglingbai",
        "date_created": datetime.datetime.utcnow().isoformat(' ')
    }

    LICENSES = [
        {
            "id": 1,
            "name": "None",
            "url": "None"
        }
    ]

    # 根据自己的需要添加种类
    CATEGORIES = [
        {
            "supercategory": "date",
            "id": 1,
            "name": "date"
        },
        {
            "supercategory": "fig",
            "id": 2,
            "name": "fig"
        },
        {
            "supercategory": "hazelnut",
            "id": 3,
            "name": "hazelnut"
        }
    ]
    # json文件的输出字典
    coco_output = {
        "info": INFO,
        "licenses": LICENSES,
        "categories": CATEGORIES,
        "images": [],
        "annotations": []
    }
    # 注释标号的初始化
    image_id = 0
    segmentation_id = 1

    # 筛选数据的原图文件
    for root, _, files in os.walk(IMAGE_DIR):
        image_files = filter_for_jpeg(root, files)

        # 遍历每一个原图文件
        for image_filename in image_files:
            image = Image.open(image_filename)
            image_info = pycococreatortools.create_image_info(
                image_id, os.path.basename(image_filename), image.size)
            coco_output["images"].append(image_info)

            # 筛选原图文件对应的标注文件
            for root, _, files in os.walk(ANNOTATION_DIR):
                annotation_files = filter_for_annotations(root, files, image_filename)

                # 遍历每一个标注文件
                for annotation_filename in annotation_files:

                    print(annotation_filename)
                    class_id = [x['id'] for x in CATEGORIES if x['name'] in annotation_filename][0]

                    category_info = {'id': class_id, 'is_crowd': 'crowd' in image_filename}
                    binary_mask = np.asarray(Image.open(annotation_filename)
                                             .convert('1')).astype(np.uint8)

                    annotation_info = pycococreatortools.create_annotation_info(
                        segmentation_id, image_id, category_info, binary_mask,
                        image.size, tolerance=2)

                    if annotation_info is not None:
                        coco_output["annotations"].append(annotation_info)

                    segmentation_id = segmentation_id + 1

            image_id = image_id + 1
    # 存储json标注文件
    with open(SAVE_PATH_DIR, 'w') as output_json_file:
        json.dump(coco_output, output_json_file)
    print('ok')

运行后的json文件可以在对应设置的文件路径中找到。


二、单图片的coco标注与Mask掩膜相互转换:

2.1 单图片进行掩膜转化

def objectMultyMask(annFile, classes_names, savePath, type="color"):
    """
    从标注文件annFile提取符合类别classes_names的掩膜,并进行保存
    :param annFile: 标注文件
    :param classes_names: 需要提取的类别名
    :param savePath: 掩膜保存的路径
    :param type: 保存的色彩选择,
        color使用matplot默认保存为彩色,
        gray使用cv2保存为灰度图
    :return:
    """
    # 获取COCO_json的数据
    coco = COCO(annFile)
    # 拿到所有需要的图片数据的id - 我需要的类别的categories的id是多少
    classes_ids = coco.getCatIds(catNms=classes_names)
    # 取所有类别的并集的所有图片id
    # 如果想要交集,不需要循环,直接把所有类别作为参数输入,即可得到所有类别都包含的图片
    imgIds_list = []
    # 循环取出每个类别id对应的有哪些图片并获取图片的id号
    for idx in classes_ids:
        imgidx = coco.getImgIds(catIds=idx)  # 将该类别的所有图片id好放入到一个列表中
        imgIds_list += imgidx
        print("搜索id... ", imgidx)
    # 去除重复的图片
    imgIds_list = list(set(imgIds_list))  # 把多种类别对应的相同图片id合并

    # 一次性获取所有图像的信息
    image_info_list = coco.loadImgs(imgIds_list)

    # 对每张图片生成一个mask
    for imageinfo in image_info_list:
        mask_pic = np.zeros(shape=(imageinfo['height'], imageinfo['width']))
        for singleClassId in classes_ids:
            # 每一类标记进行生成掩膜
            singleClassMask = np.zeros(shape=(imageinfo['height'], imageinfo['width']))
            # 找到掩膜的标号id
            singleClassAnnId = coco.getAnnIds(imgIds=imageinfo['id'], catIds=[singleClassId], iscrowd=None)
            # 提取掩膜的分割数据
            singleClassAnnList = coco.loadAnns(singleClassAnnId)
            for singleItem in singleClassAnnList:
                # 将每一个实例进行叠加
                singleItemMask = coco.annToMask(singleItem)
                singleClassMask += singleItemMask
            # 最后在原图上叠加该类的掩膜信息
            singleClassMask[singleClassMask > 0] = 1
            # 重叠的部分使用后者的掩膜信息
            mask_pic[singleClassMask == 1] = 0
            mask_pic = mask_pic + singleClassMask * singleClassId
        # 保存图片
        mask_pic = mask_pic.astype(np.uint8)
        file_name = savePath + '/' + imageinfo['file_name'][:-4] + '.png'
        if type == "color":
            plt.imsave(file_name, mask_pic)
        else:
            cv2.imwrite(file_name, mask_pic)
        print("已保存mask图片: ", file_name)

图像在保存种,每一个像素的值代表了其中该像素所属的类别。因此如果需要可视化展示可以在"type"属性中选择"color",否则选择"gary"。程序运行时直接输入:

    multyMaskGrayDir = "mydata/multyMaskGray"
    mkr(multyMaskGrayDir)
    objectMultyMask(jsondir, ['date', 'fig', 'hazelnut'], multyMaskGrayDir, type="gray")
    multyMaskColorDir = "mydata/multyMaskColor"
    mkr(multyMaskColorDir)
    objectMultyMask(jsondir, ['date', 'fig', 'hazelnut'], multyMaskColorDir, type="color")

多掩膜的彩色展示如图所示:
在这里插入图片描述
灰度图展示由于像素值为0,1,2,3,以8位图片展示全部为黑色,肉眼难以辨认,这里不再展示。

2.2 单图片掩膜进行coco注释文件转化

在上文中我们选择了pycococreator工具,这里也可以采用cv2中的工具进行实现

import cv2
import os
import numpy as np
import json

if __name__ == '__main__':
    # 初始化对应的类别名与id
    my_label = {"background": 0,
                "date": 1,
                "fig": 2,
                "hazelnut": 3
                }
    # 初始化需要保存的coco字典
    coco = dict()
    coco['images'] = []
    coco['type'] = 'instances'
    coco['annotations'] = []
    coco['categories'] = []

    # 初始化相关的变量与标记
    image_set = set()
    category_item_id = 0
    annotation_id = 0
    # 1.添加coco的categories
    my_label = sorted(my_label.items(), key=lambda item: item[1])
    for val in my_label:
        category_item = dict()
        category_item['supercategory'] = 'none'
        category_item_id = val[1]
        if 0 == category_item_id:
            continue
        category_item['id'] = category_item_id
        category_item['name'] = val[0]
        coco['categories'].append(category_item)

    # 2.添加coco的images
    IMAGE_DIR = "data/images/"
    ANNOTATION_DIR = "mydata/multyMaskGray/"
    SAVE_JSON_DIR = "myjson/multyMaskGray.json"
    # 加载图片信息
    imageListFile = os.listdir(IMAGE_DIR)
    imageListFile.sort(key=lambda x: int(x[:-4]))

    annotationListFile = os.listdir(ANNOTATION_DIR)
    annotationListFile.sort(key=lambda x: int(x[:-4]))
    assert len(imageListFile) == len(annotationListFile)

    for imageId in range(len(imageListFile)):
        assert imageListFile[imageId][0:-4] == annotationListFile[imageId][0:-4]

        annotationPath = ANNOTATION_DIR + annotationListFile[imageId]
        annotationGray = cv2.imread(annotationPath, -1)
        if len(annotationGray.shape) == 3:
            annotationGray = cv2.cvtColor(annotationGray, cv2.COLOR_BGR2GRAY)

        image_item = dict()
        image_item['id'] = imageId
        image_item['file_name'] = imageListFile[imageId]
        image_item['width'] = annotationGray.shape[1]  # size['width']
        image_item['height'] = annotationGray.shape[0]  # size['height']
        coco['images'].append(image_item)
        image_set.add(imageListFile[imageId])

        # 3.添加coco的annotations
        for current_category_id in range(1, len(my_label)):
            img_bi = np.zeros(annotationGray.shape, dtype='uint8')
            img_bi[annotationGray == current_category_id] = 255
            my_contours, _ = cv2.findContours(img_bi, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
            for c in my_contours:
                area_t = cv2.contourArea(c)
                # 这里设定阈值进行筛选
                if 0 == len(c) or area_t < 20:
                    continue
                L_pt = c
                # x,y,w,h
                bbox = cv2.boundingRect(L_pt)
                x1, y1, w1, h1 = bbox
                # 标记超过原图界限,进行忽略
                if x1 < 0 or y1 < 0 or x1 + w1 > annotationGray.shape[1] or y1 + h1 > annotationGray.shape[0]:
                    continue
                seg = []
                for val in L_pt:
                    x = val[0][0]
                    y = val[0][1]
                    seg.append(int(x))
                    seg.append(int(y))

                bbox = list(bbox)

                annotation_item = dict()
                annotation_item['segmentation'] = []
                annotation_item['segmentation'].append(seg)

                annotation_item['area'] = area_t
                annotation_item['iscrowd'] = 0
                annotation_item['ignore'] = 0
                annotation_item['image_id'] = imageId
                annotation_item['bbox'] = bbox
                annotation_item['category_id'] = current_category_id
                annotation_id += 1
                annotation_item['id'] = annotation_id
                coco['annotations'].append(annotation_item)

    json.dump(coco, open(SAVE_JSON_DIR, 'w'))
    print('ok')

运行后的json文件可以在对应设置的文件路径中找到。


三、运行结果展示:

3.1 单实例的注释文件训练预测展示:

在这里插入图片描述

3.2 单图片的注释文件训练预测展示:

在这里插入图片描述
可以看出,单实例的注释文件训练效果优于单图片的注释文件,这是由于单图片的掩膜存在两实例连接或遮挡的情况,这种情况下难以将不同实例进行分离,从而使得训练数据是将整体作为一个实例进行训练。同时预测结果经过了NMS处理,也会将这相邻的物体判定为单个实例,因此效果较差。

标签:掩膜,detectron2,Mask,annotation,item,image,coco,id
来源: https://blog.csdn.net/qq_43264202/article/details/122771241

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有