[拆轮子] PaddleDetection 中的 COCODataSet 是怎么写的

news2025/1/26 15:32:06

今日,辗转反侧,该💩的代码就是跑不成功,来看看 COCODataSet 到底是怎么写的,本文只参考当前版本的代码,当前版本 PaddleDetection2.5 COCODataSet 源码见附录

COCODataSet 类内部就三个函数:

__init__
parse_dataset  
_sample_empty     # 该函数供 parse_dataset 调用

来看一下 COCODataSet 的基类实现函数,咱挨个看

__init__
__len__
__call__
__getitem__
check_or_download_dataset
set_kwargs
set_transform
set_epoch
parse_dataset
get_anno

1. 基类parse_dataset

def parse_dataset(self, ):
    raise NotImplementedError(
        "Need to implement parse_dataset method of Dataset")

该类必须要被继承之后实现该方法,继承该类中必须解析数据集,并将数据集中的内容传给变量 self.roidbs,具体内容之后看, self.roidbs 变量是一个列表,每一项都是一张照片的内容

parse_dataset 唯一要做的一件事就是解析数据并传给变量 self.roidbs

self.roidbs 中一个 item 是:

{'gt_bbox': array([[133.51,  24.77, 366.11, 562.92]], dtype=float32),
 'gt_class': array([[14]], dtype=int32),
 'h': 640.0,
 'im_file': 'dataset/coco/COCO/val2017/000000270705.jpg',
 'im_id': array([270705]),
 'is_crowd': array([[0]], dtype=int32),
 'w': 475.0}

2. 基类__len__

def __len__(self, ):
    return len(self.roidbs) * self.repeat

len(self.roidbs) 就是原始数据的内容,self.repeat 是重复次数,所以在__getitem__ 有这么一句:

if self.repeat > 1:
    idx %= n

用来进行重复操作

3. 基类__call__

def __call__(self, *args, **kwargs):
    return self

做这个操作其实没啥说的了,实例化之后call一下还是返回自己

4. 基类其他不重要函数

  • 设置部分,用来设置自身的属性,基本没被调用
def set_kwargs(self, **kwargs):
    self.mixup_epoch = kwargs.get('mixup_epoch', -1)
    self.cutmix_epoch = kwargs.get('cutmix_epoch', -1)
    self.mosaic_epoch = kwargs.get('mosaic_epoch', -1)

def set_transform(self, transform):
    self.transform = transform

def set_epoch(self, epoch_id):
    self._epoch = epoch_id
  • 获取部分:
def get_anno(self):
    if self.anno_path is None:
        return
    return os.path.join(self.dataset_dir, self.anno_path)

获取标注 ann.json 的路径

  • 检查数据路径函数,也没被调用,不重要跳过
def check_or_download_dataset(self):
    self.dataset_dir = get_dataset_path(self.dataset_dir, self.anno_path,
                                        self.image_dir)

以上函数供 read 在 dataset类 外部调用(之后会讲到)
在这里插入图片描述

所以 self.mixup_epoch , self.cutmix_epochself.mosaic_epoch 默认值都是 -1

5. 基类 __getitem__ 函数

def __getitem__(self, idx):
	
	# ------- 用来进行重复操作的部分 -------
    n = len(self.roidbs)
    if self.repeat > 1:
        idx %= n


    # ------- 深拷贝当前的数据项 -------
    roidb = copy.deepcopy(self.roidbs[idx])
    # 以下仨 if 和数据增强有关
    if self.mixup_epoch == 0 or self._epoch < self.mixup_epoch:
        idx = np.random.randint(n)
        roidb = [roidb, copy.deepcopy(self.roidbs[idx])]
    elif self.cutmix_epoch == 0 or self._epoch < self.cutmix_epoch:
        idx = np.random.randint(n)
        roidb = [roidb, copy.deepcopy(self.roidbs[idx])]
    elif self.mosaic_epoch == 0 or self._epoch < self.mosaic_epoch:
        roidb = [roidb, ] + [
            copy.deepcopy(self.roidbs[np.random.randint(n)])
            for _ in range(4)
        ]
    
	# ------- 设置 curr_iter -------
    if isinstance(roidb, Sequence):
        for r in roidb:
            r['curr_iter'] = self._curr_iter
    else:
        roidb['curr_iter'] = self._curr_iter
    self._curr_iter += 1
    
    # ------- 对当前数据项进行之前的 transform ------- 
    return self.transform(roidb)

