(二)COCO Python API-ソースコード分析編

44403 ワード

COCOに対して与えられた寸法情報がいくつかの通常のシーンの使用にすぎない場合は、参照してください.https://github.com/dengdan/coco/blob/master/PythonAPI/pycocoDemo.ipynbシナリオはもう十分です.
シナリオの具体的な使い方は私のブログを参考にすることができます:(一)COCO Python API-使用編.
しかし、COCOデータセットをさらに掘り起こしたいパートナーにとっては、これは十分ではありません.この場合、ソースコードに直接答えを探す必要があります.
なお、YOLO公式に与えられたモデルはCOCOデータセットトレーニングに基づいているが、YOLO公式サイトで与えられたトレーニング方法では、COCOデータセットのマークアップフォーマットをYOLOに変換するために必要なマークアップフォーマットについてはスクリプトを与えるのではなく、現れたトレーニングデータファイル:train.txtと寸法ボックスデータファイル:labels/xxx.txt. ユーザーがCOCOデータセットに基づいて自分のデータセットをカスタマイズする場合、ソースコードを研究する必要がある.
実はCOCO Python APIについて、核心のファイルは:https://github.com/dengdan/coco/tree/master/PythonAPI/pycocotools/ディレクトリの下には、それぞれcocoがあります.py, cocoeval.pyとmask.pyこの3つのファイル.
1. coco.pyスクリプト
以下は、スクリプトファイル全体の説明から抜粋します.
# Microsoft COCO Toolbox.      version 2.0
# Data, paper, and tutorials available at:  http://mscoco.org/
# Code written by Piotr Dollar and Tsung-Yi Lin, 2014.

          COCO       .     COCO        ,         .  
  
  ,               COCO       ,                .   

