├── .gitignore ├── README.md ├── challenge_iccv_2025 ├── Dockerfile.jetson ├── Dockerfile.train ├── README.md ├── data │ ├── Fisheye1k.yaml │ └── Fisheye8k.yaml ├── models │ └── yolo11n_fisheye8k.pt ├── pycocotools │ ├── __init__.py │ ├── _mask.cpython-310-aarch64-linux-gnu.so │ ├── _mask.cpython-311-x86_64-linux-gnu.so │ ├── coco.py │ ├── cocoeval.py │ ├── cocoeval_modified.py │ └── mask.py ├── run_evaluation_jetson.py ├── run_train_yolo.py ├── utils.py └── yolo11n.pt ├── evaluation_Jetson ├── eval.py ├── eval_f1.py └── pycocotools │ ├── __init__.py │ ├── _mask.c │ ├── _mask.cpython-310-aarch64-linux-gnu.so │ ├── _mask.pyx │ ├── coco.py │ ├── cocoeval.py │ ├── cocoeval_modified.py │ └── mask.py ├── evaluation_Linux ├── EVALUATION.md ├── README.md ├── eval.py ├── eval_f1.py └── pycocotools │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── coco.cpython-38.pyc │ ├── cocoeval.cpython-38.pyc │ └── mask.cpython-38.pyc │ ├── _mask.cpython-38-x86_64-linux-gnu.so │ ├── coco.py │ ├── cocoeval.py │ ├── cocoeval_modified.py │ └── mask.py └── evaluation_Windows ├── EVALUATION.md ├── README.md ├── eval.py ├── eval_f1.py └── pycocotools ├── __init__.py ├── __pycache__ ├── __init__.cpython-310.pyc ├── __init__.cpython-39.pyc ├── coco.cpython-310.pyc ├── coco.cpython-39.pyc ├── cocoeval.cpython-39.pyc ├── cocoeval_modified.cpython-39.pyc └── mask.cpython-39.pyc ├── _mask.cp39-win_amd64.pyd ├── coco.py ├── cocoeval.py ├── cocoeval_modified.py └── mask.py /.gitignore: -------------------------------------------------------------------------------- 1 | #challenge_iccv_2025/models/ 2 | data/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FishEye8K: A Benchmark and Dataset for Fisheye Camera Object Detection 2 | 3 | **Authors:** 4 | [Munkhjargal Gochoo](https://github.com/moyog/), [Munkh-Erdene Otgonbold](https://github.com/omunkhuush/), [Erkhembayar Ganbold](https://github.com/Erkheme98), Ming-Ching Chang, [Ping-Yang Chen](https://github.com/pingyang1117), Byambaa Dorj, Hamad Al Jassmi, [Ganzorig Batnasan](https://github.com/ganzobtn/), Fady Alnajjar, Mohammed Abduljabbar, Fang-Pang Lin, Jun-Wei Hsieh 5 | 6 |

7 | 8 |

9 | 10 | We provide detailed information on the new FishEye8K road object detection dataset. The dataset consists of 8,000 annotated images with 157K bounding boxes of five object classes. The figure displays sample images from each of the 18 cameras with wide-angle fisheye views. These cameras offer new possibilities for extensive coverage. 11 | 12 | **Results of YOLOv7-E6E model on the input size 1280x1280.** 13 | 14 | https://github.com/MoyoG/FishEye8K/assets/73123564/f7dd27db-7613-4e2c-b5f0-ec33c44deba4 15 | 16 | 17 | 18 | **Classes (Color)** 19 | - ![#FF3333](https://placehold.co/15x15/FF3333/FF3333.png) - Bus 20 | - ![#3358FF](https://placehold.co/15x15/3358FF/3358FF.png) - Bike 21 | - ![#33FF33](https://placehold.co/15x15/33FF33/33FF33.png) - Car 22 | - ![#F6FF33](https://placehold.co/15x15/F6FF33/F6FF33.png) - Pedestrian 23 | - ![#9F33FF](https://placehold.co/15x15/9F33FF/9F33FF.png) - Truck 24 | 25 | 26 | # Dataset 27 | - [Click to download the Fisheye8K dataset](https://scidm.nchc.org.tw/en/dataset/fisheye8k) 28 | - To download the whole dataset, choose **Fisheye8K_all_including_train&test_update_2024Jan Update.zip** 29 | - Click on "**Explore**" and "**Go to resource**" 30 | 31 | ![FishEye8K download](https://github.com/MoyoG/FishEye8K/assets/10125947/a647e24c-e716-4317-8dc5-0dc31f369ee2) 32 | 33 | - Note: If you downloaded the older version **Fisheye8K_all_including_train&test_1.zip** before Jan 28, **MS COCO (train.json and test.json) labels should be replaced** with these updated versions [Click here to download correct MS COCO labels](https://drive.google.com/drive/folders/1J4nYmTwXoDc47_2tq4aSCXBkkrCxTCP3?usp=sharing) 34 | 35 | # Paper 36 | - [Click to download the paper](https://openaccess.thecvf.com/content/CVPR2023W/AICity/html/Gochoo_FishEye8K_A_Benchmark_and_Dataset_for_Fisheye_Camera_Object_Detection_CVPRW_2023_paper.html?fbclid=IwAR2UCUtrPydcCFxRjN67d7RyAyX-dNJAEi7mWPJjWhX3kTUA_SQ4AJyaFWc) 37 | 38 | # Train and Test 39 | 40 |

41 | 42 |