6. 基类 __init__ 函数

self.dataset_dir = dataset_dir if dataset_dir is not None else ''
self.anno_path = anno_path
self.image_dir = image_dir if image_dir is not None else ''
self.data_fields = data_fields           

看上边这4个参数,是和 yaml 文件中的内容是对应的:
在这里插入图片描述
基本都在 parse_dataset 调用

self.sample_num = sample_num                # parse_dataset 中调用
self.use_default_label = use_default_label  # 这个变量可能是 COCO 每个id对应的类名? 暂时没发现使用处
self.repeat = repeat
self._epoch = 0
self._curr_iter = 0

5. 子类 parse_dataset 函数

明天再说,睡了

附录

顺便备份一下当前版本PaddleDetection2.5COCODataSet 代码

class COCODataSet(DetDataset):
    """
    Load dataset with COCO format.

    Args:
        dataset_dir (str): root directory for dataset.
        image_dir (str): directory for images.
        anno_path (str): coco annotation file path.
        data_fields (list): key name of data dictionary, at least have 'image'.
        sample_num (int): number of samples to load, -1 means all.
        load_crowd (bool): whether to load crowded ground-truth. 
            False as default
        allow_empty (bool): whether to load empty entry. False as default
        empty_ratio (float): the ratio of empty record number to total 
            record's, if empty_ratio is out of [0. ,1.), do not sample the 
            records and use all the empty entries. 1. as default
        repeat (int): repeat times for dataset, use in benchmark.
    """

    def __init__(self,
                 dataset_dir=None,
                 image_dir=None,
                 anno_path=None,
                 data_fields=['image'],
                 sample_num=-1,
                 load_crowd=False,
                 allow_empty=False,
                 empty_ratio=1.,
                 repeat=1):
        super(COCODataSet, self).__init__(
            dataset_dir,
            image_dir,
            anno_path,
            data_fields,
            sample_num,
            repeat=repeat)
        self.load_image_only = False
        self.load_semantic = False
        self.load_crowd = load_crowd
        self.allow_empty = allow_empty
        self.empty_ratio = empty_ratio

    def _sample_empty(self, records, num):
        # if empty_ratio is out of [0. ,1.), do not sample the records
        if self.empty_ratio < 0. or self.empty_ratio >= 1.:
            return records
        import random
        sample_num = min(
            int(num * self.empty_ratio / (1 - self.empty_ratio)), len(records))
        records = random.sample(records, sample_num)
        return records

    def parse_dataset(self):
        anno_path = os.path.join(self.dataset_dir, self.anno_path)
        image_dir = os.path.join(self.dataset_dir, self.image_dir)

        assert anno_path.endswith('.json'), \
            'invalid coco annotation file: ' + anno_path
        from pycocotools.coco import COCO
        coco = COCO(anno_path)
        img_ids = coco.getImgIds()
        img_ids.sort()
        cat_ids = coco.getCatIds()
        records = []
        empty_records = []
        ct = 0

        self.catid2clsid = dict({catid: i for i, catid in enumerate(cat_ids)})
        self.cname2cid = dict({
            coco.loadCats(catid)[0]['name']: clsid
            for catid, clsid in self.catid2clsid.items()
        })

        if 'annotations' not in coco.dataset:
            self.load_image_only = True
            logger.warning('Annotation file: {} does not contains ground truth '
                           'and load image information only.'.format(anno_path))

        for img_id in img_ids:
            img_anno = coco.loadImgs([img_id])[0]
            im_fname = img_anno['file_name']
            im_w = float(img_anno['width'])
            im_h = float(img_anno['height'])

            im_path = os.path.join(image_dir,
                                   im_fname) if image_dir else im_fname
            is_empty = False
            if not os.path.exists(im_path):
                logger.warning('Illegal image file: {}, and it will be '
                               'ignored'.format(im_path))
                continue

            if im_w < 0 or im_h < 0:
                logger.warning('Illegal width: {} or height: {} in annotation, '
                               'and im_id: {} will be ignored'.format(
                                   im_w, im_h, img_id))
                continue

            coco_rec = {
                'im_file': im_path,
                'im_id': np.array([img_id]),
                'h': im_h,
                'w': im_w,
            } if 'image' in self.data_fields else {}

            if not self.load_image_only:
                ins_anno_ids = coco.getAnnIds(
                    imgIds=[img_id], iscrowd=None if self.load_crowd else False)
                instances = coco.loadAnns(ins_anno_ids)

                bboxes = []
                is_rbox_anno = False
                for inst in instances:
                    # check gt bbox
                    if inst.get('ignore', False):
                        continue
                    if 'bbox' not in inst.keys():
                        continue
                    else:
                        if not any(np.array(inst['bbox'])):
                            continue

                    x1, y1, box_w, box_h = inst['bbox']
                    x2 = x1 + box_w
                    y2 = y1 + box_h
                    eps = 1e-5
                    if inst['area'] > 0 and x2 - x1 > eps and y2 - y1 > eps:
                        inst['clean_bbox'] = [
                            round(float(x), 3) for x in [x1, y1, x2, y2]
                        ]
                        bboxes.append(inst)
                    else:
                        logger.warning(
                            'Found an invalid bbox in annotations: im_id: {}, '
                            'area: {} x1: {}, y1: {}, x2: {}, y2: {}.'.format(
                                img_id, float(inst['area']), x1, y1, x2, y2))

                num_bbox = len(bboxes)
                if num_bbox <= 0 and not self.allow_empty:
                    continue
                elif num_bbox <= 0:
                    is_empty = True

                gt_bbox = np.zeros((num_bbox, 4), dtype=np.float32)
                gt_class = np.zeros((num_bbox, 1), dtype=np.int32)
                is_crowd = np.zeros((num_bbox, 1), dtype=np.int32)
                gt_poly = [None] * num_bbox

                has_segmentation = False
                for i, box in enumerate(bboxes):
                    catid = box['category_id']
                    gt_class[i][0] = self.catid2clsid[catid]
                    gt_bbox[i, :] = box['clean_bbox']
                    is_crowd[i][0] = box['iscrowd']
                    # check RLE format 
                    if 'segmentation' in box and box['iscrowd'] == 1:
                        gt_poly[i] = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]
                    elif 'segmentation' in box and box['segmentation']:
                        if not np.array(box['segmentation']
                                        ).size > 0 and not self.allow_empty:
                            bboxes.pop(i)
                            gt_poly.pop(i)
                            np.delete(is_crowd, i)
                            np.delete(gt_class, i)
                            np.delete(gt_bbox, i)
                        else:
                            gt_poly[i] = box['segmentation']
                        has_segmentation = True

                if has_segmentation and not any(
                        gt_poly) and not self.allow_empty:
                    continue

                gt_rec = {
                    'is_crowd': is_crowd,
                    'gt_class': gt_class,
                    'gt_bbox': gt_bbox,
                    'gt_poly': gt_poly,
                }

                for k, v in gt_rec.items():
                    if k in self.data_fields:
                        coco_rec[k] = v

                # TODO: remove load_semantic
                if self.load_semantic and 'semantic' in self.data_fields:
                    seg_path = os.path.join(self.dataset_dir, 'stuffthingmaps',
                                            'train2017', im_fname[:-3] + 'png')
                    coco_rec.update({'semantic': seg_path})

            logger.debug('Load file: {}, im_id: {}, h: {}, w: {}.'.format(
                im_path, img_id, im_h, im_w))
            if is_empty:
                empty_records.append(coco_rec)
            else:
                records.append(coco_rec)
            ct += 1
            if self.sample_num > 0 and ct >= self.sample_num:
                break
        assert ct > 0, 'not found any coco record in %s' % (anno_path)
        logger.debug('{} samples in file {}'.format(ct, anno_path))
        if self.allow_empty and len(empty_records) > 0:
            empty_records = self._sample_empty(empty_records, len(records))
            records += empty_records
        self.roidbs = records