coco.pyでは、以下のAPIが定義されています.
  • COCOクラス-COCOマークアップファイルをロードし、必要なデータ構造を準備するために使用する.
  • decodeMask - Decode binary mask M encoded via run-length encoding.
  • encodeMask - Encode binary mask M using run-length encoding.
  • getAnnIds-条件を満たすマークアップ情報のidsを取得する.
  • getCatIds-条件を満たすカテゴリのidsを取得する.
  • getImgIds-条件を満たすピクチャのidsを取得する.
  • loadAnns-指定idsに対応するマークアップ情報をロードする.
  • loadCats-指定idsに対応するカテゴリをロードする.
  • loadImgs-指定idsに対応するピクチャをロードする.
  • annToMask-segmentationマークアップ情報を2値maskに変換する.
  • showAnns-指定idsの表示情報を対応するピクチャに表示する.
  • loadRes-アルゴリズムの結果をロードし、データにアクセスできるAPIを作成する.
  • download-mscoco.orgサーバはCOCO画像データセットをダウンロードする.

  • API機能を理解する鍵は、この中に含まれるいくつかのID情報:ピクチャID、カテゴリID、寸法情報IDを理解することである.
    与えられたAPIでは、「ann」=annotation、「cat」=category、and「img」=imageである.次に、上述APIについて分析する.
    1)COCO類の構築関数
    COCO類の構造関数はjsonファイルをロードし、その中の寸法情報を関連関係を確立する責任を負う.関連関係は、次の2つの変数に存在します.
  • imgToAnns:ピクチャidと寸法情報を関連付ける;
  • catToImgs:カテゴリidとピクチャidを関連付ける.

  • このような利点は、与えられたピクチャidに対して、そのすべての寸法情報を迅速に見つけることができることである.与えられたカテゴリidについては、そのクラスに属するすべてのピクチャを迅速に見つけることができる.
    class COCO:
        def __init__(self, annotation_file=None):
            """
                ,           .
            :param annotation_file (str):        
            :return:
            """
            #      
    
            #       
            self.dataset, self.anns, self.cats, self.imgs = dict(),dict(),dict(),dict()
            self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list)
    
            #       
            if not annotation_file == None:
                print('loading annotations into memory...')
                tic = time.time()
                dataset = json.load(open(annotation_file, 'r'))
                # dataset      dict
                assert type(dataset)==dict, 'annotation file format {} not supported'.format(type(dataset))
                print('Done (t={:0.2f}s)'.format(time.time()- tic))
                self.dataset = dataset
                self.createIndex()
    
        def createIndex(self):
            """
                ,           . 
            """
    
            # create index
            print('creating index...')
    
            anns, cats, imgs = {}, {}, {}
            # imgToAnns:     id         ;      id,              
            # catToImgs:     id     id   ;      id,               
            imgToAnns,catToImgs = defaultdict(list),defaultdict(list)
    
            if 'annotations' in self.dataset:
                for ann in self.dataset['annotations']:
                    imgToAnns[ann['image_id']].append(ann)
                    anns[ann['id']] = ann
    
            if 'images' in self.dataset:
                for img in self.dataset['images']:
                    imgs[img['id']] = img
    
            if 'categories' in self.dataset:
                for cat in self.dataset['categories']:
                    cats[cat['id']] = cat
    
            if 'annotations' in self.dataset and 'categories' in self.dataset:
                for ann in self.dataset['annotations']:
                    catToImgs[ann['category_id']].append(ann['image_id'])
    
            print('index created!!!')
    
            #         
            self.anns = anns
            self.imgs = imgs
            self.cats = cats
            self.imgToAnns = imgToAnns
            self.catToImgs = catToImgs
    
    

    データの関連関係を理解するポイントはimgToAnns変数の類型に対する理解である.なお、imgToAnnsのタイプは、defaultdict()である.dictのキーはidであり、値はlistであり、この図に属するすべての寸法情報を含む.ここでlistのメンバータイプは辞書であり、その要素は「annotations」ドメイン内の1つ(または複数)の寸法情報である.
    例:
    { 289343: [
                {'id': 1768, 'area': 702.1057499999998, 
                 'segmentation': [[510.66, ... 510.45, 423.01]], 
                 'bbox': [473.07, 395.93, 38.65, 28.67], 
                 'category_id': 18, 
                 'iscrowd': 0, 
                 'image_id': 289343
                },
                ...
            ]
    }
    

    catToImgs変数はimgToAnnsに類似する.
    2)getAnnIds()-寸法情報を取得するids
     def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None):
       """
                     id.        ,                id
       :param imgIds  (int   )     :      ids   
               catIds  (int   )     :      ids   
               areaRng (float   )   :          , 2    . (e.g. [0 inf])   
               iscrowd (boolean)      : get anns for given crowd label (False or True)
       :return: ids (int   )       :            ids
       """
       imgIds = imgIds if _isArrayLike(imgIds) else [imgIds]
       catIds = catIds if _isArrayLike(catIds) else [catIds]
    
       if len(imgIds) == len(catIds) == len(areaRng) == 0:
           anns = self.dataset['annotations']
       else:
           if not len(imgIds) == 0:
               lists = [self.imgToAnns[imgId] for imgId in imgIds if imgId in self.imgToAnns]
               anns = list(itertools.chain.from_iterable(lists))
           else:
               anns = self.dataset['annotations']
    
           anns = anns if len(catIds)  == 0 
    		       else [ann for ann in anns if ann['category_id'] in catIds]
           anns = anns if len(areaRng) == 0 
    		       else [ann for ann in anns 
    			       if ann['area'] > areaRng[0] and ann['area'] < areaRng[1]]
    
       if not iscrowd == None:
           ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd]
       else:
           ids = [ann['id'] for ann in anns]
    
       return ids
    

    3)showAnns()-指定した寸法情報を表示
    def showAnns(self, anns):
        """
                 .            ,            : plt.imshow() 
        :param anns (object   ):         
        :return: None
        """
        if len(anns) == 0:
            return 0
        if 'segmentation' in anns[0] or 'keypoints' in anns[0]:
            datasetType = 'instances'
        elif 'caption' in anns[0]:
            datasetType = 'captions'
        else:
            raise Exception('datasetType not supported')
            
        #          
        if datasetType == 'instances':
            ax = plt.gca()
            ax.set_autoscale_on(False)
            polygons = []
            color = []
            for ann in anns:
                #       mask         
                c = (np.random.random((1, 3))*0.6+0.4).tolist()[0]
                #    mask     
                if 'segmentation' in ann:
                    if type(ann['segmentation']) == list:
                        # polygon
                        for seg in ann['segmentation']:
                            #           ,   (x, y)   
                            poly = np.array(seg).reshape((int(len(seg)/2), 2))
                            polygons.append(Polygon(poly))
                            color.append(c)
                    else:
                        # mask
                        t = self.imgs[ann['image_id']]
                        if type(ann['segmentation']['counts']) == list:
                            rle = maskUtils.frPyObjects([ann['segmentation']], t['height'], t['width'])
                        else:
                            rle = [ann['segmentation']]
                        m = maskUtils.decode(rle)
                        img = np.ones( (m.shape[0], m.shape[1], 3) )
                        if ann['iscrowd'] == 1:
                            color_mask = np.array([2.0,166.0,101.0])/255
                        if ann['iscrowd'] == 0:
                            color_mask = np.random.random((1, 3)).tolist()[0]
                        for i in range(3):
                            img[:,:,i] = color_mask[i]
                        ax.imshow(np.dstack( (img, m*0.5) ))
                 #       keypoints     
                if 'keypoints' in ann and type(ann['keypoints']) == list:
                    # turn skeleton into zero-based index
                    sks = np.array(self.loadCats(ann['category_id'])[0]['skeleton'])-1
                    kp = np.array(ann['keypoints'])
                    x = kp[0::3]
                    y = kp[1::3]
                    v = kp[2::3]
                    for sk in sks:
                        if np.all(v[sk]>0):
                            plt.plot(x[sk],y[sk], linewidth=3, color=c)
                    plt.plot(x[v>0], y[v>0],'o',markersize=8, markerfacecolor=c, markeredgecolor='k',markeredgewidth=2)
                    plt.plot(x[v>1], y[v>1],'o',markersize=8, markerfacecolor=c, markeredgecolor=c, markeredgewidth=2)
            p = PatchCollection(polygons, facecolor=color, linewidths=0, alpha=0.4)
            ax.add_collection(p)
            p = PatchCollection(polygons, facecolor='none', edgecolors=color, linewidths=2)
            ax.add_collection(p)
        elif datasetType == 'captions':
            for ann in anns:
                print(ann['caption'])
    

    COCOのsegmentationの寸法情報は多角形で、私たちは多角形を描くだけでいいです.
    一般に、この関数呼び出しの前に、画像表示関数:plt.imshow(). マークアップ情報を画像に表示する方が直感的であるからである.関数の内部でpltを使用します.gca()関数は、現在のfigureの軸、すなわち、前に表示するピクチャが存在するfigureを取得し、その後の寸法情報をこの図に表示することができる.