43 | 44 | 45 | # Experimental results 46 | 47 | We evaluated using default yolo test.py 48 | 49 | - conf_thres=0.5 50 | - iou_thres=0.5 51 | 52 | | Model | Version | Input Size | Precision | Recall | mAP0.5 | mAP.5-.95 | f1\-score | APS | APM | APL | Inference[ms] | 53 | | ---------- | --------- | ---------- | --------- | ------ | ------ | --------- | --------- | ------ | ------ | ------ | ------------- | 54 | | YOLOv5 | YOLOv5l6 | 1280 | 0.7929 | 0.4076 | 0.6139 | 0.4098 | 0.535 | 0.1299 | 0.434 | 0.6665 | 22.7 | 55 | | | YOLOv5x6 | 1280 | 0.8224 | 0.4313 | 0.6387 | 0.4268 | 0.5588 | 0.133 | 0.452 | 0.6925 | 43.9 | 56 | | YOLOR | YOLOR-W6 | 1280 | 0.7871 | 0.4718 | 0.6466 | 0.4442 | 0.5899 | 0.1325 | 0.4707 | 0.6901 | 16.4 | 57 | | | YOLOR-P6 | 1280 | 0.8019 | 0.4937 | 0.6632 | 0.4406 | 0.6111 | 0.1419 | 0.4805 | 0.7216 | 13.4 | 58 | | YOLOv7 | YOLOv7-D6 | 1280 | 0.7803 | 0.4111 | 0.3977 | 0.2633 | 0.5197 | 0.1261 | 0.4462 | 0.6777 | 26.4 | 59 | | | YOLOv7-E6E| 1280 | 0.8005 | 0.5252 | 0.5081 | 0.3265 | 0.6294 | 0.1684 | 0.5019 | 0.6927 | 29.8 | 60 | | YOLOv7 | YOLOv7 | 640 | 0.7917 | 0.4373 | 0.4235 | 0.2473 | 0.5453 | 0.1108 | 0.4438 | 0.6804 | 4.3 | 61 | | | YOLOv7-X | 640 | 0.7402 | 0.4888 | 0.4674 | 0.2919 | 0.5794 | 0.1332 | 0.4605 | 0.7212 | 6.7 | 62 | | YOLOv8 | YOLOv8l | 640 | 0.7835 | 0.3877 | 0.612 | 0.4012 | 0.5187 | 0.1038 | 0.4043 | 0.6577 | 8.5 | 63 | | | YOLOv8x | 640 | 0.8418 | 0.3665 | 0.6146 | 0.4029 | 0.5106 | 0.0997 | 0.4147 | 0.7083 | 13.4 | 64 | 65 | # Citation 66 | ``` 67 | @InProceedings{Gochoo_2023_CVPR, 68 | author = {Gochoo, Munkhjargal and Otgonbold, Munkh-Erdene and Ganbold, Erkhembayar and Hsieh, Jun-Wei and Chang, Ming-Ching and Chen, Ping-Yang and Dorj, Byambaa and Al Jassmi, Hamad and Batnasan, Ganzorig and Alnajjar, Fady and Abduljabbar, Mohammed and Lin, Fang-Pang}, 69 | title = {FishEye8K: A Benchmark and Dataset for Fisheye Camera Object Detection}, 70 | booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR) Workshops}, 71 | month = {June}, 72 | year = {2023}, 73 | pages = {5304-5312} 74 | } 75 | 76 | -------------------------------------------------------------------------------- /challenge_iccv_2025/Dockerfile.jetson: -------------------------------------------------------------------------------- 1 | FROM ultralytics/ultralytics:latest-jetson-jetpack6 2 | 3 | WORKDIR /app 4 | COPY . . 5 | 6 | ENTRYPOINT ["python", "run_evaluation_jetson.py"] 7 | -------------------------------------------------------------------------------- /challenge_iccv_2025/Dockerfile.train: -------------------------------------------------------------------------------- 1 | FROM ultralytics/ultralytics:latest 2 | 3 | WORKDIR /app 4 | COPY . . 5 | # COPY /data /app/data 6 | ENTRYPOINT ["python", "run_train_yolo.py"] 7 | -------------------------------------------------------------------------------- /challenge_iccv_2025/README.md: -------------------------------------------------------------------------------- 1 | # 🐳 Docker Setup for AI City ICCV 2025 Track 4 2 | 3 | This guide provides a quick start tutorial for container submissions using a fine-tuned YOLOv11n model as a reference. 4 | 5 | **You should develop your own implementations of the get_model, preprocess_image, and postprocess_results functions in utils.py for your submission. Prize winners need to place their pretraining data (if applicable) and models in the shared Google Drive and upload training and evaluation containers on Docker Hub. Your training and evaluation scripts inside the container should load models from the mounted /models directory and the data from the /data directory.** 6 | 7 | # Evaluation Container Instruction 8 | 9 | ## 🔹 Pull the Prebuilt Docker Image 10 | 11 | Start by pulling the prebuilt Docker image designed for Jetson devices: 12 | 13 | ```bash 14 | docker pull ganzobtn/aicity_iccv_2025_track4_jetson:latest 15 | ``` 16 | ## 🔹 Build Image Locally (Optional) 17 | If you'd prefer to build the Docker image locally: 18 | 19 | ```bash 20 | docker build -f Dockerfile.jetson -t ganzobtn/aicity_iccv_2025_track4_jetson:latest . 21 | ``` 22 | 23 | ## 🔹 Run the Docker container 24 | 25 | ```bash 26 | IMAGE="ganzobtn/aicity_iccv_2025_track4_jetson:latest" 27 | DATA_DIR="/path/to/your/data" 28 | ``` 29 | 30 | ```bash 31 | docker run -it --ipc=host --runtime=nvidia -v ${DATA_DIR}:/data ${IMAGE} 32 | ``` 33 | 34 | 📁 Expected Directory Structure 35 | 36 | The `run_evaluation_jetson.py` script inside the container expects the following structure: 37 | 38 | - `/data/FishEye1K_eval/` Contains the groundtruth.json file, evaluation images and corresponding annotation files. 39 | 40 | - `models/yolo11n_fisheye8k.pt` 41 | The fine-tuned YOLOv11n model file used for inference. 42 | 43 | 44 | 45 | # Training Container Instruction 46 | 47 | This section provides a getting started guide for setting up and running the training Docker container for the challenge, which uses a YOLOv11n model finetuning pipeline. 48 | 49 | ## 🔹 Pull Prebuilt Docker Image 50 | 51 | You can use the prebuilt Docker image available on Docker Hub: 52 | 53 | ```bash 54 | docker pull ganzobtn/aicity_iccv_2025_track4_train:latest 55 | ``` 56 | --- 57 | 58 | ## 🔹 Build the Docker Image Locally (Optional) 59 | If you'd prefer to build the image from the provided Dockerfile: 60 | 61 | ```bash 62 | docker build -f Dockerfile.train -t ganzobtn/aicity_iccv_2025_track4_train:latest . 63 | ``` 64 | 65 | ## 🔹 Run the Docker Container 66 | Set your local paths and run the container: 67 | 68 | ```bash 69 | IMAGE="ganzobtn/aicity_iccv_2025_track4_train:latest" 70 | 71 | DATA_DIR="/path/to/your/data" 72 | MODEL_DIR="/path/to/your/models" 73 | 74 | docker run -it --ipc=host --runtime=nvidia \ 75 | -v ${DATA_DIR}:/data \ 76 | -v ${MODEL_DIR}:/models \ 77 | ${IMAGE} 78 | ``` 79 | 80 | 📁 Expected Directory Structure 81 | run_train_yolo.py script inside the container expects the following structure: 82 | 83 | - A dataset folder named Fisheye8K located inside /data. 84 | 85 | - Trained models and output logs will be saved to /models. 86 | 87 | -------------------------------------------------------------------------------- /challenge_iccv_2025/data/Fisheye1k.yaml: -------------------------------------------------------------------------------- 1 | path: /data 2 | 3 | val: Fisheye1K_eval/images 4 | nc: 5 5 | names: ['Bus','Bike','Car','Pedestrian','Truck'] -------------------------------------------------------------------------------- /challenge_iccv_2025/data/Fisheye8k.yaml: -------------------------------------------------------------------------------- 1 | path: /data/Fisheye8K 2 | train: train/images 3 | val: test/images 4 | 5 | nc: 5 6 | 7 | # class names 8 | names: ['Bus','Bike','Car','Pedestrian','Truck'] -------------------------------------------------------------------------------- /challenge_iccv_2025/models/yolo11n_fisheye8k.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/challenge_iccv_2025/models/yolo11n_fisheye8k.pt -------------------------------------------------------------------------------- /challenge_iccv_2025/pycocotools/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | -------------------------------------------------------------------------------- /challenge_iccv_2025/pycocotools/_mask.cpython-310-aarch64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/challenge_iccv_2025/pycocotools/_mask.cpython-310-aarch64-linux-gnu.so -------------------------------------------------------------------------------- /challenge_iccv_2025/pycocotools/_mask.cpython-311-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/challenge_iccv_2025/pycocotools/_mask.cpython-311-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /challenge_iccv_2025/pycocotools/coco.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | __version__ = '2.0' 3 | # Interface for accessing the Microsoft COCO dataset. 4 | 5 | # Microsoft COCO is a large image dataset designed for object detection, 6 | # segmentation, and caption generation. pycocotools is a Python API that 7 | # assists in loading, parsing and visualizing the annotations in COCO. 8 | # Please visit http://mscoco.org/ for more information on COCO, including 9 | # for the data, paper, and tutorials. The exact format of the annotations 10 | # is also described on the COCO website. For example usage of the pycocotools 11 | # please see pycocotools_demo.ipynb. In addition to this API, please download both 12 | # the COCO images and annotations in order to run the demo. 13 | 14 | # An alternative to using the API is to load the annotations directly 15 | # into Python dictionary 16 | # Using the API provides additional utility functions. Note that this API 17 | # supports both *instance* and *caption* annotations. In the case of 18 | # captions not all functions are defined (e.g. categories are undefined). 19 | 20 | # The following API functions are defined: 21 | # COCO - COCO api class that loads COCO annotation file and prepare data structures. 22 | # decodeMask - Decode binary mask M encoded via run-length encoding. 23 | # encodeMask - Encode binary mask M using run-length encoding. 24 | # getAnnIds - Get ann ids that satisfy given filter conditions. 25 | # getCatIds - Get cat ids that satisfy given filter conditions. 26 | # getImgIds - Get img ids that satisfy given filter conditions. 27 | # loadAnns - Load anns with the specified ids. 28 | # loadCats - Load cats with the specified ids. 29 | # loadImgs - Load imgs with the specified ids. 30 | # annToMask - Convert segmentation in an annotation to binary mask. 31 | # showAnns - Display the specified annotations. 32 | # loadRes - Load algorithm results and create API for accessing them. 33 | # download - Download COCO images from mscoco.org server. 34 | # Throughout the API "ann"=annotation, "cat"=category, and "img"=image. 35 | # Help on each functions can be accessed by: "help COCO>function". 36 | 37 | # See also COCO>decodeMask, 38 | # COCO>encodeMask, COCO>getAnnIds, COCO>getCatIds, 39 | # COCO>getImgIds, COCO>loadAnns, COCO>loadCats, 40 | # COCO>loadImgs, COCO>annToMask, COCO>showAnns 41 | 42 | # Microsoft COCO Toolbox. version 2.0 43 | # Data, paper, and tutorials available at: http://mscoco.org/ 44 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2014. 45 | # Licensed under the Simplified BSD License [see bsd.txt] 46 | 47 | import json 48 | import time 49 | import numpy as np 50 | import copy 51 | import itertools 52 | from . import mask as maskUtils 53 | import os 54 | from collections import defaultdict 55 | import sys 56 | PYTHON_VERSION = sys.version_info[0] 57 | if PYTHON_VERSION == 2: 58 | from urllib import urlretrieve 59 | elif PYTHON_VERSION == 3: 60 | from urllib.request import urlretrieve 61 | 62 | 63 | def _isArrayLike(obj): 64 | return hasattr(obj, '__iter__') and hasattr(obj, '__len__') 65 | 66 | 67 | class COCO: 68 | def __init__(self, annotation_file=None): 69 | """ 70 | Constructor of Microsoft COCO helper class for reading and visualizing annotations. 71 | :param annotation_file (str): location of annotation file 72 | :param image_folder (str): location to the folder that hosts images. 73 | :return: 74 | """ 75 | # load dataset 76 | self.dataset,self.anns,self.cats,self.imgs = dict(),dict(),dict(),dict() 77 | self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list) 78 | if not annotation_file == None: 79 | print('loading annotations into memory...') 80 | tic = time.time() 81 | with open(annotation_file, 'r') as f: 82 | dataset = json.load(f) 83 | assert type(dataset)==dict, 'annotation file format {} not supported'.format(type(dataset)) 84 | print('Done (t={:0.2f}s)'.format(time.time()- tic)) 85 | self.dataset = dataset 86 | self.createIndex() 87 | 88 | def createIndex(self): 89 | # create index 90 | print('creating index...') 91 | anns, cats, imgs = {}, {}, {} 92 | imgToAnns,catToImgs = defaultdict(list),defaultdict(list) 93 | if 'annotations' in self.dataset: 94 | for ann in self.dataset['annotations']: 95 | imgToAnns[ann['image_id']].append(ann) 96 | anns[ann['id']] = ann 97 | 98 | if 'images' in self.dataset: 99 | for img in self.dataset['images']: 100 | imgs[img['id']] = img 101 | 102 | if 'categories' in self.dataset: 103 | for cat in self.dataset['categories']: 104 | cats[cat['id']] = cat 105 | 106 | if 'annotations' in self.dataset and 'categories' in self.dataset: 107 | for ann in self.dataset['annotations']: 108 | catToImgs[ann['category_id']].append(ann['image_id']) 109 | 110 | print('index created!') 111 | 112 | # create class members 113 | self.anns = anns 114 | self.imgToAnns = imgToAnns 115 | self.catToImgs = catToImgs 116 | self.imgs = imgs 117 | self.cats = cats 118 | 119 | def info(self): 120 | """ 121 | Print information about the annotation file. 122 | :return: 123 | """ 124 | for key, value in self.dataset['info'].items(): 125 | print('{}: {}'.format(key, value)) 126 | 127 | def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None): 128 | """ 129 | Get ann ids that satisfy given filter conditions. default skips that filter 130 | :param imgIds (int array) : get anns for given imgs 131 | catIds (int array) : get anns for given cats 132 | areaRng (float array) : get anns for given area range (e.g. [0 inf]) 133 | iscrowd (boolean) : get anns for given crowd label (False or True) 134 | :return: ids (int array) : integer array of ann ids 135 | """ 136 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 137 | catIds = catIds if _isArrayLike(catIds) else [catIds] 138 | 139 | if len(imgIds) == len(catIds) == len(areaRng) == 0: 140 | anns = self.dataset['annotations'] 141 | else: 142 | if not len(imgIds) == 0: 143 | lists = [self.imgToAnns[imgId] for imgId in imgIds if imgId in self.imgToAnns] 144 | anns = list(itertools.chain.from_iterable(lists)) 145 | else: 146 | anns = self.dataset['annotations'] 147 | anns = anns if len(catIds) == 0 else [ann for ann in anns if ann['category_id'] in catIds] 148 | anns = anns if len(areaRng) == 0 else [ann for ann in anns if ann['area'] > areaRng[0] and ann['area'] < areaRng[1]] 149 | if not iscrowd == None: 150 | ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd] 151 | else: 152 | ids = [ann['id'] for ann in anns] 153 | return ids 154 | 155 | def getCatIds(self, catNms=[], supNms=[], catIds=[]): 156 | """ 157 | filtering parameters. default skips that filter. 158 | :param catNms (str array) : get cats for given cat names 159 | :param supNms (str array) : get cats for given supercategory names 160 | :param catIds (int array) : get cats for given cat ids 161 | :return: ids (int array) : integer array of cat ids 162 | """ 163 | catNms = catNms if _isArrayLike(catNms) else [catNms] 164 | supNms = supNms if _isArrayLike(supNms) else [supNms] 165 | catIds = catIds if _isArrayLike(catIds) else [catIds] 166 | 167 | if len(catNms) == len(supNms) == len(catIds) == 0: 168 | cats = self.dataset['categories'] 169 | else: 170 | cats = self.dataset['categories'] 171 | cats = cats if len(catNms) == 0 else [cat for cat in cats if cat['name'] in catNms] 172 | cats = cats if len(supNms) == 0 else [cat for cat in cats if cat['supercategory'] in supNms] 173 | cats = cats if len(catIds) == 0 else [cat for cat in cats if cat['id'] in catIds] 174 | ids = [cat['id'] for cat in cats] 175 | return ids 176 | 177 | def getImgIds(self, imgIds=[], catIds=[]): 178 | ''' 179 | Get img ids that satisfy given filter conditions. 180 | :param imgIds (int array) : get imgs for given ids 181 | :param catIds (int array) : get imgs with all given cats 182 | :return: ids (int array) : integer array of img ids 183 | ''' 184 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 185 | catIds = catIds if _isArrayLike(catIds) else [catIds] 186 | 187 | if len(imgIds) == len(catIds) == 0: 188 | ids = self.imgs.keys() 189 | else: 190 | ids = set(imgIds) 191 | for i, catId in enumerate(catIds): 192 | if i == 0 and len(ids) == 0: 193 | ids = set(self.catToImgs[catId]) 194 | else: 195 | ids &= set(self.catToImgs[catId]) 196 | return list(ids) 197 | 198 | def loadAnns(self, ids=[]): 199 | """ 200 | Load anns with the specified ids. 201 | :param ids (int array) : integer ids specifying anns 202 | :return: anns (object array) : loaded ann objects 203 | """ 204 | if _isArrayLike(ids): 205 | return [self.anns[id] for id in ids] 206 | elif type(ids) == int: 207 | return [self.anns[ids]] 208 | 209 | def loadCats(self, ids=[]): 210 | """ 211 | Load cats with the specified ids. 212 | :param ids (int array) : integer ids specifying cats 213 | :return: cats (object array) : loaded cat objects 214 | """ 215 | if _isArrayLike(ids): 216 | return [self.cats[id] for id in ids] 217 | elif type(ids) == int: 218 | return [self.cats[ids]] 219 | 220 | def loadImgs(self, ids=[]): 221 | """ 222 | Load anns with the specified ids. 223 | :param ids (int array) : integer ids specifying img 224 | :return: imgs (object array) : loaded img objects 225 | """ 226 | if _isArrayLike(ids): 227 | return [self.imgs[id] for id in ids] 228 | elif type(ids) == int: 229 | return [self.imgs[ids]] 230 | 231 | def showAnns(self, anns, draw_bbox=False): 232 | """ 233 | Display the specified annotations. 234 | :param anns (array of object): annotations to display 235 | :return: None 236 | """ 237 | if len(anns) == 0: 238 | return 0 239 | if 'segmentation' in anns[0] or 'keypoints' in anns[0]: 240 | datasetType = 'instances' 241 | elif 'caption' in anns[0]: 242 | datasetType = 'captions' 243 | else: 244 | raise Exception('datasetType not supported') 245 | if datasetType == 'instances': 246 | import matplotlib.pyplot as plt 247 | from matplotlib.collections import PatchCollection 248 | from matplotlib.patches import Polygon 249 | 250 | ax = plt.gca() 251 | ax.set_autoscale_on(False) 252 | polygons = [] 253 | color = [] 254 | for ann in anns: 255 | c = (np.random.random((1, 3))*0.6+0.4).tolist()[0] 256 | if 'segmentation' in ann: 257 | if type(ann['segmentation']) == list: 258 | # polygon 259 | for seg in ann['segmentation']: 260 | poly = np.array(seg).reshape((int(len(seg)/2), 2)) 261 | polygons.append(Polygon(poly)) 262 | color.append(c) 263 | else: 264 | # mask 265 | t = self.imgs[ann['image_id']] 266 | if type(ann['segmentation']['counts']) == list: 267 | rle = maskUtils.frPyObjects([ann['segmentation']], t['height'], t['width']) 268 | else: 269 | rle = [ann['segmentation']] 270 | m = maskUtils.decode(rle) 271 | img = np.ones( (m.shape[0], m.shape[1], 3) ) 272 | if ann['iscrowd'] == 1: 273 | color_mask = np.array([2.0,166.0,101.0])/255 274 | if ann['iscrowd'] == 0: 275 | color_mask = np.random.random((1, 3)).tolist()[0] 276 | for i in range(3): 277 | img[:,:,i] = color_mask[i] 278 | ax.imshow(np.dstack( (img, m*0.5) )) 279 | if 'keypoints' in ann and type(ann['keypoints']) == list: 280 | # turn skeleton into zero-based index 281 | sks = np.array(self.loadCats(ann['category_id'])[0]['skeleton'])-1 282 | kp = np.array(ann['keypoints']) 283 | x = kp[0::3] 284 | y = kp[1::3] 285 | v = kp[2::3] 286 | for sk in sks: 287 | if np.all(v[sk]>0): 288 | plt.plot(x[sk],y[sk], linewidth=3, color=c) 289 | plt.plot(x[v>0], y[v>0],'o',markersize=8, markerfacecolor=c, markeredgecolor='k',markeredgewidth=2) 290 | plt.plot(x[v>1], y[v>1],'o',markersize=8, markerfacecolor=c, markeredgecolor=c, markeredgewidth=2) 291 | 292 | if draw_bbox: 293 | [bbox_x, bbox_y, bbox_w, bbox_h] = ann['bbox'] 294 | poly = [[bbox_x, bbox_y], [bbox_x, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y]] 295 | np_poly = np.array(poly).reshape((4,2)) 296 | polygons.append(Polygon(np_poly)) 297 | color.append(c) 298 | 299 | p = PatchCollection(polygons, facecolor=color, linewidths=0, alpha=0.4) 300 | ax.add_collection(p) 301 | p = PatchCollection(polygons, facecolor='none', edgecolors=color, linewidths=2) 302 | ax.add_collection(p) 303 | elif datasetType == 'captions': 304 | for ann in anns: 305 | print(ann['caption']) 306 | 307 | def loadRes(self, resFile): 308 | """ 309 | Load result file and return a result api object. 310 | :param resFile (str) : file name of result file 311 | :return: res (obj) : result api object 312 | """ 313 | res = COCO() 314 | res.dataset['images'] = [img for img in self.dataset['images']] 315 | 316 | print('Loading and preparing results...') 317 | tic = time.time() 318 | if type(resFile) == str or (PYTHON_VERSION == 2 and type(resFile) == unicode): 319 | with open(resFile) as f: 320 | anns = json.load(f) 321 | elif type(resFile) == np.ndarray: 322 | anns = self.loadNumpyAnnotations(resFile) 323 | else: 324 | anns = resFile 325 | assert type(anns) == list, 'results in not an array of objects' 326 | annsImgIds = [ann['image_id'] for ann in anns] 327 | assert set(annsImgIds) == (set(annsImgIds) & set(self.getImgIds())), \ 328 | 'Results do not correspond to current coco set' 329 | if 'caption' in anns[0]: 330 | imgIds = set([img['id'] for img in res.dataset['images']]) & set([ann['image_id'] for ann in anns]) 331 | res.dataset['images'] = [img for img in res.dataset['images'] if img['id'] in imgIds] 332 | for id, ann in enumerate(anns): 333 | ann['id'] = id+1 334 | elif 'bbox' in anns[0] and not anns[0]['bbox'] == []: 335 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 336 | for id, ann in enumerate(anns): 337 | bb = ann['bbox'] 338 | x1, x2, y1, y2 = [bb[0], bb[0]+bb[2], bb[1], bb[1]+bb[3]] 339 | if not 'segmentation' in ann: 340 | ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] 341 | ann['area'] = bb[2]*bb[3] 342 | ann['id'] = id+1 343 | ann['iscrowd'] = 0 344 | elif 'segmentation' in anns[0]: 345 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 346 | for id, ann in enumerate(anns): 347 | # now only support compressed RLE format as segmentation results 348 | ann['area'] = maskUtils.area(ann['segmentation']) 349 | if not 'bbox' in ann: 350 | ann['bbox'] = maskUtils.toBbox(ann['segmentation']) 351 | ann['id'] = id+1 352 | ann['iscrowd'] = 0 353 | elif 'keypoints' in anns[0]: 354 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 355 | for id, ann in enumerate(anns): 356 | s = ann['keypoints'] 357 | x = s[0::3] 358 | y = s[1::3] 359 | x0,x1,y0,y1 = np.min(x), np.max(x), np.min(y), np.max(y) 360 | ann['area'] = (x1-x0)*(y1-y0) 361 | ann['id'] = id + 1 362 | ann['bbox'] = [x0,y0,x1-x0,y1-y0] 363 | print('DONE (t={:0.2f}s)'.format(time.time()- tic)) 364 | 365 | res.dataset['annotations'] = anns 366 | res.createIndex() 367 | return res 368 | 369 | def download(self, tarDir = None, imgIds = [] ): 370 | ''' 371 | Download COCO images from mscoco.org server. 372 | :param tarDir (str): COCO results directory name 373 | imgIds (list): images to be downloaded 374 | :return: 375 | ''' 376 | if tarDir is None: 377 | print('Please specify target directory') 378 | return -1 379 | if len(imgIds) == 0: 380 | imgs = self.imgs.values() 381 | else: 382 | imgs = self.loadImgs(imgIds) 383 | N = len(imgs) 384 | if not os.path.exists(tarDir): 385 | os.makedirs(tarDir) 386 | for i, img in enumerate(imgs): 387 | tic = time.time() 388 | fname = os.path.join(tarDir, img['file_name']) 389 | if not os.path.exists(fname): 390 | urlretrieve(img['coco_url'], fname) 391 | print('downloaded {}/{} images (t={:0.1f}s)'.format(i, N, time.time()- tic)) 392 | 393 | def loadNumpyAnnotations(self, data): 394 | """ 395 | Convert result data from a numpy array [Nx7] where each row contains {imageID,x1,y1,w,h,score,class} 396 | :param data (numpy.ndarray) 397 | :return: annotations (python nested list) 398 | """ 399 | print('Converting ndarray to lists...') 400 | assert(type(data) == np.ndarray) 401 | print(data.shape) 402 | assert(data.shape[1] == 7) 403 | N = data.shape[0] 404 | ann = [] 405 | for i in range(N): 406 | if i % 1000000 == 0: 407 | print('{}/{}'.format(i,N)) 408 | ann += [{ 409 | 'image_id' : int(data[i, 0]), 410 | 'bbox' : [ data[i, 1], data[i, 2], data[i, 3], data[i, 4] ], 411 | 'score' : data[i, 5], 412 | 'category_id': int(data[i, 6]), 413 | }] 414 | return ann 415 | 416 | def annToRLE(self, ann): 417 | """ 418 | Convert annotation which can be polygons, uncompressed RLE to RLE. 419 | :return: binary mask (numpy 2D array) 420 | """ 421 | t = self.imgs[ann['image_id']] 422 | h, w = t['height'], t['width'] 423 | segm = ann['segmentation'] 424 | if type(segm) == list: 425 | # polygon -- a single object might consist of multiple parts 426 | # we merge all parts into one mask rle code 427 | rles = maskUtils.frPyObjects(segm, h, w) 428 | rle = maskUtils.merge(rles) 429 | elif type(segm['counts']) == list: 430 | # uncompressed RLE 431 | rle = maskUtils.frPyObjects(segm, h, w) 432 | else: 433 | # rle 434 | rle = ann['segmentation'] 435 | return rle 436 | 437 | def annToMask(self, ann): 438 | """ 439 | Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask. 440 | :return: binary mask (numpy 2D array) 441 | """ 442 | rle = self.annToRLE(ann) 443 | m = maskUtils.decode(rle) 444 | return m 445 | -------------------------------------------------------------------------------- /challenge_iccv_2025/pycocotools/cocoeval.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import numpy as np 4 | import datetime 5 | import time 6 | from collections import defaultdict 7 | from . import mask as maskUtils 8 | import copy 9 | 10 | class COCOeval: 11 | # Interface for evaluating detection on the Microsoft COCO dataset. 12 | # 13 | # The usage for CocoEval is as follows: 14 | # cocoGt=..., cocoDt=... # load dataset and results 15 | # E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object 16 | # E.params.recThrs = ...; # set parameters as desired 17 | # E.evaluate(); # run per image evaluation 18 | # E.accumulate(); # accumulate per image results 19 | # E.summarize(); # display summary metrics of results 20 | # For example usage see evalDemo.m and http://mscoco.org/. 21 | # 22 | # The evaluation parameters are as follows (defaults in brackets): 23 | # imgIds - [all] N img ids to use for evaluation 24 | # catIds - [all] K cat ids to use for evaluation 25 | # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation 26 | # recThrs - [0:.01:1] R=101 recall thresholds for evaluation 27 | # areaRng - [...] A=4 object area ranges for evaluation 28 | # maxDets - [1 10 100] M=3 thresholds on max detections per image 29 | # iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints' 30 | # iouType replaced the now DEPRECATED useSegm parameter. 31 | # useCats - [1] if true use category labels for evaluation 32 | # Note: if useCats=0 category labels are ignored as in proposal scoring. 33 | # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. 34 | # 35 | # evaluate(): evaluates detections on every image and every category and 36 | # concats the results into the "evalImgs" with fields: 37 | # dtIds - [1xD] id for each of the D detections (dt) 38 | # gtIds - [1xG] id for each of the G ground truths (gt) 39 | # dtMatches - [TxD] matching gt id at each IoU or 0 40 | # gtMatches - [TxG] matching dt id at each IoU or 0 41 | # dtScores - [1xD] confidence of each dt 42 | # gtIgnore - [1xG] ignore flag for each gt 43 | # dtIgnore - [TxD] ignore flag for each dt at each IoU 44 | # 45 | # accumulate(): accumulates the per-image, per-category evaluation 46 | # results in "evalImgs" into the dictionary "eval" with fields: 47 | # params - parameters used for evaluation 48 | # date - date evaluation was performed 49 | # counts - [T,R,K,A,M] parameter dimensions (see above) 50 | # precision - [TxRxKxAxM] precision for every evaluation setting 51 | # recall - [TxKxAxM] max recall for every evaluation setting 52 | # Note: precision and recall==-1 for settings with no gt objects. 53 | # 54 | # See also coco, mask, pycocoDemo, pycocoEvalDemo 55 | # 56 | # Microsoft COCO Toolbox. version 2.0 57 | # Data, paper, and tutorials available at: http://mscoco.org/ 58 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 59 | # Licensed under the Simplified BSD License [see coco/license.txt] 60 | def __init__(self, cocoGt=None, cocoDt=None, iouType='segm'): 61 | ''' 62 | Initialize CocoEval using coco APIs for gt and dt 63 | :param cocoGt: coco object with ground truth annotations 64 | :param cocoDt: coco object with detection results 65 | :return: None 66 | ''' 67 | if not iouType: 68 | print('iouType not specified. use default iouType segm') 69 | self.cocoGt = cocoGt # ground truth COCO API 70 | self.cocoDt = cocoDt # detections COCO API 71 | self.evalImgs = defaultdict(list) # per-image per-category evaluation results [KxAxI] elements 72 | self.eval = {} # accumulated evaluation results 73 | self._gts = defaultdict(list) # gt for evaluation 74 | self._dts = defaultdict(list) # dt for evaluation 75 | self.params = Params(iouType=iouType) # parameters 76 | self._paramsEval = {} # parameters for evaluation 77 | self.stats = [] # result summarization 78 | self.ious = {} # ious between all gts and dts 79 | if not cocoGt is None: 80 | self.params.imgIds = sorted(cocoGt.getImgIds()) 81 | self.params.catIds = sorted(cocoGt.getCatIds()) 82 | 83 | 84 | def _prepare(self): 85 | ''' 86 | Prepare ._gts and ._dts for evaluation based on params 87 | :return: None 88 | ''' 89 | def _toMask(anns, coco): 90 | # modify ann['segmentation'] by reference 91 | for ann in anns: 92 | rle = coco.annToRLE(ann) 93 | ann['segmentation'] = rle 94 | p = self.params 95 | if p.useCats: 96 | gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 97 | dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 98 | else: 99 | gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds)) 100 | dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds)) 101 | 102 | # convert ground truth to mask if iouType == 'segm' 103 | if p.iouType == 'segm': 104 | _toMask(gts, self.cocoGt) 105 | _toMask(dts, self.cocoDt) 106 | # set ignore flag 107 | for gt in gts: 108 | gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0 109 | gt['ignore'] = 'iscrowd' in gt and gt['iscrowd'] 110 | if p.iouType == 'keypoints': 111 | gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore'] 112 | self._gts = defaultdict(list) # gt for evaluation 113 | self._dts = defaultdict(list) # dt for evaluation 114 | for gt in gts: 115 | self._gts[gt['image_id'], gt['category_id']].append(gt) 116 | for dt in dts: 117 | self._dts[dt['image_id'], dt['category_id']].append(dt) 118 | self.evalImgs = defaultdict(list) # per-image per-category evaluation results 119 | self.eval = {} # accumulated evaluation results 120 | 121 | def evaluate(self): 122 | ''' 123 | Run per image evaluation on given images and store results (a list of dict) in self.evalImgs 124 | :return: None 125 | ''' 126 | tic = time.time() 127 | print('Running per image evaluation...') 128 | p = self.params 129 | # add backward compatibility if useSegm is specified in params 130 | if not p.useSegm is None: 131 | p.iouType = 'segm' if p.useSegm == 1 else 'bbox' 132 | print('useSegm (deprecated) is not None. Running {} evaluation'.format(p.iouType)) 133 | print('Evaluate annotation type *{}*'.format(p.iouType)) 134 | p.imgIds = list(np.unique(p.imgIds)) 135 | if p.useCats: 136 | p.catIds = list(np.unique(p.catIds)) 137 | p.maxDets = sorted(p.maxDets) 138 | self.params=p 139 | 140 | self._prepare() 141 | # loop through images, area range, max detection number 142 | catIds = p.catIds if p.useCats else [-1] 143 | 144 | if p.iouType == 'segm' or p.iouType == 'bbox': 145 | computeIoU = self.computeIoU 146 | elif p.iouType == 'keypoints': 147 | computeIoU = self.computeOks 148 | self.ious = {(imgId, catId): computeIoU(imgId, catId) \ 149 | for imgId in p.imgIds 150 | for catId in catIds} 151 | 152 | evaluateImg = self.evaluateImg 153 | maxDet = p.maxDets[-1] 154 | self.evalImgs = [evaluateImg(imgId, catId, areaRng, maxDet) 155 | for catId in catIds 156 | for areaRng in p.areaRng 157 | for imgId in p.imgIds 158 | ] 159 | self._paramsEval = copy.deepcopy(self.params) 160 | toc = time.time() 161 | print('DONE (t={:0.2f}s).'.format(toc-tic)) 162 | 163 | def computeIoU(self, imgId, catId): 164 | p = self.params 165 | if p.useCats: 166 | gt = self._gts[imgId,catId] 167 | dt = self._dts[imgId,catId] 168 | else: 169 | gt = [_ for cId in p.catIds for _ in self._gts[imgId,cId]] 170 | dt = [_ for cId in p.catIds for _ in self._dts[imgId,cId]] 171 | if len(gt) == 0 and len(dt) ==0: 172 | return [] 173 | inds = np.argsort([-d['score'] for d in dt], kind='mergesort') 174 | dt = [dt[i] for i in inds] 175 | if len(dt) > p.maxDets[-1]: 176 | dt=dt[0:p.maxDets[-1]] 177 | 178 | if p.iouType == 'segm': 179 | g = [g['segmentation'] for g in gt] 180 | d = [d['segmentation'] for d in dt] 181 | elif p.iouType == 'bbox': 182 | g = [g['bbox'] for g in gt] 183 | d = [d['bbox'] for d in dt] 184 | else: 185 | raise Exception('unknown iouType for iou computation') 186 | 187 | # compute iou between each dt and gt region 188 | iscrowd = [int(o['iscrowd']) for o in gt] 189 | ious = maskUtils.iou(d,g,iscrowd) 190 | return ious 191 | 192 | def computeOks(self, imgId, catId): 193 | p = self.params 194 | # dimention here should be Nxm 195 | gts = self._gts[imgId, catId] 196 | dts = self._dts[imgId, catId] 197 | inds = np.argsort([-d['score'] for d in dts], kind='mergesort') 198 | dts = [dts[i] for i in inds] 199 | if len(dts) > p.maxDets[-1]: 200 | dts = dts[0:p.maxDets[-1]] 201 | # if len(gts) == 0 and len(dts) == 0: 202 | if len(gts) == 0 or len(dts) == 0: 203 | return [] 204 | ious = np.zeros((len(dts), len(gts))) 205 | sigmas = p.kpt_oks_sigmas 206 | vars = (sigmas * 2)**2 207 | k = len(sigmas) 208 | # compute oks between each detection and ground truth object 209 | for j, gt in enumerate(gts): 210 | # create bounds for ignore regions(double the gt bbox) 211 | g = np.array(gt['keypoints']) 212 | xg = g[0::3]; yg = g[1::3]; vg = g[2::3] 213 | k1 = np.count_nonzero(vg > 0) 214 | bb = gt['bbox'] 215 | x0 = bb[0] - bb[2]; x1 = bb[0] + bb[2] * 2 216 | y0 = bb[1] - bb[3]; y1 = bb[1] + bb[3] * 2 217 | for i, dt in enumerate(dts): 218 | d = np.array(dt['keypoints']) 219 | xd = d[0::3]; yd = d[1::3] 220 | if k1>0: 221 | # measure the per-keypoint distance if keypoints visible 222 | dx = xd - xg 223 | dy = yd - yg 224 | else: 225 | # measure minimum distance to keypoints in (x0,y0) & (x1,y1) 226 | z = np.zeros((k)) 227 | dx = np.max((z, x0-xd),axis=0)+np.max((z, xd-x1),axis=0) 228 | dy = np.max((z, y0-yd),axis=0)+np.max((z, yd-y1),axis=0) 229 | e = (dx**2 + dy**2) / vars / (gt['area']+np.spacing(1)) / 2 230 | if k1 > 0: 231 | e=e[vg > 0] 232 | ious[i, j] = np.sum(np.exp(-e)) / e.shape[0] 233 | return ious 234 | 235 | def evaluateImg(self, imgId, catId, aRng, maxDet): 236 | ''' 237 | perform evaluation for single category and image 238 | :return: dict (single image results) 239 | ''' 240 | p = self.params 241 | if p.useCats: 242 | gt = self._gts[imgId,catId] 243 | dt = self._dts[imgId,catId] 244 | else: 245 | gt = [_ for cId in p.catIds for _ in self._gts[imgId,cId]] 246 | dt = [_ for cId in p.catIds for _ in self._dts[imgId,cId]] 247 | if len(gt) == 0 and len(dt) ==0: 248 | return None 249 | 250 | for g in gt: 251 | if g['ignore'] or (g['area']aRng[1]): 252 | g['_ignore'] = 1 253 | else: 254 | g['_ignore'] = 0 255 | 256 | # sort dt highest score first, sort gt ignore last 257 | gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort') 258 | gt = [gt[i] for i in gtind] 259 | dtind = np.argsort([-d['score'] for d in dt], kind='mergesort') 260 | dt = [dt[i] for i in dtind[0:maxDet]] 261 | iscrowd = [int(o['iscrowd']) for o in gt] 262 | # load computed ious 263 | ious = self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] 264 | 265 | T = len(p.iouThrs) 266 | G = len(gt) 267 | D = len(dt) 268 | gtm = np.zeros((T,G)) 269 | dtm = np.zeros((T,D)) 270 | gtIg = np.array([g['_ignore'] for g in gt]) 271 | dtIg = np.zeros((T,D)) 272 | if not len(ious)==0: 273 | for tind, t in enumerate(p.iouThrs): 274 | for dind, d in enumerate(dt): 275 | # information about best match so far (m=-1 -> unmatched) 276 | iou = min([t,1-1e-10]) 277 | m = -1 278 | for gind, g in enumerate(gt): 279 | # if this gt already matched, and not a crowd, continue 280 | if gtm[tind,gind]>0 and not iscrowd[gind]: 281 | continue 282 | # if dt matched to reg gt, and on ignore gt, stop 283 | if m>-1 and gtIg[m]==0 and gtIg[gind]==1: 284 | break 285 | # continue to next gt unless better match made 286 | if ious[dind,gind] < iou: 287 | continue 288 | # if match successful and best so far, store appropriately 289 | iou=ious[dind,gind] 290 | m=gind 291 | # if match made store id of match for both dt and gt 292 | if m ==-1: 293 | continue 294 | dtIg[tind,dind] = gtIg[m] 295 | dtm[tind,dind] = gt[m]['id'] 296 | gtm[tind,m] = d['id'] 297 | # set unmatched detections outside of area range to ignore 298 | a = np.array([d['area']aRng[1] for d in dt]).reshape((1, len(dt))) 299 | dtIg = np.logical_or(dtIg, np.logical_and(dtm==0, np.repeat(a,T,0))) 300 | # store results for given image and category 301 | return { 302 | 'image_id': imgId, 303 | 'category_id': catId, 304 | 'aRng': aRng, 305 | 'maxDet': maxDet, 306 | 'dtIds': [d['id'] for d in dt], 307 | 'gtIds': [g['id'] for g in gt], 308 | 'dtMatches': dtm, 309 | 'gtMatches': gtm, 310 | 'dtScores': [d['score'] for d in dt], 311 | 'gtIgnore': gtIg, 312 | 'dtIgnore': dtIg, 313 | } 314 | 315 | def accumulate(self, p = None): 316 | ''' 317 | Accumulate per image evaluation results and store the result in self.eval 318 | :param p: input params for evaluation 319 | :return: None 320 | ''' 321 | print('Accumulating evaluation results...') 322 | tic = time.time() 323 | if not self.evalImgs: 324 | print('Please run evaluate() first') 325 | # allows input customized parameters 326 | if p is None: 327 | p = self.params 328 | p.catIds = p.catIds if p.useCats == 1 else [-1] 329 | T = len(p.iouThrs) 330 | R = len(p.recThrs) 331 | K = len(p.catIds) if p.useCats else 1 332 | A = len(p.areaRng) 333 | M = len(p.maxDets) 334 | precision = -np.ones((T,R,K,A,M)) # -1 for the precision of absent categories 335 | recall = -np.ones((T,K,A,M)) 336 | scores = -np.ones((T,R,K,A,M)) 337 | 338 | # create dictionary for future indexing 339 | _pe = self._paramsEval 340 | catIds = _pe.catIds if _pe.useCats else [-1] 341 | setK = set(catIds) 342 | setA = set(map(tuple, _pe.areaRng)) 343 | setM = set(_pe.maxDets) 344 | setI = set(_pe.imgIds) 345 | # get inds to evaluate 346 | k_list = [n for n, k in enumerate(p.catIds) if k in setK] 347 | m_list = [m for n, m in enumerate(p.maxDets) if m in setM] 348 | a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA] 349 | i_list = [n for n, i in enumerate(p.imgIds) if i in setI] 350 | I0 = len(_pe.imgIds) 351 | A0 = len(_pe.areaRng) 352 | # retrieve E at each category, area range, and max number of detections 353 | for k, k0 in enumerate(k_list): 354 | Nk = k0*A0*I0 355 | for a, a0 in enumerate(a_list): 356 | Na = a0*I0 357 | for m, maxDet in enumerate(m_list): 358 | E = [self.evalImgs[Nk + Na + i] for i in i_list] 359 | E = [e for e in E if not e is None] 360 | if len(E) == 0: 361 | continue 362 | dtScores = np.concatenate([e['dtScores'][0:maxDet] for e in E]) 363 | 364 | # different sorting method generates slightly different results. 365 | # mergesort is used to be consistent as Matlab implementation. 366 | inds = np.argsort(-dtScores, kind='mergesort') 367 | dtScoresSorted = dtScores[inds] 368 | 369 | dtm = np.concatenate([e['dtMatches'][:,0:maxDet] for e in E], axis=1)[:,inds] 370 | dtIg = np.concatenate([e['dtIgnore'][:,0:maxDet] for e in E], axis=1)[:,inds] 371 | gtIg = np.concatenate([e['gtIgnore'] for e in E]) 372 | npig = np.count_nonzero(gtIg==0 ) 373 | if npig == 0: 374 | continue 375 | tps = np.logical_and( dtm, np.logical_not(dtIg) ) 376 | fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg) ) 377 | 378 | tp_sum = np.cumsum(tps, axis=1).astype(dtype=float) 379 | fp_sum = np.cumsum(fps, axis=1).astype(dtype=float) 380 | for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): 381 | tp = np.array(tp) 382 | fp = np.array(fp) 383 | nd = len(tp) 384 | rc = tp / npig 385 | pr = tp / (fp+tp+np.spacing(1)) 386 | q = np.zeros((R,)) 387 | ss = np.zeros((R,)) 388 | 389 | if nd: 390 | recall[t,k,a,m] = rc[-1] 391 | else: 392 | recall[t,k,a,m] = 0 393 | 394 | # numpy is slow without cython optimization for accessing elements 395 | # use python array gets significant speed improvement 396 | pr = pr.tolist(); q = q.tolist() 397 | 398 | for i in range(nd-1, 0, -1): 399 | if pr[i] > pr[i-1]: 400 | pr[i-1] = pr[i] 401 | 402 | inds = np.searchsorted(rc, p.recThrs, side='left') 403 | try: 404 | for ri, pi in enumerate(inds): 405 | q[ri] = pr[pi] 406 | ss[ri] = dtScoresSorted[pi] 407 | except: 408 | pass 409 | precision[t,:,k,a,m] = np.array(q) 410 | scores[t,:,k,a,m] = np.array(ss) 411 | self.eval = { 412 | 'params': p, 413 | 'counts': [T, R, K, A, M], 414 | 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 415 | 'precision': precision, 416 | 'recall': recall, 417 | 'scores': scores, 418 | } 419 | toc = time.time() 420 | print('DONE (t={:0.2f}s).'.format( toc-tic)) 421 | 422 | def summarize(self): 423 | ''' 424 | Compute and display summary metrics for evaluation results. 425 | Note this functin can *only* be applied on the default parameter setting 426 | ''' 427 | def _summarize( ap=1, iouThr=None, areaRng='all', maxDets=100 ): 428 | p = self.params 429 | iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}' 430 | titleStr = 'Average Precision' if ap == 1 else 'Average Recall' 431 | typeStr = '(AP)' if ap==1 else '(AR)' 432 | iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \ 433 | if iouThr is None else '{:0.2f}'.format(iouThr) 434 | 435 | aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng] 436 | mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] 437 | if ap == 1: 438 | # dimension of precision: [TxRxKxAxM] 439 | s = self.eval['precision'] 440 | # IoU 441 | if iouThr is not None: 442 | t = np.where(iouThr == p.iouThrs)[0] 443 | s = s[t] 444 | s = s[:,:,:,aind,mind] 445 | else: 446 | # dimension of recall: [TxKxAxM] 447 | s = self.eval['recall'] 448 | if iouThr is not None: 449 | t = np.where(iouThr == p.iouThrs)[0] 450 | s = s[t] 451 | s = s[:,:,aind,mind] 452 | if len(s[s>-1])==0: 453 | mean_s = -1 454 | else: 455 | mean_s = np.mean(s[s>-1]) 456 | print(iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets, mean_s)) 457 | return mean_s 458 | def _summarizeDets(): 459 | stats = np.zeros((12,)) 460 | stats[0] = _summarize(1) 461 | stats[1] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2]) 462 | stats[2] = _summarize(1, iouThr=.75, maxDets=self.params.maxDets[2]) 463 | stats[3] = _summarize(1, areaRng='small', maxDets=self.params.maxDets[2]) 464 | stats[4] = _summarize(1, areaRng='medium', maxDets=self.params.maxDets[2]) 465 | stats[5] = _summarize(1, areaRng='large', maxDets=self.params.maxDets[2]) 466 | stats[6] = _summarize(0, maxDets=self.params.maxDets[0]) 467 | stats[7] = _summarize(0, maxDets=self.params.maxDets[1]) 468 | stats[8] = _summarize(0, maxDets=self.params.maxDets[2]) 469 | stats[9] = _summarize(0, areaRng='small', maxDets=self.params.maxDets[2]) 470 | stats[10] = _summarize(0, areaRng='medium', maxDets=self.params.maxDets[2]) 471 | stats[11] = _summarize(0, areaRng='large', maxDets=self.params.maxDets[2]) 472 | return stats 473 | def _summarizeKps(): 474 | stats = np.zeros((10,)) 475 | stats[0] = _summarize(1, maxDets=20) 476 | stats[1] = _summarize(1, maxDets=20, iouThr=.5) 477 | stats[2] = _summarize(1, maxDets=20, iouThr=.75) 478 | stats[3] = _summarize(1, maxDets=20, areaRng='medium') 479 | stats[4] = _summarize(1, maxDets=20, areaRng='large') 480 | stats[5] = _summarize(0, maxDets=20) 481 | stats[6] = _summarize(0, maxDets=20, iouThr=.5) 482 | stats[7] = _summarize(0, maxDets=20, iouThr=.75) 483 | stats[8] = _summarize(0, maxDets=20, areaRng='medium') 484 | stats[9] = _summarize(0, maxDets=20, areaRng='large') 485 | return stats 486 | if not self.eval: 487 | raise Exception('Please run accumulate() first') 488 | iouType = self.params.iouType 489 | if iouType == 'segm' or iouType == 'bbox': 490 | summarize = _summarizeDets 491 | elif iouType == 'keypoints': 492 | summarize = _summarizeKps 493 | self.stats = summarize() 494 | 495 | def __str__(self): 496 | self.summarize() 497 | 498 | class Params: 499 | ''' 500 | Params for coco evaluation api 501 | ''' 502 | def setDetParams(self): 503 | self.imgIds = [] 504 | self.catIds = [] 505 | # np.arange causes trouble. the data point on arange is slightly larger than the true value 506 | self.iouThrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) 507 | self.recThrs = np.linspace(.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True) 508 | self.maxDets = [1, 10, 100] 509 | self.areaRng = [[0 ** 2, 1e5 ** 2], [0 ** 2, 32 ** 2], [32 ** 2, 96 ** 2], [96 ** 2, 1e5 ** 2]] 510 | self.areaRngLbl = ['all', 'small', 'medium', 'large'] 511 | self.useCats = 1 512 | 513 | def setKpParams(self): 514 | self.imgIds = [] 515 | self.catIds = [] 516 | # np.arange causes trouble. the data point on arange is slightly larger than the true value 517 | self.iouThrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) 518 | self.recThrs = np.linspace(.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True) 519 | self.maxDets = [20] 520 | self.areaRng = [[0 ** 2, 1e5 ** 2], [32 ** 2, 96 ** 2], [96 ** 2, 1e5 ** 2]] 521 | self.areaRngLbl = ['all', 'medium', 'large'] 522 | self.useCats = 1 523 | self.kpt_oks_sigmas = np.array([.26, .25, .25, .35, .35, .79, .79, .72, .72, .62,.62, 1.07, 1.07, .87, .87, .89, .89])/10.0 524 | 525 | def __init__(self, iouType='segm'): 526 | if iouType == 'segm' or iouType == 'bbox': 527 | self.setDetParams() 528 | elif iouType == 'keypoints': 529 | self.setKpParams() 530 | else: 531 | raise Exception('iouType not supported') 532 | self.iouType = iouType 533 | # useSegm is deprecated 534 | self.useSegm = None 535 | -------------------------------------------------------------------------------- /challenge_iccv_2025/pycocotools/mask.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import pycocotools._mask as _mask 4 | 5 | # Interface for manipulating masks stored in RLE format. 6 | # 7 | # RLE is a simple yet efficient format for storing binary masks. RLE 8 | # first divides a vector (or vectorized image) into a series of piecewise 9 | # constant regions and then for each piece simply stores the length of 10 | # that piece. For example, given M=[0 0 1 1 1 0 1] the RLE counts would 11 | # be [2 3 1 1], or for M=[1 1 1 1 1 1 0] the counts would be [0 6 1] 12 | # (note that the odd counts are always the numbers of zeros). Instead of 13 | # storing the counts directly, additional compression is achieved with a 14 | # variable bitrate representation based on a common scheme called LEB128. 15 | # 16 | # Compression is greatest given large piecewise constant regions. 17 | # Specifically, the size of the RLE is proportional to the number of 18 | # *boundaries* in M (or for an image the number of boundaries in the y 19 | # direction). Assuming fairly simple shapes, the RLE representation is 20 | # O(sqrt(n)) where n is number of pixels in the object. Hence space usage 21 | # is substantially lower, especially for large simple objects (large n). 22 | # 23 | # Many common operations on masks can be computed directly using the RLE 24 | # (without need for decoding). This includes computations such as area, 25 | # union, intersection, etc. All of these operations are linear in the 26 | # size of the RLE, in other words they are O(sqrt(n)) where n is the area 27 | # of the object. Computing these operations on the original mask is O(n). 28 | # Thus, using the RLE can result in substantial computational savings. 29 | # 30 | # The following API functions are defined: 31 | # encode - Encode binary masks using RLE. 32 | # decode - Decode binary masks encoded via RLE. 33 | # merge - Compute union or intersection of encoded masks. 34 | # iou - Compute intersection over union between masks. 35 | # area - Compute area of encoded masks. 36 | # toBbox - Get bounding boxes surrounding encoded masks. 37 | # frPyObjects - Convert polygon, bbox, and uncompressed RLE to encoded RLE mask. 38 | # 39 | # Usage: 40 | # Rs = encode( masks ) 41 | # masks = decode( Rs ) 42 | # R = merge( Rs, intersect=false ) 43 | # o = iou( dt, gt, iscrowd ) 44 | # a = area( Rs ) 45 | # bbs = toBbox( Rs ) 46 | # Rs = frPyObjects( [pyObjects], h, w ) 47 | # 48 | # In the API the following formats are used: 49 | # Rs - [dict] Run-length encoding of binary masks 50 | # R - dict Run-length encoding of binary mask 51 | # masks - [hxwxn] Binary mask(s) (must have type np.ndarray(dtype=uint8) in column-major order) 52 | # iscrowd - [nx1] list of np.ndarray. 1 indicates corresponding gt image has crowd region to ignore 53 | # bbs - [nx4] Bounding box(es) stored as [x y w h] 54 | # poly - Polygon stored as [[x1 y1 x2 y2...],[x1 y1 ...],...] (2D list) 55 | # dt,gt - May be either bounding boxes or encoded masks 56 | # Both poly and bbs are 0-indexed (bbox=[0 0 1 1] encloses first pixel). 57 | # 58 | # Finally, a note about the intersection over union (iou) computation. 59 | # The standard iou of a ground truth (gt) and detected (dt) object is 60 | # iou(gt,dt) = area(intersect(gt,dt)) / area(union(gt,dt)) 61 | # For "crowd" regions, we use a modified criteria. If a gt object is 62 | # marked as "iscrowd", we allow a dt to match any subregion of the gt. 63 | # Choosing gt' in the crowd gt that best matches the dt can be done using 64 | # gt'=intersect(dt,gt). Since by definition union(gt',dt)=dt, computing 65 | # iou(gt,dt,iscrowd) = iou(gt',dt) = area(intersect(gt,dt)) / area(dt) 66 | # For crowd gt regions we use this modified criteria above for the iou. 67 | # 68 | # To compile run "python setup.py build_ext --inplace" 69 | # Please do not contact us for help with compiling. 70 | # 71 | # Microsoft COCO Toolbox. version 2.0 72 | # Data, paper, and tutorials available at: http://mscoco.org/ 73 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 74 | # Licensed under the Simplified BSD License [see coco/license.txt] 75 | 76 | iou = _mask.iou 77 | merge = _mask.merge 78 | frPyObjects = _mask.frPyObjects 79 | 80 | def encode(bimask): 81 | if len(bimask.shape) == 3: 82 | return _mask.encode(bimask) 83 | elif len(bimask.shape) == 2: 84 | h, w = bimask.shape 85 | return _mask.encode(bimask.reshape((h, w, 1), order='F'))[0] 86 | 87 | def decode(rleObjs): 88 | if type(rleObjs) == list: 89 | return _mask.decode(rleObjs) 90 | else: 91 | return _mask.decode([rleObjs])[:,:,0] 92 | 93 | def area(rleObjs): 94 | if type(rleObjs) == list: 95 | return _mask.area(rleObjs) 96 | else: 97 | return _mask.area([rleObjs])[0] 98 | 99 | def toBbox(rleObjs): 100 | if type(rleObjs) == list: 101 | return _mask.toBbox(rleObjs) 102 | else: 103 | return _mask.toBbox([rleObjs])[0] -------------------------------------------------------------------------------- /challenge_iccv_2025/run_evaluation_jetson.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import argparse 4 | import cv2 5 | import json 6 | import numpy as np 7 | from ultralytics import YOLO 8 | 9 | from utils import f1_score 10 | 11 | from utils import get_model , preprocess_image , postprocess_result, changeId 12 | import json 13 | 14 | def main(): 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('--image_folder', type=str, default='/data/Fisheye1K_eval/images', help='Path to image folder') 17 | parser.add_argument('--model_path', type=str, default='models/yolo11n_fisheye8k.pt', help='Path to the model') 18 | parser.add_argument('--max_fps', type=float, default=25.0, help='Maximum FPS for evaluation') 19 | parser.add_argument('--output_json', type=str, default='/data/Fisheye1K_eval/predictions.json', help='Output JSON file for predictions') 20 | #parser.add_argument('--ground_truths_path', type=str, default='/data/Fisheye1K_eval/groundtruth.json', help='Path to ground truths JSON file') 21 | args = parser.parse_args() 22 | 23 | image_folder = args.image_folder 24 | model_path = args.model_path 25 | 26 | model = get_model(model_path) 27 | 28 | image_files = sorted([ 29 | os.path.join(image_folder, f) 30 | for f in os.listdir(image_folder) 31 | if f.lower().endswith(('.jpg', '.jpeg', '.png')) 32 | ]) 33 | 34 | print(f"Found {len(image_files)} images.") 35 | 36 | predictions = [] 37 | print('Prediction started') 38 | start_time = time.time() 39 | for image_path in image_files: 40 | img = preprocess_image(image_path) 41 | results = model(img,verbose=False) 42 | results = postprocess_result(results) # [boxes, scores, classes] 43 | predictions.append((image_path,results)) 44 | #print(f"Processed {os.path.basename(image_path)}: {len(results[0])} objects detected.") 45 | 46 | end_time = time.time() 47 | elapsed_time = end_time - start_time 48 | print(f"Processed {len(image_files)} images in {elapsed_time:.2f} seconds.") 49 | 50 | predictions_json = [] 51 | # Save predictions to JSON 52 | with open(args.output_json, 'w') as f: 53 | json.dump(predictions_json, f, indent=2) 54 | 55 | fps = len(image_files) / elapsed_time 56 | normfps = min(fps, args.max_fps)/args.max_fps 57 | 58 | #f1 = f1_score(args.output_json, args.ground_truths_path) 59 | #harmonic_mean = 2 * f1 * normfps / (f1 + normfps) 60 | 61 | print(f"\n--- Evaluation Complete ---") 62 | print(f"Total inference time: {elapsed_time:.2f} seconds") 63 | print(f"FPS: {fps:.2f}") 64 | print(f"Normalized FPS: {normfps:.4f}") 65 | #print(f"F1-score: {f1:.4f}") 66 | #print(f"Metric (harmonic mean of F1-score and NormalizedFPS): {harmonic_mean:.4f}") 67 | 68 | if __name__ == "__main__": 69 | main() 70 | -------------------------------------------------------------------------------- /challenge_iccv_2025/run_train_yolo.py: -------------------------------------------------------------------------------- 1 | from ultralytics import YOLO 2 | 3 | # Load a model 4 | model = YOLO("yolo11n.pt") # load a pretrained model (recommended for training) 5 | 6 | # Train the model 7 | results = model.train(data="data/Fisheye8k.yaml", 8 | epochs=100, 9 | batch =32, 10 | imgsz=640, 11 | device="0", # specify the device to use, e.g., "0" for GPU 0 12 | project="/models/", # specify the project directory to save the model 13 | name="yolo11n_fisheye8k", # specify the name of the model 14 | save_period=10, # save the model every 10 epochs 15 | save=True, # save the model after training 16 | ) # train the model with the specified parameters -------------------------------------------------------------------------------- /challenge_iccv_2025/utils.py: -------------------------------------------------------------------------------- 1 | from ultralytics import YOLO 2 | import os 3 | import cv2 4 | import numpy as np 5 | from pycocotools.coco import COCO 6 | from pycocotools.cocoeval_modified import COCOeval 7 | import json 8 | 9 | def f1_score(predictions_path, ground_truths_path): 10 | coco_gt = COCO(ground_truths_path) 11 | 12 | gt_image_ids = coco_gt.getImgIds() 13 | 14 | with open(predictions_path, 'r') as f: 15 | detection_data = json.load(f) 16 | filtered_detection_data = [ 17 | item for item in detection_data if item['image_id'] in gt_image_ids] 18 | with open('./temp.json', 'w') as f: 19 | json.dump(filtered_detection_data, f) 20 | coco_dt = coco_gt.loadRes('./temp.json') 21 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 22 | coco_eval.evaluate() 23 | coco_eval.accumulate() 24 | coco_eval.summarize() 25 | 26 | # Assuming the F1 score is at index 20 in the stats array 27 | return coco_eval.stats[20] # Return the F1 score from the evaluation stats 28 | # return 0.85 # Simulated constant value for demo purposes 29 | 30 | def get_model(model_path): 31 | if not os.path.exists(model_path): 32 | raise FileNotFoundError(f"Model file {model_path} does not exist.") 33 | # You need to implement your model here 34 | model = YOLO(model_path) 35 | model.export(format="engine") # creates 'yolo11n.engine' 36 | engine_path = model_path.replace(".pt",".engine") 37 | # Load the exported TensorRT model 38 | trt_model = YOLO(engine_path) 39 | 40 | return trt_model 41 | def preprocess_image(image_path): 42 | image = cv2.imread(image_path) 43 | if image is None: 44 | raise ValueError("Input image is None.") 45 | # Preprocess the image for your own model 46 | 47 | return image 48 | def postprocess_result(results): 49 | if not results or len(results) == 0: 50 | return [] 51 | boxes = results[0].boxes.xyxy.cpu().numpy().tolist() # shape: (N, 4) 52 | scores = results[0].boxes.conf.cpu().numpy().tolist() # shape: (N,) 53 | classes = results[0].boxes.cls.cpu().numpy().tolist() # shape: (N,) 54 | 55 | return [boxes, scores, classes] 56 | def changeId(id): 57 | sceneList = ['M', 'A', 'E', 'N'] 58 | cameraId = int(id.split('_')[0].split('camera')[1]) 59 | sceneId = sceneList.index(id.split('_')[1]) 60 | frameId = int(id.split('_')[2]) 61 | imageId = int(str(cameraId)+str(sceneId)+str(frameId)) 62 | return imageId 63 | -------------------------------------------------------------------------------- /challenge_iccv_2025/yolo11n.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/challenge_iccv_2025/yolo11n.pt -------------------------------------------------------------------------------- /evaluation_Jetson/eval.py: -------------------------------------------------------------------------------- 1 | from pycocotools.coco import COCO 2 | from pycocotools.cocoeval import COCOeval 3 | 4 | 5 | coco_gt = COCO('groundtruth.json') 6 | coco_dt = coco_gt.loadRes( 7 | 'detection_dgx.json') 8 | 9 | print(type(coco_dt)) 10 | print(type(coco_gt)) 11 | 12 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 13 | coco_eval.evaluate() 14 | coco_eval.accumulate() 15 | coco_eval.summarize() 16 | -------------------------------------------------------------------------------- /evaluation_Jetson/eval_f1.py: -------------------------------------------------------------------------------- 1 | from pycocotools.coco import COCO 2 | from pycocotools.cocoeval_modified import COCOeval 3 | import json 4 | 5 | coco_gt = COCO('groundtruth.json') 6 | 7 | gt_image_ids = coco_gt.getImgIds() 8 | 9 | print("Total images ", len(gt_image_ids)) 10 | 11 | 12 | with open('detections.json', 'r') as f: 13 | detection_data = json.load(f) 14 | 15 | filtered_detection_data = [ 16 | item for item in detection_data if item['image_id'] in gt_image_ids] 17 | 18 | with open('./temp.json', 'w') as f: 19 | json.dump(filtered_detection_data, f) 20 | 21 | coco_dt = coco_gt.loadRes('./temp.json') 22 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 23 | coco_eval.evaluate() 24 | coco_eval.accumulate() 25 | coco_eval.summarize() 26 | print('----------------------------------------') 27 | print('AP_0.5-0.95', coco_eval.stats[0]) 28 | print('AP_0.5', coco_eval.stats[1]) 29 | print('AP_S', coco_eval.stats[3]) 30 | print('AP_M', coco_eval.stats[4]) 31 | print('AP_L', coco_eval.stats[5]) 32 | print('f1_score: ', coco_eval.stats[20]) 33 | print('----------------------------------------') 34 | -------------------------------------------------------------------------------- /evaluation_Jetson/pycocotools/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | -------------------------------------------------------------------------------- /evaluation_Jetson/pycocotools/_mask.cpython-310-aarch64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Jetson/pycocotools/_mask.cpython-310-aarch64-linux-gnu.so -------------------------------------------------------------------------------- /evaluation_Jetson/pycocotools/_mask.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c 2 | # distutils: sources = ../common/maskApi.c 3 | 4 | #************************************************************************** 5 | # Microsoft COCO Toolbox. version 2.0 6 | # Data, paper, and tutorials available at: http://mscoco.org/ 7 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 8 | # Licensed under the Simplified BSD License [see coco/license.txt] 9 | #************************************************************************** 10 | 11 | __author__ = 'tsungyi' 12 | 13 | import sys 14 | PYTHON_VERSION = sys.version_info[0] 15 | 16 | # import both Python-level and C-level symbols of Numpy 17 | # the API uses Numpy to interface C and Python 18 | import numpy as np 19 | cimport numpy as np 20 | from libc.stdlib cimport malloc, free 21 | 22 | # intialized Numpy. must do. 23 | np.import_array() 24 | 25 | # import numpy C function 26 | # we use PyArray_ENABLEFLAGS to make Numpy ndarray responsible to memoery management 27 | cdef extern from "numpy/arrayobject.h": 28 | void PyArray_ENABLEFLAGS(np.ndarray arr, int flags) 29 | 30 | # Declare the prototype of the C functions in MaskApi.h 31 | cdef extern from "maskApi.h": 32 | ctypedef unsigned int uint 33 | ctypedef unsigned long siz 34 | ctypedef unsigned char byte 35 | ctypedef double* BB 36 | ctypedef struct RLE: 37 | siz h, 38 | siz w, 39 | siz m, 40 | uint* cnts, 41 | void rlesInit( RLE **R, siz n ) 42 | void rleEncode( RLE *R, const byte *M, siz h, siz w, siz n ) 43 | void rleDecode( const RLE *R, byte *mask, siz n ) 44 | void rleMerge( const RLE *R, RLE *M, siz n, int intersect ) 45 | void rleArea( const RLE *R, siz n, uint *a ) 46 | void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o ) 47 | void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o ) 48 | void rleToBbox( const RLE *R, BB bb, siz n ) 49 | void rleFrBbox( RLE *R, const BB bb, siz h, siz w, siz n ) 50 | void rleFrPoly( RLE *R, const double *xy, siz k, siz h, siz w ) 51 | char* rleToString( const RLE *R ) 52 | void rleFrString( RLE *R, char *s, siz h, siz w ) 53 | 54 | # python class to wrap RLE array in C 55 | # the class handles the memory allocation and deallocation 56 | cdef class RLEs: 57 | cdef RLE *_R 58 | cdef siz _n 59 | 60 | def __cinit__(self, siz n =0): 61 | rlesInit(&self._R, n) 62 | self._n = n 63 | 64 | # free the RLE array here 65 | def __dealloc__(self): 66 | if self._R is not NULL: 67 | for i in range(self._n): 68 | free(self._R[i].cnts) 69 | free(self._R) 70 | def __getattr__(self, key): 71 | if key == 'n': 72 | return self._n 73 | raise AttributeError(key) 74 | 75 | # python class to wrap Mask array in C 76 | # the class handles the memory allocation and deallocation 77 | cdef class Masks: 78 | cdef byte *_mask 79 | cdef siz _h 80 | cdef siz _w 81 | cdef siz _n 82 | 83 | def __cinit__(self, h, w, n): 84 | self._mask = malloc(h*w*n* sizeof(byte)) 85 | self._h = h 86 | self._w = w 87 | self._n = n 88 | # def __dealloc__(self): 89 | # the memory management of _mask has been passed to np.ndarray 90 | # it doesn't need to be freed here 91 | 92 | # called when passing into np.array() and return an np.ndarray in column-major order 93 | def __array__(self): 94 | cdef np.npy_intp shape[1] 95 | shape[0] = self._h*self._w*self._n 96 | # Create a 1D array, and reshape it to fortran/Matlab column-major array 97 | ndarray = np.PyArray_SimpleNewFromData(1, shape, np.NPY_UINT8, self._mask).reshape((self._h, self._w, self._n), order='F') 98 | # The _mask allocated by Masks is now handled by ndarray 99 | PyArray_ENABLEFLAGS(ndarray, np.NPY_OWNDATA) 100 | return ndarray 101 | 102 | # internal conversion from Python RLEs object to compressed RLE format 103 | def _toString(RLEs Rs): 104 | cdef siz n = Rs.n 105 | cdef bytes py_string 106 | cdef char* c_string 107 | objs = [] 108 | for i in range(n): 109 | c_string = rleToString( &Rs._R[i] ) 110 | py_string = c_string 111 | objs.append({ 112 | 'size': [Rs._R[i].h, Rs._R[i].w], 113 | 'counts': py_string 114 | }) 115 | free(c_string) 116 | return objs 117 | 118 | # internal conversion from compressed RLE format to Python RLEs object 119 | def _frString(rleObjs): 120 | cdef siz n = len(rleObjs) 121 | Rs = RLEs(n) 122 | cdef bytes py_string 123 | cdef char* c_string 124 | for i, obj in enumerate(rleObjs): 125 | if PYTHON_VERSION == 2: 126 | py_string = str(obj['counts']).encode('utf8') 127 | elif PYTHON_VERSION == 3: 128 | py_string = str.encode(obj['counts']) if type(obj['counts']) == str else obj['counts'] 129 | else: 130 | raise Exception('Python version must be 2 or 3') 131 | c_string = py_string 132 | rleFrString( &Rs._R[i], c_string, obj['size'][0], obj['size'][1] ) 133 | return Rs 134 | 135 | # encode mask to RLEs objects 136 | # list of RLE string can be generated by RLEs member function 137 | def encode(np.ndarray[np.uint8_t, ndim=3, mode='fortran'] mask): 138 | h, w, n = mask.shape[0], mask.shape[1], mask.shape[2] 139 | cdef RLEs Rs = RLEs(n) 140 | rleEncode(Rs._R,mask.data,h,w,n) 141 | objs = _toString(Rs) 142 | return objs 143 | 144 | # decode mask from compressed list of RLE string or RLEs object 145 | def decode(rleObjs): 146 | cdef RLEs Rs = _frString(rleObjs) 147 | h, w, n = Rs._R[0].h, Rs._R[0].w, Rs._n 148 | masks = Masks(h, w, n) 149 | rleDecode(Rs._R, masks._mask, n); 150 | return np.array(masks) 151 | 152 | def merge(rleObjs, intersect=0): 153 | cdef RLEs Rs = _frString(rleObjs) 154 | cdef RLEs R = RLEs(1) 155 | rleMerge(Rs._R, R._R, Rs._n, intersect) 156 | obj = _toString(R)[0] 157 | return obj 158 | 159 | def area(rleObjs): 160 | cdef RLEs Rs = _frString(rleObjs) 161 | cdef uint* _a = malloc(Rs._n* sizeof(uint)) 162 | rleArea(Rs._R, Rs._n, _a) 163 | cdef np.npy_intp shape[1] 164 | shape[0] = Rs._n 165 | a = np.array((Rs._n, ), dtype=np.uint8) 166 | a = np.PyArray_SimpleNewFromData(1, shape, np.NPY_UINT32, _a) 167 | PyArray_ENABLEFLAGS(a, np.NPY_OWNDATA) 168 | return a 169 | 170 | # iou computation. support function overload (RLEs-RLEs and bbox-bbox). 171 | def iou( dt, gt, pyiscrowd ): 172 | def _preproc(objs): 173 | if len(objs) == 0: 174 | return objs 175 | if type(objs) == np.ndarray: 176 | if len(objs.shape) == 1: 177 | objs = objs.reshape((objs[0], 1)) 178 | # check if it's Nx4 bbox 179 | if not len(objs.shape) == 2 or not objs.shape[1] == 4: 180 | raise Exception('numpy ndarray input is only for *bounding boxes* and should have Nx4 dimension') 181 | objs = objs.astype(np.double) 182 | elif type(objs) == list: 183 | # check if list is in box format and convert it to np.ndarray 184 | isbox = np.all(np.array([(len(obj)==4) and ((type(obj)==list) or (type(obj)==np.ndarray)) for obj in objs])) 185 | isrle = np.all(np.array([type(obj) == dict for obj in objs])) 186 | if isbox: 187 | objs = np.array(objs, dtype=np.double) 188 | if len(objs.shape) == 1: 189 | objs = objs.reshape((1,objs.shape[0])) 190 | elif isrle: 191 | objs = _frString(objs) 192 | else: 193 | raise Exception('list input can be bounding box (Nx4) or RLEs ([RLE])') 194 | else: 195 | raise Exception('unrecognized type. The following type: RLEs (rle), np.ndarray (box), and list (box) are supported.') 196 | return objs 197 | def _rleIou(RLEs dt, RLEs gt, np.ndarray[np.uint8_t, ndim=1] iscrowd, siz m, siz n, np.ndarray[np.double_t, ndim=1] _iou): 198 | rleIou( dt._R, gt._R, m, n, iscrowd.data, _iou.data ) 199 | def _bbIou(np.ndarray[np.double_t, ndim=2] dt, np.ndarray[np.double_t, ndim=2] gt, np.ndarray[np.uint8_t, ndim=1] iscrowd, siz m, siz n, np.ndarray[np.double_t, ndim=1] _iou): 200 | bbIou( dt.data, gt.data, m, n, iscrowd.data, _iou.data ) 201 | def _len(obj): 202 | cdef siz N = 0 203 | if type(obj) == RLEs: 204 | N = obj.n 205 | elif len(obj)==0: 206 | pass 207 | elif type(obj) == np.ndarray: 208 | N = obj.shape[0] 209 | return N 210 | # convert iscrowd to numpy array 211 | cdef np.ndarray[np.uint8_t, ndim=1] iscrowd = np.array(pyiscrowd, dtype=np.uint8) 212 | # simple type checking 213 | cdef siz m, n 214 | dt = _preproc(dt) 215 | gt = _preproc(gt) 216 | m = _len(dt) 217 | n = _len(gt) 218 | if m == 0 or n == 0: 219 | return [] 220 | if not type(dt) == type(gt): 221 | raise Exception('The dt and gt should have the same data type, either RLEs, list or np.ndarray') 222 | 223 | # define local variables 224 | cdef double* _iou = 0 225 | cdef np.npy_intp shape[1] 226 | # check type and assign iou function 227 | if type(dt) == RLEs: 228 | _iouFun = _rleIou 229 | elif type(dt) == np.ndarray: 230 | _iouFun = _bbIou 231 | else: 232 | raise Exception('input data type not allowed.') 233 | _iou = malloc(m*n* sizeof(double)) 234 | iou = np.zeros((m*n, ), dtype=np.double) 235 | shape[0] = m*n 236 | iou = np.PyArray_SimpleNewFromData(1, shape, np.NPY_DOUBLE, _iou) 237 | PyArray_ENABLEFLAGS(iou, np.NPY_OWNDATA) 238 | _iouFun(dt, gt, iscrowd, m, n, iou) 239 | return iou.reshape((m,n), order='F') 240 | 241 | def toBbox( rleObjs ): 242 | cdef RLEs Rs = _frString(rleObjs) 243 | cdef siz n = Rs.n 244 | cdef BB _bb = malloc(4*n* sizeof(double)) 245 | rleToBbox( Rs._R, _bb, n ) 246 | cdef np.npy_intp shape[1] 247 | shape[0] = 4*n 248 | bb = np.array((1,4*n), dtype=np.double) 249 | bb = np.PyArray_SimpleNewFromData(1, shape, np.NPY_DOUBLE, _bb).reshape((n, 4)) 250 | PyArray_ENABLEFLAGS(bb, np.NPY_OWNDATA) 251 | return bb 252 | 253 | def frBbox(np.ndarray[np.double_t, ndim=2] bb, siz h, siz w ): 254 | cdef siz n = bb.shape[0] 255 | Rs = RLEs(n) 256 | rleFrBbox( Rs._R, bb.data, h, w, n ) 257 | objs = _toString(Rs) 258 | return objs 259 | 260 | def frPoly( poly, siz h, siz w ): 261 | cdef np.ndarray[np.double_t, ndim=1] np_poly 262 | n = len(poly) 263 | Rs = RLEs(n) 264 | for i, p in enumerate(poly): 265 | np_poly = np.array(p, dtype=np.double, order='F') 266 | rleFrPoly( &Rs._R[i], np_poly.data, int(len(p)/2), h, w ) 267 | objs = _toString(Rs) 268 | return objs 269 | 270 | def frUncompressedRLE(ucRles, siz h, siz w): 271 | cdef np.ndarray[np.uint32_t, ndim=1] cnts 272 | cdef RLE R 273 | cdef uint *data 274 | n = len(ucRles) 275 | objs = [] 276 | for i in range(n): 277 | Rs = RLEs(1) 278 | cnts = np.array(ucRles[i]['counts'], dtype=np.uint32) 279 | # time for malloc can be saved here but it's fine 280 | data = malloc(len(cnts)* sizeof(uint)) 281 | for j in range(len(cnts)): 282 | data[j] = cnts[j] 283 | R = RLE(ucRles[i]['size'][0], ucRles[i]['size'][1], len(cnts), data) 284 | Rs._R[0] = R 285 | objs.append(_toString(Rs)[0]) 286 | return objs 287 | 288 | def frPyObjects(pyobj, h, w): 289 | # encode rle from a list of python objects 290 | if type(pyobj) == np.ndarray: 291 | objs = frBbox(pyobj, h, w) 292 | elif type(pyobj) == list and len(pyobj[0]) == 4: 293 | objs = frBbox(pyobj, h, w) 294 | elif type(pyobj) == list and len(pyobj[0]) > 4: 295 | objs = frPoly(pyobj, h, w) 296 | elif type(pyobj) == list and type(pyobj[0]) == dict \ 297 | and 'counts' in pyobj[0] and 'size' in pyobj[0]: 298 | objs = frUncompressedRLE(pyobj, h, w) 299 | # encode rle from single python object 300 | elif type(pyobj) == list and len(pyobj) == 4: 301 | objs = frBbox([pyobj], h, w)[0] 302 | elif type(pyobj) == list and len(pyobj) > 4: 303 | objs = frPoly([pyobj], h, w)[0] 304 | elif type(pyobj) == dict and 'counts' in pyobj and 'size' in pyobj: 305 | objs = frUncompressedRLE([pyobj], h, w)[0] 306 | else: 307 | raise Exception('input type is not supported.') 308 | return objs 309 | -------------------------------------------------------------------------------- /evaluation_Jetson/pycocotools/coco.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | __version__ = '2.0' 3 | # Interface for accessing the Microsoft COCO dataset. 4 | 5 | # Microsoft COCO is a large image dataset designed for object detection, 6 | # segmentation, and caption generation. pycocotools is a Python API that 7 | # assists in loading, parsing and visualizing the annotations in COCO. 8 | # Please visit http://mscoco.org/ for more information on COCO, including 9 | # for the data, paper, and tutorials. The exact format of the annotations 10 | # is also described on the COCO website. For example usage of the pycocotools 11 | # please see pycocotools_demo.ipynb. In addition to this API, please download both 12 | # the COCO images and annotations in order to run the demo. 13 | 14 | # An alternative to using the API is to load the annotations directly 15 | # into Python dictionary 16 | # Using the API provides additional utility functions. Note that this API 17 | # supports both *instance* and *caption* annotations. In the case of 18 | # captions not all functions are defined (e.g. categories are undefined). 19 | 20 | # The following API functions are defined: 21 | # COCO - COCO api class that loads COCO annotation file and prepare data structures. 22 | # decodeMask - Decode binary mask M encoded via run-length encoding. 23 | # encodeMask - Encode binary mask M using run-length encoding. 24 | # getAnnIds - Get ann ids that satisfy given filter conditions. 25 | # getCatIds - Get cat ids that satisfy given filter conditions. 26 | # getImgIds - Get img ids that satisfy given filter conditions. 27 | # loadAnns - Load anns with the specified ids. 28 | # loadCats - Load cats with the specified ids. 29 | # loadImgs - Load imgs with the specified ids. 30 | # annToMask - Convert segmentation in an annotation to binary mask. 31 | # showAnns - Display the specified annotations. 32 | # loadRes - Load algorithm results and create API for accessing them. 33 | # download - Download COCO images from mscoco.org server. 34 | # Throughout the API "ann"=annotation, "cat"=category, and "img"=image. 35 | # Help on each functions can be accessed by: "help COCO>function". 36 | 37 | # See also COCO>decodeMask, 38 | # COCO>encodeMask, COCO>getAnnIds, COCO>getCatIds, 39 | # COCO>getImgIds, COCO>loadAnns, COCO>loadCats, 40 | # COCO>loadImgs, COCO>annToMask, COCO>showAnns 41 | 42 | # Microsoft COCO Toolbox. version 2.0 43 | # Data, paper, and tutorials available at: http://mscoco.org/ 44 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2014. 45 | # Licensed under the Simplified BSD License [see bsd.txt] 46 | 47 | import json 48 | import time 49 | import numpy as np 50 | import copy 51 | import itertools 52 | from . import mask as maskUtils 53 | import os 54 | from collections import defaultdict 55 | import sys 56 | PYTHON_VERSION = sys.version_info[0] 57 | if PYTHON_VERSION == 2: 58 | from urllib import urlretrieve 59 | elif PYTHON_VERSION == 3: 60 | from urllib.request import urlretrieve 61 | 62 | 63 | def _isArrayLike(obj): 64 | return hasattr(obj, '__iter__') and hasattr(obj, '__len__') 65 | 66 | 67 | class COCO: 68 | def __init__(self, annotation_file=None): 69 | """ 70 | Constructor of Microsoft COCO helper class for reading and visualizing annotations. 71 | :param annotation_file (str): location of annotation file 72 | :param image_folder (str): location to the folder that hosts images. 73 | :return: 74 | """ 75 | # load dataset 76 | self.dataset,self.anns,self.cats,self.imgs = dict(),dict(),dict(),dict() 77 | self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list) 78 | if not annotation_file == None: 79 | print('loading annotations into memory...') 80 | tic = time.time() 81 | with open(annotation_file, 'r') as f: 82 | dataset = json.load(f) 83 | assert type(dataset)==dict, 'annotation file format {} not supported'.format(type(dataset)) 84 | print('Done (t={:0.2f}s)'.format(time.time()- tic)) 85 | self.dataset = dataset 86 | self.createIndex() 87 | 88 | def createIndex(self): 89 | # create index 90 | print('creating index...') 91 | anns, cats, imgs = {}, {}, {} 92 | imgToAnns,catToImgs = defaultdict(list),defaultdict(list) 93 | if 'annotations' in self.dataset: 94 | for ann in self.dataset['annotations']: 95 | imgToAnns[ann['image_id']].append(ann) 96 | anns[ann['id']] = ann 97 | 98 | if 'images' in self.dataset: 99 | for img in self.dataset['images']: 100 | imgs[img['id']] = img 101 | 102 | if 'categories' in self.dataset: 103 | for cat in self.dataset['categories']: 104 | cats[cat['id']] = cat 105 | 106 | if 'annotations' in self.dataset and 'categories' in self.dataset: 107 | for ann in self.dataset['annotations']: 108 | catToImgs[ann['category_id']].append(ann['image_id']) 109 | 110 | print('index created!') 111 | 112 | # create class members 113 | self.anns = anns 114 | self.imgToAnns = imgToAnns 115 | self.catToImgs = catToImgs 116 | self.imgs = imgs 117 | self.cats = cats 118 | 119 | def info(self): 120 | """ 121 | Print information about the annotation file. 122 | :return: 123 | """ 124 | for key, value in self.dataset['info'].items(): 125 | print('{}: {}'.format(key, value)) 126 | 127 | def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None): 128 | """ 129 | Get ann ids that satisfy given filter conditions. default skips that filter 130 | :param imgIds (int array) : get anns for given imgs 131 | catIds (int array) : get anns for given cats 132 | areaRng (float array) : get anns for given area range (e.g. [0 inf]) 133 | iscrowd (boolean) : get anns for given crowd label (False or True) 134 | :return: ids (int array) : integer array of ann ids 135 | """ 136 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 137 | catIds = catIds if _isArrayLike(catIds) else [catIds] 138 | 139 | if len(imgIds) == len(catIds) == len(areaRng) == 0: 140 | anns = self.dataset['annotations'] 141 | else: 142 | if not len(imgIds) == 0: 143 | lists = [self.imgToAnns[imgId] for imgId in imgIds if imgId in self.imgToAnns] 144 | anns = list(itertools.chain.from_iterable(lists)) 145 | else: 146 | anns = self.dataset['annotations'] 147 | anns = anns if len(catIds) == 0 else [ann for ann in anns if ann['category_id'] in catIds] 148 | anns = anns if len(areaRng) == 0 else [ann for ann in anns if ann['area'] > areaRng[0] and ann['area'] < areaRng[1]] 149 | if not iscrowd == None: 150 | ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd] 151 | else: 152 | ids = [ann['id'] for ann in anns] 153 | return ids 154 | 155 | def getCatIds(self, catNms=[], supNms=[], catIds=[]): 156 | """ 157 | filtering parameters. default skips that filter. 158 | :param catNms (str array) : get cats for given cat names 159 | :param supNms (str array) : get cats for given supercategory names 160 | :param catIds (int array) : get cats for given cat ids 161 | :return: ids (int array) : integer array of cat ids 162 | """ 163 | catNms = catNms if _isArrayLike(catNms) else [catNms] 164 | supNms = supNms if _isArrayLike(supNms) else [supNms] 165 | catIds = catIds if _isArrayLike(catIds) else [catIds] 166 | 167 | if len(catNms) == len(supNms) == len(catIds) == 0: 168 | cats = self.dataset['categories'] 169 | else: 170 | cats = self.dataset['categories'] 171 | cats = cats if len(catNms) == 0 else [cat for cat in cats if cat['name'] in catNms] 172 | cats = cats if len(supNms) == 0 else [cat for cat in cats if cat['supercategory'] in supNms] 173 | cats = cats if len(catIds) == 0 else [cat for cat in cats if cat['id'] in catIds] 174 | ids = [cat['id'] for cat in cats] 175 | return ids 176 | 177 | def getImgIds(self, imgIds=[], catIds=[]): 178 | ''' 179 | Get img ids that satisfy given filter conditions. 180 | :param imgIds (int array) : get imgs for given ids 181 | :param catIds (int array) : get imgs with all given cats 182 | :return: ids (int array) : integer array of img ids 183 | ''' 184 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 185 | catIds = catIds if _isArrayLike(catIds) else [catIds] 186 | 187 | if len(imgIds) == len(catIds) == 0: 188 | ids = self.imgs.keys() 189 | else: 190 | ids = set(imgIds) 191 | for i, catId in enumerate(catIds): 192 | if i == 0 and len(ids) == 0: 193 | ids = set(self.catToImgs[catId]) 194 | else: 195 | ids &= set(self.catToImgs[catId]) 196 | return list(ids) 197 | 198 | def loadAnns(self, ids=[]): 199 | """ 200 | Load anns with the specified ids. 201 | :param ids (int array) : integer ids specifying anns 202 | :return: anns (object array) : loaded ann objects 203 | """ 204 | if _isArrayLike(ids): 205 | return [self.anns[id] for id in ids] 206 | elif type(ids) == int: 207 | return [self.anns[ids]] 208 | 209 | def loadCats(self, ids=[]): 210 | """ 211 | Load cats with the specified ids. 212 | :param ids (int array) : integer ids specifying cats 213 | :return: cats (object array) : loaded cat objects 214 | """ 215 | if _isArrayLike(ids): 216 | return [self.cats[id] for id in ids] 217 | elif type(ids) == int: 218 | return [self.cats[ids]] 219 | 220 | def loadImgs(self, ids=[]): 221 | """ 222 | Load anns with the specified ids. 223 | :param ids (int array) : integer ids specifying img 224 | :return: imgs (object array) : loaded img objects 225 | """ 226 | if _isArrayLike(ids): 227 | return [self.imgs[id] for id in ids] 228 | elif type(ids) == int: 229 | return [self.imgs[ids]] 230 | 231 | def showAnns(self, anns, draw_bbox=False): 232 | """ 233 | Display the specified annotations. 234 | :param anns (array of object): annotations to display 235 | :return: None 236 | """ 237 | if len(anns) == 0: 238 | return 0 239 | if 'segmentation' in anns[0] or 'keypoints' in anns[0]: 240 | datasetType = 'instances' 241 | elif 'caption' in anns[0]: 242 | datasetType = 'captions' 243 | else: 244 | raise Exception('datasetType not supported') 245 | if datasetType == 'instances': 246 | import matplotlib.pyplot as plt 247 | from matplotlib.collections import PatchCollection 248 | from matplotlib.patches import Polygon 249 | 250 | ax = plt.gca() 251 | ax.set_autoscale_on(False) 252 | polygons = [] 253 | color = [] 254 | for ann in anns: 255 | c = (np.random.random((1, 3))*0.6+0.4).tolist()[0] 256 | if 'segmentation' in ann: 257 | if type(ann['segmentation']) == list: 258 | # polygon 259 | for seg in ann['segmentation']: 260 | poly = np.array(seg).reshape((int(len(seg)/2), 2)) 261 | polygons.append(Polygon(poly)) 262 | color.append(c) 263 | else: 264 | # mask 265 | t = self.imgs[ann['image_id']] 266 | if type(ann['segmentation']['counts']) == list: 267 | rle = maskUtils.frPyObjects([ann['segmentation']], t['height'], t['width']) 268 | else: 269 | rle = [ann['segmentation']] 270 | m = maskUtils.decode(rle) 271 | img = np.ones( (m.shape[0], m.shape[1], 3) ) 272 | if ann['iscrowd'] == 1: 273 | color_mask = np.array([2.0,166.0,101.0])/255 274 | if ann['iscrowd'] == 0: 275 | color_mask = np.random.random((1, 3)).tolist()[0] 276 | for i in range(3): 277 | img[:,:,i] = color_mask[i] 278 | ax.imshow(np.dstack( (img, m*0.5) )) 279 | if 'keypoints' in ann and type(ann['keypoints']) == list: 280 | # turn skeleton into zero-based index 281 | sks = np.array(self.loadCats(ann['category_id'])[0]['skeleton'])-1 282 | kp = np.array(ann['keypoints']) 283 | x = kp[0::3] 284 | y = kp[1::3] 285 | v = kp[2::3] 286 | for sk in sks: 287 | if np.all(v[sk]>0): 288 | plt.plot(x[sk],y[sk], linewidth=3, color=c) 289 | plt.plot(x[v>0], y[v>0],'o',markersize=8, markerfacecolor=c, markeredgecolor='k',markeredgewidth=2) 290 | plt.plot(x[v>1], y[v>1],'o',markersize=8, markerfacecolor=c, markeredgecolor=c, markeredgewidth=2) 291 | 292 | if draw_bbox: 293 | [bbox_x, bbox_y, bbox_w, bbox_h] = ann['bbox'] 294 | poly = [[bbox_x, bbox_y], [bbox_x, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y]] 295 | np_poly = np.array(poly).reshape((4,2)) 296 | polygons.append(Polygon(np_poly)) 297 | color.append(c) 298 | 299 | p = PatchCollection(polygons, facecolor=color, linewidths=0, alpha=0.4) 300 | ax.add_collection(p) 301 | p = PatchCollection(polygons, facecolor='none', edgecolors=color, linewidths=2) 302 | ax.add_collection(p) 303 | elif datasetType == 'captions': 304 | for ann in anns: 305 | print(ann['caption']) 306 | 307 | def loadRes(self, resFile): 308 | """ 309 | Load result file and return a result api object. 310 | :param resFile (str) : file name of result file 311 | :return: res (obj) : result api object 312 | """ 313 | res = COCO() 314 | res.dataset['images'] = [img for img in self.dataset['images']] 315 | 316 | print('Loading and preparing results...') 317 | tic = time.time() 318 | if type(resFile) == str or (PYTHON_VERSION == 2 and type(resFile) == unicode): 319 | with open(resFile) as f: 320 | anns = json.load(f) 321 | elif type(resFile) == np.ndarray: 322 | anns = self.loadNumpyAnnotations(resFile) 323 | else: 324 | anns = resFile 325 | assert type(anns) == list, 'results in not an array of objects' 326 | annsImgIds = [ann['image_id'] for ann in anns] 327 | assert set(annsImgIds) == (set(annsImgIds) & set(self.getImgIds())), \ 328 | 'Results do not correspond to current coco set' 329 | if 'caption' in anns[0]: 330 | imgIds = set([img['id'] for img in res.dataset['images']]) & set([ann['image_id'] for ann in anns]) 331 | res.dataset['images'] = [img for img in res.dataset['images'] if img['id'] in imgIds] 332 | for id, ann in enumerate(anns): 333 | ann['id'] = id+1 334 | elif 'bbox' in anns[0] and not anns[0]['bbox'] == []: 335 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 336 | for id, ann in enumerate(anns): 337 | bb = ann['bbox'] 338 | x1, x2, y1, y2 = [bb[0], bb[0]+bb[2], bb[1], bb[1]+bb[3]] 339 | if not 'segmentation' in ann: 340 | ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] 341 | ann['area'] = bb[2]*bb[3] 342 | ann['id'] = id+1 343 | ann['iscrowd'] = 0 344 | elif 'segmentation' in anns[0]: 345 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 346 | for id, ann in enumerate(anns): 347 | # now only support compressed RLE format as segmentation results 348 | ann['area'] = maskUtils.area(ann['segmentation']) 349 | if not 'bbox' in ann: 350 | ann['bbox'] = maskUtils.toBbox(ann['segmentation']) 351 | ann['id'] = id+1 352 | ann['iscrowd'] = 0 353 | elif 'keypoints' in anns[0]: 354 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 355 | for id, ann in enumerate(anns): 356 | s = ann['keypoints'] 357 | x = s[0::3] 358 | y = s[1::3] 359 | x0,x1,y0,y1 = np.min(x), np.max(x), np.min(y), np.max(y) 360 | ann['area'] = (x1-x0)*(y1-y0) 361 | ann['id'] = id + 1 362 | ann['bbox'] = [x0,y0,x1-x0,y1-y0] 363 | print('DONE (t={:0.2f}s)'.format(time.time()- tic)) 364 | 365 | res.dataset['annotations'] = anns 366 | res.createIndex() 367 | return res 368 | 369 | def download(self, tarDir = None, imgIds = [] ): 370 | ''' 371 | Download COCO images from mscoco.org server. 372 | :param tarDir (str): COCO results directory name 373 | imgIds (list): images to be downloaded 374 | :return: 375 | ''' 376 | if tarDir is None: 377 | print('Please specify target directory') 378 | return -1 379 | if len(imgIds) == 0: 380 | imgs = self.imgs.values() 381 | else: 382 | imgs = self.loadImgs(imgIds) 383 | N = len(imgs) 384 | if not os.path.exists(tarDir): 385 | os.makedirs(tarDir) 386 | for i, img in enumerate(imgs): 387 | tic = time.time() 388 | fname = os.path.join(tarDir, img['file_name']) 389 | if not os.path.exists(fname): 390 | urlretrieve(img['coco_url'], fname) 391 | print('downloaded {}/{} images (t={:0.1f}s)'.format(i, N, time.time()- tic)) 392 | 393 | def loadNumpyAnnotations(self, data): 394 | """ 395 | Convert result data from a numpy array [Nx7] where each row contains {imageID,x1,y1,w,h,score,class} 396 | :param data (numpy.ndarray) 397 | :return: annotations (python nested list) 398 | """ 399 | print('Converting ndarray to lists...') 400 | assert(type(data) == np.ndarray) 401 | print(data.shape) 402 | assert(data.shape[1] == 7) 403 | N = data.shape[0] 404 | ann = [] 405 | for i in range(N): 406 | if i % 1000000 == 0: 407 | print('{}/{}'.format(i,N)) 408 | ann += [{ 409 | 'image_id' : int(data[i, 0]), 410 | 'bbox' : [ data[i, 1], data[i, 2], data[i, 3], data[i, 4] ], 411 | 'score' : data[i, 5], 412 | 'category_id': int(data[i, 6]), 413 | }] 414 | return ann 415 | 416 | def annToRLE(self, ann): 417 | """ 418 | Convert annotation which can be polygons, uncompressed RLE to RLE. 419 | :return: binary mask (numpy 2D array) 420 | """ 421 | t = self.imgs[ann['image_id']] 422 | h, w = t['height'], t['width'] 423 | segm = ann['segmentation'] 424 | if type(segm) == list: 425 | # polygon -- a single object might consist of multiple parts 426 | # we merge all parts into one mask rle code 427 | rles = maskUtils.frPyObjects(segm, h, w) 428 | rle = maskUtils.merge(rles) 429 | elif type(segm['counts']) == list: 430 | # uncompressed RLE 431 | rle = maskUtils.frPyObjects(segm, h, w) 432 | else: 433 | # rle 434 | rle = ann['segmentation'] 435 | return rle 436 | 437 | def annToMask(self, ann): 438 | """ 439 | Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask. 440 | :return: binary mask (numpy 2D array) 441 | """ 442 | rle = self.annToRLE(ann) 443 | m = maskUtils.decode(rle) 444 | return m 445 | -------------------------------------------------------------------------------- /evaluation_Jetson/pycocotools/cocoeval.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import numpy as np 4 | import datetime 5 | import time 6 | from collections import defaultdict 7 | from . import mask as maskUtils 8 | import copy 9 | 10 | class COCOeval: 11 | # Interface for evaluating detection on the Microsoft COCO dataset. 12 | # 13 | # The usage for CocoEval is as follows: 14 | # cocoGt=..., cocoDt=... # load dataset and results 15 | # E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object 16 | # E.params.recThrs = ...; # set parameters as desired 17 | # E.evaluate(); # run per image evaluation 18 | # E.accumulate(); # accumulate per image results 19 | # E.summarize(); # display summary metrics of results 20 | # For example usage see evalDemo.m and http://mscoco.org/. 21 | # 22 | # The evaluation parameters are as follows (defaults in brackets): 23 | # imgIds - [all] N img ids to use for evaluation 24 | # catIds - [all] K cat ids to use for evaluation 25 | # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation 26 | # recThrs - [0:.01:1] R=101 recall thresholds for evaluation 27 | # areaRng - [...] A=4 object area ranges for evaluation 28 | # maxDets - [1 10 100] M=3 thresholds on max detections per image 29 | # iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints' 30 | # iouType replaced the now DEPRECATED useSegm parameter. 31 | # useCats - [1] if true use category labels for evaluation 32 | # Note: if useCats=0 category labels are ignored as in proposal scoring. 33 | # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. 34 | # 35 | # evaluate(): evaluates detections on every image and every category and 36 | # concats the results into the "evalImgs" with fields: 37 | # dtIds - [1xD] id for each of the D detections (dt) 38 | # gtIds - [1xG] id for each of the G ground truths (gt) 39 | # dtMatches - [TxD] matching gt id at each IoU or 0 40 | # gtMatches - [TxG] matching dt id at each IoU or 0 41 | # dtScores - [1xD] confidence of each dt 42 | # gtIgnore - [1xG] ignore flag for each gt 43 | # dtIgnore - [TxD] ignore flag for each dt at each IoU 44 | # 45 | # accumulate(): accumulates the per-image, per-category evaluation 46 | # results in "evalImgs" into the dictionary "eval" with fields: 47 | # params - parameters used for evaluation 48 | # date - date evaluation was performed 49 | # counts - [T,R,K,A,M] parameter dimensions (see above) 50 | # precision - [TxRxKxAxM] precision for every evaluation setting 51 | # recall - [TxKxAxM] max recall for every evaluation setting 52 | # Note: precision and recall==-1 for settings with no gt objects. 53 | # 54 | # See also coco, mask, pycocoDemo, pycocoEvalDemo 55 | # 56 | # Microsoft COCO Toolbox. version 2.0 57 | # Data, paper, and tutorials available at: http://mscoco.org/ 58 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 59 | # Licensed under the Simplified BSD License [see coco/license.txt] 60 | def __init__(self, cocoGt=None, cocoDt=None, iouType='segm'): 61 | ''' 62 | Initialize CocoEval using coco APIs for gt and dt 63 | :param cocoGt: coco object with ground truth annotations 64 | :param cocoDt: coco object with detection results 65 | :return: None 66 | ''' 67 | if not iouType: 68 | print('iouType not specified. use default iouType segm') 69 | self.cocoGt = cocoGt # ground truth COCO API 70 | self.cocoDt = cocoDt # detections COCO API 71 | self.evalImgs = defaultdict(list) # per-image per-category evaluation results [KxAxI] elements 72 | self.eval = {} # accumulated evaluation results 73 | self._gts = defaultdict(list) # gt for evaluation 74 | self._dts = defaultdict(list) # dt for evaluation 75 | self.params = Params(iouType=iouType) # parameters 76 | self._paramsEval = {} # parameters for evaluation 77 | self.stats = [] # result summarization 78 | self.ious = {} # ious between all gts and dts 79 | if not cocoGt is None: 80 | self.params.imgIds = sorted(cocoGt.getImgIds()) 81 | self.params.catIds = sorted(cocoGt.getCatIds()) 82 | 83 | 84 | def _prepare(self): 85 | ''' 86 | Prepare ._gts and ._dts for evaluation based on params 87 | :return: None 88 | ''' 89 | def _toMask(anns, coco): 90 | # modify ann['segmentation'] by reference 91 | for ann in anns: 92 | rle = coco.annToRLE(ann) 93 | ann['segmentation'] = rle 94 | p = self.params 95 | if p.useCats: 96 | gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 97 | dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 98 | else: 99 | gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds)) 100 | dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds)) 101 | 102 | # convert ground truth to mask if iouType == 'segm' 103 | if p.iouType == 'segm': 104 | _toMask(gts, self.cocoGt) 105 | _toMask(dts, self.cocoDt) 106 | # set ignore flag 107 | for gt in gts: 108 | gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0 109 | gt['ignore'] = 'iscrowd' in gt and gt['iscrowd'] 110 | if p.iouType == 'keypoints': 111 | gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore'] 112 | self._gts = defaultdict(list) # gt for evaluation 113 | self._dts = defaultdict(list) # dt for evaluation 114 | for gt in gts: 115 | self._gts[gt['image_id'], gt['category_id']].append(gt) 116 | for dt in dts: 117 | self._dts[dt['image_id'], dt['category_id']].append(dt) 118 | self.evalImgs = defaultdict(list) # per-image per-category evaluation results 119 | self.eval = {} # accumulated evaluation results 120 | 121 | def evaluate(self): 122 | ''' 123 | Run per image evaluation on given images and store results (a list of dict) in self.evalImgs 124 | :return: None 125 | ''' 126 | tic = time.time() 127 | print('Running per image evaluation...') 128 | p = self.params 129 | # add backward compatibility if useSegm is specified in params 130 | if not p.useSegm is None: 131 | p.iouType = 'segm' if p.useSegm == 1 else 'bbox' 132 | print('useSegm (deprecated) is not None. Running {} evaluation'.format(p.iouType)) 133 | print('Evaluate annotation type *{}*'.format(p.iouType)) 134 | p.imgIds = list(np.unique(p.imgIds)) 135 | if p.useCats: 136 | p.catIds = list(np.unique(p.catIds)) 137 | p.maxDets = sorted(p.maxDets) 138 | self.params=p 139 | 140 | self._prepare() 141 | # loop through images, area range, max detection number 142 | catIds = p.catIds if p.useCats else [-1] 143 | 144 | if p.iouType == 'segm' or p.iouType == 'bbox': 145 | computeIoU = self.computeIoU 146 | elif p.iouType == 'keypoints': 147 | computeIoU = self.computeOks 148 | self.ious = {(imgId, catId): computeIoU(imgId, catId) \ 149 | for imgId in p.imgIds 150 | for catId in catIds} 151 | 152 | evaluateImg = self.evaluateImg 153 | maxDet = p.maxDets[-1] 154 | self.evalImgs = [evaluateImg(imgId, catId, areaRng, maxDet) 155 | for catId in catIds 156 | for areaRng in p.areaRng 157 | for imgId in p.imgIds 158 | ] 159 | self._paramsEval = copy.deepcopy(self.params) 160 | toc = time.time() 161 | print('DONE (t={:0.2f}s).'.format(toc-tic)) 162 | 163 | def computeIoU(self, imgId, catId): 164 | p = self.params 165 | if p.useCats: 166 | gt = self._gts[imgId,catId] 167 | dt = self._dts[imgId,catId] 168 | else: 169 | gt = [_ for cId in p.catIds for _ in self._gts[imgId,cId]] 170 | dt = [_ for cId in p.catIds for _ in self._dts[imgId,cId]] 171 | if len(gt) == 0 and len(dt) ==0: 172 | return [] 173 | inds = np.argsort([-d['score'] for d in dt], kind='mergesort') 174 | dt = [dt[i] for i in inds] 175 | if len(dt) > p.maxDets[-1]: 176 | dt=dt[0:p.maxDets[-1]] 177 | 178 | if p.iouType == 'segm': 179 | g = [g['segmentation'] for g in gt] 180 | d = [d['segmentation'] for d in dt] 181 | elif p.iouType == 'bbox': 182 | g = [g['bbox'] for g in gt] 183 | d = [d['bbox'] for d in dt] 184 | else: 185 | raise Exception('unknown iouType for iou computation') 186 | 187 | # compute iou between each dt and gt region 188 | iscrowd = [int(o['iscrowd']) for o in gt] 189 | ious = maskUtils.iou(d,g,iscrowd) 190 | return ious 191 | 192 | def computeOks(self, imgId, catId): 193 | p = self.params 194 | # dimention here should be Nxm 195 | gts = self._gts[imgId, catId] 196 | dts = self._dts[imgId, catId] 197 | inds = np.argsort([-d['score'] for d in dts], kind='mergesort') 198 | dts = [dts[i] for i in inds] 199 | if len(dts) > p.maxDets[-1]: 200 | dts = dts[0:p.maxDets[-1]] 201 | # if len(gts) == 0 and len(dts) == 0: 202 | if len(gts) == 0 or len(dts) == 0: 203 | return [] 204 | ious = np.zeros((len(dts), len(gts))) 205 | sigmas = p.kpt_oks_sigmas 206 | vars = (sigmas * 2)**2 207 | k = len(sigmas) 208 | # compute oks between each detection and ground truth object 209 | for j, gt in enumerate(gts): 210 | # create bounds for ignore regions(double the gt bbox) 211 | g = np.array(gt['keypoints']) 212 | xg = g[0::3]; yg = g[1::3]; vg = g[2::3] 213 | k1 = np.count_nonzero(vg > 0) 214 | bb = gt['bbox'] 215 | x0 = bb[0] - bb[2]; x1 = bb[0] + bb[2] * 2 216 | y0 = bb[1] - bb[3]; y1 = bb[1] + bb[3] * 2 217 | for i, dt in enumerate(dts): 218 | d = np.array(dt['keypoints']) 219 | xd = d[0::3]; yd = d[1::3] 220 | if k1>0: 221 | # measure the per-keypoint distance if keypoints visible 222 | dx = xd - xg 223 | dy = yd - yg 224 | else: 225 | # measure minimum distance to keypoints in (x0,y0) & (x1,y1) 226 | z = np.zeros((k)) 227 | dx = np.max((z, x0-xd),axis=0)+np.max((z, xd-x1),axis=0) 228 | dy = np.max((z, y0-yd),axis=0)+np.max((z, yd-y1),axis=0) 229 | e = (dx**2 + dy**2) / vars / (gt['area']+np.spacing(1)) / 2 230 | if k1 > 0: 231 | e=e[vg > 0] 232 | ious[i, j] = np.sum(np.exp(-e)) / e.shape[0] 233 | return ious 234 | 235 | def evaluateImg(self, imgId, catId, aRng, maxDet): 236 | ''' 237 | perform evaluation for single category and image 238 | :return: dict (single image results) 239 | ''' 240 | p = self.params 241 | if p.useCats: 242 | gt = self._gts[imgId,catId] 243 | dt = self._dts[imgId,catId] 244 | else: 245 | gt = [_ for cId in p.catIds for _ in self._gts[imgId,cId]] 246 | dt = [_ for cId in p.catIds for _ in self._dts[imgId,cId]] 247 | if len(gt) == 0 and len(dt) ==0: 248 | return None 249 | 250 | for g in gt: 251 | if g['ignore'] or (g['area']aRng[1]): 252 | g['_ignore'] = 1 253 | else: 254 | g['_ignore'] = 0 255 | 256 | # sort dt highest score first, sort gt ignore last 257 | gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort') 258 | gt = [gt[i] for i in gtind] 259 | dtind = np.argsort([-d['score'] for d in dt], kind='mergesort') 260 | dt = [dt[i] for i in dtind[0:maxDet]] 261 | iscrowd = [int(o['iscrowd']) for o in gt] 262 | # load computed ious 263 | ious = self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] 264 | 265 | T = len(p.iouThrs) 266 | G = len(gt) 267 | D = len(dt) 268 | gtm = np.zeros((T,G)) 269 | dtm = np.zeros((T,D)) 270 | gtIg = np.array([g['_ignore'] for g in gt]) 271 | dtIg = np.zeros((T,D)) 272 | if not len(ious)==0: 273 | for tind, t in enumerate(p.iouThrs): 274 | for dind, d in enumerate(dt): 275 | # information about best match so far (m=-1 -> unmatched) 276 | iou = min([t,1-1e-10]) 277 | m = -1 278 | for gind, g in enumerate(gt): 279 | # if this gt already matched, and not a crowd, continue 280 | if gtm[tind,gind]>0 and not iscrowd[gind]: 281 | continue 282 | # if dt matched to reg gt, and on ignore gt, stop 283 | if m>-1 and gtIg[m]==0 and gtIg[gind]==1: 284 | break 285 | # continue to next gt unless better match made 286 | if ious[dind,gind] < iou: 287 | continue 288 | # if match successful and best so far, store appropriately 289 | iou=ious[dind,gind] 290 | m=gind 291 | # if match made store id of match for both dt and gt 292 | if m ==-1: 293 | continue 294 | dtIg[tind,dind] = gtIg[m] 295 | dtm[tind,dind] = gt[m]['id'] 296 | gtm[tind,m] = d['id'] 297 | # set unmatched detections outside of area range to ignore 298 | a = np.array([d['area']aRng[1] for d in dt]).reshape((1, len(dt))) 299 | dtIg = np.logical_or(dtIg, np.logical_and(dtm==0, np.repeat(a,T,0))) 300 | # store results for given image and category 301 | return { 302 | 'image_id': imgId, 303 | 'category_id': catId, 304 | 'aRng': aRng, 305 | 'maxDet': maxDet, 306 | 'dtIds': [d['id'] for d in dt], 307 | 'gtIds': [g['id'] for g in gt], 308 | 'dtMatches': dtm, 309 | 'gtMatches': gtm, 310 | 'dtScores': [d['score'] for d in dt], 311 | 'gtIgnore': gtIg, 312 | 'dtIgnore': dtIg, 313 | } 314 | 315 | def accumulate(self, p = None): 316 | ''' 317 | Accumulate per image evaluation results and store the result in self.eval 318 | :param p: input params for evaluation 319 | :return: None 320 | ''' 321 | print('Accumulating evaluation results...') 322 | tic = time.time() 323 | if not self.evalImgs: 324 | print('Please run evaluate() first') 325 | # allows input customized parameters 326 | if p is None: 327 | p = self.params 328 | p.catIds = p.catIds if p.useCats == 1 else [-1] 329 | T = len(p.iouThrs) 330 | R = len(p.recThrs) 331 | K = len(p.catIds) if p.useCats else 1 332 | A = len(p.areaRng) 333 | M = len(p.maxDets) 334 | precision = -np.ones((T,R,K,A,M)) # -1 for the precision of absent categories 335 | recall = -np.ones((T,K,A,M)) 336 | scores = -np.ones((T,R,K,A,M)) 337 | 338 | # create dictionary for future indexing 339 | _pe = self._paramsEval 340 | catIds = _pe.catIds if _pe.useCats else [-1] 341 | setK = set(catIds) 342 | setA = set(map(tuple, _pe.areaRng)) 343 | setM = set(_pe.maxDets) 344 | setI = set(_pe.imgIds) 345 | # get inds to evaluate 346 | k_list = [n for n, k in enumerate(p.catIds) if k in setK] 347 | m_list = [m for n, m in enumerate(p.maxDets) if m in setM] 348 | a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA] 349 | i_list = [n for n, i in enumerate(p.imgIds) if i in setI] 350 | I0 = len(_pe.imgIds) 351 | A0 = len(_pe.areaRng) 352 | # retrieve E at each category, area range, and max number of detections 353 | for k, k0 in enumerate(k_list): 354 | Nk = k0*A0*I0 355 | for a, a0 in enumerate(a_list): 356 | Na = a0*I0 357 | for m, maxDet in enumerate(m_list): 358 | E = [self.evalImgs[Nk + Na + i] for i in i_list] 359 | E = [e for e in E if not e is None] 360 | if len(E) == 0: 361 | continue 362 | dtScores = np.concatenate([e['dtScores'][0:maxDet] for e in E]) 363 | 364 | # different sorting method generates slightly different results. 365 | # mergesort is used to be consistent as Matlab implementation. 366 | inds = np.argsort(-dtScores, kind='mergesort') 367 | dtScoresSorted = dtScores[inds] 368 | 369 | dtm = np.concatenate([e['dtMatches'][:,0:maxDet] for e in E], axis=1)[:,inds] 370 | dtIg = np.concatenate([e['dtIgnore'][:,0:maxDet] for e in E], axis=1)[:,inds] 371 | gtIg = np.concatenate([e['gtIgnore'] for e in E]) 372 | npig = np.count_nonzero(gtIg==0 ) 373 | if npig == 0: 374 | continue 375 | tps = np.logical_and( dtm, np.logical_not(dtIg) ) 376 | fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg) ) 377 | 378 | tp_sum = np.cumsum(tps, axis=1).astype(dtype=float) 379 | fp_sum = np.cumsum(fps, axis=1).astype(dtype=float) 380 | for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): 381 | tp = np.array(tp) 382 | fp = np.array(fp) 383 | nd = len(tp) 384 | rc = tp / npig 385 | pr = tp / (fp+tp+np.spacing(1)) 386 | q = np.zeros((R,)) 387 | ss = np.zeros((R,)) 388 | 389 | if nd: 390 | recall[t,k,a,m] = rc[-1] 391 | else: 392 | recall[t,k,a,m] = 0 393 | 394 | # numpy is slow without cython optimization for accessing elements 395 | # use python array gets significant speed improvement 396 | pr = pr.tolist(); q = q.tolist() 397 | 398 | for i in range(nd-1, 0, -1): 399 | if pr[i] > pr[i-1]: 400 | pr[i-1] = pr[i] 401 | 402 | inds = np.searchsorted(rc, p.recThrs, side='left') 403 | try: 404 | for ri, pi in enumerate(inds): 405 | q[ri] = pr[pi] 406 | ss[ri] = dtScoresSorted[pi] 407 | except: 408 | pass 409 | precision[t,:,k,a,m] = np.array(q) 410 | scores[t,:,k,a,m] = np.array(ss) 411 | self.eval = { 412 | 'params': p, 413 | 'counts': [T, R, K, A, M], 414 | 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 415 | 'precision': precision, 416 | 'recall': recall, 417 | 'scores': scores, 418 | } 419 | toc = time.time() 420 | print('DONE (t={:0.2f}s).'.format( toc-tic)) 421 | 422 | def summarize(self): 423 | ''' 424 | Compute and display summary metrics for evaluation results. 425 | Note this functin can *only* be applied on the default parameter setting 426 | ''' 427 | def _summarize( ap=1, iouThr=None, areaRng='all', maxDets=100 ): 428 | p = self.params 429 | iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}' 430 | titleStr = 'Average Precision' if ap == 1 else 'Average Recall' 431 | typeStr = '(AP)' if ap==1 else '(AR)' 432 | iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \ 433 | if iouThr is None else '{:0.2f}'.format(iouThr) 434 | 435 | aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng] 436 | mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] 437 | if ap == 1: 438 | # dimension of precision: [TxRxKxAxM] 439 | s = self.eval['precision'] 440 | # IoU 441 | if iouThr is not None: 442 | t = np.where(iouThr == p.iouThrs)[0] 443 | s = s[t] 444 | s = s[:,:,:,aind,mind] 445 | else: 446 | # dimension of recall: [TxKxAxM] 447 | s = self.eval['recall'] 448 | if iouThr is not None: 449 | t = np.where(iouThr == p.iouThrs)[0] 450 | s = s[t] 451 | s = s[:,:,aind,mind] 452 | if len(s[s>-1])==0: 453 | mean_s = -1 454 | else: 455 | mean_s = np.mean(s[s>-1]) 456 | print(iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets, mean_s)) 457 | return mean_s 458 | def _summarizeDets(): 459 | stats = np.zeros((12,)) 460 | stats[0] = _summarize(1) 461 | stats[1] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2]) 462 | stats[2] = _summarize(1, iouThr=.75, maxDets=self.params.maxDets[2]) 463 | stats[3] = _summarize(1, areaRng='small', maxDets=self.params.maxDets[2]) 464 | stats[4] = _summarize(1, areaRng='medium', maxDets=self.params.maxDets[2]) 465 | stats[5] = _summarize(1, areaRng='large', maxDets=self.params.maxDets[2]) 466 | stats[6] = _summarize(0, maxDets=self.params.maxDets[0]) 467 | stats[7] = _summarize(0, maxDets=self.params.maxDets[1]) 468 | stats[8] = _summarize(0, maxDets=self.params.maxDets[2]) 469 | stats[9] = _summarize(0, areaRng='small', maxDets=self.params.maxDets[2]) 470 | stats[10] = _summarize(0, areaRng='medium', maxDets=self.params.maxDets[2]) 471 | stats[11] = _summarize(0, areaRng='large', maxDets=self.params.maxDets[2]) 472 | return stats 473 | def _summarizeKps(): 474 | stats = np.zeros((10,)) 475 | stats[0] = _summarize(1, maxDets=20) 476 | stats[1] = _summarize(1, maxDets=20, iouThr=.5) 477 | stats[2] = _summarize(1, maxDets=20, iouThr=.75) 478 | stats[3] = _summarize(1, maxDets=20, areaRng='medium') 479 | stats[4] = _summarize(1, maxDets=20, areaRng='large') 480 | stats[5] = _summarize(0, maxDets=20) 481 | stats[6] = _summarize(0, maxDets=20, iouThr=.5) 482 | stats[7] = _summarize(0, maxDets=20, iouThr=.75) 483 | stats[8] = _summarize(0, maxDets=20, areaRng='medium') 484 | stats[9] = _summarize(0, maxDets=20, areaRng='large') 485 | return stats 486 | if not self.eval: 487 | raise Exception('Please run accumulate() first') 488 | iouType = self.params.iouType 489 | if iouType == 'segm' or iouType == 'bbox': 490 | summarize = _summarizeDets 491 | elif iouType == 'keypoints': 492 | summarize = _summarizeKps 493 | self.stats = summarize() 494 | 495 | def __str__(self): 496 | self.summarize() 497 | 498 | class Params: 499 | ''' 500 | Params for coco evaluation api 501 | ''' 502 | def setDetParams(self): 503 | self.imgIds = [] 504 | self.catIds = [] 505 | # np.arange causes trouble. the data point on arange is slightly larger than the true value 506 | self.iouThrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) 507 | self.recThrs = np.linspace(.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True) 508 | self.maxDets = [1, 10, 100] 509 | #self.areaRng = [[0 ** 2, 1e5 ** 2], [0 ** 2, 32 ** 2], [32 ** 2, 96 ** 2], [96 ** 2, 1e5 ** 2]] 510 | #replaced 511 | self.areaRng = [[0 ** 2, 1e5 ** 2], [0 ** 2, 64 ** 2],[64 ** 2, 192 ** 2], [192 ** 2, 1e5 ** 2]] 512 | self.areaRngLbl = ['all', 'small', 'medium', 'large'] 513 | self.useCats = 1 514 | 515 | def setKpParams(self): 516 | self.imgIds = [] 517 | self.catIds = [] 518 | # np.arange causes trouble. the data point on arange is slightly larger than the true value 519 | self.iouThrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) 520 | self.recThrs = np.linspace(.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True) 521 | self.maxDets = [20] 522 | self.areaRng = [[0 ** 2, 1e5 ** 2], [32 ** 2, 96 ** 2], [96 ** 2, 1e5 ** 2]] 523 | self.areaRngLbl = ['all', 'medium', 'large'] 524 | self.useCats = 1 525 | self.kpt_oks_sigmas = np.array([.26, .25, .25, .35, .35, .79, .79, .72, .72, .62,.62, 1.07, 1.07, .87, .87, .89, .89])/10.0 526 | 527 | def __init__(self, iouType='segm'): 528 | if iouType == 'segm' or iouType == 'bbox': 529 | self.setDetParams() 530 | elif iouType == 'keypoints': 531 | self.setKpParams() 532 | else: 533 | raise Exception('iouType not supported') 534 | self.iouType = iouType 535 | # useSegm is deprecated 536 | self.useSegm = None 537 | -------------------------------------------------------------------------------- /evaluation_Jetson/pycocotools/mask.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import pycocotools._mask as _mask 4 | 5 | # Interface for manipulating masks stored in RLE format. 6 | # 7 | # RLE is a simple yet efficient format for storing binary masks. RLE 8 | # first divides a vector (or vectorized image) into a series of piecewise 9 | # constant regions and then for each piece simply stores the length of 10 | # that piece. For example, given M=[0 0 1 1 1 0 1] the RLE counts would 11 | # be [2 3 1 1], or for M=[1 1 1 1 1 1 0] the counts would be [0 6 1] 12 | # (note that the odd counts are always the numbers of zeros). Instead of 13 | # storing the counts directly, additional compression is achieved with a 14 | # variable bitrate representation based on a common scheme called LEB128. 15 | # 16 | # Compression is greatest given large piecewise constant regions. 17 | # Specifically, the size of the RLE is proportional to the number of 18 | # *boundaries* in M (or for an image the number of boundaries in the y 19 | # direction). Assuming fairly simple shapes, the RLE representation is 20 | # O(sqrt(n)) where n is number of pixels in the object. Hence space usage 21 | # is substantially lower, especially for large simple objects (large n). 22 | # 23 | # Many common operations on masks can be computed directly using the RLE 24 | # (without need for decoding). This includes computations such as area, 25 | # union, intersection, etc. All of these operations are linear in the 26 | # size of the RLE, in other words they are O(sqrt(n)) where n is the area 27 | # of the object. Computing these operations on the original mask is O(n). 28 | # Thus, using the RLE can result in substantial computational savings. 29 | # 30 | # The following API functions are defined: 31 | # encode - Encode binary masks using RLE. 32 | # decode - Decode binary masks encoded via RLE. 33 | # merge - Compute union or intersection of encoded masks. 34 | # iou - Compute intersection over union between masks. 35 | # area - Compute area of encoded masks. 36 | # toBbox - Get bounding boxes surrounding encoded masks. 37 | # frPyObjects - Convert polygon, bbox, and uncompressed RLE to encoded RLE mask. 38 | # 39 | # Usage: 40 | # Rs = encode( masks ) 41 | # masks = decode( Rs ) 42 | # R = merge( Rs, intersect=false ) 43 | # o = iou( dt, gt, iscrowd ) 44 | # a = area( Rs ) 45 | # bbs = toBbox( Rs ) 46 | # Rs = frPyObjects( [pyObjects], h, w ) 47 | # 48 | # In the API the following formats are used: 49 | # Rs - [dict] Run-length encoding of binary masks 50 | # R - dict Run-length encoding of binary mask 51 | # masks - [hxwxn] Binary mask(s) (must have type np.ndarray(dtype=uint8) in column-major order) 52 | # iscrowd - [nx1] list of np.ndarray. 1 indicates corresponding gt image has crowd region to ignore 53 | # bbs - [nx4] Bounding box(es) stored as [x y w h] 54 | # poly - Polygon stored as [[x1 y1 x2 y2...],[x1 y1 ...],...] (2D list) 55 | # dt,gt - May be either bounding boxes or encoded masks 56 | # Both poly and bbs are 0-indexed (bbox=[0 0 1 1] encloses first pixel). 57 | # 58 | # Finally, a note about the intersection over union (iou) computation. 59 | # The standard iou of a ground truth (gt) and detected (dt) object is 60 | # iou(gt,dt) = area(intersect(gt,dt)) / area(union(gt,dt)) 61 | # For "crowd" regions, we use a modified criteria. If a gt object is 62 | # marked as "iscrowd", we allow a dt to match any subregion of the gt. 63 | # Choosing gt' in the crowd gt that best matches the dt can be done using 64 | # gt'=intersect(dt,gt). Since by definition union(gt',dt)=dt, computing 65 | # iou(gt,dt,iscrowd) = iou(gt',dt) = area(intersect(gt,dt)) / area(dt) 66 | # For crowd gt regions we use this modified criteria above for the iou. 67 | # 68 | # To compile run "python setup.py build_ext --inplace" 69 | # Please do not contact us for help with compiling. 70 | # 71 | # Microsoft COCO Toolbox. version 2.0 72 | # Data, paper, and tutorials available at: http://mscoco.org/ 73 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 74 | # Licensed under the Simplified BSD License [see coco/license.txt] 75 | 76 | iou = _mask.iou 77 | merge = _mask.merge 78 | frPyObjects = _mask.frPyObjects 79 | 80 | def encode(bimask): 81 | if len(bimask.shape) == 3: 82 | return _mask.encode(bimask) 83 | elif len(bimask.shape) == 2: 84 | h, w = bimask.shape 85 | return _mask.encode(bimask.reshape((h, w, 1), order='F'))[0] 86 | 87 | def decode(rleObjs): 88 | if type(rleObjs) == list: 89 | return _mask.decode(rleObjs) 90 | else: 91 | return _mask.decode([rleObjs])[:,:,0] 92 | 93 | def area(rleObjs): 94 | if type(rleObjs) == list: 95 | return _mask.area(rleObjs) 96 | else: 97 | return _mask.area([rleObjs])[0] 98 | 99 | def toBbox(rleObjs): 100 | if type(rleObjs) == list: 101 | return _mask.toBbox(rleObjs) 102 | else: 103 | return _mask.toBbox([rleObjs])[0] -------------------------------------------------------------------------------- /evaluation_Linux/EVALUATION.md: -------------------------------------------------------------------------------- 1 | # Evaluation Metric for FishEye8k dataset 2 | -------------------------------------------------------------------------------- /evaluation_Linux/README.md: -------------------------------------------------------------------------------- 1 | # Evaluation Metric for FishEye8k dataset 2 | 3 | Update: 4 | 5 | `2024/03/18`: Change the bug in calculating f1 score. 6 | 7 | 8 | -------------------------------------------------------------------------------- /evaluation_Linux/eval.py: -------------------------------------------------------------------------------- 1 | from pycocotools.coco import COCO 2 | from pycocotools.cocoeval import COCOeval 3 | 4 | 5 | coco_gt = COCO('groundtruth.json') 6 | coco_dt = coco_gt.loadRes( 7 | 'detection_dgx.json') 8 | 9 | print(type(coco_dt)) 10 | print(type(coco_gt)) 11 | 12 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 13 | coco_eval.evaluate() 14 | coco_eval.accumulate() 15 | coco_eval.summarize() 16 | -------------------------------------------------------------------------------- /evaluation_Linux/eval_f1.py: -------------------------------------------------------------------------------- 1 | from pycocotools.coco import COCO 2 | from pycocotools.cocoeval_modified import COCOeval 3 | import json 4 | 5 | coco_gt = COCO('groundtruth.json') 6 | 7 | gt_image_ids = coco_gt.getImgIds() 8 | 9 | print("Total images ", len(gt_image_ids)) 10 | 11 | 12 | with open('detections.json', 'r') as f: 13 | detection_data = json.load(f) 14 | 15 | filtered_detection_data = [ 16 | item for item in detection_data if item['image_id'] in gt_image_ids] 17 | 18 | with open('./temp.json', 'w') as f: 19 | json.dump(filtered_detection_data, f) 20 | 21 | coco_dt = coco_gt.loadRes('./temp.json') 22 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 23 | coco_eval.evaluate() 24 | coco_eval.accumulate() 25 | coco_eval.summarize() 26 | print('----------------------------------------') 27 | print('AP_0.5-0.95', coco_eval.stats[0]) 28 | print('AP_0.5', coco_eval.stats[1]) 29 | print('AP_S', coco_eval.stats[3]) 30 | print('AP_M', coco_eval.stats[4]) 31 | print('AP_L', coco_eval.stats[5]) 32 | print('f1_score: ', coco_eval.stats[20]) 33 | print('----------------------------------------') 34 | -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Linux/pycocotools/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/__pycache__/coco.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Linux/pycocotools/__pycache__/coco.cpython-38.pyc -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/__pycache__/cocoeval.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Linux/pycocotools/__pycache__/cocoeval.cpython-38.pyc -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/__pycache__/mask.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Linux/pycocotools/__pycache__/mask.cpython-38.pyc -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/_mask.cpython-38-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Linux/pycocotools/_mask.cpython-38-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/coco.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | __version__ = '2.0' 3 | # Interface for accessing the Microsoft COCO dataset. 4 | 5 | # Microsoft COCO is a large image dataset designed for object detection, 6 | # segmentation, and caption generation. pycocotools is a Python API that 7 | # assists in loading, parsing and visualizing the annotations in COCO. 8 | # Please visit http://mscoco.org/ for more information on COCO, including 9 | # for the data, paper, and tutorials. The exact format of the annotations 10 | # is also described on the COCO website. For example usage of the pycocotools 11 | # please see pycocotools_demo.ipynb. In addition to this API, please download both 12 | # the COCO images and annotations in order to run the demo. 13 | 14 | # An alternative to using the API is to load the annotations directly 15 | # into Python dictionary 16 | # Using the API provides additional utility functions. Note that this API 17 | # supports both *instance* and *caption* annotations. In the case of 18 | # captions not all functions are defined (e.g. categories are undefined). 19 | 20 | # The following API functions are defined: 21 | # COCO - COCO api class that loads COCO annotation file and prepare data structures. 22 | # decodeMask - Decode binary mask M encoded via run-length encoding. 23 | # encodeMask - Encode binary mask M using run-length encoding. 24 | # getAnnIds - Get ann ids that satisfy given filter conditions. 25 | # getCatIds - Get cat ids that satisfy given filter conditions. 26 | # getImgIds - Get img ids that satisfy given filter conditions. 27 | # loadAnns - Load anns with the specified ids. 28 | # loadCats - Load cats with the specified ids. 29 | # loadImgs - Load imgs with the specified ids. 30 | # annToMask - Convert segmentation in an annotation to binary mask. 31 | # showAnns - Display the specified annotations. 32 | # loadRes - Load algorithm results and create API for accessing them. 33 | # download - Download COCO images from mscoco.org server. 34 | # Throughout the API "ann"=annotation, "cat"=category, and "img"=image. 35 | # Help on each functions can be accessed by: "help COCO>function". 36 | 37 | # See also COCO>decodeMask, 38 | # COCO>encodeMask, COCO>getAnnIds, COCO>getCatIds, 39 | # COCO>getImgIds, COCO>loadAnns, COCO>loadCats, 40 | # COCO>loadImgs, COCO>annToMask, COCO>showAnns 41 | 42 | # Microsoft COCO Toolbox. version 2.0 43 | # Data, paper, and tutorials available at: http://mscoco.org/ 44 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2014. 45 | # Licensed under the Simplified BSD License [see bsd.txt] 46 | 47 | import json 48 | import time 49 | import numpy as np 50 | import copy 51 | import itertools 52 | from . import mask as maskUtils 53 | import os 54 | from collections import defaultdict 55 | import sys 56 | PYTHON_VERSION = sys.version_info[0] 57 | if PYTHON_VERSION == 2: 58 | from urllib import urlretrieve 59 | elif PYTHON_VERSION == 3: 60 | from urllib.request import urlretrieve 61 | 62 | 63 | def _isArrayLike(obj): 64 | return hasattr(obj, '__iter__') and hasattr(obj, '__len__') 65 | 66 | 67 | class COCO: 68 | def __init__(self, annotation_file=None): 69 | """ 70 | Constructor of Microsoft COCO helper class for reading and visualizing annotations. 71 | :param annotation_file (str): location of annotation file 72 | :param image_folder (str): location to the folder that hosts images. 73 | :return: 74 | """ 75 | # load dataset 76 | self.dataset,self.anns,self.cats,self.imgs = dict(),dict(),dict(),dict() 77 | self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list) 78 | if not annotation_file == None: 79 | print('loading annotations into memory...') 80 | tic = time.time() 81 | with open(annotation_file, 'r') as f: 82 | dataset = json.load(f) 83 | assert type(dataset)==dict, 'annotation file format {} not supported'.format(type(dataset)) 84 | print('Done (t={:0.2f}s)'.format(time.time()- tic)) 85 | self.dataset = dataset 86 | self.createIndex() 87 | 88 | def createIndex(self): 89 | # create index 90 | print('creating index...') 91 | anns, cats, imgs = {}, {}, {} 92 | imgToAnns,catToImgs = defaultdict(list),defaultdict(list) 93 | if 'annotations' in self.dataset: 94 | for ann in self.dataset['annotations']: 95 | imgToAnns[ann['image_id']].append(ann) 96 | anns[ann['id']] = ann 97 | 98 | if 'images' in self.dataset: 99 | for img in self.dataset['images']: 100 | imgs[img['id']] = img 101 | 102 | if 'categories' in self.dataset: 103 | for cat in self.dataset['categories']: 104 | cats[cat['id']] = cat 105 | 106 | if 'annotations' in self.dataset and 'categories' in self.dataset: 107 | for ann in self.dataset['annotations']: 108 | catToImgs[ann['category_id']].append(ann['image_id']) 109 | 110 | print('index created!') 111 | 112 | # create class members 113 | self.anns = anns 114 | self.imgToAnns = imgToAnns 115 | self.catToImgs = catToImgs 116 | self.imgs = imgs 117 | self.cats = cats 118 | 119 | def info(self): 120 | """ 121 | Print information about the annotation file. 122 | :return: 123 | """ 124 | for key, value in self.dataset['info'].items(): 125 | print('{}: {}'.format(key, value)) 126 | 127 | def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None): 128 | """ 129 | Get ann ids that satisfy given filter conditions. default skips that filter 130 | :param imgIds (int array) : get anns for given imgs 131 | catIds (int array) : get anns for given cats 132 | areaRng (float array) : get anns for given area range (e.g. [0 inf]) 133 | iscrowd (boolean) : get anns for given crowd label (False or True) 134 | :return: ids (int array) : integer array of ann ids 135 | """ 136 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 137 | catIds = catIds if _isArrayLike(catIds) else [catIds] 138 | 139 | if len(imgIds) == len(catIds) == len(areaRng) == 0: 140 | anns = self.dataset['annotations'] 141 | else: 142 | if not len(imgIds) == 0: 143 | lists = [self.imgToAnns[imgId] for imgId in imgIds if imgId in self.imgToAnns] 144 | anns = list(itertools.chain.from_iterable(lists)) 145 | else: 146 | anns = self.dataset['annotations'] 147 | anns = anns if len(catIds) == 0 else [ann for ann in anns if ann['category_id'] in catIds] 148 | anns = anns if len(areaRng) == 0 else [ann for ann in anns if ann['area'] > areaRng[0] and ann['area'] < areaRng[1]] 149 | if not iscrowd == None: 150 | ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd] 151 | else: 152 | ids = [ann['id'] for ann in anns] 153 | return ids 154 | 155 | def getCatIds(self, catNms=[], supNms=[], catIds=[]): 156 | """ 157 | filtering parameters. default skips that filter. 158 | :param catNms (str array) : get cats for given cat names 159 | :param supNms (str array) : get cats for given supercategory names 160 | :param catIds (int array) : get cats for given cat ids 161 | :return: ids (int array) : integer array of cat ids 162 | """ 163 | catNms = catNms if _isArrayLike(catNms) else [catNms] 164 | supNms = supNms if _isArrayLike(supNms) else [supNms] 165 | catIds = catIds if _isArrayLike(catIds) else [catIds] 166 | 167 | if len(catNms) == len(supNms) == len(catIds) == 0: 168 | cats = self.dataset['categories'] 169 | else: 170 | cats = self.dataset['categories'] 171 | cats = cats if len(catNms) == 0 else [cat for cat in cats if cat['name'] in catNms] 172 | cats = cats if len(supNms) == 0 else [cat for cat in cats if cat['supercategory'] in supNms] 173 | cats = cats if len(catIds) == 0 else [cat for cat in cats if cat['id'] in catIds] 174 | ids = [cat['id'] for cat in cats] 175 | return ids 176 | 177 | def getImgIds(self, imgIds=[], catIds=[]): 178 | ''' 179 | Get img ids that satisfy given filter conditions. 180 | :param imgIds (int array) : get imgs for given ids 181 | :param catIds (int array) : get imgs with all given cats 182 | :return: ids (int array) : integer array of img ids 183 | ''' 184 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 185 | catIds = catIds if _isArrayLike(catIds) else [catIds] 186 | 187 | if len(imgIds) == len(catIds) == 0: 188 | ids = self.imgs.keys() 189 | else: 190 | ids = set(imgIds) 191 | for i, catId in enumerate(catIds): 192 | if i == 0 and len(ids) == 0: 193 | ids = set(self.catToImgs[catId]) 194 | else: 195 | ids &= set(self.catToImgs[catId]) 196 | return list(ids) 197 | 198 | def loadAnns(self, ids=[]): 199 | """ 200 | Load anns with the specified ids. 201 | :param ids (int array) : integer ids specifying anns 202 | :return: anns (object array) : loaded ann objects 203 | """ 204 | if _isArrayLike(ids): 205 | return [self.anns[id] for id in ids] 206 | elif type(ids) == int: 207 | return [self.anns[ids]] 208 | 209 | def loadCats(self, ids=[]): 210 | """ 211 | Load cats with the specified ids. 212 | :param ids (int array) : integer ids specifying cats 213 | :return: cats (object array) : loaded cat objects 214 | """ 215 | if _isArrayLike(ids): 216 | return [self.cats[id] for id in ids] 217 | elif type(ids) == int: 218 | return [self.cats[ids]] 219 | 220 | def loadImgs(self, ids=[]): 221 | """ 222 | Load anns with the specified ids. 223 | :param ids (int array) : integer ids specifying img 224 | :return: imgs (object array) : loaded img objects 225 | """ 226 | if _isArrayLike(ids): 227 | return [self.imgs[id] for id in ids] 228 | elif type(ids) == int: 229 | return [self.imgs[ids]] 230 | 231 | def showAnns(self, anns, draw_bbox=False): 232 | """ 233 | Display the specified annotations. 234 | :param anns (array of object): annotations to display 235 | :return: None 236 | """ 237 | if len(anns) == 0: 238 | return 0 239 | if 'segmentation' in anns[0] or 'keypoints' in anns[0]: 240 | datasetType = 'instances' 241 | elif 'caption' in anns[0]: 242 | datasetType = 'captions' 243 | else: 244 | raise Exception('datasetType not supported') 245 | if datasetType == 'instances': 246 | import matplotlib.pyplot as plt 247 | from matplotlib.collections import PatchCollection 248 | from matplotlib.patches import Polygon 249 | 250 | ax = plt.gca() 251 | ax.set_autoscale_on(False) 252 | polygons = [] 253 | color = [] 254 | for ann in anns: 255 | c = (np.random.random((1, 3))*0.6+0.4).tolist()[0] 256 | if 'segmentation' in ann: 257 | if type(ann['segmentation']) == list: 258 | # polygon 259 | for seg in ann['segmentation']: 260 | poly = np.array(seg).reshape((int(len(seg)/2), 2)) 261 | polygons.append(Polygon(poly)) 262 | color.append(c) 263 | else: 264 | # mask 265 | t = self.imgs[ann['image_id']] 266 | if type(ann['segmentation']['counts']) == list: 267 | rle = maskUtils.frPyObjects([ann['segmentation']], t['height'], t['width']) 268 | else: 269 | rle = [ann['segmentation']] 270 | m = maskUtils.decode(rle) 271 | img = np.ones( (m.shape[0], m.shape[1], 3) ) 272 | if ann['iscrowd'] == 1: 273 | color_mask = np.array([2.0,166.0,101.0])/255 274 | if ann['iscrowd'] == 0: 275 | color_mask = np.random.random((1, 3)).tolist()[0] 276 | for i in range(3): 277 | img[:,:,i] = color_mask[i] 278 | ax.imshow(np.dstack( (img, m*0.5) )) 279 | if 'keypoints' in ann and type(ann['keypoints']) == list: 280 | # turn skeleton into zero-based index 281 | sks = np.array(self.loadCats(ann['category_id'])[0]['skeleton'])-1 282 | kp = np.array(ann['keypoints']) 283 | x = kp[0::3] 284 | y = kp[1::3] 285 | v = kp[2::3] 286 | for sk in sks: 287 | if np.all(v[sk]>0): 288 | plt.plot(x[sk],y[sk], linewidth=3, color=c) 289 | plt.plot(x[v>0], y[v>0],'o',markersize=8, markerfacecolor=c, markeredgecolor='k',markeredgewidth=2) 290 | plt.plot(x[v>1], y[v>1],'o',markersize=8, markerfacecolor=c, markeredgecolor=c, markeredgewidth=2) 291 | 292 | if draw_bbox: 293 | [bbox_x, bbox_y, bbox_w, bbox_h] = ann['bbox'] 294 | poly = [[bbox_x, bbox_y], [bbox_x, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y]] 295 | np_poly = np.array(poly).reshape((4,2)) 296 | polygons.append(Polygon(np_poly)) 297 | color.append(c) 298 | 299 | p = PatchCollection(polygons, facecolor=color, linewidths=0, alpha=0.4) 300 | ax.add_collection(p) 301 | p = PatchCollection(polygons, facecolor='none', edgecolors=color, linewidths=2) 302 | ax.add_collection(p) 303 | elif datasetType == 'captions': 304 | for ann in anns: 305 | print(ann['caption']) 306 | 307 | def loadRes(self, resFile): 308 | """ 309 | Load result file and return a result api object. 310 | :param resFile (str) : file name of result file 311 | :return: res (obj) : result api object 312 | """ 313 | res = COCO() 314 | res.dataset['images'] = [img for img in self.dataset['images']] 315 | 316 | print('Loading and preparing results...') 317 | tic = time.time() 318 | if type(resFile) == str or (PYTHON_VERSION == 2 and type(resFile) == unicode): 319 | with open(resFile) as f: 320 | anns = json.load(f) 321 | elif type(resFile) == np.ndarray: 322 | anns = self.loadNumpyAnnotations(resFile) 323 | else: 324 | anns = resFile 325 | assert type(anns) == list, 'results in not an array of objects' 326 | annsImgIds = [ann['image_id'] for ann in anns] 327 | assert set(annsImgIds) == (set(annsImgIds) & set(self.getImgIds())), \ 328 | 'Results do not correspond to current coco set' 329 | if 'caption' in anns[0]: 330 | imgIds = set([img['id'] for img in res.dataset['images']]) & set([ann['image_id'] for ann in anns]) 331 | res.dataset['images'] = [img for img in res.dataset['images'] if img['id'] in imgIds] 332 | for id, ann in enumerate(anns): 333 | ann['id'] = id+1 334 | elif 'bbox' in anns[0] and not anns[0]['bbox'] == []: 335 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 336 | for id, ann in enumerate(anns): 337 | bb = ann['bbox'] 338 | x1, x2, y1, y2 = [bb[0], bb[0]+bb[2], bb[1], bb[1]+bb[3]] 339 | if not 'segmentation' in ann: 340 | ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] 341 | ann['area'] = bb[2]*bb[3] 342 | ann['id'] = id+1 343 | ann['iscrowd'] = 0 344 | elif 'segmentation' in anns[0]: 345 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 346 | for id, ann in enumerate(anns): 347 | # now only support compressed RLE format as segmentation results 348 | ann['area'] = maskUtils.area(ann['segmentation']) 349 | if not 'bbox' in ann: 350 | ann['bbox'] = maskUtils.toBbox(ann['segmentation']) 351 | ann['id'] = id+1 352 | ann['iscrowd'] = 0 353 | elif 'keypoints' in anns[0]: 354 | res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) 355 | for id, ann in enumerate(anns): 356 | s = ann['keypoints'] 357 | x = s[0::3] 358 | y = s[1::3] 359 | x0,x1,y0,y1 = np.min(x), np.max(x), np.min(y), np.max(y) 360 | ann['area'] = (x1-x0)*(y1-y0) 361 | ann['id'] = id + 1 362 | ann['bbox'] = [x0,y0,x1-x0,y1-y0] 363 | print('DONE (t={:0.2f}s)'.format(time.time()- tic)) 364 | 365 | res.dataset['annotations'] = anns 366 | res.createIndex() 367 | return res 368 | 369 | def download(self, tarDir = None, imgIds = [] ): 370 | ''' 371 | Download COCO images from mscoco.org server. 372 | :param tarDir (str): COCO results directory name 373 | imgIds (list): images to be downloaded 374 | :return: 375 | ''' 376 | if tarDir is None: 377 | print('Please specify target directory') 378 | return -1 379 | if len(imgIds) == 0: 380 | imgs = self.imgs.values() 381 | else: 382 | imgs = self.loadImgs(imgIds) 383 | N = len(imgs) 384 | if not os.path.exists(tarDir): 385 | os.makedirs(tarDir) 386 | for i, img in enumerate(imgs): 387 | tic = time.time() 388 | fname = os.path.join(tarDir, img['file_name']) 389 | if not os.path.exists(fname): 390 | urlretrieve(img['coco_url'], fname) 391 | print('downloaded {}/{} images (t={:0.1f}s)'.format(i, N, time.time()- tic)) 392 | 393 | def loadNumpyAnnotations(self, data): 394 | """ 395 | Convert result data from a numpy array [Nx7] where each row contains {imageID,x1,y1,w,h,score,class} 396 | :param data (numpy.ndarray) 397 | :return: annotations (python nested list) 398 | """ 399 | print('Converting ndarray to lists...') 400 | assert(type(data) == np.ndarray) 401 | print(data.shape) 402 | assert(data.shape[1] == 7) 403 | N = data.shape[0] 404 | ann = [] 405 | for i in range(N): 406 | if i % 1000000 == 0: 407 | print('{}/{}'.format(i,N)) 408 | ann += [{ 409 | 'image_id' : int(data[i, 0]), 410 | 'bbox' : [ data[i, 1], data[i, 2], data[i, 3], data[i, 4] ], 411 | 'score' : data[i, 5], 412 | 'category_id': int(data[i, 6]), 413 | }] 414 | return ann 415 | 416 | def annToRLE(self, ann): 417 | """ 418 | Convert annotation which can be polygons, uncompressed RLE to RLE. 419 | :return: binary mask (numpy 2D array) 420 | """ 421 | t = self.imgs[ann['image_id']] 422 | h, w = t['height'], t['width'] 423 | segm = ann['segmentation'] 424 | if type(segm) == list: 425 | # polygon -- a single object might consist of multiple parts 426 | # we merge all parts into one mask rle code 427 | rles = maskUtils.frPyObjects(segm, h, w) 428 | rle = maskUtils.merge(rles) 429 | elif type(segm['counts']) == list: 430 | # uncompressed RLE 431 | rle = maskUtils.frPyObjects(segm, h, w) 432 | else: 433 | # rle 434 | rle = ann['segmentation'] 435 | return rle 436 | 437 | def annToMask(self, ann): 438 | """ 439 | Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask. 440 | :return: binary mask (numpy 2D array) 441 | """ 442 | rle = self.annToRLE(ann) 443 | m = maskUtils.decode(rle) 444 | return m 445 | -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/cocoeval.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import numpy as np 4 | import datetime 5 | import time 6 | from collections import defaultdict 7 | from . import mask as maskUtils 8 | import copy 9 | 10 | class COCOeval: 11 | # Interface for evaluating detection on the Microsoft COCO dataset. 12 | # 13 | # The usage for CocoEval is as follows: 14 | # cocoGt=..., cocoDt=... # load dataset and results 15 | # E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object 16 | # E.params.recThrs = ...; # set parameters as desired 17 | # E.evaluate(); # run per image evaluation 18 | # E.accumulate(); # accumulate per image results 19 | # E.summarize(); # display summary metrics of results 20 | # For example usage see evalDemo.m and http://mscoco.org/. 21 | # 22 | # The evaluation parameters are as follows (defaults in brackets): 23 | # imgIds - [all] N img ids to use for evaluation 24 | # catIds - [all] K cat ids to use for evaluation 25 | # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation 26 | # recThrs - [0:.01:1] R=101 recall thresholds for evaluation 27 | # areaRng - [...] A=4 object area ranges for evaluation 28 | # maxDets - [1 10 100] M=3 thresholds on max detections per image 29 | # iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints' 30 | # iouType replaced the now DEPRECATED useSegm parameter. 31 | # useCats - [1] if true use category labels for evaluation 32 | # Note: if useCats=0 category labels are ignored as in proposal scoring. 33 | # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. 34 | # 35 | # evaluate(): evaluates detections on every image and every category and 36 | # concats the results into the "evalImgs" with fields: 37 | # dtIds - [1xD] id for each of the D detections (dt) 38 | # gtIds - [1xG] id for each of the G ground truths (gt) 39 | # dtMatches - [TxD] matching gt id at each IoU or 0 40 | # gtMatches - [TxG] matching dt id at each IoU or 0 41 | # dtScores - [1xD] confidence of each dt 42 | # gtIgnore - [1xG] ignore flag for each gt 43 | # dtIgnore - [TxD] ignore flag for each dt at each IoU 44 | # 45 | # accumulate(): accumulates the per-image, per-category evaluation 46 | # results in "evalImgs" into the dictionary "eval" with fields: 47 | # params - parameters used for evaluation 48 | # date - date evaluation was performed 49 | # counts - [T,R,K,A,M] parameter dimensions (see above) 50 | # precision - [TxRxKxAxM] precision for every evaluation setting 51 | # recall - [TxKxAxM] max recall for every evaluation setting 52 | # Note: precision and recall==-1 for settings with no gt objects. 53 | # 54 | # See also coco, mask, pycocoDemo, pycocoEvalDemo 55 | # 56 | # Microsoft COCO Toolbox. version 2.0 57 | # Data, paper, and tutorials available at: http://mscoco.org/ 58 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 59 | # Licensed under the Simplified BSD License [see coco/license.txt] 60 | def __init__(self, cocoGt=None, cocoDt=None, iouType='segm'): 61 | ''' 62 | Initialize CocoEval using coco APIs for gt and dt 63 | :param cocoGt: coco object with ground truth annotations 64 | :param cocoDt: coco object with detection results 65 | :return: None 66 | ''' 67 | if not iouType: 68 | print('iouType not specified. use default iouType segm') 69 | self.cocoGt = cocoGt # ground truth COCO API 70 | self.cocoDt = cocoDt # detections COCO API 71 | self.evalImgs = defaultdict(list) # per-image per-category evaluation results [KxAxI] elements 72 | self.eval = {} # accumulated evaluation results 73 | self._gts = defaultdict(list) # gt for evaluation 74 | self._dts = defaultdict(list) # dt for evaluation 75 | self.params = Params(iouType=iouType) # parameters 76 | self._paramsEval = {} # parameters for evaluation 77 | self.stats = [] # result summarization 78 | self.ious = {} # ious between all gts and dts 79 | if not cocoGt is None: 80 | self.params.imgIds = sorted(cocoGt.getImgIds()) 81 | self.params.catIds = sorted(cocoGt.getCatIds()) 82 | 83 | 84 | def _prepare(self): 85 | ''' 86 | Prepare ._gts and ._dts for evaluation based on params 87 | :return: None 88 | ''' 89 | def _toMask(anns, coco): 90 | # modify ann['segmentation'] by reference 91 | for ann in anns: 92 | rle = coco.annToRLE(ann) 93 | ann['segmentation'] = rle 94 | p = self.params 95 | if p.useCats: 96 | gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 97 | dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 98 | else: 99 | gts=self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds)) 100 | dts=self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds)) 101 | 102 | # convert ground truth to mask if iouType == 'segm' 103 | if p.iouType == 'segm': 104 | _toMask(gts, self.cocoGt) 105 | _toMask(dts, self.cocoDt) 106 | # set ignore flag 107 | for gt in gts: 108 | gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0 109 | gt['ignore'] = 'iscrowd' in gt and gt['iscrowd'] 110 | if p.iouType == 'keypoints': 111 | gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore'] 112 | self._gts = defaultdict(list) # gt for evaluation 113 | self._dts = defaultdict(list) # dt for evaluation 114 | for gt in gts: 115 | self._gts[gt['image_id'], gt['category_id']].append(gt) 116 | for dt in dts: 117 | self._dts[dt['image_id'], dt['category_id']].append(dt) 118 | self.evalImgs = defaultdict(list) # per-image per-category evaluation results 119 | self.eval = {} # accumulated evaluation results 120 | 121 | def evaluate(self): 122 | ''' 123 | Run per image evaluation on given images and store results (a list of dict) in self.evalImgs 124 | :return: None 125 | ''' 126 | tic = time.time() 127 | print('Running per image evaluation...') 128 | p = self.params 129 | # add backward compatibility if useSegm is specified in params 130 | if not p.useSegm is None: 131 | p.iouType = 'segm' if p.useSegm == 1 else 'bbox' 132 | print('useSegm (deprecated) is not None. Running {} evaluation'.format(p.iouType)) 133 | print('Evaluate annotation type *{}*'.format(p.iouType)) 134 | p.imgIds = list(np.unique(p.imgIds)) 135 | if p.useCats: 136 | p.catIds = list(np.unique(p.catIds)) 137 | p.maxDets = sorted(p.maxDets) 138 | self.params=p 139 | 140 | self._prepare() 141 | # loop through images, area range, max detection number 142 | catIds = p.catIds if p.useCats else [-1] 143 | 144 | if p.iouType == 'segm' or p.iouType == 'bbox': 145 | computeIoU = self.computeIoU 146 | elif p.iouType == 'keypoints': 147 | computeIoU = self.computeOks 148 | self.ious = {(imgId, catId): computeIoU(imgId, catId) \ 149 | for imgId in p.imgIds 150 | for catId in catIds} 151 | 152 | evaluateImg = self.evaluateImg 153 | maxDet = p.maxDets[-1] 154 | self.evalImgs = [evaluateImg(imgId, catId, areaRng, maxDet) 155 | for catId in catIds 156 | for areaRng in p.areaRng 157 | for imgId in p.imgIds 158 | ] 159 | self._paramsEval = copy.deepcopy(self.params) 160 | toc = time.time() 161 | print('DONE (t={:0.2f}s).'.format(toc-tic)) 162 | 163 | def computeIoU(self, imgId, catId): 164 | p = self.params 165 | if p.useCats: 166 | gt = self._gts[imgId,catId] 167 | dt = self._dts[imgId,catId] 168 | else: 169 | gt = [_ for cId in p.catIds for _ in self._gts[imgId,cId]] 170 | dt = [_ for cId in p.catIds for _ in self._dts[imgId,cId]] 171 | if len(gt) == 0 and len(dt) ==0: 172 | return [] 173 | inds = np.argsort([-d['score'] for d in dt], kind='mergesort') 174 | dt = [dt[i] for i in inds] 175 | if len(dt) > p.maxDets[-1]: 176 | dt=dt[0:p.maxDets[-1]] 177 | 178 | if p.iouType == 'segm': 179 | g = [g['segmentation'] for g in gt] 180 | d = [d['segmentation'] for d in dt] 181 | elif p.iouType == 'bbox': 182 | g = [g['bbox'] for g in gt] 183 | d = [d['bbox'] for d in dt] 184 | else: 185 | raise Exception('unknown iouType for iou computation') 186 | 187 | # compute iou between each dt and gt region 188 | iscrowd = [int(o['iscrowd']) for o in gt] 189 | ious = maskUtils.iou(d,g,iscrowd) 190 | return ious 191 | 192 | def computeOks(self, imgId, catId): 193 | p = self.params 194 | # dimention here should be Nxm 195 | gts = self._gts[imgId, catId] 196 | dts = self._dts[imgId, catId] 197 | inds = np.argsort([-d['score'] for d in dts], kind='mergesort') 198 | dts = [dts[i] for i in inds] 199 | if len(dts) > p.maxDets[-1]: 200 | dts = dts[0:p.maxDets[-1]] 201 | # if len(gts) == 0 and len(dts) == 0: 202 | if len(gts) == 0 or len(dts) == 0: 203 | return [] 204 | ious = np.zeros((len(dts), len(gts))) 205 | sigmas = p.kpt_oks_sigmas 206 | vars = (sigmas * 2)**2 207 | k = len(sigmas) 208 | # compute oks between each detection and ground truth object 209 | for j, gt in enumerate(gts): 210 | # create bounds for ignore regions(double the gt bbox) 211 | g = np.array(gt['keypoints']) 212 | xg = g[0::3]; yg = g[1::3]; vg = g[2::3] 213 | k1 = np.count_nonzero(vg > 0) 214 | bb = gt['bbox'] 215 | x0 = bb[0] - bb[2]; x1 = bb[0] + bb[2] * 2 216 | y0 = bb[1] - bb[3]; y1 = bb[1] + bb[3] * 2 217 | for i, dt in enumerate(dts): 218 | d = np.array(dt['keypoints']) 219 | xd = d[0::3]; yd = d[1::3] 220 | if k1>0: 221 | # measure the per-keypoint distance if keypoints visible 222 | dx = xd - xg 223 | dy = yd - yg 224 | else: 225 | # measure minimum distance to keypoints in (x0,y0) & (x1,y1) 226 | z = np.zeros((k)) 227 | dx = np.max((z, x0-xd),axis=0)+np.max((z, xd-x1),axis=0) 228 | dy = np.max((z, y0-yd),axis=0)+np.max((z, yd-y1),axis=0) 229 | e = (dx**2 + dy**2) / vars / (gt['area']+np.spacing(1)) / 2 230 | if k1 > 0: 231 | e=e[vg > 0] 232 | ious[i, j] = np.sum(np.exp(-e)) / e.shape[0] 233 | return ious 234 | 235 | def evaluateImg(self, imgId, catId, aRng, maxDet): 236 | ''' 237 | perform evaluation for single category and image 238 | :return: dict (single image results) 239 | ''' 240 | p = self.params 241 | if p.useCats: 242 | gt = self._gts[imgId,catId] 243 | dt = self._dts[imgId,catId] 244 | else: 245 | gt = [_ for cId in p.catIds for _ in self._gts[imgId,cId]] 246 | dt = [_ for cId in p.catIds for _ in self._dts[imgId,cId]] 247 | if len(gt) == 0 and len(dt) ==0: 248 | return None 249 | 250 | for g in gt: 251 | if g['ignore'] or (g['area']aRng[1]): 252 | g['_ignore'] = 1 253 | else: 254 | g['_ignore'] = 0 255 | 256 | # sort dt highest score first, sort gt ignore last 257 | gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort') 258 | gt = [gt[i] for i in gtind] 259 | dtind = np.argsort([-d['score'] for d in dt], kind='mergesort') 260 | dt = [dt[i] for i in dtind[0:maxDet]] 261 | iscrowd = [int(o['iscrowd']) for o in gt] 262 | # load computed ious 263 | ious = self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] 264 | 265 | T = len(p.iouThrs) 266 | G = len(gt) 267 | D = len(dt) 268 | gtm = np.zeros((T,G)) 269 | dtm = np.zeros((T,D)) 270 | gtIg = np.array([g['_ignore'] for g in gt]) 271 | dtIg = np.zeros((T,D)) 272 | if not len(ious)==0: 273 | for tind, t in enumerate(p.iouThrs): 274 | for dind, d in enumerate(dt): 275 | # information about best match so far (m=-1 -> unmatched) 276 | iou = min([t,1-1e-10]) 277 | m = -1 278 | for gind, g in enumerate(gt): 279 | # if this gt already matched, and not a crowd, continue 280 | if gtm[tind,gind]>0 and not iscrowd[gind]: 281 | continue 282 | # if dt matched to reg gt, and on ignore gt, stop 283 | if m>-1 and gtIg[m]==0 and gtIg[gind]==1: 284 | break 285 | # continue to next gt unless better match made 286 | if ious[dind,gind] < iou: 287 | continue 288 | # if match successful and best so far, store appropriately 289 | iou=ious[dind,gind] 290 | m=gind 291 | # if match made store id of match for both dt and gt 292 | if m ==-1: 293 | continue 294 | dtIg[tind,dind] = gtIg[m] 295 | dtm[tind,dind] = gt[m]['id'] 296 | gtm[tind,m] = d['id'] 297 | # set unmatched detections outside of area range to ignore 298 | a = np.array([d['area']aRng[1] for d in dt]).reshape((1, len(dt))) 299 | dtIg = np.logical_or(dtIg, np.logical_and(dtm==0, np.repeat(a,T,0))) 300 | # store results for given image and category 301 | return { 302 | 'image_id': imgId, 303 | 'category_id': catId, 304 | 'aRng': aRng, 305 | 'maxDet': maxDet, 306 | 'dtIds': [d['id'] for d in dt], 307 | 'gtIds': [g['id'] for g in gt], 308 | 'dtMatches': dtm, 309 | 'gtMatches': gtm, 310 | 'dtScores': [d['score'] for d in dt], 311 | 'gtIgnore': gtIg, 312 | 'dtIgnore': dtIg, 313 | } 314 | 315 | def accumulate(self, p = None): 316 | ''' 317 | Accumulate per image evaluation results and store the result in self.eval 318 | :param p: input params for evaluation 319 | :return: None 320 | ''' 321 | print('Accumulating evaluation results...') 322 | tic = time.time() 323 | if not self.evalImgs: 324 | print('Please run evaluate() first') 325 | # allows input customized parameters 326 | if p is None: 327 | p = self.params 328 | p.catIds = p.catIds if p.useCats == 1 else [-1] 329 | T = len(p.iouThrs) 330 | R = len(p.recThrs) 331 | K = len(p.catIds) if p.useCats else 1 332 | A = len(p.areaRng) 333 | M = len(p.maxDets) 334 | precision = -np.ones((T,R,K,A,M)) # -1 for the precision of absent categories 335 | recall = -np.ones((T,K,A,M)) 336 | scores = -np.ones((T,R,K,A,M)) 337 | 338 | # create dictionary for future indexing 339 | _pe = self._paramsEval 340 | catIds = _pe.catIds if _pe.useCats else [-1] 341 | setK = set(catIds) 342 | setA = set(map(tuple, _pe.areaRng)) 343 | setM = set(_pe.maxDets) 344 | setI = set(_pe.imgIds) 345 | # get inds to evaluate 346 | k_list = [n for n, k in enumerate(p.catIds) if k in setK] 347 | m_list = [m for n, m in enumerate(p.maxDets) if m in setM] 348 | a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA] 349 | i_list = [n for n, i in enumerate(p.imgIds) if i in setI] 350 | I0 = len(_pe.imgIds) 351 | A0 = len(_pe.areaRng) 352 | # retrieve E at each category, area range, and max number of detections 353 | for k, k0 in enumerate(k_list): 354 | Nk = k0*A0*I0 355 | for a, a0 in enumerate(a_list): 356 | Na = a0*I0 357 | for m, maxDet in enumerate(m_list): 358 | E = [self.evalImgs[Nk + Na + i] for i in i_list] 359 | E = [e for e in E if not e is None] 360 | if len(E) == 0: 361 | continue 362 | dtScores = np.concatenate([e['dtScores'][0:maxDet] for e in E]) 363 | 364 | # different sorting method generates slightly different results. 365 | # mergesort is used to be consistent as Matlab implementation. 366 | inds = np.argsort(-dtScores, kind='mergesort') 367 | dtScoresSorted = dtScores[inds] 368 | 369 | dtm = np.concatenate([e['dtMatches'][:,0:maxDet] for e in E], axis=1)[:,inds] 370 | dtIg = np.concatenate([e['dtIgnore'][:,0:maxDet] for e in E], axis=1)[:,inds] 371 | gtIg = np.concatenate([e['gtIgnore'] for e in E]) 372 | npig = np.count_nonzero(gtIg==0 ) 373 | if npig == 0: 374 | continue 375 | tps = np.logical_and( dtm, np.logical_not(dtIg) ) 376 | fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg) ) 377 | 378 | tp_sum = np.cumsum(tps, axis=1).astype(dtype=float) 379 | fp_sum = np.cumsum(fps, axis=1).astype(dtype=float) 380 | for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): 381 | tp = np.array(tp) 382 | fp = np.array(fp) 383 | nd = len(tp) 384 | rc = tp / npig 385 | pr = tp / (fp+tp+np.spacing(1)) 386 | q = np.zeros((R,)) 387 | ss = np.zeros((R,)) 388 | 389 | if nd: 390 | recall[t,k,a,m] = rc[-1] 391 | else: 392 | recall[t,k,a,m] = 0 393 | 394 | # numpy is slow without cython optimization for accessing elements 395 | # use python array gets significant speed improvement 396 | pr = pr.tolist(); q = q.tolist() 397 | 398 | for i in range(nd-1, 0, -1): 399 | if pr[i] > pr[i-1]: 400 | pr[i-1] = pr[i] 401 | 402 | inds = np.searchsorted(rc, p.recThrs, side='left') 403 | try: 404 | for ri, pi in enumerate(inds): 405 | q[ri] = pr[pi] 406 | ss[ri] = dtScoresSorted[pi] 407 | except: 408 | pass 409 | precision[t,:,k,a,m] = np.array(q) 410 | scores[t,:,k,a,m] = np.array(ss) 411 | self.eval = { 412 | 'params': p, 413 | 'counts': [T, R, K, A, M], 414 | 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 415 | 'precision': precision, 416 | 'recall': recall, 417 | 'scores': scores, 418 | } 419 | toc = time.time() 420 | print('DONE (t={:0.2f}s).'.format( toc-tic)) 421 | 422 | def summarize(self): 423 | ''' 424 | Compute and display summary metrics for evaluation results. 425 | Note this functin can *only* be applied on the default parameter setting 426 | ''' 427 | def _summarize( ap=1, iouThr=None, areaRng='all', maxDets=100 ): 428 | p = self.params 429 | iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}' 430 | titleStr = 'Average Precision' if ap == 1 else 'Average Recall' 431 | typeStr = '(AP)' if ap==1 else '(AR)' 432 | iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \ 433 | if iouThr is None else '{:0.2f}'.format(iouThr) 434 | 435 | aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng] 436 | mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] 437 | if ap == 1: 438 | # dimension of precision: [TxRxKxAxM] 439 | s = self.eval['precision'] 440 | # IoU 441 | if iouThr is not None: 442 | t = np.where(iouThr == p.iouThrs)[0] 443 | s = s[t] 444 | s = s[:,:,:,aind,mind] 445 | else: 446 | # dimension of recall: [TxKxAxM] 447 | s = self.eval['recall'] 448 | if iouThr is not None: 449 | t = np.where(iouThr == p.iouThrs)[0] 450 | s = s[t] 451 | s = s[:,:,aind,mind] 452 | if len(s[s>-1])==0: 453 | mean_s = -1 454 | else: 455 | mean_s = np.mean(s[s>-1]) 456 | print(iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets, mean_s)) 457 | return mean_s 458 | def _summarizeDets(): 459 | stats = np.zeros((12,)) 460 | stats[0] = _summarize(1) 461 | stats[1] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2]) 462 | stats[2] = _summarize(1, iouThr=.75, maxDets=self.params.maxDets[2]) 463 | stats[3] = _summarize(1, areaRng='small', maxDets=self.params.maxDets[2]) 464 | stats[4] = _summarize(1, areaRng='medium', maxDets=self.params.maxDets[2]) 465 | stats[5] = _summarize(1, areaRng='large', maxDets=self.params.maxDets[2]) 466 | stats[6] = _summarize(0, maxDets=self.params.maxDets[0]) 467 | stats[7] = _summarize(0, maxDets=self.params.maxDets[1]) 468 | stats[8] = _summarize(0, maxDets=self.params.maxDets[2]) 469 | stats[9] = _summarize(0, areaRng='small', maxDets=self.params.maxDets[2]) 470 | stats[10] = _summarize(0, areaRng='medium', maxDets=self.params.maxDets[2]) 471 | stats[11] = _summarize(0, areaRng='large', maxDets=self.params.maxDets[2]) 472 | return stats 473 | def _summarizeKps(): 474 | stats = np.zeros((10,)) 475 | stats[0] = _summarize(1, maxDets=20) 476 | stats[1] = _summarize(1, maxDets=20, iouThr=.5) 477 | stats[2] = _summarize(1, maxDets=20, iouThr=.75) 478 | stats[3] = _summarize(1, maxDets=20, areaRng='medium') 479 | stats[4] = _summarize(1, maxDets=20, areaRng='large') 480 | stats[5] = _summarize(0, maxDets=20) 481 | stats[6] = _summarize(0, maxDets=20, iouThr=.5) 482 | stats[7] = _summarize(0, maxDets=20, iouThr=.75) 483 | stats[8] = _summarize(0, maxDets=20, areaRng='medium') 484 | stats[9] = _summarize(0, maxDets=20, areaRng='large') 485 | return stats 486 | if not self.eval: 487 | raise Exception('Please run accumulate() first') 488 | iouType = self.params.iouType 489 | if iouType == 'segm' or iouType == 'bbox': 490 | summarize = _summarizeDets 491 | elif iouType == 'keypoints': 492 | summarize = _summarizeKps 493 | self.stats = summarize() 494 | 495 | def __str__(self): 496 | self.summarize() 497 | 498 | class Params: 499 | ''' 500 | Params for coco evaluation api 501 | ''' 502 | def setDetParams(self): 503 | self.imgIds = [] 504 | self.catIds = [] 505 | # np.arange causes trouble. the data point on arange is slightly larger than the true value 506 | self.iouThrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) 507 | self.recThrs = np.linspace(.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True) 508 | self.maxDets = [1, 10, 100] 509 | #self.areaRng = [[0 ** 2, 1e5 ** 2], [0 ** 2, 32 ** 2], [32 ** 2, 96 ** 2], [96 ** 2, 1e5 ** 2]] 510 | #replaced 511 | self.areaRng = [[0 ** 2, 1e5 ** 2], [0 ** 2, 64 ** 2],[64 ** 2, 192 ** 2], [192 ** 2, 1e5 ** 2]] 512 | self.areaRngLbl = ['all', 'small', 'medium', 'large'] 513 | self.useCats = 1 514 | 515 | def setKpParams(self): 516 | self.imgIds = [] 517 | self.catIds = [] 518 | # np.arange causes trouble. the data point on arange is slightly larger than the true value 519 | self.iouThrs = np.linspace(.5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) 520 | self.recThrs = np.linspace(.0, 1.00, int(np.round((1.00 - .0) / .01)) + 1, endpoint=True) 521 | self.maxDets = [20] 522 | self.areaRng = [[0 ** 2, 1e5 ** 2], [32 ** 2, 96 ** 2], [96 ** 2, 1e5 ** 2]] 523 | self.areaRngLbl = ['all', 'medium', 'large'] 524 | self.useCats = 1 525 | self.kpt_oks_sigmas = np.array([.26, .25, .25, .35, .35, .79, .79, .72, .72, .62,.62, 1.07, 1.07, .87, .87, .89, .89])/10.0 526 | 527 | def __init__(self, iouType='segm'): 528 | if iouType == 'segm' or iouType == 'bbox': 529 | self.setDetParams() 530 | elif iouType == 'keypoints': 531 | self.setKpParams() 532 | else: 533 | raise Exception('iouType not supported') 534 | self.iouType = iouType 535 | # useSegm is deprecated 536 | self.useSegm = None 537 | -------------------------------------------------------------------------------- /evaluation_Linux/pycocotools/mask.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import pycocotools._mask as _mask 4 | 5 | # Interface for manipulating masks stored in RLE format. 6 | # 7 | # RLE is a simple yet efficient format for storing binary masks. RLE 8 | # first divides a vector (or vectorized image) into a series of piecewise 9 | # constant regions and then for each piece simply stores the length of 10 | # that piece. For example, given M=[0 0 1 1 1 0 1] the RLE counts would 11 | # be [2 3 1 1], or for M=[1 1 1 1 1 1 0] the counts would be [0 6 1] 12 | # (note that the odd counts are always the numbers of zeros). Instead of 13 | # storing the counts directly, additional compression is achieved with a 14 | # variable bitrate representation based on a common scheme called LEB128. 15 | # 16 | # Compression is greatest given large piecewise constant regions. 17 | # Specifically, the size of the RLE is proportional to the number of 18 | # *boundaries* in M (or for an image the number of boundaries in the y 19 | # direction). Assuming fairly simple shapes, the RLE representation is 20 | # O(sqrt(n)) where n is number of pixels in the object. Hence space usage 21 | # is substantially lower, especially for large simple objects (large n). 22 | # 23 | # Many common operations on masks can be computed directly using the RLE 24 | # (without need for decoding). This includes computations such as area, 25 | # union, intersection, etc. All of these operations are linear in the 26 | # size of the RLE, in other words they are O(sqrt(n)) where n is the area 27 | # of the object. Computing these operations on the original mask is O(n). 28 | # Thus, using the RLE can result in substantial computational savings. 29 | # 30 | # The following API functions are defined: 31 | # encode - Encode binary masks using RLE. 32 | # decode - Decode binary masks encoded via RLE. 33 | # merge - Compute union or intersection of encoded masks. 34 | # iou - Compute intersection over union between masks. 35 | # area - Compute area of encoded masks. 36 | # toBbox - Get bounding boxes surrounding encoded masks. 37 | # frPyObjects - Convert polygon, bbox, and uncompressed RLE to encoded RLE mask. 38 | # 39 | # Usage: 40 | # Rs = encode( masks ) 41 | # masks = decode( Rs ) 42 | # R = merge( Rs, intersect=false ) 43 | # o = iou( dt, gt, iscrowd ) 44 | # a = area( Rs ) 45 | # bbs = toBbox( Rs ) 46 | # Rs = frPyObjects( [pyObjects], h, w ) 47 | # 48 | # In the API the following formats are used: 49 | # Rs - [dict] Run-length encoding of binary masks 50 | # R - dict Run-length encoding of binary mask 51 | # masks - [hxwxn] Binary mask(s) (must have type np.ndarray(dtype=uint8) in column-major order) 52 | # iscrowd - [nx1] list of np.ndarray. 1 indicates corresponding gt image has crowd region to ignore 53 | # bbs - [nx4] Bounding box(es) stored as [x y w h] 54 | # poly - Polygon stored as [[x1 y1 x2 y2...],[x1 y1 ...],...] (2D list) 55 | # dt,gt - May be either bounding boxes or encoded masks 56 | # Both poly and bbs are 0-indexed (bbox=[0 0 1 1] encloses first pixel). 57 | # 58 | # Finally, a note about the intersection over union (iou) computation. 59 | # The standard iou of a ground truth (gt) and detected (dt) object is 60 | # iou(gt,dt) = area(intersect(gt,dt)) / area(union(gt,dt)) 61 | # For "crowd" regions, we use a modified criteria. If a gt object is 62 | # marked as "iscrowd", we allow a dt to match any subregion of the gt. 63 | # Choosing gt' in the crowd gt that best matches the dt can be done using 64 | # gt'=intersect(dt,gt). Since by definition union(gt',dt)=dt, computing 65 | # iou(gt,dt,iscrowd) = iou(gt',dt) = area(intersect(gt,dt)) / area(dt) 66 | # For crowd gt regions we use this modified criteria above for the iou. 67 | # 68 | # To compile run "python setup.py build_ext --inplace" 69 | # Please do not contact us for help with compiling. 70 | # 71 | # Microsoft COCO Toolbox. version 2.0 72 | # Data, paper, and tutorials available at: http://mscoco.org/ 73 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 74 | # Licensed under the Simplified BSD License [see coco/license.txt] 75 | 76 | iou = _mask.iou 77 | merge = _mask.merge 78 | frPyObjects = _mask.frPyObjects 79 | 80 | def encode(bimask): 81 | if len(bimask.shape) == 3: 82 | return _mask.encode(bimask) 83 | elif len(bimask.shape) == 2: 84 | h, w = bimask.shape 85 | return _mask.encode(bimask.reshape((h, w, 1), order='F'))[0] 86 | 87 | def decode(rleObjs): 88 | if type(rleObjs) == list: 89 | return _mask.decode(rleObjs) 90 | else: 91 | return _mask.decode([rleObjs])[:,:,0] 92 | 93 | def area(rleObjs): 94 | if type(rleObjs) == list: 95 | return _mask.area(rleObjs) 96 | else: 97 | return _mask.area([rleObjs])[0] 98 | 99 | def toBbox(rleObjs): 100 | if type(rleObjs) == list: 101 | return _mask.toBbox(rleObjs) 102 | else: 103 | return _mask.toBbox([rleObjs])[0] -------------------------------------------------------------------------------- /evaluation_Windows/EVALUATION.md: -------------------------------------------------------------------------------- 1 | # Evaluation Metric for FishEye8k dataset 2 | -------------------------------------------------------------------------------- /evaluation_Windows/README.md: -------------------------------------------------------------------------------- 1 | # Evaluation Metric for FishEye8k dataset 2 | 3 | Update: 4 | 5 | `2024/03/18`: Change the bug in calculating f1 score. 6 | 7 | 8 | -------------------------------------------------------------------------------- /evaluation_Windows/eval.py: -------------------------------------------------------------------------------- 1 | from pycocotools.coco import COCO 2 | from pycocotools.cocoeval import COCOeval 3 | 4 | 5 | coco_gt = COCO('../../../evaluation/Fisheye8K/groundtruth.json') 6 | coco_dt = coco_gt.loadRes( 7 | '../../../evaluation/Fisheye8K/detections.json') 8 | 9 | print(type(coco_dt)) 10 | print(type(coco_gt)) 11 | 12 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 13 | coco_eval.evaluate() 14 | coco_eval.accumulate() 15 | coco_eval.summarize() 16 | -------------------------------------------------------------------------------- /evaluation_Windows/eval_f1.py: -------------------------------------------------------------------------------- 1 | from pycocotools.coco import COCO 2 | from pycocotools.cocoeval_modified import COCOeval 3 | import json 4 | 5 | coco_gt = COCO('groundtruth.json') 6 | 7 | gt_image_ids = coco_gt.getImgIds() 8 | 9 | print("Total images ", len(gt_image_ids)) 10 | 11 | with open('detections.json', 'r') as f: 12 | detection_data = json.load(f) 13 | 14 | filtered_detection_data = [ 15 | item for item in detection_data if item['image_id'] in gt_image_ids] 16 | 17 | with open('./temp.json', 'w') as f: 18 | json.dump(filtered_detection_data, f) 19 | 20 | coco_dt = coco_gt.loadRes('./temp.json') 21 | coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') 22 | coco_eval.evaluate() 23 | coco_eval.accumulate() 24 | coco_eval.summarize() 25 | 26 | print('----------------------------------------') 27 | print('AP_0.5-0.95', coco_eval.stats[0]) 28 | print('AP_0.5', coco_eval.stats[1]) 29 | print('AP_S', coco_eval.stats[3]) 30 | print('AP_M', coco_eval.stats[4]) 31 | print('AP_L', coco_eval.stats[5]) 32 | print('f1_score: ', coco_eval.stats[20]) 33 | print('----------------------------------------') 34 | -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/coco.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/coco.cpython-310.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/coco.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/coco.cpython-39.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/cocoeval.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/cocoeval.cpython-39.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/cocoeval_modified.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/cocoeval_modified.cpython-39.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/__pycache__/mask.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/__pycache__/mask.cpython-39.pyc -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/_mask.cp39-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoyoG/FishEye8K/bfa5002c49f5d2425ba10cad604d71fed05c79fd/evaluation_Windows/pycocotools/_mask.cp39-win_amd64.pyd -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/coco.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | __version__ = '2.0' 3 | # Interface for accessing the Microsoft COCO dataset. 4 | 5 | # Microsoft COCO is a large image dataset designed for object detection, 6 | # segmentation, and caption generation. pycocotools is a Python API that 7 | # assists in loading, parsing and visualizing the annotations in COCO. 8 | # Please visit http://mscoco.org/ for more information on COCO, including 9 | # for the data, paper, and tutorials. The exact format of the annotations 10 | # is also described on the COCO website. For example usage of the pycocotools 11 | # please see pycocotools_demo.ipynb. In addition to this API, please download both 12 | # the COCO images and annotations in order to run the demo. 13 | 14 | # An alternative to using the API is to load the annotations directly 15 | # into Python dictionary 16 | # Using the API provides additional utility functions. Note that this API 17 | # supports both *instance* and *caption* annotations. In the case of 18 | # captions not all functions are defined (e.g. categories are undefined). 19 | 20 | # The following API functions are defined: 21 | # COCO - COCO api class that loads COCO annotation file and prepare data structures. 22 | # decodeMask - Decode binary mask M encoded via run-length encoding. 23 | # encodeMask - Encode binary mask M using run-length encoding. 24 | # getAnnIds - Get ann ids that satisfy given filter conditions. 25 | # getCatIds - Get cat ids that satisfy given filter conditions. 26 | # getImgIds - Get img ids that satisfy given filter conditions. 27 | # loadAnns - Load anns with the specified ids. 28 | # loadCats - Load cats with the specified ids. 29 | # loadImgs - Load imgs with the specified ids. 30 | # annToMask - Convert segmentation in an annotation to binary mask. 31 | # showAnns - Display the specified annotations. 32 | # loadRes - Load algorithm results and create API for accessing them. 33 | # download - Download COCO images from mscoco.org server. 34 | # Throughout the API "ann"=annotation, "cat"=category, and "img"=image. 35 | # Help on each functions can be accessed by: "help COCO>function". 36 | 37 | # See also COCO>decodeMask, 38 | # COCO>encodeMask, COCO>getAnnIds, COCO>getCatIds, 39 | # COCO>getImgIds, COCO>loadAnns, COCO>loadCats, 40 | # COCO>loadImgs, COCO>annToMask, COCO>showAnns 41 | 42 | # Microsoft COCO Toolbox. version 2.0 43 | # Data, paper, and tutorials available at: http://mscoco.org/ 44 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2014. 45 | # Licensed under the Simplified BSD License [see bsd.txt] 46 | 47 | import json 48 | import time 49 | import numpy as np 50 | import copy 51 | import itertools 52 | from . import mask as maskUtils 53 | import os 54 | from collections import defaultdict 55 | import sys 56 | PYTHON_VERSION = sys.version_info[0] 57 | if PYTHON_VERSION == 2: 58 | from urllib import urlretrieve 59 | elif PYTHON_VERSION == 3: 60 | from urllib.request import urlretrieve 61 | 62 | 63 | def _isArrayLike(obj): 64 | return hasattr(obj, '__iter__') and hasattr(obj, '__len__') 65 | 66 | 67 | class COCO: 68 | def __init__(self, annotation_file=None): 69 | """ 70 | Constructor of Microsoft COCO helper class for reading and visualizing annotations. 71 | :param annotation_file (str): location of annotation file 72 | :param image_folder (str): location to the folder that hosts images. 73 | :return: 74 | """ 75 | # load dataset 76 | self.dataset, self.anns, self.cats, self.imgs = dict(), dict(), dict(), dict() 77 | self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list) 78 | if not annotation_file == None: 79 | print('loading annotations into memory...') 80 | tic = time.time() 81 | with open(annotation_file, 'r') as f: 82 | dataset = json.load(f) 83 | assert type(dataset) == dict, 'annotation file format {} not supported'.format( 84 | type(dataset)) 85 | print('Done (t={:0.2f}s)'.format(time.time() - tic)) 86 | self.dataset = dataset 87 | self.createIndex() 88 | 89 | def createIndex(self): 90 | # create index 91 | print('creating index...') 92 | anns, cats, imgs = {}, {}, {} 93 | imgToAnns, catToImgs = defaultdict(list), defaultdict(list) 94 | if 'annotations' in self.dataset: 95 | for ann in self.dataset['annotations']: 96 | imgToAnns[ann['image_id']].append(ann) 97 | anns[ann['id']] = ann 98 | 99 | if 'images' in self.dataset: 100 | for img in self.dataset['images']: 101 | imgs[img['id']] = img 102 | 103 | if 'categories' in self.dataset: 104 | for cat in self.dataset['categories']: 105 | cats[cat['id']] = cat 106 | 107 | if 'annotations' in self.dataset and 'categories' in self.dataset: 108 | for ann in self.dataset['annotations']: 109 | catToImgs[ann['category_id']].append(ann['image_id']) 110 | 111 | print('index created!') 112 | 113 | # create class members 114 | self.anns = anns 115 | self.imgToAnns = imgToAnns 116 | self.catToImgs = catToImgs 117 | self.imgs = imgs 118 | self.cats = cats 119 | 120 | def info(self): 121 | """ 122 | Print information about the annotation file. 123 | :return: 124 | """ 125 | for key, value in self.dataset['info'].items(): 126 | print('{}: {}'.format(key, value)) 127 | 128 | def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None): 129 | """ 130 | Get ann ids that satisfy given filter conditions. default skips that filter 131 | :param imgIds (int array) : get anns for given imgs 132 | catIds (int array) : get anns for given cats 133 | areaRng (float array) : get anns for given area range (e.g. [0 inf]) 134 | iscrowd (boolean) : get anns for given crowd label (False or True) 135 | :return: ids (int array) : integer array of ann ids 136 | """ 137 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 138 | catIds = catIds if _isArrayLike(catIds) else [catIds] 139 | 140 | if len(imgIds) == len(catIds) == len(areaRng) == 0: 141 | anns = self.dataset['annotations'] 142 | else: 143 | if not len(imgIds) == 0: 144 | lists = [self.imgToAnns[imgId] 145 | for imgId in imgIds if imgId in self.imgToAnns] 146 | anns = list(itertools.chain.from_iterable(lists)) 147 | else: 148 | anns = self.dataset['annotations'] 149 | anns = anns if len(catIds) == 0 else [ 150 | ann for ann in anns if ann['category_id'] in catIds] 151 | anns = anns if len(areaRng) == 0 else [ 152 | ann for ann in anns if ann['area'] > areaRng[0] and ann['area'] < areaRng[1]] 153 | if not iscrowd == None: 154 | ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd] 155 | else: 156 | ids = [ann['id'] for ann in anns] 157 | return ids 158 | 159 | def getCatIds(self, catNms=[], supNms=[], catIds=[]): 160 | """ 161 | filtering parameters. default skips that filter. 162 | :param catNms (str array) : get cats for given cat names 163 | :param supNms (str array) : get cats for given supercategory names 164 | :param catIds (int array) : get cats for given cat ids 165 | :return: ids (int array) : integer array of cat ids 166 | """ 167 | catNms = catNms if _isArrayLike(catNms) else [catNms] 168 | supNms = supNms if _isArrayLike(supNms) else [supNms] 169 | catIds = catIds if _isArrayLike(catIds) else [catIds] 170 | if len(catNms) == len(supNms) == len(catIds) == 0: 171 | cats = self.dataset['categories'] 172 | else: 173 | cats = self.dataset['categories'] 174 | 175 | cats = cats if len(catNms) == 0 else [ 176 | cat for cat in cats if cat['name'] in catNms] 177 | cats = cats if len(supNms) == 0 else [ 178 | cat for cat in cats if cat['supercategory'] in supNms] 179 | cats = cats if len(catIds) == 0 else [ 180 | cat for cat in cats if cat['id'] in catIds] 181 | ids = [cat['id'] for cat in cats] 182 | return ids 183 | 184 | def getImgIds(self, imgIds=[], catIds=[]): 185 | ''' 186 | Get img ids that satisfy given filter conditions. 187 | :param imgIds (int array) : get imgs for given ids 188 | :param catIds (int array) : get imgs with all given cats 189 | :return: ids (int array) : integer array of img ids 190 | ''' 191 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 192 | catIds = catIds if _isArrayLike(catIds) else [catIds] 193 | 194 | if len(imgIds) == len(catIds) == 0: 195 | ids = self.imgs.keys() 196 | else: 197 | ids = set(imgIds) 198 | for i, catId in enumerate(catIds): 199 | if i == 0 and len(ids) == 0: 200 | ids = set(self.catToImgs[catId]) 201 | else: 202 | ids &= set(self.catToImgs[catId]) 203 | return list(ids) 204 | 205 | def loadAnns(self, ids=[]): 206 | """ 207 | Load anns with the specified ids. 208 | :param ids (int array) : integer ids specifying anns 209 | :return: anns (object array) : loaded ann objects 210 | """ 211 | if _isArrayLike(ids): 212 | return [self.anns[id] for id in ids] 213 | elif type(ids) == int: 214 | return [self.anns[ids]] 215 | 216 | def loadCats(self, ids=[]): 217 | """ 218 | Load cats with the specified ids. 219 | :param ids (int array) : integer ids specifying cats 220 | :return: cats (object array) : loaded cat objects 221 | """ 222 | if _isArrayLike(ids): 223 | return [self.cats[id] for id in ids] 224 | elif type(ids) == int: 225 | return [self.cats[ids]] 226 | 227 | def loadImgs(self, ids=[]): 228 | """ 229 | Load anns with the specified ids. 230 | :param ids (int array) : integer ids specifying img 231 | :return: imgs (object array) : loaded img objects 232 | """ 233 | if _isArrayLike(ids): 234 | return [self.imgs[id] for id in ids] 235 | elif type(ids) == int: 236 | return [self.imgs[ids]] 237 | 238 | def showAnns(self, anns, draw_bbox=False): 239 | """ 240 | Display the specified annotations. 241 | :param anns (array of object): annotations to display 242 | :return: None 243 | """ 244 | if len(anns) == 0: 245 | return 0 246 | if 'segmentation' in anns[0] or 'keypoints' in anns[0]: 247 | datasetType = 'instances' 248 | elif 'caption' in anns[0]: 249 | datasetType = 'captions' 250 | else: 251 | raise Exception('datasetType not supported') 252 | if datasetType == 'instances': 253 | import matplotlib.pyplot as plt 254 | from matplotlib.collections import PatchCollection 255 | from matplotlib.patches import Polygon 256 | 257 | ax = plt.gca() 258 | ax.set_autoscale_on(False) 259 | polygons = [] 260 | color = [] 261 | for ann in anns: 262 | c = (np.random.random((1, 3))*0.6+0.4).tolist()[0] 263 | if 'segmentation' in ann: 264 | if type(ann['segmentation']) == list: 265 | # polygon 266 | for seg in ann['segmentation']: 267 | poly = np.array(seg).reshape((int(len(seg)/2), 2)) 268 | polygons.append(Polygon(poly)) 269 | color.append(c) 270 | else: 271 | # mask 272 | t = self.imgs[ann['image_id']] 273 | if type(ann['segmentation']['counts']) == list: 274 | rle = maskUtils.frPyObjects( 275 | [ann['segmentation']], t['height'], t['width']) 276 | else: 277 | rle = [ann['segmentation']] 278 | m = maskUtils.decode(rle) 279 | img = np.ones((m.shape[0], m.shape[1], 3)) 280 | if ann['iscrowd'] == 1: 281 | color_mask = np.array([2.0, 166.0, 101.0])/255 282 | if ann['iscrowd'] == 0: 283 | color_mask = np.random.random((1, 3)).tolist()[0] 284 | for i in range(3): 285 | img[:, :, i] = color_mask[i] 286 | ax.imshow(np.dstack((img, m*0.5))) 287 | if 'keypoints' in ann and type(ann['keypoints']) == list: 288 | # turn skeleton into zero-based index 289 | sks = np.array(self.loadCats( 290 | ann['category_id'])[0]['skeleton'])-1 291 | kp = np.array(ann['keypoints']) 292 | x = kp[0::3] 293 | y = kp[1::3] 294 | v = kp[2::3] 295 | for sk in sks: 296 | if np.all(v[sk] > 0): 297 | plt.plot(x[sk], y[sk], linewidth=3, color=c) 298 | plt.plot(x[v > 0], y[v > 0], 'o', markersize=8, 299 | markerfacecolor=c, markeredgecolor='k', markeredgewidth=2) 300 | plt.plot(x[v > 1], y[v > 1], 'o', markersize=8, 301 | markerfacecolor=c, markeredgecolor=c, markeredgewidth=2) 302 | 303 | if draw_bbox: 304 | [bbox_x, bbox_y, bbox_w, bbox_h] = ann['bbox'] 305 | poly = [[bbox_x, bbox_y], [bbox_x, bbox_y+bbox_h], 306 | [bbox_x+bbox_w, bbox_y+bbox_h], [bbox_x+bbox_w, bbox_y]] 307 | np_poly = np.array(poly).reshape((4, 2)) 308 | polygons.append(Polygon(np_poly)) 309 | color.append(c) 310 | 311 | p = PatchCollection(polygons, facecolor=color, 312 | linewidths=0, alpha=0.4) 313 | ax.add_collection(p) 314 | p = PatchCollection(polygons, facecolor='none', 315 | edgecolors=color, linewidths=2) 316 | ax.add_collection(p) 317 | elif datasetType == 'captions': 318 | for ann in anns: 319 | print(ann['caption']) 320 | 321 | def loadRes(self, resFile): 322 | """ 323 | Load result file and return a result api object. 324 | :param resFile (str) : file name of result file 325 | :return: res (obj) : result api object 326 | """ 327 | res = COCO() 328 | res.dataset['images'] = [img for img in self.dataset['images']] 329 | 330 | print('Loading and preparing results...') 331 | tic = time.time() 332 | if type(resFile) == str or (PYTHON_VERSION == 2 and type(resFile) == unicode): 333 | with open(resFile) as f: 334 | anns = json.load(f) 335 | elif type(resFile) == np.ndarray: 336 | anns = self.loadNumpyAnnotations(resFile) 337 | else: 338 | anns = resFile 339 | assert type(anns) == list, 'results in not an array of objects' 340 | annsImgIds = [ann['image_id'] for ann in anns] 341 | assert set(annsImgIds) == (set(annsImgIds) & set(self.getImgIds())), \ 342 | 'Results do not correspond to current coco set' 343 | if 'caption' in anns[0]: 344 | imgIds = set([img['id'] for img in res.dataset['images']]) & set( 345 | [ann['image_id'] for ann in anns]) 346 | res.dataset['images'] = [ 347 | img for img in res.dataset['images'] if img['id'] in imgIds] 348 | for id, ann in enumerate(anns): 349 | ann['id'] = id+1 350 | elif 'bbox' in anns[0] and not anns[0]['bbox'] == []: 351 | res.dataset['categories'] = copy.deepcopy( 352 | self.dataset['categories']) 353 | for id, ann in enumerate(anns): 354 | bb = ann['bbox'] 355 | x1, x2, y1, y2 = [bb[0], bb[0]+bb[2], bb[1], bb[1]+bb[3]] 356 | if not 'segmentation' in ann: 357 | ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] 358 | ann['area'] = bb[2]*bb[3] 359 | ann['id'] = id+1 360 | ann['iscrowd'] = 0 361 | elif 'segmentation' in anns[0]: 362 | res.dataset['categories'] = copy.deepcopy( 363 | self.dataset['categories']) 364 | for id, ann in enumerate(anns): 365 | # now only support compressed RLE format as segmentation results 366 | ann['area'] = maskUtils.area(ann['segmentation']) 367 | if not 'bbox' in ann: 368 | ann['bbox'] = maskUtils.toBbox(ann['segmentation']) 369 | ann['id'] = id+1 370 | ann['iscrowd'] = 0 371 | elif 'keypoints' in anns[0]: 372 | res.dataset['categories'] = copy.deepcopy( 373 | self.dataset['categories']) 374 | for id, ann in enumerate(anns): 375 | s = ann['keypoints'] 376 | x = s[0::3] 377 | y = s[1::3] 378 | x0, x1, y0, y1 = np.min(x), np.max(x), np.min(y), np.max(y) 379 | ann['area'] = (x1-x0)*(y1-y0) 380 | ann['id'] = id + 1 381 | ann['bbox'] = [x0, y0, x1-x0, y1-y0] 382 | print('DONE (t={:0.2f}s)'.format(time.time() - tic)) 383 | 384 | res.dataset['annotations'] = anns 385 | res.createIndex() 386 | return res 387 | 388 | def download(self, tarDir=None, imgIds=[]): 389 | ''' 390 | Download COCO images from mscoco.org server. 391 | :param tarDir (str): COCO results directory name 392 | imgIds (list): images to be downloaded 393 | :return: 394 | ''' 395 | if tarDir is None: 396 | print('Please specify target directory') 397 | return -1 398 | if len(imgIds) == 0: 399 | imgs = self.imgs.values() 400 | else: 401 | imgs = self.loadImgs(imgIds) 402 | N = len(imgs) 403 | if not os.path.exists(tarDir): 404 | os.makedirs(tarDir) 405 | for i, img in enumerate(imgs): 406 | tic = time.time() 407 | fname = os.path.join(tarDir, img['file_name']) 408 | if not os.path.exists(fname): 409 | urlretrieve(img['coco_url'], fname) 410 | print('downloaded {}/{} images (t={:0.1f}s)'.format(i, N, time.time() - tic)) 411 | 412 | def loadNumpyAnnotations(self, data): 413 | """ 414 | Convert result data from a numpy array [Nx7] where each row contains {imageID,x1,y1,w,h,score,class} 415 | :param data (numpy.ndarray) 416 | :return: annotations (python nested list) 417 | """ 418 | print('Converting ndarray to lists...') 419 | assert (type(data) == np.ndarray) 420 | print(data.shape) 421 | assert (data.shape[1] == 7) 422 | N = data.shape[0] 423 | ann = [] 424 | for i in range(N): 425 | if i % 1000000 == 0: 426 | print('{}/{}'.format(i, N)) 427 | ann += [{ 428 | 'image_id': int(data[i, 0]), 429 | 'bbox': [data[i, 1], data[i, 2], data[i, 3], data[i, 4]], 430 | 'score': data[i, 5], 431 | 'category_id': int(data[i, 6]), 432 | }] 433 | return ann 434 | 435 | def annToRLE(self, ann): 436 | """ 437 | Convert annotation which can be polygons, uncompressed RLE to RLE. 438 | :return: binary mask (numpy 2D array) 439 | """ 440 | t = self.imgs[ann['image_id']] 441 | h, w = t['height'], t['width'] 442 | segm = ann['segmentation'] 443 | if type(segm) == list: 444 | # polygon -- a single object might consist of multiple parts 445 | # we merge all parts into one mask rle code 446 | rles = maskUtils.frPyObjects(segm, h, w) 447 | rle = maskUtils.merge(rles) 448 | elif type(segm['counts']) == list: 449 | # uncompressed RLE 450 | rle = maskUtils.frPyObjects(segm, h, w) 451 | else: 452 | # rle 453 | rle = ann['segmentation'] 454 | return rle 455 | 456 | def annToMask(self, ann): 457 | """ 458 | Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask. 459 | :return: binary mask (numpy 2D array) 460 | """ 461 | rle = self.annToRLE(ann) 462 | m = maskUtils.decode(rle) 463 | return m 464 | -------------------------------------------------------------------------------- /evaluation_Windows/pycocotools/mask.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import pycocotools._mask as _mask 4 | 5 | # Interface for manipulating masks stored in RLE format. 6 | # 7 | # RLE is a simple yet efficient format for storing binary masks. RLE 8 | # first divides a vector (or vectorized image) into a series of piecewise 9 | # constant regions and then for each piece simply stores the length of 10 | # that piece. For example, given M=[0 0 1 1 1 0 1] the RLE counts would 11 | # be [2 3 1 1], or for M=[1 1 1 1 1 1 0] the counts would be [0 6 1] 12 | # (note that the odd counts are always the numbers of zeros). Instead of 13 | # storing the counts directly, additional compression is achieved with a 14 | # variable bitrate representation based on a common scheme called LEB128. 15 | # 16 | # Compression is greatest given large piecewise constant regions. 17 | # Specifically, the size of the RLE is proportional to the number of 18 | # *boundaries* in M (or for an image the number of boundaries in the y 19 | # direction). Assuming fairly simple shapes, the RLE representation is 20 | # O(sqrt(n)) where n is number of pixels in the object. Hence space usage 21 | # is substantially lower, especially for large simple objects (large n). 22 | # 23 | # Many common operations on masks can be computed directly using the RLE 24 | # (without need for decoding). This includes computations such as area, 25 | # union, intersection, etc. All of these operations are linear in the 26 | # size of the RLE, in other words they are O(sqrt(n)) where n is the area 27 | # of the object. Computing these operations on the original mask is O(n). 28 | # Thus, using the RLE can result in substantial computational savings. 29 | # 30 | # The following API functions are defined: 31 | # encode - Encode binary masks using RLE. 32 | # decode - Decode binary masks encoded via RLE. 33 | # merge - Compute union or intersection of encoded masks. 34 | # iou - Compute intersection over union between masks. 35 | # area - Compute area of encoded masks. 36 | # toBbox - Get bounding boxes surrounding encoded masks. 37 | # frPyObjects - Convert polygon, bbox, and uncompressed RLE to encoded RLE mask. 38 | # 39 | # Usage: 40 | # Rs = encode( masks ) 41 | # masks = decode( Rs ) 42 | # R = merge( Rs, intersect=false ) 43 | # o = iou( dt, gt, iscrowd ) 44 | # a = area( Rs ) 45 | # bbs = toBbox( Rs ) 46 | # Rs = frPyObjects( [pyObjects], h, w ) 47 | # 48 | # In the API the following formats are used: 49 | # Rs - [dict] Run-length encoding of binary masks 50 | # R - dict Run-length encoding of binary mask 51 | # masks - [hxwxn] Binary mask(s) (must have type np.ndarray(dtype=uint8) in column-major order) 52 | # iscrowd - [nx1] list of np.ndarray. 1 indicates corresponding gt image has crowd region to ignore 53 | # bbs - [nx4] Bounding box(es) stored as [x y w h] 54 | # poly - Polygon stored as [[x1 y1 x2 y2...],[x1 y1 ...],...] (2D list) 55 | # dt,gt - May be either bounding boxes or encoded masks 56 | # Both poly and bbs are 0-indexed (bbox=[0 0 1 1] encloses first pixel). 57 | # 58 | # Finally, a note about the intersection over union (iou) computation. 59 | # The standard iou of a ground truth (gt) and detected (dt) object is 60 | # iou(gt,dt) = area(intersect(gt,dt)) / area(union(gt,dt)) 61 | # For "crowd" regions, we use a modified criteria. If a gt object is 62 | # marked as "iscrowd", we allow a dt to match any subregion of the gt. 63 | # Choosing gt' in the crowd gt that best matches the dt can be done using 64 | # gt'=intersect(dt,gt). Since by definition union(gt',dt)=dt, computing 65 | # iou(gt,dt,iscrowd) = iou(gt',dt) = area(intersect(gt,dt)) / area(dt) 66 | # For crowd gt regions we use this modified criteria above for the iou. 67 | # 68 | # To compile run "python setup.py build_ext --inplace" 69 | # Please do not contact us for help with compiling. 70 | # 71 | # Microsoft COCO Toolbox. version 2.0 72 | # Data, paper, and tutorials available at: http://mscoco.org/ 73 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 74 | # Licensed under the Simplified BSD License [see coco/license.txt] 75 | 76 | iou = _mask.iou 77 | merge = _mask.merge 78 | frPyObjects = _mask.frPyObjects 79 | 80 | def encode(bimask): 81 | if len(bimask.shape) == 3: 82 | return _mask.encode(bimask) 83 | elif len(bimask.shape) == 2: 84 | h, w = bimask.shape 85 | return _mask.encode(bimask.reshape((h, w, 1), order='F'))[0] 86 | 87 | def decode(rleObjs): 88 | if type(rleObjs) == list: 89 | return _mask.decode(rleObjs) 90 | else: 91 | return _mask.decode([rleObjs])[:,:,0] 92 | 93 | def area(rleObjs): 94 | if type(rleObjs) == list: 95 | return _mask.area(rleObjs) 96 | else: 97 | return _mask.area([rleObjs])[0] 98 | 99 | def toBbox(rleObjs): 100 | if type(rleObjs) == list: 101 | return _mask.toBbox(rleObjs) 102 | else: 103 | return _mask.toBbox([rleObjs])[0] --------------------------------------------------------------------------------