其基类 DetDataset :

from paddle.io import Dataset

class DetDataset(Dataset):
    """
    Load detection dataset.

    Args:
        dataset_dir (str): root directory for dataset.
        image_dir (str): directory for images.
        anno_path (str): annotation file path.
        data_fields (list): key name of data dictionary, at least have 'image'.
        sample_num (int): number of samples to load, -1 means all.
        use_default_label (bool): whether to load default label list.
        repeat (int): repeat times for dataset, use in benchmark.
    """

    def __init__(self,
                 dataset_dir=None,
                 image_dir=None,
                 anno_path=None,
                 data_fields=['image'],
                 sample_num=-1,
                 use_default_label=None,
                 repeat=1,
                 **kwargs):
        super(DetDataset, self).__init__()
        self.dataset_dir = dataset_dir if dataset_dir is not None else ''
        self.anno_path = anno_path
        self.image_dir = image_dir if image_dir is not None else ''
        self.data_fields = data_fields
        self.sample_num = sample_num
        self.use_default_label = use_default_label
        self.repeat = repeat
        self._epoch = 0
        self._curr_iter = 0

    def __len__(self, ):
        return len(self.roidbs) * self.repeat

    def __call__(self, *args, **kwargs):
        return self

    def __getitem__(self, idx):
        n = len(self.roidbs)
        if self.repeat > 1:
            idx %= n
        # data batch
        roidb = copy.deepcopy(self.roidbs[idx])
        if self.mixup_epoch == 0 or self._epoch < self.mixup_epoch:
            idx = np.random.randint(n)
            roidb = [roidb, copy.deepcopy(self.roidbs[idx])]
        elif self.cutmix_epoch == 0 or self._epoch < self.cutmix_epoch:
            idx = np.random.randint(n)
            roidb = [roidb, copy.deepcopy(self.roidbs[idx])]
        elif self.mosaic_epoch == 0 or self._epoch < self.mosaic_epoch:
            roidb = [roidb, ] + [
                copy.deepcopy(self.roidbs[np.random.randint(n)])
                for _ in range(4)
            ]
        if isinstance(roidb, Sequence):
            for r in roidb:
                r['curr_iter'] = self._curr_iter
        else:
            roidb['curr_iter'] = self._curr_iter
        self._curr_iter += 1
        
        # roidb['num_classes'] = len(self.catid2clsid) # COCODataset 80 cls

        return self.transform(roidb)

    def check_or_download_dataset(self):
        self.dataset_dir = get_dataset_path(self.dataset_dir, self.anno_path,
                                            self.image_dir)

    def set_kwargs(self, **kwargs):
        self.mixup_epoch = kwargs.get('mixup_epoch', -1)
        self.cutmix_epoch = kwargs.get('cutmix_epoch', -1)
        self.mosaic_epoch = kwargs.get('mosaic_epoch', -1)

    def set_transform(self, transform):
        self.transform = transform

    def set_epoch(self, epoch_id):
        self._epoch = epoch_id

    def parse_dataset(self, ):
        raise NotImplementedError(
            "Need to implement parse_dataset method of Dataset")

    def get_anno(self):
        if self.anno_path is None:
            return
        return os.path.join(self.dataset_dir, self.anno_path)

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

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

相关文章

词义和词义消歧

Synsets(“synonym sets”, effectively senses) are the basic unit of organization in WordNet.同义词集 对于许多应用程序&#xff0c;我们希望消除歧义 • 我们可能只对一种含义感兴趣 • 在网络上搜索chemical plant 化工厂&#xff0c;我们不想搜到香蕉中的化学物质 所以…

【SpringBoot扩展点】 容器刷新前回调ApplicationContextInitializer

本文将作为Spring系列教程中源码版块的第一篇&#xff0c;整个源码系列将分为两部分进行介绍&#xff1b;单纯的源码解析&#xff0c;大概率是个吃力没人看的事情&#xff0c;因此我们将结合源码解析&#xff0c;一个是学习下别人的优秀设计&#xff0c;一个是站在源码的角度看…

【MySQL】索引和事务重点知识汇总

目录1.索引:1.1 索引的使用:1.2 索引背后的核心数据结构:1.2.1 先认识 B 树(N叉搜索树):1.2.2 再认识 B 树(N叉搜索树):2.事务:2.1 隔离性:2.1.1 脏读问题:2.1.2 不可重复读问题:2.1.3 幻读问题:2.1.4 总结:2.1.5 隔离级别:1.索引: 索引存在的意义就是为了提高查询到效率.索引…

【AI理论学习】Python机器学习中的特征选择

Python机器学习中的特征选择特征选择方法特征选择的Python库使用Scikit-learn实现特征选择方差卡方检验ANOVALasso正则化递归特征消除使用Feature-engine进行特征选择单变量特征选择相关性Python 中的更多特性选择方法参考资料任何数据科学项目的一个重要步骤是选择最具预测性的…

vue实现文件下载

引言 最近在自己做项目的需求的过程中&#xff0c;需要vuespringboot实现文件的下载功能&#xff08;导出博客文件&#xff09;。 问题重现 在我后端文件下载接口开发完成后&#xff0c;使用vue前端去进行对接时出现了问题。 我是直接使用的axios去进行请求接口&#xff0c…

Python 炫技操作:条件语句的七种写法

原代码 这是一段非常简单的通过年龄判断一个人是否成年的代码&#xff0c;由于代码行数过多&#xff0c;有些人就不太愿意这样写&#xff0c;因为这体现不出自己多年的 Python 功力。 if age > 18:return "已成年" else:return "未成年"下面我列举了六…

SwiftUI 中创建谷歌字体浏览器

Google Fonts是设计用户界面时使用的免费字体的转到站点。本教程将展示如何编写一个简单的工具来预览这些字体,而无需在系统中注册每种字体。 该应用程序包含一个拆分视图,该视图在左侧面板中包含字体列表。右侧面板将显示字体样式选项的预览。 项目设置 创建一个名为 Googl…

Vue2之webpack篇(一)

目录 前言 1、什么是webpack&#xff1f; 2、传统开发模式 一、传统开发模式 1、场景 2、问题 3、原因 4、解决方案 二、ES6模块化 1、ES6的解决方案 3、拓展 4、取别名 5、*搭配取别名 6、导出default{} 三、CommonJS规范 1、推荐文档 2、使用CommonJS规范解决方…

十二、DockerFile构建过程解析

1、概述 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 在Docker 常用命令篇中&#xff0c;我们已经知道了2中构建镜像的方式 export\import 和 commit方式。这两种方式都需要先运行并创建容器&#xff0c;然后在容器…

python自学之《21天学通Python》(5)

第8章 复杂程序组织 当一个应用程序简单时&#xff0c;将程序代码写入一个文件即可。但随着应用程序或项目复杂度增加时&#xff0c;如果将所有代码都写入同一个文件中时&#xff0c;会出现文件过长或过大&#xff0c;即不方便代码浏览&#xff0c;也不方便代码的管理、使用与维…

人工智能人才缺口暴增,想转行的你赶紧把Python学起来...

当前AI人才极度紧缺&#xff0c;据《中国ICT人才生态白皮书》研究分析&#xff0c;到2018年底&#xff0c;我国人工智能人才缺口将突破100万&#xff0c;到2020年&#xff0c;这一数字将攀升到226万。 在过去的几年中&#xff0c;Python已经成为现代软件开发&#xff0c;基础设…

Web测试的各个测试点,居然这么全!(文末送web测试方法大全一份)

1 什么是Web测试&#xff1f; Web测试测试Web或Web应用程序的潜在错误。它是在上线前对基于网络的应用程序进行完整的测试。 UI测试功能测试数据库测试性能测试兼容性测试安全测试自动化测试 2 WEB测试主要测试场景 1.UI测试 界面是否美观&#xff0c;风格、字体、样式是否…

初识: 对象的属性特征

1. 前言 2. 什么是对象的属性特征 3. 灵活控制对象的属性特征 4. configurable: false 是单向设置的 1. 前言 众所周知&#xff0c;默认情况下我们可以任意对自己定义的对象进行增删改的。但是&#xff0c;在某些情况下&#xff0c;我们不能让别人去随便修改我们定义的对象的…

《数据结构》二叉数

学习目录树型结构概念树的重要概念树的表示形式二叉数概念特殊的二叉树二叉树的性质练习题树型结构 概念 树是一种非线性的数据结构&#xff0c;由 n 个有限节点组成一个有层次关系的集合 它具有以下的特点&#xff1a; 有一个特殊的结点&#xff0c;称为根结点&#xff0c;…

【 Threejs 】- Shader 着色器实例渲染教程

着色器在threejs中是一个难点&#xff0c;话不多说&#xff0c;先来看看着色器是什么&#xff1f; 如果您已经有使用计算机绘图的经验&#xff0c;您就会知道在这个过程中您先画一个圆&#xff0c;然后画一个矩形、一条线、一些三角形&#xff0c;直到您组成您想要的图像。这个…

面试真题 | 什么是 Redis ? Redis缓存应用场景有哪些?

面试官问题 redis击穿、穿透有什么区别&#xff1f;如何设计用例及测试 Redis 的基本概念 在没有添加 Redis 的时候&#xff0c;后端的查询流程是&#xff1a; 用户访问页面。请求后端服务。经过逻辑处理后&#xff0c;去数据库查询信息。 在添加 Redis 的之后&#xff0c;…

MySQL 服务端口大全

介绍 MySQL默认服务端口3306/TCP都不会陌生&#xff0c;但MySQL提供服务只有单纯的这个端口吗。在8.0版本默认启动的时候会发现&#xff0c;出现新的端口。 可以说MySQL使用的端口数量取决于所启用的特性、所使用的组件、应用程序连接的方式以及环境的其他方面。 按照官方说…

转速传感器信号隔离变送器正弦波输入方波信号输出

特点 转速传感器信号直接输入&#xff0c;方波信号输出正弦波、锯齿波信号输入&#xff0c;方波信号输出200mV峰值微弱信号的放大与整形不改变原波形频率&#xff0c;响应速度快电源、信号&#xff1a;输入/输出 3000VDC三隔离辅助电源&#xff1a;5V、12V、15V或24V直流单电源…

Huffman编码

目录背景Huffman编码代码部分背景 在数据传输&#xff0c;保存的时候&#xff0c;特别是在数据量特别大的时候传输&#xff0c;保存数据是一件特别麻烦的事。比如逛淘宝的时候&#xff0c;首页会有很多商家展示自己产品的高清图片&#xff0c;如果不对图片进行压缩服务端保存图…

经历百度、美团两次被裁后,我能在小公司躺平吗?

百度裁员后我进入体制内&#xff0c;专心学习自动化 百度被裁后&#xff0c;我意识到自学效果不佳&#xff0c;跟不上职场的所需&#xff0c;于是有了系统学习的想法。 这时的新工作是在体制内&#xff0c;工作强度不大&#xff0c;时间上也比较自由&#xff0c;便正式成为了…