├── .isort.cfg ├── MANIFEST.in ├── aitodpycocotools ├── aitodpycocotools │ ├── __init__.py │ ├── mask.py │ ├── _mask.pyx │ ├── coco.py │ └── cocoeval.py ├── MANIFEST.in ├── Makefile ├── setup.py ├── common │ ├── maskApi.h │ ├── gason.h │ ├── maskApi.c │ └── gason.cpp └── pycocoEvalDemo.ipynb ├── lvis ├── requirements.txt ├── lvis │ ├── __init__.py │ ├── results.py │ ├── colormap.py │ ├── lvis.py │ ├── vis.py │ └── eval.py └── setup.py ├── README.md ├── .pre-commit-config.yaml ├── .github └── workflows │ ├── deploy.yml │ └── build.yml ├── license.txt ├── .gitignore └── results ├── val2014_fake_eval_res.txt └── person_keypoints_val2014_fakekeypoints100_results.json /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | known_third_party = cv2,matplotlib,numpy,setuptools 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include aitodpycocotools/aitodpycocotools/*.pyx 2 | include lvis/requirements.txt 3 | -------------------------------------------------------------------------------- /aitodpycocotools/aitodpycocotools/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tylin' 2 | __version__ = '12.0.2' 3 | -------------------------------------------------------------------------------- /aitodpycocotools/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include common/*.cpp common/*.h common/*.c 2 | include aitodpycocotools/_mask.pyx 3 | -------------------------------------------------------------------------------- /lvis/requirements.txt: -------------------------------------------------------------------------------- 1 | cycler>=0.10.0 2 | Cython>=0.29.12 3 | kiwisolver>=1.1.0 4 | matplotlib>=3.1.1 5 | numpy>=1.18.2 6 | opencv-python>=4.1.0.25 7 | pyparsing>=2.4.0 8 | python-dateutil>=2.8.0 9 | six>=1.12.0 10 | -------------------------------------------------------------------------------- /lvis/lvis/__init__.py: -------------------------------------------------------------------------------- 1 | from .eval import LVISEval 2 | from .lvis import LVIS 3 | from .results import LVISResults 4 | from .vis import LVISVis 5 | 6 | __all__ = ['LVIS', 'LVISResults', 'LVISEval', 'LVISVis'] 7 | __version__ = '10.5.3' 8 | -------------------------------------------------------------------------------- /aitodpycocotools/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | # install pycocotools locally 3 | python setup.py build_ext --inplace 4 | rm -rf build 5 | 6 | install: 7 | # install pycocotools to the Python site-packages 8 | python setup.py build_ext install 9 | rm -rf build 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # COCO API for AI-TOD Dataset 2 | 3 | In this repo, we fork the cocoapi from open-mmlab for AI-TOD Dataset. Besides, we merge the LRP-Error to this project. 4 | 5 | ## Installation 6 | 7 | Currently, you could install by run 8 | 9 | ```shell 10 | # Install cocoapi 11 | pip install "git+https://github.com/jwwangchn/cocoapi-aitod.git#subdirectory=aitodpycocotools" 12 | ``` 13 | 14 | ## Reference 15 | 16 | * [LRP-Error](https://github.com/kemaloksuz/LRP-Error). 17 | * [cocoapi](https://github.com/cocodataset/cocoapi) of [COCO dataset](http://cocodataset.org/). 18 | * [lvis-api](https://github.com/lvis-dataset/lvis-api) of [LVIS dataset](http://lvisdataset.org). 19 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://gitlab.com/pycqa/flake8.git 3 | rev: 3.8.0 4 | hooks: 5 | - id: flake8 6 | - repo: https://github.com/asottile/seed-isort-config 7 | rev: v2.1.0 8 | hooks: 9 | - id: seed-isort-config 10 | - repo: https://github.com/timothycrosley/isort 11 | rev: 4.3.21 12 | hooks: 13 | - id: isort 14 | - repo: https://github.com/pre-commit/mirrors-yapf 15 | rev: v0.29.0 16 | hooks: 17 | - id: yapf 18 | - repo: https://github.com/pre-commit/pre-commit-hooks 19 | rev: v2.5.0 20 | hooks: 21 | - id: trailing-whitespace 22 | - id: check-yaml 23 | - id: end-of-file-fixer 24 | - id: requirements-txt-fixer 25 | - id: double-quote-string-fixer 26 | - id: check-merge-conflict 27 | - id: fix-encoding-pragma 28 | args: ["--remove"] 29 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | 3 | on: push 4 | 5 | jobs: 6 | build-n-publish: 7 | runs-on: ubuntu-latest 8 | if: startsWith(github.event.ref, 'refs/tags') 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up Python 3.7 12 | uses: actions/setup-python@v2 13 | with: 14 | python-version: 3.7 15 | - name: Build aitodpycocotools 16 | run: | 17 | pip install -r lvis/requirements.txt 18 | cd aitodpycocotools 19 | python setup.py sdist 20 | - name: Build mmvlis 21 | run: | 22 | pip install wheel 23 | cd lvis 24 | python setup.py sdist bdist_wheel 25 | - name: Publish distribution to PyPI 26 | run: | 27 | pip install twine 28 | twine upload aitodpycocotools/dist/* -u __token__ -p ${{ secrets.pypi_password }} 29 | twine upload lvis/dist/* -u __token__ -p ${{ secrets.pypi_password }} 30 | -------------------------------------------------------------------------------- /aitodpycocotools/setup.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from setuptools import Extension, setup 3 | 4 | # To compile and install locally run "python setup.py build_ext --inplace" 5 | # To install library to Python site-packages run 6 | # "python setup.py build_ext install" 7 | # Note that the original compile flags below are GCC flags unsupported by 8 | # the Visual C++ 2015 build tools. 9 | # They can safely be removed. 10 | 11 | ext_modules = [ 12 | Extension( 13 | 'aitodpycocotools._mask', 14 | sources=['common/maskApi.c', 'aitodpycocotools/_mask.pyx'], 15 | include_dirs=[np.get_include(), 'common'], 16 | # extra_compile_args=['-Wno-cpp', '-Wno-unused-function', '-std=c99'], 17 | extra_compile_args=[], 18 | ) 19 | ] 20 | 21 | setup(name='aitodpycocotools', 22 | packages=['aitodpycocotools'], 23 | package_dir={'aitodpycocotools': 'aitodpycocotools'}, 24 | install_requires=[ 25 | 'setuptools>=18.0', 'cython>=0.27.3', 'matplotlib>=2.1.0' 26 | ], 27 | version='12.0.3', 28 | ext_modules=ext_modules) 29 | -------------------------------------------------------------------------------- /lvis/setup.py: -------------------------------------------------------------------------------- 1 | """LVIS (pronounced ‘el-vis’): is a new dataset for Large Vocabulary Instance 2 | Segmentation. We collect over 2 million high-quality instance segmentation 3 | masks for over 1200 entry-level object categories in 164k images. LVIS API 4 | enables reading and interacting with annotation files, visualizing annotations, 5 | and evaluating results. 6 | 7 | """ 8 | import os.path 9 | import sys 10 | 11 | import setuptools 12 | 13 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lvis')) 14 | 15 | dir_path = os.path.dirname(os.path.realpath(__file__)) 16 | with open(os.path.join(dir_path, 'requirements.txt')) as f: 17 | reqs = f.read() 18 | 19 | DISTNAME = 'mmlvis' 20 | DESCRIPTION = 'Python API for LVIS dataset.' 21 | AUTHOR = 'Agrim Gupta' 22 | REQUIREMENTS = (reqs.strip().split('\n'), ) 23 | DOCLINES = (__doc__ or '') 24 | 25 | if __name__ == '__main__': 26 | setuptools.setup(name=DISTNAME, 27 | install_requires=REQUIREMENTS, 28 | packages=setuptools.find_packages(), 29 | version='10.5.3', 30 | description=DESCRIPTION, 31 | long_description=DOCLINES, 32 | long_description_content_type='text/markdown', 33 | author=AUTHOR) 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: build 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: [3.6, 3.7, 3.8] 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v1 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install linting dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install flake8 isort==4.3.21 yapf 26 | - name: Lint with flake8 27 | run: flake8 --max-complexity 20 . 28 | - name: Lint with isort 29 | run: isort -rc --check-only --diff aitodpycocotools/ lvis/ 30 | - name: Format with yapf 31 | run: yapf -r -d aitodpycocotools/ lvis/ 32 | - name: Install python dependencies 33 | run: pip install numpy 34 | - name: Build and install aitodpycocotools 35 | run: cd aitodpycocotools && rm -rf *.eggs-info && pip install . 36 | - name: Build and install lvis 37 | run: cd lvis && rm -rf *.eggs-info && pip install . 38 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Piotr Dollar and Tsung-Yi Lin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. 27 | -------------------------------------------------------------------------------- /aitodpycocotools/common/maskApi.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Microsoft COCO Toolbox. version 2.0 3 | * Data, paper, and tutorials available at: http://mscoco.org/ 4 | * Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 5 | * Licensed under the Simplified BSD License [see coco/license.txt] 6 | **************************************************************************/ 7 | #pragma once 8 | 9 | typedef unsigned int uint; 10 | typedef unsigned long siz; 11 | typedef unsigned char byte; 12 | typedef double* BB; 13 | typedef struct { siz h, w, m; uint *cnts; } RLE; 14 | 15 | /* Initialize/destroy RLE. */ 16 | void rleInit( RLE *R, siz h, siz w, siz m, uint *cnts ); 17 | void rleFree( RLE *R ); 18 | 19 | /* Initialize/destroy RLE array. */ 20 | void rlesInit( RLE **R, siz n ); 21 | void rlesFree( RLE **R, siz n ); 22 | 23 | /* Encode binary masks using RLE. */ 24 | void rleEncode( RLE *R, const byte *mask, siz h, siz w, siz n ); 25 | 26 | /* Decode binary masks encoded via RLE. */ 27 | void rleDecode( const RLE *R, byte *mask, siz n ); 28 | 29 | /* Compute union or intersection of encoded masks. */ 30 | void rleMerge( const RLE *R, RLE *M, siz n, int intersect ); 31 | 32 | /* Compute area of encoded masks. */ 33 | void rleArea( const RLE *R, siz n, uint *a ); 34 | 35 | /* Compute intersection over union between masks. */ 36 | void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o ); 37 | 38 | /* Compute non-maximum suppression between bounding masks */ 39 | void rleNms( RLE *dt, siz n, uint *keep, double thr ); 40 | 41 | /* Compute intersection over union between bounding boxes. */ 42 | void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o ); 43 | 44 | /* Compute non-maximum suppression between bounding boxes */ 45 | void bbNms( BB dt, siz n, uint *keep, double thr ); 46 | 47 | /* Get bounding boxes surrounding encoded masks. */ 48 | void rleToBbox( const RLE *R, BB bb, siz n ); 49 | 50 | /* Convert bounding boxes to encoded masks. */ 51 | void rleFrBbox( RLE *R, const BB bb, siz h, siz w, siz n ); 52 | 53 | /* Convert polygon to encoded mask. */ 54 | void rleFrPoly( RLE *R, const double *xy, siz k, siz h, siz w ); 55 | 56 | /* Get compressed string representation of encoded mask. */ 57 | char* rleToString( const RLE *R ); 58 | 59 | /* Convert from compressed string representation of encoded mask. */ 60 | void rleFrString( RLE *R, char *s, siz h, siz w ); 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | 140 | images/ 141 | annotations/ 142 | results/ 143 | external/ 144 | .vscode 145 | .idea 146 | .DS_Store 147 | 148 | aitodpycocotools/aitodpycocotools/__init__.pyc 149 | aitodpycocotools/aitodpycocotools/_mask.c 150 | aitodpycocotools/aitodpycocotools/_mask.so 151 | aitodpycocotools/aitodpycocotools/coco.pyc 152 | aitodpycocotools/aitodpycocotools/cocoeval.pyc 153 | aitodpycocotools/aitodpycocotools/mask.pyc 154 | -------------------------------------------------------------------------------- /results/val2014_fake_eval_res.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | type=segm 3 | Running per image evaluation... DONE (t=0.45s). 4 | Accumulating evaluation results... DONE (t=0.08s). 5 | Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.320 6 | Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.562 7 | Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.299 8 | Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.387 9 | Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.310 10 | Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.327 11 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.268 12 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.415 13 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.417 14 | Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.469 15 | Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.377 16 | Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.381 17 | 18 | ------------------------------------------------------------------------------ 19 | type=bbox 20 | Running per image evaluation... DONE (t=0.34s). 21 | Accumulating evaluation results... DONE (t=0.08s). 22 | Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.505 23 | Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.697 24 | Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.573 25 | Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.586 26 | Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.519 27 | Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.501 28 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.387 29 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.594 30 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.595 31 | Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.640 32 | Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.566 33 | Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.564 34 | 35 | ------------------------------------------------------------------------------ 36 | type=keypoints 37 | Running per image evaluation... DONE (t=0.06s). 38 | Accumulating evaluation results... DONE (t=0.00s). 39 | Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets= 20 ] = 0.372 40 | Average Precision (AP) @[ IoU=0.50 | area= all | maxDets= 20 ] = 0.636 41 | Average Precision (AP) @[ IoU=0.75 | area= all | maxDets= 20 ] = 0.348 42 | Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.384 43 | Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.386 44 | Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 20 ] = 0.514 45 | Average Recall (AR) @[ IoU=0.50 | area= all | maxDets= 20 ] = 0.734 46 | Average Recall (AR) @[ IoU=0.75 | area= all | maxDets= 20 ] = 0.504 47 | Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.508 48 | Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.522 49 | -------------------------------------------------------------------------------- /lvis/lvis/results.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from collections import defaultdict 3 | from copy import deepcopy 4 | 5 | import aitodpycocotools.mask as mask_utils 6 | 7 | from .lvis import LVIS 8 | 9 | 10 | class LVISResults(LVIS): 11 | def __init__(self, lvis_gt, results, max_dets=300): 12 | """Constructor for LVIS results. 13 | Args: 14 | lvis_gt (LVIS class instance, or str containing path of 15 | annotation file) 16 | results (str containing path of result file or a list of dicts) 17 | max_dets (int): max number of detections per image. The official 18 | value of max_dets for LVIS is 300. 19 | """ 20 | if isinstance(lvis_gt, LVIS): 21 | self.dataset = deepcopy(lvis_gt.dataset) 22 | elif isinstance(lvis_gt, str): 23 | self.dataset = self._load_json(lvis_gt) 24 | else: 25 | raise TypeError('Unsupported type {} of lvis_gt.'.format(lvis_gt)) 26 | 27 | self.logger = logging.getLogger(__name__) 28 | self.logger.info('Loading and preparing results.') 29 | 30 | if isinstance(results, str): 31 | result_anns = self._load_json(results) 32 | else: 33 | # this path way is provided to avoid saving and loading result 34 | # during training. 35 | self.logger.warn( 36 | 'Assuming user provided the results in correct format.') 37 | result_anns = results 38 | 39 | assert isinstance(result_anns, list), 'results is not a list.' 40 | 41 | if max_dets >= 0: 42 | result_anns = self.limit_dets_per_image(result_anns, max_dets) 43 | 44 | if 'bbox' in result_anns[0]: 45 | for id, ann in enumerate(result_anns): 46 | x1, y1, w, h = ann['bbox'] 47 | x2 = x1 + w 48 | y2 = y1 + h 49 | 50 | if 'segmentation' not in ann: 51 | ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] 52 | 53 | ann['area'] = w * h 54 | ann['id'] = id + 1 55 | 56 | elif 'segmentation' in result_anns[0]: 57 | for id, ann in enumerate(result_anns): 58 | # Only support compressed RLE format as segmentation results 59 | ann['area'] = mask_utils.area(ann['segmentation']) 60 | 61 | if 'bbox' not in ann: 62 | ann['bbox'] = mask_utils.toBbox(ann['segmentation']) 63 | 64 | ann['id'] = id + 1 65 | 66 | self.dataset['annotations'] = result_anns 67 | self._create_index() 68 | 69 | img_ids_in_result = [ann['image_id'] for ann in result_anns] 70 | 71 | assert set(img_ids_in_result) == ( 72 | set(img_ids_in_result) & set(self.get_img_ids()) 73 | ), 'Results do not correspond to current LVIS set.' 74 | 75 | def limit_dets_per_image(self, anns, max_dets): 76 | img_ann = defaultdict(list) 77 | for ann in anns: 78 | img_ann[ann['image_id']].append(ann) 79 | 80 | for img_id, _anns in img_ann.items(): 81 | if len(_anns) <= max_dets: 82 | continue 83 | _anns = sorted(_anns, key=lambda ann: ann['score'], reverse=True) 84 | img_ann[img_id] = _anns[:max_dets] 85 | 86 | return [ann for anns in img_ann.values() for ann in anns] 87 | 88 | def get_top_results(self, img_id, score_thrs): 89 | ann_ids = self.get_ann_ids(img_ids=[img_id]) 90 | anns = self.load_anns(ann_ids) 91 | return list(filter(lambda ann: ann['score'] > score_thrs, anns)) 92 | -------------------------------------------------------------------------------- /aitodpycocotools/common/gason.h: -------------------------------------------------------------------------------- 1 | // https://github.com/vivkin/gason - pulled January 10, 2016 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum JsonTag { 9 | JSON_NUMBER = 0, 10 | JSON_STRING, 11 | JSON_ARRAY, 12 | JSON_OBJECT, 13 | JSON_TRUE, 14 | JSON_FALSE, 15 | JSON_NULL = 0xF 16 | }; 17 | 18 | struct JsonNode; 19 | 20 | #define JSON_VALUE_PAYLOAD_MASK 0x00007FFFFFFFFFFFULL 21 | #define JSON_VALUE_NAN_MASK 0x7FF8000000000000ULL 22 | #define JSON_VALUE_TAG_MASK 0xF 23 | #define JSON_VALUE_TAG_SHIFT 47 24 | 25 | union JsonValue { 26 | uint64_t ival; 27 | double fval; 28 | 29 | JsonValue(double x) 30 | : fval(x) { 31 | } 32 | JsonValue(JsonTag tag = JSON_NULL, void *payload = nullptr) { 33 | assert((uintptr_t)payload <= JSON_VALUE_PAYLOAD_MASK); 34 | ival = JSON_VALUE_NAN_MASK | ((uint64_t)tag << JSON_VALUE_TAG_SHIFT) | (uintptr_t)payload; 35 | } 36 | bool isDouble() const { 37 | return (int64_t)ival <= (int64_t)JSON_VALUE_NAN_MASK; 38 | } 39 | JsonTag getTag() const { 40 | return isDouble() ? JSON_NUMBER : JsonTag((ival >> JSON_VALUE_TAG_SHIFT) & JSON_VALUE_TAG_MASK); 41 | } 42 | uint64_t getPayload() const { 43 | assert(!isDouble()); 44 | return ival & JSON_VALUE_PAYLOAD_MASK; 45 | } 46 | double toNumber() const { 47 | assert(getTag() == JSON_NUMBER); 48 | return fval; 49 | } 50 | char *toString() const { 51 | assert(getTag() == JSON_STRING); 52 | return (char *)getPayload(); 53 | } 54 | JsonNode *toNode() const { 55 | assert(getTag() == JSON_ARRAY || getTag() == JSON_OBJECT); 56 | return (JsonNode *)getPayload(); 57 | } 58 | }; 59 | 60 | struct JsonNode { 61 | JsonValue value; 62 | JsonNode *next; 63 | char *key; 64 | }; 65 | 66 | struct JsonIterator { 67 | JsonNode *p; 68 | 69 | void operator++() { 70 | p = p->next; 71 | } 72 | bool operator!=(const JsonIterator &x) const { 73 | return p != x.p; 74 | } 75 | JsonNode *operator*() const { 76 | return p; 77 | } 78 | JsonNode *operator->() const { 79 | return p; 80 | } 81 | }; 82 | 83 | inline JsonIterator begin(JsonValue o) { 84 | return JsonIterator{o.toNode()}; 85 | } 86 | inline JsonIterator end(JsonValue) { 87 | return JsonIterator{nullptr}; 88 | } 89 | 90 | #define JSON_ERRNO_MAP(XX) \ 91 | XX(OK, "ok") \ 92 | XX(BAD_NUMBER, "bad number") \ 93 | XX(BAD_STRING, "bad string") \ 94 | XX(BAD_IDENTIFIER, "bad identifier") \ 95 | XX(STACK_OVERFLOW, "stack overflow") \ 96 | XX(STACK_UNDERFLOW, "stack underflow") \ 97 | XX(MISMATCH_BRACKET, "mismatch bracket") \ 98 | XX(UNEXPECTED_CHARACTER, "unexpected character") \ 99 | XX(UNQUOTED_KEY, "unquoted key") \ 100 | XX(BREAKING_BAD, "breaking bad") \ 101 | XX(ALLOCATION_FAILURE, "allocation failure") 102 | 103 | enum JsonErrno { 104 | #define XX(no, str) JSON_##no, 105 | JSON_ERRNO_MAP(XX) 106 | #undef XX 107 | }; 108 | 109 | const char *jsonStrError(int err); 110 | 111 | class JsonAllocator { 112 | struct Zone { 113 | Zone *next; 114 | size_t used; 115 | } *head = nullptr; 116 | 117 | public: 118 | JsonAllocator() = default; 119 | JsonAllocator(const JsonAllocator &) = delete; 120 | JsonAllocator &operator=(const JsonAllocator &) = delete; 121 | JsonAllocator(JsonAllocator &&x) : head(x.head) { 122 | x.head = nullptr; 123 | } 124 | JsonAllocator &operator=(JsonAllocator &&x) { 125 | head = x.head; 126 | x.head = nullptr; 127 | return *this; 128 | } 129 | ~JsonAllocator() { 130 | deallocate(); 131 | } 132 | void *allocate(size_t size); 133 | void deallocate(); 134 | }; 135 | 136 | int jsonParse(char *str, char **endptr, JsonValue *value, JsonAllocator &allocator); 137 | -------------------------------------------------------------------------------- /aitodpycocotools/aitodpycocotools/mask.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import aitodpycocotools._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 38 | # RLE mask. 39 | # 40 | # Usage: 41 | # Rs = encode( masks ) 42 | # masks = decode( Rs ) 43 | # R = merge( Rs, intersect=false ) 44 | # o = iou( dt, gt, iscrowd ) 45 | # a = area( Rs ) 46 | # bbs = toBbox( Rs ) 47 | # Rs = frPyObjects( [pyObjects], h, w ) 48 | # 49 | # In the API the following formats are used: 50 | # Rs - [dict] Run-length encoding of binary masks 51 | # R - dict Run-length encoding of binary mask 52 | # masks - [hxwxn] Binary mask(s) (must have type np.ndarray(dtype=uint8) 53 | # in column-major order) 54 | # iscrowd - [nx1] list of np.ndarray. 1 indicates corresponding gt image has 55 | # crowd region to ignore 56 | # bbs - [nx4] Bounding box(es) stored as [x y w h] 57 | # poly - Polygon stored as [[x1 y1 x2 y2...],[x1 y1 ...],...] (2D list) 58 | # dt,gt - May be either bounding boxes or encoded masks 59 | # Both poly and bbs are 0-indexed (bbox=[0 0 1 1] encloses first pixel). 60 | # 61 | # Finally, a note about the intersection over union (iou) computation. 62 | # The standard iou of a ground truth (gt) and detected (dt) object is 63 | # iou(gt,dt) = area(intersect(gt,dt)) / area(union(gt,dt)) 64 | # For "crowd" regions, we use a modified criteria. If a gt object is 65 | # marked as "iscrowd", we allow a dt to match any subregion of the gt. 66 | # Choosing gt' in the crowd gt that best matches the dt can be done using 67 | # gt'=intersect(dt,gt). Since by definition union(gt',dt)=dt, computing 68 | # iou(gt,dt,iscrowd) = iou(gt',dt) = area(intersect(gt,dt)) / area(dt) 69 | # For crowd gt regions we use this modified criteria above for the iou. 70 | # 71 | # To compile run "python setup.py build_ext --inplace" 72 | # Please do not contact us for help with compiling. 73 | # 74 | # Microsoft COCO Toolbox. version 2.0 75 | # Data, paper, and tutorials available at: http://mscoco.org/ 76 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 77 | # Licensed under the Simplified BSD License [see coco/license.txt] 78 | 79 | iou = _mask.iou 80 | merge = _mask.merge 81 | frPyObjects = _mask.frPyObjects 82 | 83 | 84 | def encode(bimask): 85 | if len(bimask.shape) == 3: 86 | return _mask.encode(bimask) 87 | elif len(bimask.shape) == 2: 88 | h, w = bimask.shape 89 | return _mask.encode(bimask.reshape((h, w, 1), order='F'))[0] 90 | 91 | 92 | def decode(rleObjs): 93 | if type(rleObjs) == list: 94 | return _mask.decode(rleObjs) 95 | else: 96 | return _mask.decode([rleObjs])[:, :, 0] 97 | 98 | 99 | def area(rleObjs): 100 | if type(rleObjs) == list: 101 | return _mask.area(rleObjs) 102 | else: 103 | return _mask.area([rleObjs])[0] 104 | 105 | 106 | def toBbox(rleObjs): 107 | if type(rleObjs) == list: 108 | return _mask.toBbox(rleObjs) 109 | else: 110 | return _mask.toBbox([rleObjs])[0] 111 | -------------------------------------------------------------------------------- /lvis/lvis/colormap.py: -------------------------------------------------------------------------------- 1 | """An awesome colormap for really neat visualizations. Taken from detectron.""" 2 | 3 | import numpy as np 4 | 5 | 6 | def colormap(rgb=False): 7 | color_list = np.array([ 8 | 0.000, 9 | 0.447, 10 | 0.741, 11 | 0.850, 12 | 0.325, 13 | 0.098, 14 | 0.929, 15 | 0.694, 16 | 0.125, 17 | 0.494, 18 | 0.184, 19 | 0.556, 20 | 0.466, 21 | 0.674, 22 | 0.188, 23 | 0.301, 24 | 0.745, 25 | 0.933, 26 | 0.635, 27 | 0.078, 28 | 0.184, 29 | 0.300, 30 | 0.300, 31 | 0.300, 32 | 0.600, 33 | 0.600, 34 | 0.600, 35 | 1.000, 36 | 0.000, 37 | 0.000, 38 | 1.000, 39 | 0.500, 40 | 0.000, 41 | 0.749, 42 | 0.749, 43 | 0.000, 44 | 0.000, 45 | 1.000, 46 | 0.000, 47 | 0.000, 48 | 0.000, 49 | 1.000, 50 | 0.667, 51 | 0.000, 52 | 1.000, 53 | 0.333, 54 | 0.333, 55 | 0.000, 56 | 0.333, 57 | 0.667, 58 | 0.000, 59 | 0.333, 60 | 1.000, 61 | 0.000, 62 | 0.667, 63 | 0.333, 64 | 0.000, 65 | 0.667, 66 | 0.667, 67 | 0.000, 68 | 0.667, 69 | 1.000, 70 | 0.000, 71 | 1.000, 72 | 0.333, 73 | 0.000, 74 | 1.000, 75 | 0.667, 76 | 0.000, 77 | 1.000, 78 | 1.000, 79 | 0.000, 80 | 0.000, 81 | 0.333, 82 | 0.500, 83 | 0.000, 84 | 0.667, 85 | 0.500, 86 | 0.000, 87 | 1.000, 88 | 0.500, 89 | 0.333, 90 | 0.000, 91 | 0.500, 92 | 0.333, 93 | 0.333, 94 | 0.500, 95 | 0.333, 96 | 0.667, 97 | 0.500, 98 | 0.333, 99 | 1.000, 100 | 0.500, 101 | 0.667, 102 | 0.000, 103 | 0.500, 104 | 0.667, 105 | 0.333, 106 | 0.500, 107 | 0.667, 108 | 0.667, 109 | 0.500, 110 | 0.667, 111 | 1.000, 112 | 0.500, 113 | 1.000, 114 | 0.000, 115 | 0.500, 116 | 1.000, 117 | 0.333, 118 | 0.500, 119 | 1.000, 120 | 0.667, 121 | 0.500, 122 | 1.000, 123 | 1.000, 124 | 0.500, 125 | 0.000, 126 | 0.333, 127 | 1.000, 128 | 0.000, 129 | 0.667, 130 | 1.000, 131 | 0.000, 132 | 1.000, 133 | 1.000, 134 | 0.333, 135 | 0.000, 136 | 1.000, 137 | 0.333, 138 | 0.333, 139 | 1.000, 140 | 0.333, 141 | 0.667, 142 | 1.000, 143 | 0.333, 144 | 1.000, 145 | 1.000, 146 | 0.667, 147 | 0.000, 148 | 1.000, 149 | 0.667, 150 | 0.333, 151 | 1.000, 152 | 0.667, 153 | 0.667, 154 | 1.000, 155 | 0.667, 156 | 1.000, 157 | 1.000, 158 | 1.000, 159 | 0.000, 160 | 1.000, 161 | 1.000, 162 | 0.333, 163 | 1.000, 164 | 1.000, 165 | 0.667, 166 | 1.000, 167 | 0.167, 168 | 0.000, 169 | 0.000, 170 | 0.333, 171 | 0.000, 172 | 0.000, 173 | 0.500, 174 | 0.000, 175 | 0.000, 176 | 0.667, 177 | 0.000, 178 | 0.000, 179 | 0.833, 180 | 0.000, 181 | 0.000, 182 | 1.000, 183 | 0.000, 184 | 0.000, 185 | 0.000, 186 | 0.167, 187 | 0.000, 188 | 0.000, 189 | 0.333, 190 | 0.000, 191 | 0.000, 192 | 0.500, 193 | 0.000, 194 | 0.000, 195 | 0.667, 196 | 0.000, 197 | 0.000, 198 | 0.833, 199 | 0.000, 200 | 0.000, 201 | 1.000, 202 | 0.000, 203 | 0.000, 204 | 0.000, 205 | 0.167, 206 | 0.000, 207 | 0.000, 208 | 0.333, 209 | 0.000, 210 | 0.000, 211 | 0.500, 212 | 0.000, 213 | 0.000, 214 | 0.667, 215 | 0.000, 216 | 0.000, 217 | 0.833, 218 | 0.000, 219 | 0.000, 220 | 1.000, 221 | 0.000, 222 | 0.000, 223 | 0.000, 224 | 0.143, 225 | 0.143, 226 | 0.143, 227 | 0.286, 228 | 0.286, 229 | 0.286, 230 | 0.429, 231 | 0.429, 232 | 0.429, 233 | 0.571, 234 | 0.571, 235 | 0.571, 236 | 0.714, 237 | 0.714, 238 | 0.714, 239 | 0.857, 240 | 0.857, 241 | 0.857, 242 | 1.000, 243 | 1.000, 244 | 1.000, 245 | ]).astype(np.float32) 246 | color_list = color_list.reshape((-1, 3)) * 255 247 | if not rgb: 248 | color_list = color_list[:, ::-1] 249 | return color_list 250 | -------------------------------------------------------------------------------- /aitodpycocotools/pycocoEvalDemo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline\n", 12 | "import matplotlib.pyplot as plt\n", 13 | "from pycocotools.coco import COCO\n", 14 | "from pycocotools.cocoeval import COCOeval\n", 15 | "import numpy as np\n", 16 | "import skimage.io as io\n", 17 | "import pylab\n", 18 | "pylab.rcParams['figure.figsize'] = (10.0, 8.0)" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": { 25 | "collapsed": false 26 | }, 27 | "outputs": [ 28 | { 29 | "name": "stdout", 30 | "output_type": "stream", 31 | "text": [ 32 | "Running demo for *bbox* results.\n" 33 | ] 34 | } 35 | ], 36 | "source": [ 37 | "annType = ['segm','bbox','keypoints']\n", 38 | "annType = annType[1] #specify type here\n", 39 | "prefix = 'person_keypoints' if annType=='keypoints' else 'instances'\n", 40 | "print 'Running demo for *%s* results.'%(annType)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "metadata": { 47 | "collapsed": false 48 | }, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "loading annotations into memory...\n", 55 | "Done (t=8.01s)\n", 56 | "creating index...\n", 57 | "index created!\n" 58 | ] 59 | } 60 | ], 61 | "source": [ 62 | "#initialize COCO ground truth api\n", 63 | "dataDir='../'\n", 64 | "dataType='val2014'\n", 65 | "annFile = '%s/annotations/%s_%s.json'%(dataDir,prefix,dataType)\n", 66 | "cocoGt=COCO(annFile)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 4, 72 | "metadata": { 73 | "collapsed": false 74 | }, 75 | "outputs": [ 76 | { 77 | "name": "stdout", 78 | "output_type": "stream", 79 | "text": [ 80 | "Loading and preparing results... \n", 81 | "DONE (t=0.05s)\n", 82 | "creating index...\n", 83 | "index created!\n" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "#initialize COCO detections api\n", 89 | "resFile='%s/results/%s_%s_fake%s100_results.json'\n", 90 | "resFile = resFile%(dataDir, prefix, dataType, annType)\n", 91 | "cocoDt=cocoGt.loadRes(resFile)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 5, 97 | "metadata": { 98 | "collapsed": false 99 | }, 100 | "outputs": [], 101 | "source": [ 102 | "imgIds=sorted(cocoGt.getImgIds())\n", 103 | "imgIds=imgIds[0:100]\n", 104 | "imgId = imgIds[np.random.randint(100)]" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 6, 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "outputs": [ 114 | { 115 | "name": "stdout", 116 | "output_type": "stream", 117 | "text": [ 118 | "Running per image evaluation... \n", 119 | "DONE (t=0.46s).\n", 120 | "Accumulating evaluation results... \n", 121 | "DONE (t=0.38s).\n", 122 | " Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.505\n", 123 | " Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.697\n", 124 | " Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.573\n", 125 | " Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.586\n", 126 | " Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.519\n", 127 | " Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.501\n", 128 | " Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.387\n", 129 | " Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.594\n", 130 | " Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.595\n", 131 | " Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.640\n", 132 | " Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.566\n", 133 | " Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.564\n" 134 | ] 135 | } 136 | ], 137 | "source": [ 138 | "# running evaluation\n", 139 | "cocoEval = COCOeval(cocoGt,cocoDt,annType)\n", 140 | "cocoEval.params.imgIds = imgIds\n", 141 | "cocoEval.evaluate()\n", 142 | "cocoEval.accumulate()\n", 143 | "cocoEval.summarize()" 144 | ] 145 | } 146 | ], 147 | "metadata": { 148 | "kernelspec": { 149 | "display_name": "Python 2", 150 | "language": "python", 151 | "name": "python2" 152 | }, 153 | "language_info": { 154 | "codemirror_mode": { 155 | "name": "ipython", 156 | "version": 2 157 | }, 158 | "file_extension": ".py", 159 | "mimetype": "text/x-python", 160 | "name": "python", 161 | "nbconvert_exporter": "python", 162 | "pygments_lexer": "ipython2", 163 | "version": "2.7.10" 164 | } 165 | }, 166 | "nbformat": 4, 167 | "nbformat_minor": 0 168 | } 169 | -------------------------------------------------------------------------------- /lvis/lvis/lvis.py: -------------------------------------------------------------------------------- 1 | """ 2 | API for accessing LVIS Dataset: https://lvisdataset.org. 3 | 4 | LVIS API is a Python API that assists in loading, parsing and visualizing 5 | the annotations in LVIS. In addition to this API, please download 6 | images and annotations from the LVIS website. 7 | """ 8 | 9 | import json 10 | import logging 11 | import os 12 | from collections import defaultdict 13 | from urllib.request import urlretrieve 14 | 15 | import aitodpycocotools.mask as mask_utils 16 | 17 | 18 | class LVIS: 19 | def __init__(self, annotation_path): 20 | """Class for reading and visualizing annotations. 21 | Args: 22 | annotation_path (str): location of annotation file 23 | """ 24 | self.logger = logging.getLogger(__name__) 25 | self.logger.info('Loading annotations.') 26 | 27 | self.dataset = self._load_json(annotation_path) 28 | 29 | assert (type(self.dataset) == dict 30 | ), 'Annotation file format {} not supported.'.format( 31 | type(self.dataset)) 32 | self._create_index() 33 | 34 | def _load_json(self, path): 35 | with open(path, 'r') as f: 36 | return json.load(f) 37 | 38 | def _create_index(self): 39 | self.logger.info('Creating index.') 40 | 41 | self.img_ann_map = defaultdict(list) 42 | self.cat_img_map = defaultdict(list) 43 | 44 | self.anns = {} 45 | self.cats = {} 46 | self.imgs = {} 47 | 48 | for ann in self.dataset['annotations']: 49 | self.img_ann_map[ann['image_id']].append(ann) 50 | self.anns[ann['id']] = ann 51 | 52 | for img in self.dataset['images']: 53 | self.imgs[img['id']] = img 54 | 55 | for cat in self.dataset['categories']: 56 | self.cats[cat['id']] = cat 57 | 58 | for ann in self.dataset['annotations']: 59 | self.cat_img_map[ann['category_id']].append(ann['image_id']) 60 | 61 | self.logger.info('Index created.') 62 | 63 | def get_ann_ids(self, img_ids=None, cat_ids=None, area_rng=None): 64 | """Get ann ids that satisfy given filter conditions. 65 | 66 | Args: 67 | img_ids (int array): get anns for given imgs 68 | cat_ids (int array): get anns for given cats 69 | area_rng (float array): get anns for a given area range. 70 | e.g [0, inf] 71 | 72 | Returns: 73 | ids (int array): integer array of ann ids 74 | """ 75 | anns = [] 76 | if img_ids is not None: 77 | for img_id in img_ids: 78 | anns.extend(self.img_ann_map[img_id]) 79 | else: 80 | anns = self.dataset['annotations'] 81 | 82 | # return early if no more filtering required 83 | if cat_ids is None and area_rng is None: 84 | return [_ann['id'] for _ann in anns] 85 | 86 | cat_ids = set(cat_ids) 87 | 88 | if area_rng is None: 89 | area_rng = [0, float('inf')] 90 | 91 | ann_ids = [ 92 | _ann['id'] for _ann in anns if _ann['category_id'] in cat_ids 93 | and _ann['area'] > area_rng[0] and _ann['area'] < area_rng[1] 94 | ] 95 | return ann_ids 96 | 97 | def get_cat_ids(self): 98 | """Get all category ids. 99 | 100 | Returns: 101 | ids (int array): integer array of category ids 102 | """ 103 | return list(self.cats.keys()) 104 | 105 | def get_img_ids(self): 106 | """Get all img ids. 107 | 108 | Returns: 109 | ids (int array): integer array of image ids 110 | """ 111 | return list(self.imgs.keys()) 112 | 113 | def _load_helper(self, _dict, ids): 114 | if ids is None: 115 | return list(_dict.values()) 116 | else: 117 | return [_dict[id] for id in ids] 118 | 119 | def load_anns(self, ids=None): 120 | """Load anns with the specified ids. If ids=None load all anns. 121 | 122 | Args: 123 | ids (int array): integer array of annotation ids 124 | 125 | Returns: 126 | anns (dict array) : loaded annotation objects 127 | """ 128 | return self._load_helper(self.anns, ids) 129 | 130 | def load_cats(self, ids): 131 | """Load categories with the specified ids. If ids=None load all 132 | categories. 133 | 134 | Args: 135 | ids (int array): integer array of category ids 136 | 137 | Returns: 138 | cats (dict array) : loaded category dicts 139 | """ 140 | return self._load_helper(self.cats, ids) 141 | 142 | def load_imgs(self, ids): 143 | """Load categories with the specified ids. If ids=None load all images. 144 | 145 | Args: 146 | ids (int array): integer array of image ids 147 | 148 | Returns: 149 | imgs (dict array) : loaded image dicts 150 | """ 151 | return self._load_helper(self.imgs, ids) 152 | 153 | def download(self, save_dir, img_ids=None): 154 | """Download images from mscoco.org server. 155 | Args: 156 | save_dir (str): dir to save downloaded images 157 | img_ids (int array): img ids of images to download 158 | """ 159 | imgs = self.load_imgs(img_ids) 160 | 161 | if not os.path.exists(save_dir): 162 | os.makedirs(save_dir) 163 | 164 | for img in imgs: 165 | file_name = os.path.join(save_dir, img['coco_url'].split('/')[-1]) 166 | if not os.path.exists(file_name): 167 | urlretrieve(img['coco_url'], file_name) 168 | 169 | def ann_to_rle(self, ann): 170 | """Convert annotation which can be polygons, uncompressed RLE to RLE. 171 | Args: 172 | ann (dict) : annotation object 173 | 174 | Returns: 175 | ann (rle) 176 | """ 177 | img_data = self.imgs[ann['image_id']] 178 | h, w = img_data['height'], img_data['width'] 179 | segm = ann['segmentation'] 180 | if isinstance(segm, list): 181 | # polygon -- a single object might consist of multiple parts 182 | # we merge all parts into one mask rle code 183 | rles = mask_utils.frPyObjects(segm, h, w) 184 | rle = mask_utils.merge(rles) 185 | elif isinstance(segm['counts'], list): 186 | # uncompressed RLE 187 | rle = mask_utils.frPyObjects(segm, h, w) 188 | else: 189 | # rle 190 | rle = ann['segmentation'] 191 | return rle 192 | 193 | def ann_to_mask(self, ann): 194 | """Convert annotation which can be polygons, uncompressed RLE, or RLE 195 | to binary mask. 196 | Args: 197 | ann (dict) : annotation object 198 | 199 | Returns: 200 | binary mask (numpy 2D array) 201 | """ 202 | rle = self.ann_to_rle(ann) 203 | return mask_utils.decode(rle) 204 | -------------------------------------------------------------------------------- /lvis/lvis/vis.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | import cv2 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | from matplotlib.patches import Polygon 8 | 9 | from lvis.colormap import colormap 10 | from lvis.lvis import LVIS 11 | from lvis.results import LVISResults 12 | 13 | 14 | class LVISVis: 15 | def __init__(self, lvis_gt, lvis_dt=None, img_dir=None, dpi=75): 16 | """Constructor for LVISVis. 17 | 18 | Args: 19 | lvis_gt (LVIS class instance, or str containing path of annotation 20 | file) 21 | lvis_dt (LVISResult class instance, or str containing path of 22 | result file, or list of dict) 23 | img_dir (str): path of folder containing all images. If None, the 24 | image to be displayed will be downloaded to the current working 25 | dir. 26 | dpi (int): dpi for figure size setup 27 | """ 28 | self.logger = logging.getLogger(__name__) 29 | 30 | if isinstance(lvis_gt, LVIS): 31 | self.lvis_gt = lvis_gt 32 | elif isinstance(lvis_gt, str): 33 | self.lvis_gt = LVIS(lvis_gt) 34 | else: 35 | raise TypeError('Unsupported type {} of lvis_gt.'.format(lvis_gt)) 36 | 37 | if lvis_dt is not None: 38 | if isinstance(lvis_dt, LVISResults): 39 | self.lvis_dt = lvis_dt 40 | elif isinstance(lvis_dt, (str, list)): 41 | self.lvis_dt = LVISResults(self.lvis_gt, lvis_dt) 42 | else: 43 | raise TypeError( 44 | 'Unsupported type {} of lvis_dt.'.format(lvis_dt)) 45 | else: 46 | self.lvis_dt = None 47 | self.dpi = dpi 48 | self.img_dir = img_dir if img_dir else '.' 49 | if self.img_dir == '.': 50 | self.logger.warn( 51 | 'img_dir not specified. Images will be downloaded.') 52 | 53 | def coco_segm_to_poly(self, _list): 54 | x = _list[0::2] 55 | y = _list[1::2] 56 | points = np.asarray([x, y]) 57 | return np.transpose(points) 58 | 59 | def get_synset(self, idx): 60 | synset = self.lvis_gt.load_cats(ids=[idx])[0]['synset'] 61 | text = synset.split('.') 62 | text = '{}.{}'.format(text[0], int(text[-1])) 63 | return text 64 | 65 | def setup_figure(self, img, title='', dpi=75): 66 | fig = plt.figure(frameon=False) 67 | fig.set_size_inches(img.shape[1] / dpi, img.shape[0] / dpi) 68 | ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0]) 69 | ax.set_title(title) 70 | ax.axis('off') 71 | fig.add_axes(ax) 72 | ax.imshow(img) 73 | return fig, ax 74 | 75 | def vis_bbox(self, ax, bbox, box_alpha=0.5, edgecolor='g', linestyle='--'): 76 | # bbox should be of the form x, y, w, h 77 | ax.add_patch( 78 | plt.Rectangle( 79 | (bbox[0], bbox[1]), 80 | bbox[2], 81 | bbox[3], 82 | fill=False, 83 | edgecolor=edgecolor, 84 | linewidth=2.5, 85 | alpha=box_alpha, 86 | linestyle=linestyle, 87 | )) 88 | 89 | def vis_text(self, ax, bbox, text, color='w'): 90 | ax.text( 91 | bbox[0], 92 | bbox[1] - 2, 93 | text, 94 | fontsize=15, 95 | family='serif', 96 | bbox=dict(facecolor='none', alpha=0.4, pad=0, edgecolor='none'), 97 | color=color, 98 | zorder=10, 99 | ) 100 | 101 | def vis_mask(self, ax, segm, color): 102 | # segm is numpy array of shape Nx2 103 | polygon = Polygon(segm, 104 | fill=True, 105 | facecolor=color, 106 | edgecolor=color, 107 | linewidth=3, 108 | alpha=0.5) 109 | ax.add_patch(polygon) 110 | 111 | def get_color(self, idx): 112 | color_list = colormap(rgb=True) / 255 113 | return color_list[idx % len(color_list), 0:3] 114 | 115 | def load_img(self, img_id): 116 | img = self.lvis_gt.load_imgs([img_id])[0] 117 | img_path = os.path.join(self.img_dir, img['coco_url'].split('/')[-1]) 118 | if not os.path.exists(img_path): 119 | self.lvis_gt.download(self.img_dir, img_ids=[img_id]) 120 | img = cv2.imread(img_path) 121 | b, g, r = cv2.split(img) 122 | return cv2.merge([r, g, b]) 123 | 124 | def vis_img(self, 125 | img_id, 126 | show_boxes=False, 127 | show_segms=True, 128 | show_classes=False, 129 | cat_ids_to_show=None): 130 | ann_ids = self.lvis_gt.get_ann_ids(img_ids=[img_id]) 131 | anns = self.lvis_gt.load_anns(ids=ann_ids) 132 | boxes, segms, classes = [], [], [] 133 | for ann in anns: 134 | boxes.append(ann['bbox']) 135 | segms.append(ann['segmentation']) 136 | classes.append(ann['category_id']) 137 | 138 | if len(boxes) == 0: 139 | self.logger.warn('No gt anno found for img_id: {}'.format(img_id)) 140 | return 141 | 142 | boxes = np.asarray(boxes) 143 | areas = boxes[:, 2] * boxes[:, 3] 144 | sorted_inds = np.argsort(-areas) 145 | 146 | fig, ax = self.setup_figure(self.load_img(img_id)) 147 | 148 | for idx in sorted_inds: 149 | if cat_ids_to_show is not None and classes[ 150 | idx] not in cat_ids_to_show: 151 | continue 152 | color = self.get_color(idx) 153 | if show_boxes: 154 | self.vis_bbox(ax, boxes[idx], edgecolor=color) 155 | if show_classes: 156 | text = self.get_synset(classes[idx]) 157 | self.vis_text(ax, boxes[idx], text) 158 | if show_segms: 159 | for segm in segms[idx]: 160 | self.vis_mask(ax, self.coco_segm_to_poly(segm), color) 161 | 162 | def vis_result(self, 163 | img_id, 164 | show_boxes=False, 165 | show_segms=True, 166 | show_classes=False, 167 | cat_ids_to_show=None, 168 | score_thrs=0.0, 169 | show_scores=True): 170 | assert self.lvis_dt is not None, 'lvis_dt was not specified.' 171 | anns = self.lvis_dt.get_top_results(img_id, score_thrs) 172 | boxes, segms, classes, scores = [], [], [], [] 173 | for ann in anns: 174 | boxes.append(ann['bbox']) 175 | segms.append(ann['segmentation']) 176 | classes.append(ann['category_id']) 177 | scores.append(ann['score']) 178 | 179 | if len(boxes) == 0: 180 | self.logger.warn('No gt anno found for img_id: {}'.format(img_id)) 181 | return 182 | 183 | boxes = np.asarray(boxes) 184 | areas = boxes[:, 2] * boxes[:, 3] 185 | sorted_inds = np.argsort(-areas) 186 | 187 | fig, ax = self.setup_figure(self.load_img(img_id)) 188 | 189 | for idx in sorted_inds: 190 | if cat_ids_to_show is not None and classes[ 191 | idx] not in cat_ids_to_show: 192 | continue 193 | color = self.get_color(idx) 194 | if show_boxes: 195 | self.vis_bbox(ax, boxes[idx], edgecolor=color) 196 | if show_classes: 197 | text = self.get_synset(classes[idx]) 198 | if show_scores: 199 | text = '{}: {:.2f}'.format(text, scores[idx]) 200 | self.vis_text(ax, boxes[idx], text) 201 | if show_segms: 202 | for segm in segms[idx]: 203 | self.vis_mask(ax, self.coco_segm_to_poly(segm), color) 204 | -------------------------------------------------------------------------------- /aitodpycocotools/common/maskApi.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Microsoft COCO Toolbox. version 2.0 3 | * Data, paper, and tutorials available at: http://mscoco.org/ 4 | * Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 5 | * Licensed under the Simplified BSD License [see coco/license.txt] 6 | **************************************************************************/ 7 | #include "maskApi.h" 8 | #include 9 | #include 10 | 11 | uint umin( uint a, uint b ) { return (ab) ? a : b; } 13 | 14 | void rleInit( RLE *R, siz h, siz w, siz m, uint *cnts ) { 15 | R->h=h; R->w=w; R->m=m; R->cnts=(m==0)?0:malloc(sizeof(uint)*m); 16 | siz j; if(cnts) for(j=0; jcnts[j]=cnts[j]; 17 | } 18 | 19 | void rleFree( RLE *R ) { 20 | free(R->cnts); R->cnts=0; 21 | } 22 | 23 | void rlesInit( RLE **R, siz n ) { 24 | siz i; *R = (RLE*) malloc(sizeof(RLE)*n); 25 | for(i=0; i0 ) { 61 | c=umin(ca,cb); cc+=c; ct=0; 62 | ca-=c; if(!ca && a0) { 83 | crowd=iscrowd!=NULL && iscrowd[g]; 84 | if(dt[d].h!=gt[g].h || dt[d].w!=gt[g].w) { o[g*m+d]=-1; continue; } 85 | siz ka, kb, a, b; uint c, ca, cb, ct, i, u; int va, vb; 86 | ca=dt[d].cnts[0]; ka=dt[d].m; va=vb=0; 87 | cb=gt[g].cnts[0]; kb=gt[g].m; a=b=1; i=u=0; ct=1; 88 | while( ct>0 ) { 89 | c=umin(ca,cb); if(va||vb) { u+=c; if(va&&vb) i+=c; } ct=0; 90 | ca-=c; if(!ca && athr) keep[j]=0; 105 | } 106 | } 107 | } 108 | 109 | void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o ) { 110 | double h, w, i, u, ga, da; siz g, d; int crowd; 111 | for( g=0; gthr) keep[j]=0; 129 | } 130 | } 131 | } 132 | 133 | void rleToBbox( const RLE *R, BB bb, siz n ) { 134 | siz i; for( i=0; id?1:c=dy && xs>xe) || (dxye); 174 | if(flip) { t=xs; xs=xe; xe=t; t=ys; ys=ye; ye=t; } 175 | s = dx>=dy ? (double)(ye-ys)/dx : (double)(xe-xs)/dy; 176 | if(dx>=dy) for( d=0; d<=dx; d++ ) { 177 | t=flip?dx-d:d; u[m]=t+xs; v[m]=(int)(ys+s*t+.5); m++; 178 | } else for( d=0; d<=dy; d++ ) { 179 | t=flip?dy-d:d; v[m]=t+ys; u[m]=(int)(xs+s*t+.5); m++; 180 | } 181 | } 182 | /* get points along y-boundary and downsample */ 183 | free(x); free(y); k=m; m=0; double xd, yd; 184 | x=malloc(sizeof(int)*k); y=malloc(sizeof(int)*k); 185 | for( j=1; jw-1 ) continue; 188 | yd=(double)(v[j]h) yd=h; yd=ceil(yd); 190 | x[m]=(int) xd; y[m]=(int) yd; m++; 191 | } 192 | /* compute rle encoding given y-boundary points */ 193 | k=m; a=malloc(sizeof(uint)*(k+1)); 194 | for( j=0; j0) b[m++]=a[j++]; else { 200 | j++; if(jm, p=0; long x; int more; 207 | char *s=malloc(sizeof(char)*m*6); 208 | for( i=0; icnts[i]; if(i>2) x-=(long) R->cnts[i-2]; more=1; 210 | while( more ) { 211 | char c=x & 0x1f; x >>= 5; more=(c & 0x10) ? x!=-1 : x!=0; 212 | if(more) c |= 0x20; c+=48; s[p++]=c; 213 | } 214 | } 215 | s[p]=0; return s; 216 | } 217 | 218 | void rleFrString( RLE *R, char *s, siz h, siz w ) { 219 | siz m=0, p=0, k; long x; int more; uint *cnts; 220 | while( s[m] ) m++; cnts=malloc(sizeof(uint)*m); m=0; 221 | while( s[p] ) { 222 | x=0; k=0; more=1; 223 | while( more ) { 224 | char c=s[p]-48; x |= (c & 0x1f) << 5*k; 225 | more = c & 0x20; p++; k++; 226 | if(!more && (c & 0x10)) x |= -1 << 5*k; 227 | } 228 | if(m>2) x+=(long) cnts[m-2]; cnts[m++]=(uint) x; 229 | } 230 | rleInit(R,h,w,m,cnts); free(cnts); 231 | } 232 | -------------------------------------------------------------------------------- /aitodpycocotools/common/gason.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/vivkin/gason - pulled January 10, 2016 2 | #include "gason.h" 3 | #include 4 | 5 | #define JSON_ZONE_SIZE 4096 6 | #define JSON_STACK_SIZE 32 7 | 8 | const char *jsonStrError(int err) { 9 | switch (err) { 10 | #define XX(no, str) \ 11 | case JSON_##no: \ 12 | return str; 13 | JSON_ERRNO_MAP(XX) 14 | #undef XX 15 | default: 16 | return "unknown"; 17 | } 18 | } 19 | 20 | void *JsonAllocator::allocate(size_t size) { 21 | size = (size + 7) & ~7; 22 | 23 | if (head && head->used + size <= JSON_ZONE_SIZE) { 24 | char *p = (char *)head + head->used; 25 | head->used += size; 26 | return p; 27 | } 28 | 29 | size_t allocSize = sizeof(Zone) + size; 30 | Zone *zone = (Zone *)malloc(allocSize <= JSON_ZONE_SIZE ? JSON_ZONE_SIZE : allocSize); 31 | if (zone == nullptr) 32 | return nullptr; 33 | zone->used = allocSize; 34 | if (allocSize <= JSON_ZONE_SIZE || head == nullptr) { 35 | zone->next = head; 36 | head = zone; 37 | } else { 38 | zone->next = head->next; 39 | head->next = zone; 40 | } 41 | return (char *)zone + sizeof(Zone); 42 | } 43 | 44 | void JsonAllocator::deallocate() { 45 | while (head) { 46 | Zone *next = head->next; 47 | free(head); 48 | head = next; 49 | } 50 | } 51 | 52 | static inline bool isspace(char c) { 53 | return c == ' ' || (c >= '\t' && c <= '\r'); 54 | } 55 | 56 | static inline bool isdelim(char c) { 57 | return c == ',' || c == ':' || c == ']' || c == '}' || isspace(c) || !c; 58 | } 59 | 60 | static inline bool isdigit(char c) { 61 | return c >= '0' && c <= '9'; 62 | } 63 | 64 | static inline bool isxdigit(char c) { 65 | return (c >= '0' && c <= '9') || ((c & ~' ') >= 'A' && (c & ~' ') <= 'F'); 66 | } 67 | 68 | static inline int char2int(char c) { 69 | if (c <= '9') 70 | return c - '0'; 71 | return (c & ~' ') - 'A' + 10; 72 | } 73 | 74 | static double string2double(char *s, char **endptr) { 75 | char ch = *s; 76 | if (ch == '-') 77 | ++s; 78 | 79 | double result = 0; 80 | while (isdigit(*s)) 81 | result = (result * 10) + (*s++ - '0'); 82 | 83 | if (*s == '.') { 84 | ++s; 85 | 86 | double fraction = 1; 87 | while (isdigit(*s)) { 88 | fraction *= 0.1; 89 | result += (*s++ - '0') * fraction; 90 | } 91 | } 92 | 93 | if (*s == 'e' || *s == 'E') { 94 | ++s; 95 | 96 | double base = 10; 97 | if (*s == '+') 98 | ++s; 99 | else if (*s == '-') { 100 | ++s; 101 | base = 0.1; 102 | } 103 | 104 | unsigned int exponent = 0; 105 | while (isdigit(*s)) 106 | exponent = (exponent * 10) + (*s++ - '0'); 107 | 108 | double power = 1; 109 | for (; exponent; exponent >>= 1, base *= base) 110 | if (exponent & 1) 111 | power *= base; 112 | 113 | result *= power; 114 | } 115 | 116 | *endptr = s; 117 | return ch == '-' ? -result : result; 118 | } 119 | 120 | static inline JsonNode *insertAfter(JsonNode *tail, JsonNode *node) { 121 | if (!tail) 122 | return node->next = node; 123 | node->next = tail->next; 124 | tail->next = node; 125 | return node; 126 | } 127 | 128 | static inline JsonValue listToValue(JsonTag tag, JsonNode *tail) { 129 | if (tail) { 130 | auto head = tail->next; 131 | tail->next = nullptr; 132 | return JsonValue(tag, head); 133 | } 134 | return JsonValue(tag, nullptr); 135 | } 136 | 137 | int jsonParse(char *s, char **endptr, JsonValue *value, JsonAllocator &allocator) { 138 | JsonNode *tails[JSON_STACK_SIZE]; 139 | JsonTag tags[JSON_STACK_SIZE]; 140 | char *keys[JSON_STACK_SIZE]; 141 | JsonValue o; 142 | int pos = -1; 143 | bool separator = true; 144 | JsonNode *node; 145 | *endptr = s; 146 | 147 | while (*s) { 148 | while (isspace(*s)) { 149 | ++s; 150 | if (!*s) break; 151 | } 152 | *endptr = s++; 153 | switch (**endptr) { 154 | case '-': 155 | if (!isdigit(*s) && *s != '.') { 156 | *endptr = s; 157 | return JSON_BAD_NUMBER; 158 | } 159 | case '0': 160 | case '1': 161 | case '2': 162 | case '3': 163 | case '4': 164 | case '5': 165 | case '6': 166 | case '7': 167 | case '8': 168 | case '9': 169 | o = JsonValue(string2double(*endptr, &s)); 170 | if (!isdelim(*s)) { 171 | *endptr = s; 172 | return JSON_BAD_NUMBER; 173 | } 174 | break; 175 | case '"': 176 | o = JsonValue(JSON_STRING, s); 177 | for (char *it = s; *s; ++it, ++s) { 178 | int c = *it = *s; 179 | if (c == '\\') { 180 | c = *++s; 181 | switch (c) { 182 | case '\\': 183 | case '"': 184 | case '/': 185 | *it = c; 186 | break; 187 | case 'b': 188 | *it = '\b'; 189 | break; 190 | case 'f': 191 | *it = '\f'; 192 | break; 193 | case 'n': 194 | *it = '\n'; 195 | break; 196 | case 'r': 197 | *it = '\r'; 198 | break; 199 | case 't': 200 | *it = '\t'; 201 | break; 202 | case 'u': 203 | c = 0; 204 | for (int i = 0; i < 4; ++i) { 205 | if (isxdigit(*++s)) { 206 | c = c * 16 + char2int(*s); 207 | } else { 208 | *endptr = s; 209 | return JSON_BAD_STRING; 210 | } 211 | } 212 | if (c < 0x80) { 213 | *it = c; 214 | } else if (c < 0x800) { 215 | *it++ = 0xC0 | (c >> 6); 216 | *it = 0x80 | (c & 0x3F); 217 | } else { 218 | *it++ = 0xE0 | (c >> 12); 219 | *it++ = 0x80 | ((c >> 6) & 0x3F); 220 | *it = 0x80 | (c & 0x3F); 221 | } 222 | break; 223 | default: 224 | *endptr = s; 225 | return JSON_BAD_STRING; 226 | } 227 | } else if ((unsigned int)c < ' ' || c == '\x7F') { 228 | *endptr = s; 229 | return JSON_BAD_STRING; 230 | } else if (c == '"') { 231 | *it = 0; 232 | ++s; 233 | break; 234 | } 235 | } 236 | if (!isdelim(*s)) { 237 | *endptr = s; 238 | return JSON_BAD_STRING; 239 | } 240 | break; 241 | case 't': 242 | if (!(s[0] == 'r' && s[1] == 'u' && s[2] == 'e' && isdelim(s[3]))) 243 | return JSON_BAD_IDENTIFIER; 244 | o = JsonValue(JSON_TRUE); 245 | s += 3; 246 | break; 247 | case 'f': 248 | if (!(s[0] == 'a' && s[1] == 'l' && s[2] == 's' && s[3] == 'e' && isdelim(s[4]))) 249 | return JSON_BAD_IDENTIFIER; 250 | o = JsonValue(JSON_FALSE); 251 | s += 4; 252 | break; 253 | case 'n': 254 | if (!(s[0] == 'u' && s[1] == 'l' && s[2] == 'l' && isdelim(s[3]))) 255 | return JSON_BAD_IDENTIFIER; 256 | o = JsonValue(JSON_NULL); 257 | s += 3; 258 | break; 259 | case ']': 260 | if (pos == -1) 261 | return JSON_STACK_UNDERFLOW; 262 | if (tags[pos] != JSON_ARRAY) 263 | return JSON_MISMATCH_BRACKET; 264 | o = listToValue(JSON_ARRAY, tails[pos--]); 265 | break; 266 | case '}': 267 | if (pos == -1) 268 | return JSON_STACK_UNDERFLOW; 269 | if (tags[pos] != JSON_OBJECT) 270 | return JSON_MISMATCH_BRACKET; 271 | if (keys[pos] != nullptr) 272 | return JSON_UNEXPECTED_CHARACTER; 273 | o = listToValue(JSON_OBJECT, tails[pos--]); 274 | break; 275 | case '[': 276 | if (++pos == JSON_STACK_SIZE) 277 | return JSON_STACK_OVERFLOW; 278 | tails[pos] = nullptr; 279 | tags[pos] = JSON_ARRAY; 280 | keys[pos] = nullptr; 281 | separator = true; 282 | continue; 283 | case '{': 284 | if (++pos == JSON_STACK_SIZE) 285 | return JSON_STACK_OVERFLOW; 286 | tails[pos] = nullptr; 287 | tags[pos] = JSON_OBJECT; 288 | keys[pos] = nullptr; 289 | separator = true; 290 | continue; 291 | case ':': 292 | if (separator || keys[pos] == nullptr) 293 | return JSON_UNEXPECTED_CHARACTER; 294 | separator = true; 295 | continue; 296 | case ',': 297 | if (separator || keys[pos] != nullptr) 298 | return JSON_UNEXPECTED_CHARACTER; 299 | separator = true; 300 | continue; 301 | case '\0': 302 | continue; 303 | default: 304 | return JSON_UNEXPECTED_CHARACTER; 305 | } 306 | 307 | separator = false; 308 | 309 | if (pos == -1) { 310 | *endptr = s; 311 | *value = o; 312 | return JSON_OK; 313 | } 314 | 315 | if (tags[pos] == JSON_OBJECT) { 316 | if (!keys[pos]) { 317 | if (o.getTag() != JSON_STRING) 318 | return JSON_UNQUOTED_KEY; 319 | keys[pos] = o.toString(); 320 | continue; 321 | } 322 | if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode))) == nullptr) 323 | return JSON_ALLOCATION_FAILURE; 324 | tails[pos] = insertAfter(tails[pos], node); 325 | tails[pos]->key = keys[pos]; 326 | keys[pos] = nullptr; 327 | } else { 328 | if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode) - sizeof(char *))) == nullptr) 329 | return JSON_ALLOCATION_FAILURE; 330 | tails[pos] = insertAfter(tails[pos], node); 331 | } 332 | tails[pos]->value = o; 333 | } 334 | return JSON_BREAKING_BAD; 335 | } 336 | -------------------------------------------------------------------------------- /aitodpycocotools/aitodpycocotools/_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 | -------------------------------------------------------------------------------- /aitodpycocotools/aitodpycocotools/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 12 | # both 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 22 | # structures. 23 | # decodeMask - Decode binary mask M encoded via run-length encoding. 24 | # encodeMask - Encode binary mask M using run-length encoding. 25 | # getAnnIds - Get ann ids that satisfy given filter conditions. 26 | # getCatIds - Get cat ids that satisfy given filter conditions. 27 | # getImgIds - Get img ids that satisfy given filter conditions. 28 | # loadAnns - Load anns with the specified ids. 29 | # loadCats - Load cats with the specified ids. 30 | # loadImgs - Load imgs with the specified ids. 31 | # annToMask - Convert segmentation in an annotation to binary mask. 32 | # showAnns - Display the specified annotations. 33 | # loadRes - Load algorithm results and create API for accessing them. 34 | # download - Download COCO images from mscoco.org server. 35 | # Throughout the API "ann"=annotation, "cat"=category, and "img"=image. 36 | # Help on each functions can be accessed by: "help COCO>function". 37 | 38 | # See also COCO>decodeMask, 39 | # COCO>encodeMask, COCO>getAnnIds, COCO>getCatIds, 40 | # COCO>getImgIds, COCO>loadAnns, COCO>loadCats, 41 | # COCO>loadImgs, COCO>annToMask, COCO>showAnns 42 | 43 | # Microsoft COCO Toolbox. version 2.0 44 | # Data, paper, and tutorials available at: http://mscoco.org/ 45 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2014. 46 | # Licensed under the Simplified BSD License [see bsd.txt] 47 | 48 | import copy 49 | import itertools 50 | import json 51 | import os 52 | import time 53 | from collections import defaultdict 54 | from urllib.request import urlretrieve 55 | 56 | import matplotlib.pyplot as plt 57 | import numpy as np 58 | from matplotlib.collections import PatchCollection 59 | from matplotlib.patches import Polygon 60 | 61 | from . import mask as maskUtils 62 | 63 | 64 | def _isArrayLike(obj): 65 | return hasattr(obj, '__iter__') and hasattr(obj, '__len__') 66 | 67 | 68 | class COCO: 69 | def __init__(self, annotation_file=None): 70 | """ 71 | Constructor of Microsoft COCO helper class for reading and visualizing 72 | annotations. 73 | :param annotation_file (str): location of annotation file 74 | :param image_folder (str): location to the folder that hosts images. 75 | :return: 76 | """ 77 | # load dataset 78 | self.dataset, self.anns, self.cats, self.imgs = dict(), dict(), dict( 79 | ), dict() 80 | self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list) 81 | if annotation_file is not None: 82 | print('loading annotations into memory...') 83 | tic = time.time() 84 | with open(annotation_file, 'r') as f: 85 | dataset = json.load(f) 86 | assert type( 87 | dataset 88 | ) == dict, 'annotation file format {} not supported'.format( 89 | type(dataset)) 90 | print('Done (t={:0.2f}s)'.format(time.time() - tic)) 91 | self.dataset = dataset 92 | self.createIndex() 93 | self.img_ann_map = self.imgToAnns 94 | self.cat_img_map = self.catToImgs 95 | 96 | def createIndex(self): 97 | # create index 98 | print('creating index...') 99 | anns, cats, imgs = {}, {}, {} 100 | imgToAnns, catToImgs = defaultdict(list), defaultdict(list) 101 | if 'annotations' in self.dataset: 102 | for ann in self.dataset['annotations']: 103 | imgToAnns[ann['image_id']].append(ann) 104 | anns[ann['id']] = ann 105 | 106 | if 'images' in self.dataset: 107 | for img in self.dataset['images']: 108 | imgs[img['id']] = img 109 | 110 | if 'categories' in self.dataset: 111 | for cat in self.dataset['categories']: 112 | cats[cat['id']] = cat 113 | 114 | if 'annotations' in self.dataset and 'categories' in self.dataset: 115 | for ann in self.dataset['annotations']: 116 | catToImgs[ann['category_id']].append(ann['image_id']) 117 | 118 | print('index created!') 119 | 120 | # create class members 121 | self.anns = anns 122 | self.imgToAnns = imgToAnns 123 | self.catToImgs = catToImgs 124 | self.imgs = imgs 125 | self.cats = cats 126 | 127 | def info(self): 128 | """ 129 | Print information about the annotation file. 130 | :return: 131 | """ 132 | for key, value in self.dataset['info'].items(): 133 | print('{}: {}'.format(key, value)) 134 | 135 | def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None): 136 | """ 137 | Get ann ids that satisfy given filter conditions. default skips that 138 | filter 139 | :param imgIds (int array) : get anns for given imgs 140 | catIds (int array) : get anns for given cats 141 | areaRng (float array) : get anns for given area range 142 | (e.g. [0 inf]) 143 | iscrowd (boolean) : get anns for given crowd label 144 | (False or True) 145 | :return: ids (int array) : integer array of ann ids 146 | """ 147 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 148 | catIds = catIds if _isArrayLike(catIds) else [catIds] 149 | 150 | if len(imgIds) == len(catIds) == len(areaRng) == 0: 151 | anns = self.dataset['annotations'] 152 | else: 153 | if not len(imgIds) == 0: 154 | lists = [ 155 | self.imgToAnns[imgId] for imgId in imgIds 156 | if imgId in self.imgToAnns 157 | ] 158 | anns = list(itertools.chain.from_iterable(lists)) 159 | else: 160 | anns = self.dataset['annotations'] 161 | anns = anns if len(catIds) == 0 else [ 162 | ann for ann in anns if ann['category_id'] in catIds 163 | ] 164 | anns = anns if len(areaRng) == 0 else [ 165 | ann for ann in anns 166 | if ann['area'] > areaRng[0] and ann['area'] < areaRng[1] 167 | ] 168 | if iscrowd is not None: 169 | ids = [ann['id'] for ann in anns if ann['iscrowd'] == iscrowd] 170 | else: 171 | ids = [ann['id'] for ann in anns] 172 | return ids 173 | 174 | def get_ann_ids(self, img_ids=[], cat_ids=[], area_rng=[], iscrowd=None): 175 | return self.getAnnIds(img_ids, cat_ids, area_rng, iscrowd) 176 | 177 | def getCatIds(self, catNms=[], supNms=[], catIds=[]): 178 | """ 179 | filtering parameters. default skips that filter. 180 | :param catNms (str array) : get cats for given cat names 181 | :param supNms (str array) : get cats for given supercategory names 182 | :param catIds (int array) : get cats for given cat ids 183 | :return: ids (int array) : integer array of cat ids 184 | """ 185 | catNms = catNms if _isArrayLike(catNms) else [catNms] 186 | supNms = supNms if _isArrayLike(supNms) else [supNms] 187 | catIds = catIds if _isArrayLike(catIds) else [catIds] 188 | 189 | if len(catNms) == len(supNms) == len(catIds) == 0: 190 | cats = self.dataset['categories'] 191 | else: 192 | cats = self.dataset['categories'] 193 | cats = cats if len(catNms) == 0 else [ 194 | cat for cat in cats if cat['name'] in catNms 195 | ] 196 | cats = cats if len(supNms) == 0 else [ 197 | cat for cat in cats if cat['supercategory'] in supNms 198 | ] 199 | cats = cats if len(catIds) == 0 else [ 200 | cat for cat in cats if cat['id'] in catIds 201 | ] 202 | ids = [cat['id'] for cat in cats] 203 | return ids 204 | 205 | def get_cat_ids(self, cat_names=[], sup_names=[], cat_ids=[]): 206 | return self.getCatIds(cat_names, sup_names, cat_ids) 207 | 208 | def getImgIds(self, imgIds=[], catIds=[]): 209 | ''' 210 | Get img ids that satisfy given filter conditions. 211 | :param imgIds (int array) : get imgs for given ids 212 | :param catIds (int array) : get imgs with all given cats 213 | :return: ids (int array) : integer array of img ids 214 | ''' 215 | imgIds = imgIds if _isArrayLike(imgIds) else [imgIds] 216 | catIds = catIds if _isArrayLike(catIds) else [catIds] 217 | 218 | if len(imgIds) == len(catIds) == 0: 219 | ids = self.imgs.keys() 220 | else: 221 | ids = set(imgIds) 222 | for i, catId in enumerate(catIds): 223 | if i == 0 and len(ids) == 0: 224 | ids = set(self.catToImgs[catId]) 225 | else: 226 | ids &= set(self.catToImgs[catId]) 227 | return list(ids) 228 | 229 | def get_img_ids(self, img_ids=[], cat_ids=[]): 230 | return self.getImgIds(img_ids, cat_ids) 231 | 232 | def loadAnns(self, ids=[]): 233 | """ 234 | Load anns with the specified ids. 235 | :param ids (int array) : integer ids specifying anns 236 | :return: anns (object array) : loaded ann objects 237 | """ 238 | if _isArrayLike(ids): 239 | return [self.anns[id] for id in ids] 240 | elif type(ids) == int: 241 | return [self.anns[ids]] 242 | 243 | load_anns = loadAnns 244 | 245 | def loadCats(self, ids=[]): 246 | """ 247 | Load cats with the specified ids. 248 | :param ids (int array) : integer ids specifying cats 249 | :return: cats (object array) : loaded cat objects 250 | """ 251 | if _isArrayLike(ids): 252 | return [self.cats[id] for id in ids] 253 | elif type(ids) == int: 254 | return [self.cats[ids]] 255 | 256 | load_cats = loadCats 257 | 258 | def loadImgs(self, ids=[]): 259 | """ 260 | Load anns with the specified ids. 261 | :param ids (int array) : integer ids specifying img 262 | :return: imgs (object array) : loaded img objects 263 | """ 264 | if _isArrayLike(ids): 265 | return [self.imgs[id] for id in ids] 266 | elif type(ids) == int: 267 | return [self.imgs[ids]] 268 | 269 | load_imgs = loadImgs 270 | 271 | def showAnns(self, anns, draw_bbox=False): 272 | """ 273 | Display the specified annotations. 274 | :param anns (array of object): annotations to display 275 | :return: None 276 | """ 277 | if len(anns) == 0: 278 | return 0 279 | if 'segmentation' in anns[0] or 'keypoints' in anns[0]: 280 | datasetType = 'instances' 281 | elif 'caption' in anns[0]: 282 | datasetType = 'captions' 283 | else: 284 | raise Exception('datasetType not supported') 285 | if datasetType == 'instances': 286 | ax = plt.gca() 287 | ax.set_autoscale_on(False) 288 | polygons = [] 289 | color = [] 290 | for ann in anns: 291 | c = (np.random.random((1, 3)) * 0.6 + 0.4).tolist()[0] 292 | if 'segmentation' in ann: 293 | if type(ann['segmentation']) == list: 294 | # polygon 295 | for seg in ann['segmentation']: 296 | poly = np.array(seg).reshape( 297 | (int(len(seg) / 2), 2)) 298 | polygons.append(Polygon(poly)) 299 | color.append(c) 300 | else: 301 | # mask 302 | t = self.imgs[ann['image_id']] 303 | if type(ann['segmentation']['counts']) == list: 304 | rle = maskUtils.frPyObjects([ann['segmentation']], 305 | t['height'], 306 | t['width']) 307 | else: 308 | rle = [ann['segmentation']] 309 | m = maskUtils.decode(rle) 310 | img = np.ones((m.shape[0], m.shape[1], 3)) 311 | if ann['iscrowd'] == 1: 312 | color_mask = np.array([2.0, 166.0, 101.0]) / 255 313 | if ann['iscrowd'] == 0: 314 | color_mask = np.random.random((1, 3)).tolist()[0] 315 | for i in range(3): 316 | img[:, :, i] = color_mask[i] 317 | ax.imshow(np.dstack((img, m * 0.5))) 318 | if 'keypoints' in ann and type(ann['keypoints']) == list: 319 | # turn skeleton into zero-based index 320 | sks = np.array( 321 | self.loadCats(ann['category_id'])[0]['skeleton']) - 1 322 | kp = np.array(ann['keypoints']) 323 | x = kp[0::3] 324 | y = kp[1::3] 325 | v = kp[2::3] 326 | for sk in sks: 327 | if np.all(v[sk] > 0): 328 | plt.plot(x[sk], y[sk], linewidth=3, color=c) 329 | plt.plot(x[v > 0], 330 | y[v > 0], 331 | 'o', 332 | markersize=8, 333 | markerfacecolor=c, 334 | markeredgecolor='k', 335 | markeredgewidth=2) 336 | plt.plot(x[v > 1], 337 | y[v > 1], 338 | 'o', 339 | markersize=8, 340 | markerfacecolor=c, 341 | markeredgecolor=c, 342 | markeredgewidth=2) 343 | 344 | if draw_bbox: 345 | [bbox_x, bbox_y, bbox_w, bbox_h] = ann['bbox'] 346 | poly = [[bbox_x, bbox_y], [bbox_x, bbox_y + bbox_h], 347 | [bbox_x + bbox_w, bbox_y + bbox_h], 348 | [bbox_x + bbox_w, bbox_y]] 349 | np_poly = np.array(poly).reshape((4, 2)) 350 | polygons.append(Polygon(np_poly)) 351 | color.append(c) 352 | 353 | p = PatchCollection(polygons, 354 | facecolor=color, 355 | linewidths=0, 356 | alpha=0.4) 357 | ax.add_collection(p) 358 | p = PatchCollection(polygons, 359 | facecolor='none', 360 | edgecolors=color, 361 | linewidths=2) 362 | ax.add_collection(p) 363 | elif datasetType == 'captions': 364 | for ann in anns: 365 | print(ann['caption']) 366 | 367 | def loadRes(self, resFile): 368 | """ 369 | Load result file and return a result api object. 370 | :param resFile (str) : file name of result file 371 | :return: res (obj) : result api object 372 | """ 373 | res = COCO() 374 | res.dataset['images'] = [img for img in self.dataset['images']] 375 | 376 | print('Loading and preparing results...') 377 | tic = time.time() 378 | if type(resFile) == str: 379 | with open(resFile) as f: 380 | anns = json.load(f) 381 | elif type(resFile) == np.ndarray: 382 | anns = self.loadNumpyAnnotations(resFile) 383 | else: 384 | anns = resFile 385 | assert type(anns) == list, 'results in not an array of objects' 386 | annsImgIds = [ann['image_id'] for ann in anns] 387 | assert set(annsImgIds) == (set(annsImgIds) & set(self.getImgIds())), \ 388 | 'Results do not correspond to current coco set' 389 | if 'caption' in anns[0]: 390 | imgIds = set([img['id'] for img in res.dataset['images']]) & set( 391 | [ann['image_id'] for ann in anns]) 392 | res.dataset['images'] = [ 393 | img for img in res.dataset['images'] if img['id'] in imgIds 394 | ] 395 | for id, ann in enumerate(anns): 396 | ann['id'] = id + 1 397 | elif 'bbox' in anns[0] and not anns[0]['bbox'] == []: 398 | res.dataset['categories'] = copy.deepcopy( 399 | self.dataset['categories']) 400 | for id, ann in enumerate(anns): 401 | bb = ann['bbox'] 402 | x1, x2, y1, y2 = [bb[0], bb[0] + bb[2], bb[1], bb[1] + bb[3]] 403 | if 'segmentation' not in ann: 404 | ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] 405 | ann['area'] = bb[2] * bb[3] 406 | ann['id'] = id + 1 407 | ann['iscrowd'] = 0 408 | elif 'segmentation' in anns[0]: 409 | res.dataset['categories'] = copy.deepcopy( 410 | self.dataset['categories']) 411 | for id, ann in enumerate(anns): 412 | # now only support compressed RLE format as segmentation 413 | # results 414 | ann['area'] = maskUtils.area(ann['segmentation']) 415 | if 'bbox' not in ann: 416 | ann['bbox'] = maskUtils.toBbox(ann['segmentation']) 417 | ann['id'] = id + 1 418 | ann['iscrowd'] = 0 419 | elif 'keypoints' in anns[0]: 420 | res.dataset['categories'] = copy.deepcopy( 421 | self.dataset['categories']) 422 | for id, ann in enumerate(anns): 423 | s = ann['keypoints'] 424 | x = s[0::3] 425 | y = s[1::3] 426 | x0, x1, y0, y1 = np.min(x), np.max(x), np.min(y), np.max(y) 427 | ann['area'] = (x1 - x0) * (y1 - y0) 428 | ann['id'] = id + 1 429 | ann['bbox'] = [x0, y0, x1 - x0, y1 - y0] 430 | print('DONE (t={:0.2f}s)'.format(time.time() - tic)) 431 | 432 | res.dataset['annotations'] = anns 433 | res.createIndex() 434 | return res 435 | 436 | def download(self, tarDir=None, imgIds=[]): 437 | ''' 438 | Download COCO images from mscoco.org server. 439 | :param tarDir (str): COCO results directory name 440 | imgIds (list): images to be downloaded 441 | :return: 442 | ''' 443 | if tarDir is None: 444 | print('Please specify target directory') 445 | return -1 446 | if len(imgIds) == 0: 447 | imgs = self.imgs.values() 448 | else: 449 | imgs = self.loadImgs(imgIds) 450 | N = len(imgs) 451 | if not os.path.exists(tarDir): 452 | os.makedirs(tarDir) 453 | for i, img in enumerate(imgs): 454 | tic = time.time() 455 | fname = os.path.join(tarDir, img['file_name']) 456 | if not os.path.exists(fname): 457 | urlretrieve(img['coco_url'], fname) 458 | print('downloaded {}/{} images (t={:0.1f}s)'.format( 459 | i, N, 460 | time.time() - tic)) 461 | 462 | def loadNumpyAnnotations(self, data): 463 | """ 464 | Convert result data from a numpy array [Nx7] where each row contains 465 | {imageID,x1,y1,w,h,score,class} 466 | :param data (numpy.ndarray) 467 | :return: annotations (python nested list) 468 | """ 469 | print('Converting ndarray to lists...') 470 | assert (type(data) == np.ndarray) 471 | print(data.shape) 472 | assert (data.shape[1] == 7) 473 | N = data.shape[0] 474 | ann = [] 475 | for i in range(N): 476 | if i % 1000000 == 0: 477 | print('{}/{}'.format(i, N)) 478 | ann += [{ 479 | 'image_id': int(data[i, 0]), 480 | 'bbox': [data[i, 1], data[i, 2], data[i, 3], data[i, 4]], 481 | 'score': data[i, 5], 482 | 'category_id': int(data[i, 6]), 483 | }] 484 | return ann 485 | 486 | def annToRLE(self, ann): 487 | """ 488 | Convert annotation which can be polygons, uncompressed RLE to RLE. 489 | :return: binary mask (numpy 2D array) 490 | """ 491 | t = self.imgs[ann['image_id']] 492 | h, w = t['height'], t['width'] 493 | segm = ann['segmentation'] 494 | if type(segm) == list: 495 | # polygon -- a single object might consist of multiple parts 496 | # we merge all parts into one mask rle code 497 | rles = maskUtils.frPyObjects(segm, h, w) 498 | rle = maskUtils.merge(rles) 499 | elif type(segm['counts']) == list: 500 | # uncompressed RLE 501 | rle = maskUtils.frPyObjects(segm, h, w) 502 | else: 503 | # rle 504 | rle = ann['segmentation'] 505 | return rle 506 | 507 | ann_to_rle = annToRLE 508 | 509 | def annToMask(self, ann): 510 | """ 511 | Convert annotation which can be polygons, uncompressed RLE, or RLE to 512 | binary mask. 513 | :return: binary mask (numpy 2D array) 514 | """ 515 | rle = self.annToRLE(ann) 516 | m = maskUtils.decode(rle) 517 | return m 518 | 519 | ann_to_mask = annToMask 520 | -------------------------------------------------------------------------------- /lvis/lvis/eval.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | from collections import OrderedDict, defaultdict 4 | 5 | import numpy as np 6 | 7 | import aitodpycocotools.mask as mask_utils 8 | 9 | from .lvis import LVIS 10 | from .results import LVISResults 11 | 12 | 13 | class LVISEval: 14 | def __init__(self, lvis_gt, lvis_dt, iou_type='segm'): 15 | """Constructor for LVISEval. 16 | Args: 17 | lvis_gt (LVIS class instance, or str containing path of annotation 18 | file) 19 | lvis_dt (LVISResult class instance, or str containing path of 20 | result file, or list of dict) 21 | iou_type (str): segm or bbox evaluation 22 | """ 23 | self.logger = logging.getLogger(__name__) 24 | 25 | if iou_type not in ['bbox', 'segm']: 26 | raise ValueError('iou_type: {} is not supported.'.format(iou_type)) 27 | 28 | if isinstance(lvis_gt, LVIS): 29 | self.lvis_gt = lvis_gt 30 | elif isinstance(lvis_gt, str): 31 | self.lvis_gt = LVIS(lvis_gt) 32 | else: 33 | raise TypeError('Unsupported type {} of lvis_gt.'.format(lvis_gt)) 34 | 35 | if isinstance(lvis_dt, LVISResults): 36 | self.lvis_dt = lvis_dt 37 | elif isinstance(lvis_dt, (str, list)): 38 | self.lvis_dt = LVISResults(self.lvis_gt, lvis_dt) 39 | else: 40 | raise TypeError('Unsupported type {} of lvis_dt.'.format(lvis_dt)) 41 | 42 | # per-image per-category evaluation results 43 | self.eval_imgs = defaultdict(list) 44 | self.eval = {} # accumulated evaluation results 45 | self._gts = defaultdict(list) # gt for evaluation 46 | self._dts = defaultdict(list) # dt for evaluation 47 | self.params = Params(iou_type=iou_type) # parameters 48 | self.results = OrderedDict() 49 | self.ious = {} # ious between all gts and dts 50 | 51 | self.params.img_ids = sorted(self.lvis_gt.get_img_ids()) 52 | self.params.cat_ids = sorted(self.lvis_gt.get_cat_ids()) 53 | 54 | def _to_mask(self, anns, lvis): 55 | for ann in anns: 56 | rle = lvis.ann_to_rle(ann) 57 | ann['segmentation'] = rle 58 | 59 | def _prepare(self): 60 | """Prepare self._gts and self._dts for evaluation based on params.""" 61 | 62 | cat_ids = self.params.cat_ids if self.params.cat_ids else None 63 | 64 | gts = self.lvis_gt.load_anns( 65 | self.lvis_gt.get_ann_ids(img_ids=self.params.img_ids, 66 | cat_ids=cat_ids)) 67 | dts = self.lvis_dt.load_anns( 68 | self.lvis_dt.get_ann_ids(img_ids=self.params.img_ids, 69 | cat_ids=cat_ids)) 70 | # convert ground truth to mask if iou_type == 'segm' 71 | if self.params.iou_type == 'segm': 72 | self._to_mask(gts, self.lvis_gt) 73 | self._to_mask(dts, self.lvis_dt) 74 | 75 | # set ignore flag 76 | for gt in gts: 77 | if 'ignore' not in gt: 78 | gt['ignore'] = 0 79 | 80 | for gt in gts: 81 | self._gts[gt['image_id'], gt['category_id']].append(gt) 82 | 83 | # For federated dataset evaluation we will filter out all dt for an 84 | # image which belong to categories not present in gt and not present in 85 | # the negative list for an image. In other words detector is not 86 | # penalized for categories about which we don't have gt information 87 | # about their presence or absence in an image. 88 | img_data = self.lvis_gt.load_imgs(ids=self.params.img_ids) 89 | # per image map of categories not present in image 90 | img_nl = {d['id']: d['neg_category_ids'] for d in img_data} 91 | # per image list of categories present in image 92 | img_pl = defaultdict(set) 93 | for ann in gts: 94 | img_pl[ann['image_id']].add(ann['category_id']) 95 | # per image map of categoires which have missing gt. For these 96 | # categories we don't penalize the detector for flase positives. 97 | self.img_nel = { 98 | d['id']: d['not_exhaustive_category_ids'] 99 | for d in img_data 100 | } 101 | 102 | for dt in dts: 103 | img_id, cat_id = dt['image_id'], dt['category_id'] 104 | if cat_id not in img_nl[img_id] and cat_id not in img_pl[img_id]: 105 | continue 106 | self._dts[img_id, cat_id].append(dt) 107 | 108 | self.freq_groups = self._prepare_freq_group() 109 | 110 | def _prepare_freq_group(self): 111 | freq_groups = [[] for _ in self.params.img_count_lbl] 112 | cat_data = self.lvis_gt.load_cats(self.params.cat_ids) 113 | for idx, _cat_data in enumerate(cat_data): 114 | frequency = _cat_data['frequency'] 115 | freq_groups[self.params.img_count_lbl.index(frequency)].append(idx) 116 | return freq_groups 117 | 118 | def evaluate(self): 119 | """ 120 | Run per image evaluation on given images and store results 121 | (a list of dict) in self.eval_imgs. 122 | """ 123 | self.logger.info('Running per image evaluation.') 124 | self.logger.info('Evaluate annotation type *{}*'.format( 125 | self.params.iou_type)) 126 | 127 | self.params.img_ids = list(np.unique(self.params.img_ids)) 128 | 129 | if self.params.use_cats: 130 | cat_ids = self.params.cat_ids 131 | else: 132 | cat_ids = [-1] 133 | 134 | self._prepare() 135 | 136 | self.ious = {(img_id, cat_id): self.compute_iou(img_id, cat_id) 137 | for img_id in self.params.img_ids for cat_id in cat_ids} 138 | 139 | # loop through images, area range, max detection number 140 | self.eval_imgs = [ 141 | self.evaluate_img(img_id, cat_id, area_rng) for cat_id in cat_ids 142 | for area_rng in self.params.area_rng 143 | for img_id in self.params.img_ids 144 | ] 145 | 146 | def _get_gt_dt(self, img_id, cat_id): 147 | """Create gt, dt which are list of anns/dets. If use_cats is true 148 | only anns/dets corresponding to tuple (img_id, cat_id) will be 149 | used. Else, all anns/dets in image are used and cat_id is not used. 150 | """ 151 | if self.params.use_cats: 152 | gt = self._gts[img_id, cat_id] 153 | dt = self._dts[img_id, cat_id] 154 | else: 155 | gt = [ 156 | _ann for _cat_id in self.params.cat_ids 157 | for _ann in self._gts[img_id, cat_id] 158 | ] 159 | dt = [ 160 | _ann for _cat_id in self.params.cat_ids 161 | for _ann in self._dts[img_id, cat_id] 162 | ] 163 | return gt, dt 164 | 165 | def compute_iou(self, img_id, cat_id): 166 | gt, dt = self._get_gt_dt(img_id, cat_id) 167 | 168 | if len(gt) == 0 and len(dt) == 0: 169 | return [] 170 | 171 | # Sort detections in decreasing order of score. 172 | idx = np.argsort([-d['score'] for d in dt], kind='mergesort') 173 | dt = [dt[i] for i in idx] 174 | 175 | iscrowd = [int(False)] * len(gt) 176 | 177 | if self.params.iou_type == 'segm': 178 | ann_type = 'segmentation' 179 | elif self.params.iou_type == 'bbox': 180 | ann_type = 'bbox' 181 | else: 182 | raise ValueError('Unknown iou_type for iou computation.') 183 | gt = [g[ann_type] for g in gt] 184 | dt = [d[ann_type] for d in dt] 185 | 186 | # compute iou between each dt and gt region 187 | # will return array of shape len(dt), len(gt) 188 | ious = mask_utils.iou(dt, gt, iscrowd) 189 | return ious 190 | 191 | def evaluate_img(self, img_id, cat_id, area_rng): 192 | """Perform evaluation for single category and image.""" 193 | gt, dt = self._get_gt_dt(img_id, cat_id) 194 | 195 | if len(gt) == 0 and len(dt) == 0: 196 | return None 197 | 198 | # Add another filed _ignore to only consider anns based on area range. 199 | for g in gt: 200 | if g['ignore'] or (g['area'] < area_rng[0] 201 | or g['area'] > area_rng[1]): 202 | g['_ignore'] = 1 203 | else: 204 | g['_ignore'] = 0 205 | 206 | # Sort gt ignore last 207 | gt_idx = np.argsort([g['_ignore'] for g in gt], kind='mergesort') 208 | gt = [gt[i] for i in gt_idx] 209 | 210 | # Sort dt highest score first 211 | dt_idx = np.argsort([-d['score'] for d in dt], kind='mergesort') 212 | dt = [dt[i] for i in dt_idx] 213 | 214 | # load computed ious 215 | ious = (self.ious[img_id, cat_id][:, gt_idx] 216 | if len(self.ious[img_id, cat_id]) > 0 else self.ious[img_id, 217 | cat_id]) 218 | 219 | num_thrs = len(self.params.iou_thrs) 220 | num_gt = len(gt) 221 | num_dt = len(dt) 222 | 223 | # Array to store the "id" of the matched dt/gt 224 | gt_m = np.zeros((num_thrs, num_gt)) 225 | dt_m = np.zeros((num_thrs, num_dt)) 226 | 227 | gt_ig = np.array([g['_ignore'] for g in gt]) 228 | dt_ig = np.zeros((num_thrs, num_dt)) 229 | 230 | for iou_thr_idx, iou_thr in enumerate(self.params.iou_thrs): 231 | if len(ious) == 0: 232 | break 233 | 234 | for dt_idx, _dt in enumerate(dt): 235 | iou = min([iou_thr, 1 - 1e-10]) 236 | # information about best match so far (m=-1 -> unmatched) 237 | # store the gt_idx which matched for _dt 238 | m = -1 239 | for gt_idx, _ in enumerate(gt): 240 | # if this gt already matched continue 241 | if gt_m[iou_thr_idx, gt_idx] > 0: 242 | continue 243 | # if _dt matched to reg gt, and on ignore gt, stop 244 | if m > -1 and gt_ig[m] == 0 and gt_ig[gt_idx] == 1: 245 | break 246 | # continue to next gt unless better match made 247 | if ious[dt_idx, gt_idx] < iou: 248 | continue 249 | # if match successful and best so far, store appropriately 250 | iou = ious[dt_idx, gt_idx] 251 | m = gt_idx 252 | 253 | # No match found for _dt, go to next _dt 254 | if m == -1: 255 | continue 256 | 257 | # if gt to ignore for some reason update dt_ig. 258 | # Should not be used in evaluation. 259 | dt_ig[iou_thr_idx, dt_idx] = gt_ig[m] 260 | # _dt match found, update gt_m, and dt_m with "id" 261 | dt_m[iou_thr_idx, dt_idx] = gt[m]['id'] 262 | gt_m[iou_thr_idx, m] = _dt['id'] 263 | 264 | # For LVIS we will ignore any unmatched detection if that category was 265 | # not exhaustively annotated in gt. 266 | dt_ig_mask = [ 267 | d['area'] < area_rng[0] or d['area'] > area_rng[1] 268 | or d['category_id'] in self.img_nel[d['image_id']] for d in dt 269 | ] 270 | dt_ig_mask = np.array(dt_ig_mask).reshape((1, num_dt)) # 1 X num_dt 271 | dt_ig_mask = np.repeat(dt_ig_mask, num_thrs, 0) # num_thrs X num_dt 272 | # Based on dt_ig_mask ignore any unmatched detection by updating dt_ig 273 | dt_ig = np.logical_or(dt_ig, np.logical_and(dt_m == 0, dt_ig_mask)) 274 | # store results for given image and category 275 | return { 276 | 'image_id': img_id, 277 | 'category_id': cat_id, 278 | 'area_rng': area_rng, 279 | 'dt_ids': [d['id'] for d in dt], 280 | 'gt_ids': [g['id'] for g in gt], 281 | 'dt_matches': dt_m, 282 | 'gt_matches': gt_m, 283 | 'dt_scores': [d['score'] for d in dt], 284 | 'gt_ignore': gt_ig, 285 | 'dt_ignore': dt_ig, 286 | } 287 | 288 | def accumulate(self): 289 | """Accumulate per image evaluation results and store the result in 290 | self.eval. 291 | """ 292 | self.logger.info('Accumulating evaluation results.') 293 | 294 | if not self.eval_imgs: 295 | self.logger.warn('Please run evaluate first.') 296 | 297 | if self.params.use_cats: 298 | cat_ids = self.params.cat_ids 299 | else: 300 | cat_ids = [-1] 301 | 302 | num_thrs = len(self.params.iou_thrs) 303 | num_recalls = len(self.params.rec_thrs) 304 | num_cats = len(cat_ids) 305 | num_area_rngs = len(self.params.area_rng) 306 | num_imgs = len(self.params.img_ids) 307 | 308 | # -1 for absent categories 309 | precision = -np.ones((num_thrs, num_recalls, num_cats, num_area_rngs)) 310 | recall = -np.ones((num_thrs, num_cats, num_area_rngs)) 311 | 312 | # Initialize dt_pointers 313 | dt_pointers = {} 314 | for cat_idx in range(num_cats): 315 | dt_pointers[cat_idx] = {} 316 | for area_idx in range(num_area_rngs): 317 | dt_pointers[cat_idx][area_idx] = {} 318 | 319 | # Per category evaluation 320 | for cat_idx in range(num_cats): 321 | Nk = cat_idx * num_area_rngs * num_imgs 322 | for area_idx in range(num_area_rngs): 323 | Na = area_idx * num_imgs 324 | E = [ 325 | self.eval_imgs[Nk + Na + img_idx] 326 | for img_idx in range(num_imgs) 327 | ] 328 | # Remove elements which are None 329 | E = [e for e in E if e is not None] 330 | if len(E) == 0: 331 | continue 332 | 333 | # Append all scores: shape (N,) 334 | dt_scores = np.concatenate([e['dt_scores'] for e in E], axis=0) 335 | dt_ids = np.concatenate([e['dt_ids'] for e in E], axis=0) 336 | 337 | dt_idx = np.argsort(-dt_scores, kind='mergesort') 338 | dt_scores = dt_scores[dt_idx] 339 | dt_ids = dt_ids[dt_idx] 340 | 341 | dt_m = np.concatenate([e['dt_matches'] for e in E], 342 | axis=1)[:, dt_idx] 343 | dt_ig = np.concatenate([e['dt_ignore'] for e in E], 344 | axis=1)[:, dt_idx] 345 | 346 | gt_ig = np.concatenate([e['gt_ignore'] for e in E]) 347 | # num gt anns to consider 348 | num_gt = np.count_nonzero(gt_ig == 0) 349 | 350 | if num_gt == 0: 351 | continue 352 | 353 | tps = np.logical_and(dt_m, np.logical_not(dt_ig)) 354 | fps = np.logical_and(np.logical_not(dt_m), 355 | np.logical_not(dt_ig)) 356 | 357 | tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float) 358 | fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float) 359 | 360 | dt_pointers[cat_idx][area_idx] = { 361 | 'dt_ids': dt_ids, 362 | 'tps': tps, 363 | 'fps': fps, 364 | } 365 | 366 | for iou_thr_idx, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): 367 | tp = np.array(tp) 368 | fp = np.array(fp) 369 | num_tp = len(tp) 370 | rc = tp / num_gt 371 | if num_tp: 372 | recall[iou_thr_idx, cat_idx, area_idx] = rc[-1] 373 | else: 374 | recall[iou_thr_idx, cat_idx, area_idx] = 0 375 | 376 | # np.spacing(1) ~= eps 377 | pr = tp / (fp + tp + np.spacing(1)) 378 | pr = pr.tolist() 379 | 380 | # Replace each precision value with the maximum precision 381 | # value to the right of that recall level. This ensures 382 | # that the calculated AP value will be less suspectable 383 | # to small variations in the ranking. 384 | for i in range(num_tp - 1, 0, -1): 385 | if pr[i] > pr[i - 1]: 386 | pr[i - 1] = pr[i] 387 | 388 | rec_thrs_insert_idx = np.searchsorted(rc, 389 | self.params.rec_thrs, 390 | side='left') 391 | 392 | pr_at_recall = [0.0] * num_recalls 393 | 394 | try: 395 | for _idx, pr_idx in enumerate(rec_thrs_insert_idx): 396 | pr_at_recall[_idx] = pr[pr_idx] 397 | except: # noqa: E722 398 | pass 399 | precision[iou_thr_idx, :, cat_idx, 400 | area_idx] = np.array(pr_at_recall) 401 | 402 | self.eval = { 403 | 'params': self.params, 404 | 'counts': [num_thrs, num_recalls, num_cats, num_area_rngs], 405 | 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 406 | 'precision': precision, 407 | 'recall': recall, 408 | 'dt_pointers': dt_pointers, 409 | } 410 | 411 | def _summarize(self, 412 | summary_type, 413 | iou_thr=None, 414 | area_rng='all', 415 | freq_group_idx=None): 416 | aidx = [ 417 | idx for idx, _area_rng in enumerate(self.params.area_rng_lbl) 418 | if _area_rng == area_rng 419 | ] 420 | 421 | if summary_type == 'ap': 422 | s = self.eval['precision'] 423 | if iou_thr is not None: 424 | tidx = np.where(iou_thr == self.params.iou_thrs)[0] 425 | s = s[tidx] 426 | if freq_group_idx is not None: 427 | s = s[:, :, self.freq_groups[freq_group_idx], aidx] 428 | else: 429 | s = s[:, :, :, aidx] 430 | else: 431 | s = self.eval['recall'] 432 | if iou_thr is not None: 433 | tidx = np.where(iou_thr == self.params.iou_thrs)[0] 434 | s = s[tidx] 435 | s = s[:, :, aidx] 436 | 437 | if len(s[s > -1]) == 0: 438 | mean_s = -1 439 | else: 440 | mean_s = np.mean(s[s > -1]) 441 | return mean_s 442 | 443 | def summarize(self): 444 | """Compute and display summary metrics for evaluation results.""" 445 | if not self.eval: 446 | raise RuntimeError('Please run accumulate() first.') 447 | 448 | max_dets = self.params.max_dets 449 | 450 | self.results['AP'] = self._summarize('ap') 451 | self.results['AP50'] = self._summarize('ap', iou_thr=0.50) 452 | self.results['AP75'] = self._summarize('ap', iou_thr=0.75) 453 | self.results['APs'] = self._summarize('ap', area_rng='small') 454 | self.results['APm'] = self._summarize('ap', area_rng='medium') 455 | self.results['APl'] = self._summarize('ap', area_rng='large') 456 | self.results['APr'] = self._summarize('ap', freq_group_idx=0) 457 | self.results['APc'] = self._summarize('ap', freq_group_idx=1) 458 | self.results['APf'] = self._summarize('ap', freq_group_idx=2) 459 | 460 | key = 'AR@{}'.format(max_dets) 461 | self.results[key] = self._summarize('ar') 462 | 463 | for area_rng in ['small', 'medium', 'large']: 464 | key = 'AR{}@{}'.format(area_rng[0], max_dets) 465 | self.results[key] = self._summarize('ar', area_rng=area_rng) 466 | 467 | def run(self): 468 | """Wrapper function which calculates the results.""" 469 | self.evaluate() 470 | self.accumulate() 471 | self.summarize() 472 | 473 | def print_results(self): 474 | template = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} catIds={:>3s}] = {:0.3f}' # noqa: E501 475 | 476 | for key, value in self.results.items(): 477 | max_dets = self.params.max_dets 478 | if 'AP' in key: 479 | title = 'Average Precision' 480 | _type = '(AP)' 481 | else: 482 | title = 'Average Recall' 483 | _type = '(AR)' 484 | 485 | if len(key) > 2 and key[2].isdigit(): 486 | iou_thr = (float(key[2:]) / 100) 487 | iou = '{:0.2f}'.format(iou_thr) 488 | else: 489 | iou = '{:0.2f}:{:0.2f}'.format(self.params.iou_thrs[0], 490 | self.params.iou_thrs[-1]) 491 | 492 | if len(key) > 2 and key[2] in ['r', 'c', 'f']: 493 | cat_group_name = key[2] 494 | else: 495 | cat_group_name = 'all' 496 | 497 | if len(key) > 2 and key[2] in ['s', 'm', 'l']: 498 | area_rng = key[2] 499 | else: 500 | area_rng = 'all' 501 | 502 | print( 503 | template.format(title, _type, iou, area_rng, max_dets, 504 | cat_group_name, value)) 505 | 506 | def get_results(self): 507 | if not self.results: 508 | self.logger.warn('results is empty. Call run().') 509 | return self.results 510 | 511 | 512 | class Params: 513 | def __init__(self, iou_type): 514 | """Params for LVIS evaluation API.""" 515 | self.img_ids = [] 516 | self.cat_ids = [] 517 | # np.arange causes trouble. the data point on arange is slightly 518 | # larger than the true value 519 | self.iou_thrs = np.linspace(0.5, 520 | 0.95, 521 | int(np.round((0.95 - 0.5) / 0.05)) + 1, 522 | endpoint=True) 523 | self.rec_thrs = np.linspace(0.0, 524 | 1.00, 525 | int(np.round((1.00 - 0.0) / 0.01)) + 1, 526 | endpoint=True) 527 | self.max_dets = 300 528 | self.area_rng = [ 529 | [0**2, 1e5**2], 530 | [0**2, 32**2], 531 | [32**2, 96**2], 532 | [96**2, 1e5**2], 533 | ] 534 | self.area_rng_lbl = ['all', 'small', 'medium', 'large'] 535 | self.use_cats = 1 536 | # We bin categories in three bins based how many images of the training 537 | # set the category is present in. 538 | # r: Rare : < 10 539 | # c: Common : >= 10 and < 100 540 | # f: Frequent: >= 100 541 | self.img_count_lbl = ['r', 'c', 'f'] 542 | self.iou_type = iou_type 543 | -------------------------------------------------------------------------------- /results/person_keypoints_val2014_fakekeypoints100_results.json: -------------------------------------------------------------------------------- 1 | [{"image_id":136,"category_id":1,"keypoints":[36,181,2,20.25,191,0,35,166,2,20.25,191,0,8,171,2,20.25,191,0,2,246,2,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0,20.25,191,0],"score":0.897},{"image_id":136,"category_id":1,"keypoints":[32.5,221,0,32.5,221,0,50,103,2,32.5,221,0,42,102,2,32.5,221,0,20,176,1,32.5,221,0,12,265,1,32.5,221,0,41,340,1,32.5,221,0,30,340,1,32.5,221,0,32.5,221,0,32.5,221,0,32.5,221,0],"score":0.664},{"image_id":139,"category_id":1,"keypoints":[429,170,1,431,169,2,442.4,216.266666667,0,436,168,2,442.4,216.266666667,0,443,177,2,448,177,2,439,200,2,432,206,2,432,220,2,422,215,2,447,226,2,454,223,2,449,260,2,456,257,2,457,290,2,461,286,2],"score":0.771},{"image_id":192,"category_id":1,"keypoints":[427.583333333,353.416666667,0,427.583333333,353.416666667,0,427.583333333,353.416666667,0,424,281,2,447,284,2,406,301,2,464,312,2,370,333,2,481,350,2,393,351,2,462,372,2,408,382,2,441,387,2,400,440,2,435,448,2,427.583333333,353.416666667,0,427.583333333,353.416666667,0],"score":0.376},{"image_id":192,"category_id":1,"keypoints":[474.363636364,340.181818182,0,474.363636364,340.181818182,0,474.363636364,340.181818182,0,463,242,2,474.363636364,340.181818182,0,455,274,2,501,270,2,440,305,1,504,290,2,474.363636364,340.181818182,0,474.363636364,340.181818182,0,458,336,1,487,335,2,462,399,2,488,390,2,464,451,2,496,450,2],"score":0.879},{"image_id":192,"category_id":1,"keypoints":[22,351.25,0,22,351.25,0,22,351.25,0,22,351.25,0,13,297,2,22,351.25,0,17,331,2,22,351.25,0,29,378,2,22,351.25,0,29,399,2,22,351.25,0,22,351.25,0,22,351.25,0,22,351.25,0,22,351.25,0,22,351.25,0],"score":0.195},{"image_id":241,"category_id":1,"keypoints":[196,71,2,209,59,2,192,63,2,249,63,2,233.1875,279.6875,0,284,140,2,206,144,2,310,244,2,187,237,2,283,342,2,165,303,2,257,342,2,201,332,2,262,494,2,214,465,2,281,600,2,235,576,2],"score":0.274},{"image_id":241,"category_id":1,"keypoints":[45,296,2,56,288,2,40,286,2,76,295,2,31,289,2,87,331,2,15,344,2,106,386,2,28,413,2,106,431,2,77,440,2,80,415,2,30,429,2,143,436,2,61,462,2,174,519,2,84,566,2],"score":0.862},{"image_id":241,"category_id":1,"keypoints":[367,298,2,373,293,2,361,293,2,387,291,2,357.692307692,357.692307692,0,396,332,2,342,318,2,396,381,2,357.692307692,357.692307692,0,381,406,2,317,345,1,364,396,2,330,387,2,324,410,2,357.692307692,357.692307692,0,312,500,1,357.692307692,357.692307692,0],"score":0.868},{"image_id":241,"category_id":1,"keypoints":[14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,14,496,0,16,452,2,14,496,0,12,540,2,14,496,0],"score":0.417},{"image_id":241,"category_id":1,"keypoints":[435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,460,349,2,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,435.666666667,402.333333333,0,389,413,2,435.666666667,402.333333333,0,458,445,2],"score":0.69},{"image_id":257,"category_id":1,"keypoints":[160,360,2,162,356,2,172.5,394.6,0,170,355,2,172.5,394.6,0,173,369,2,191,367,1,162,400,2,172.5,394.6,0,150,421,2,172.5,394.6,0,181,425,2,199,425,2,177,468,2,172.5,394.6,0,172.5,394.6,0,172.5,394.6,0],"score":0.862},{"image_id":257,"category_id":1,"keypoints":[249.153846154,365.538461538,0,249.153846154,365.538461538,0,249.153846154,365.538461538,0,251,332,2,249.153846154,365.538461538,0,242,340,2,257,344,2,239,352,2,260,357,2,241,360,2,261,371,1,244,366,2,254,367,2,243,383,2,254,382,2,240,399,2,253,399,2],"score":0.05},{"image_id":257,"category_id":1,"keypoints":[298,376.545454545,0,298,376.545454545,0,298,376.545454545,0,298,376.545454545,0,298,376.545454545,0,289,353,2,303,351,2,286,365,2,310,360,2,284,373,2,298,376.545454545,0,293,373,2,303,371,2,296,391,2,306,390,2,298,408,2,310,407,2],"score":0.147},{"image_id":257,"category_id":1,"keypoints":[324,340,2,327,339,2,325,338,2,332,340,2,327.142857143,364.285714286,0,333,349,2,327,348,2,333,362,2,327.142857143,364.285714286,0,324,357,2,327.142857143,364.285714286,0,330,372,2,322,371,2,328,389,2,322,388,2,329,404,2,324,403,2],"score":0.195},{"image_id":294,"category_id":1,"keypoints":[154,133,2,164,119,2,137,119,2,185,129,2,113,132,2,179,224,2,119,196,2,231,351,2,243,280,2,329,337,2,285,213,2,194.454545455,203,0,194.454545455,203,0,194.454545455,203,0,194.454545455,203,0,194.454545455,203,0,194.454545455,203,0],"score":0.881},{"image_id":328,"category_id":1,"keypoints":[176,139,2,187,124,2,168,123,2,201,122,2,154,117,2,233,134,2,137,121,2,218,209,2,124,198,2,174,233,2,137,212,2,192,232,2,147,228,2,207,248,2,112,233,2,188,376,2,81,355,2],"score":0.175},{"image_id":328,"category_id":1,"keypoints":[306,163,2,312,154,2,296,153,2,337,139,2,293.875,239.875,0,351,162,2,281,153,2,380,253,2,243,224,2,337,258,2,254,247,2,325,269,2,273,264,2,303,290,2,240,275,2,232,419,2,232,415,2],"score":0.164},{"image_id":328,"category_id":1,"keypoints":[371,145,2,387,134,2,371,131,2,418,147,2,400.6875,248.5,0,453,183,2,344,178,2,478,257,2,346,258,2,434,289,2,382,195,2,440,298,2,373,289,2,495,314,2,336,289,2,472,449,2,311,420,2],"score":0.806},{"image_id":338,"category_id":1,"keypoints":[377,177,2,378,171,2,374,170,2,365.416666667,210.333333333,0,356,163,2,368,191,2,345,190,2,365,226,2,340,236,2,390,221,2,373,231,2,373,271,1,346,277,2,365.416666667,210.333333333,0,365.416666667,210.333333333,0,365.416666667,210.333333333,0,365.416666667,210.333333333,0],"score":0.474},{"image_id":338,"category_id":1,"keypoints":[496,199,2,477.153846154,239.615384615,0,493,196,2,477.153846154,239.615384615,0,482,194,2,489,201,2,473,201,2,477.153846154,239.615384615,0,476,226,2,477.153846154,239.615384615,0,493,233,2,471,238,2,460,237,2,470,275,2,464,280,2,471,313,2,465,322,2],"score":0.375},{"image_id":395,"category_id":1,"keypoints":[164,291,2,208,264,2,167,258,2,300,282,2,230,339.714285714,0,387,429,2,114,425,2,230,339.714285714,0,230,339.714285714,0,270,429,2,230,339.714285714,0,230,339.714285714,0,230,339.714285714,0,230,339.714285714,0,230,339.714285714,0,230,339.714285714,0,230,339.714285714,0],"score":0.86},{"image_id":395,"category_id":1,"keypoints":[18,230,2,28,225,2,35.6666666667,326.266666667,0,47,232,2,35.6666666667,326.266666667,0,63,264,2,6,265,1,74,304,1,0,304,2,70,337,2,0,328,1,55,347,2,17,348,2,55,395,2,20,404,2,52,455,2,34,456,2],"score":0.201},{"image_id":395,"category_id":1,"keypoints":[595,238,2,582.846153846,377.769230769,0,583,233,2,582.846153846,377.769230769,0,564,239,2,611,288,1,538,295,2,582.846153846,377.769230769,0,540,354,2,582.846153846,377.769230769,0,588,335,2,612,409,2,566,414,2,627,484,2,566,494,2,623,566,2,564,562,2],"score":0.084},{"image_id":395,"category_id":1,"keypoints":[115,248,2,117,244,2,112,244,2,124,246,2,109,246,2,130,262,2,106,262,2,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0,116.142857143,250.285714286,0],"score":0.145},{"image_id":395,"category_id":1,"keypoints":[196,261,2,201,258,2,192,257,2,207,259,1,185,259,2,212,281,2,173,274,2,222,315,2,161,312,2,206,339,2,178,337,2,204,349,1,176,348,2,193.307692308,296.076923077,0,193.307692308,296.076923077,0,193.307692308,296.076923077,0,193.307692308,296.076923077,0],"score":0.178},{"image_id":395,"category_id":1,"keypoints":[214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,242,329,1,214.333333333,379,0,176,386,2,214.333333333,379,0,225,422,1,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0,214.333333333,379,0],"score":0.053},{"image_id":395,"category_id":1,"keypoints":[77,229,2,81,225,2,75,224,2,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0,89,246,2,64,247,2,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0,89,289,2,75,291,2,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0,78.5714285714,250.142857143,0],"score":0.155},{"image_id":395,"category_id":1,"keypoints":[477,286,2,485,281,1,470,281,2,503,287,1,464,285,2,474.833333333,290.5,0,450,323,2,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0,474.833333333,290.5,0],"score":0.863},{"image_id":397,"category_id":1,"keypoints":[155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,238,86,1,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,177,197,1,50,222,1,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0,155,168.333333333,0],"score":0.3},{"image_id":415,"category_id":1,"keypoints":[127,283,2,73.1818181818,353.727272727,0,121,276,2,73.1818181818,353.727272727,0,105,280,2,98,317,2,79,298,2,73.1818181818,353.727272727,0,61,353,2,73.1818181818,353.727272727,0,70,387,2,36,378,2,16,354,2,44,445,2,73.1818181818,353.727272727,0,48,520,2,73.1818181818,353.727272727,0],"score":0.125},{"image_id":428,"category_id":1,"keypoints":[360,132,2,395,117,2,343,115,2,458,156,2,392.2,215.9,0,472,231,2,324,240,2,531,330,1,286,349,2,507,186,2,246,303,2,392.2,215.9,0,392.2,215.9,0,392.2,215.9,0,392.2,215.9,0,392.2,215.9,0,392.2,215.9,0],"score":0.134},{"image_id":459,"category_id":1,"keypoints":[162,190,2,194,170,2,143,166,2,236,183,2,120,171,2,302,310,2,49,312,2,355,492,2,15,476,2,452,375,2,18,627,2,186,315.636363636,0,186,315.636363636,0,186,315.636363636,0,186,315.636363636,0,186,315.636363636,0,186,315.636363636,0],"score":0.631},{"image_id":474,"category_id":1,"keypoints":[118,129,2,114,115,2,149.466666667,245.266666667,0,134,114,2,149.466666667,245.266666667,0,175,146,2,213,141,2,102,240,2,172,240,2,57,263,2,101,267,2,230,268,2,272,274,2,119,329,2,149,304,2,114,432,2,172,417,2],"score":0.04},{"image_id":488,"category_id":1,"keypoints":[263,231,2,244.266666667,268.933333333,0,261,229,2,244.266666667,268.933333333,0,253,231,2,258,239,2,235,239,2,264,259,2,219,262,2,269,267,2,240,256,2,241,278,2,226,279,2,266,297,2,216,310,2,264,328,2,189,329,2],"score":0.048},{"image_id":488,"category_id":1,"keypoints":[146.083333333,301.75,0,146.083333333,301.75,0,146.083333333,301.75,0,146.083333333,301.75,0,158,261,2,153,275,2,139,275,2,174,282,2,126,299,2,201,285,2,117,329,2,146.083333333,301.75,0,111,311,2,154,307,2,164,310,2,124,341,2,132,346,2],"score":0.679},{"image_id":488,"category_id":1,"keypoints":[88.2727272727,278.636363636,0,88.2727272727,278.636363636,0,88.2727272727,278.636363636,0,88.2727272727,278.636363636,0,111,227,2,97,232,2,106,232,2,88.2727272727,278.636363636,0,102,257,2,88.2727272727,278.636363636,0,99,282,2,60,270,2,70,271,2,65,302,2,104,303,2,50,343,2,107,346,2],"score":0.401},{"image_id":536,"category_id":1,"keypoints":[342,164,2,348,157,2,335,157,2,356,159,2,328,159,2,366,184,2,326,186,2,399,224,2,286,224,2,369,233,2,330,227,2,373,264,2,337,265,2,362,314,2,373,255,2,353.75,217.875,0,430,314,2],"score":0.192},{"image_id":536,"category_id":1,"keypoints":[220,111,2,226,105,2,213,107,2,216.75,187.375,0,201,114,2,237,142,2,183,142,2,239,187,2,193,192,2,241,188,2,204,150,2,231,212,2,187,212,2,239,236,2,221,240,2,204,327,2,229,333,2],"score":0.611},{"image_id":536,"category_id":1,"keypoints":[123,114,2,130,107,2,115,106,2,136,112,2,101,111,2,144,149,2,84,148,2,148,207,2,84,194,2,134,251,2,94,145,2,140,253,2,96,252,2,142,319,2,108,320,2,118.6,185.866666667,0,118.6,185.866666667,0],"score":0.515},{"image_id":544,"category_id":1,"keypoints":[279.4,326.3,0,279.4,326.3,0,279.4,326.3,0,279.4,326.3,0,279.4,326.3,0,260,280,2,285,266,2,259,309,2,295,289,2,279.4,326.3,0,279.4,326.3,0,264,317,2,283,316,2,271,359,2,308,347,2,241,392,2,328,388,2],"score":0.089},{"image_id":544,"category_id":1,"keypoints":[171,318,2,170,314,2,166,315,2,154.5,347.25,0,159,318,2,162,331,2,144,328,2,176,344,2,140,344,2,206,348,2,157,360,2,131,366,2,115,367,2,169,357,2,149,362,2,140,391,2,117,393,2],"score":0.438},{"image_id":544,"category_id":1,"keypoints":[94,288,2,66.1333333333,329.933333333,0,88,284,2,66.1333333333,329.933333333,0,79,286,2,74,299,2,69,299,2,84,320,2,50,320,2,107,334,2,58,342,2,35,337,2,32,335,2,53,360,2,68,358,2,41,392,2,60,395,2],"score":0.231},{"image_id":544,"category_id":1,"keypoints":[21,229,2,23,228,2,18,228,2,25,229,2,16,229,2,32,237,2,11,237,2,35,253,1,3,250,1,33,262,1,7,260,2,30,271,1,14,271,2,30,291,1,15,291,2,30,311,1,14,311,1],"score":0.231},{"image_id":544,"category_id":1,"keypoints":[39,247,2,40,245,2,37,246,2,39.2666666667,265.133333333,0,39.2666666667,265.133333333,0,47,250,2,28,253,1,59,261,2,27,268,2,45,261,2,33,273,2,45,274,2,37,275,2,53,266,2,22,275,2,52,291,2,25,292,2],"score":0.774},{"image_id":564,"category_id":1,"keypoints":[334,365,2,342,359,2,329,357,2,353,363,2,318.6875,442.1875,0,367,395,2,310,384,2,370,443,2,270,419,2,337,454,2,283,445,2,341,465,2,303,454,2,309,516,2,304,512,2,257,560,2,290,584,2],"score":0.776},{"image_id":564,"category_id":1,"keypoints":[518,221,2,492.9,249.7,0,515,218,2,492.9,249.7,0,509,220,1,492.9,249.7,0,503,236,2,492.9,249.7,0,481,247,2,492.9,249.7,0,466,252,2,492.9,249.7,0,489,271,2,501,262,2,488,286,2,459,284,2,492.9,249.7,0],"score":0.553},{"image_id":569,"category_id":1,"keypoints":[134.142857143,336.285714286,0,134.142857143,336.285714286,0,134.142857143,336.285714286,0,135,287,1,150,288,1,126,304,2,155,307,2,105,294,2,158,330,2,89,279,2,146,334,2,129,341,2,145,342,2,127,383,2,143,384,2,127,418,1,143,417,1],"score":0.214},{"image_id":589,"category_id":1,"keypoints":[453,212,2,458,206,2,450,206,2,467,208,2,460.9375,254.4375,0,473,218,2,447,219,2,506,208,2,481,241,2,492,213,2,503,233,2,459,283,2,434,284,2,488,319,2,409,325,2,466,348,2,389,348,2],"score":0.359},{"image_id":692,"category_id":1,"keypoints":[233,237,1,248,214,1,210,224,1,282,212,1,183,229,1,298,263,1,209,301,1,376,332,1,193,362,1,306,316,1,157,367,1,327,428,2,266,445,1,382,419,1,219,453,1,259.266666667,320.133333333,0,259.266666667,320.133333333,0],"score":0.735},{"image_id":692,"category_id":1,"keypoints":[361,240,2,379,217,2,338,226,2,412,215,2,316,238,2,438,274,2,330,305,2,506,335,2,318,361,2,432,319,2,285,366,2,459,417,1,394,434,1,508,420,2,353,441,1,388.6,320.533333333,0,388.6,320.533333333,0],"score":0.566},{"image_id":693,"category_id":1,"keypoints":[449,114,2,451,97,2,436,112,2,451.4375,192.6875,0,402,118,2,449,121,2,397,170,2,460,151,2,390,227,2,505,145,2,391,278,2,473,208,2,443,233,2,498,234,2,473,287,2,518,263,1,488,325,2],"score":0.163},{"image_id":761,"category_id":1,"keypoints":[513,172,1,513,161,1,546.307692308,285.384615385,0,532,161,2,546.307692308,285.384615385,0,541,215,2,573,193,2,561,302,2,603,274,2,554,368,2,599,328,2,526,341,2,547,324,2,523,439,2,517,432,2,546.307692308,285.384615385,0,546.307692308,285.384615385,0],"score":0.551},{"image_id":761,"category_id":1,"keypoints":[589.6,191.4,0,589.6,191.4,0,589.6,191.4,0,589.6,191.4,0,589.6,191.4,0,578,165,2,604,165,2,571,199,1,589.6,191.4,0,589.6,191.4,0,589.6,191.4,0,590,214,2,605,214,1,589.6,191.4,0,589.6,191.4,0,589.6,191.4,0,589.6,191.4,0],"score":0.125},{"image_id":761,"category_id":1,"keypoints":[329,141,2,331,134,2,323,136,2,331.533333333,205.333333333,0,331.533333333,205.333333333,0,355,154,2,309,154,2,380,174,2,299,180,2,368,199,2,300,198,2,336,212,2,312,212,2,336,278,2,317,276,2,349,319,2,329,313,2],"score":0.227},{"image_id":761,"category_id":1,"keypoints":[108.25,338.25,0,108.25,338.25,0,108.25,338.25,0,108.25,338.25,0,108.25,338.25,0,17,289,2,98,261,2,108.25,338.25,0,152,333,2,108.25,338.25,0,108.25,338.25,0,108.25,338.25,0,166,470,2,108.25,338.25,0,108.25,338.25,0,108.25,338.25,0,108.25,338.25,0],"score":0.111},{"image_id":761,"category_id":1,"keypoints":[67.1428571429,177.214285714,0,67.1428571429,177.214285714,0,67.1428571429,177.214285714,0,70,148,2,78,149,2,66,156,2,79,159,2,57,163,2,84,169,2,48,168,2,86,179,2,60,181,2,69,182,2,56,199,2,61,199,2,61,215,2,65,214,2],"score":0.122},{"image_id":761,"category_id":1,"keypoints":[245,141,2,235.733333333,249.066666667,0,236,134,2,235.733333333,249.066666667,0,212,132,2,264,174,2,189,153,2,295,225,2,184,206,2,296,259,2,185,240,2,248,277,2,204,275,2,224,357,2,239,353,2,233,419,2,282,391,2],"score":0.221},{"image_id":761,"category_id":1,"keypoints":[160,144,2,162,141,2,167.444444444,185.666666667,0,171,141,2,167.444444444,185.666666667,0,178,154,1,167.444444444,185.666666667,0,172,178,2,167.444444444,185.666666667,0,150,173,2,167.444444444,185.666666667,0,176,210,1,167.444444444,185.666666667,0,166,245,2,167.444444444,185.666666667,0,172,285,2,167.444444444,185.666666667,0],"score":0.136},{"image_id":764,"category_id":1,"keypoints":[120,186.5,0,120,186.5,0,120,186.5,0,120,186.5,0,113,114,2,96,138,2,127,132,2,120,186.5,0,140,157,2,120,186.5,0,120,186.5,0,111,190,2,134,188,2,106,224,2,148,217,2,93,257,2,132,248,2],"score":0.556},{"image_id":764,"category_id":1,"keypoints":[310,148,2,314,143,2,309,143,2,326,140,2,331.125,200.5,0,349,160,2,306,163,2,375,183,2,281,175,2,357,169,2,252,171,2,355,226,2,329,227,2,378,270,2,322,269,2,399,307,2,336,314,2],"score":0.861},{"image_id":764,"category_id":1,"keypoints":[203,116,2,204,114,1,202,114,2,201.375,148.5,0,199,116,2,210,128,2,194,126,2,213,145,2,188,142,2,215,157,2,193,152,2,208,156,2,195,155,2,209,180,2,193,178,2,207,199,2,189,198,2],"score":0.838},{"image_id":764,"category_id":1,"keypoints":[458.875,144.25,0,458.875,144.25,0,458.875,144.25,0,458.875,144.25,0,458.875,144.25,0,454,116,2,469,117,2,458.875,144.25,0,458.875,144.25,0,458.875,144.25,0,458.875,144.25,0,453,138,2,464,138,2,451,155,2,465,155,2,452,167,2,463,168,2],"score":0.42},{"image_id":785,"category_id":1,"keypoints":[365,81,2,372,73,2,358,75,2,384,78,2,354,81,2,397,108,2,356,129,2,431,142,2,339,159,2,447,165,2,307,178,2,422,203,2,391,214,2,427,294,2,365,273,2,464,362,2,394,341,2],"score":0.972},{"image_id":810,"category_id":1,"keypoints":[525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,525.166666667,20.1666666667,0,517,8,2,497,8,1,534,17,2,517,2,1,537,68,2,549,18,1],"score":0.183},{"image_id":810,"category_id":1,"keypoints":[25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,25,95,0,16,61,2,25,95,0,34,129,2],"score":0.036},{"image_id":810,"category_id":1,"keypoints":[88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,0,88,42,2,88,42,0,88,42,0],"score":0.033},{"image_id":831,"category_id":1,"keypoints":[212,151,2,218,147,2,210,147,2,232,137,2,213.4375,185.9375,0,248,149,2,197,159,2,254,190,2,188,200,2,222,206,2,181,246,2,236,199,2,213,196,2,239,178,2,198,179,2,211,248,2,156,243,2],"score":0.081},{"image_id":836,"category_id":1,"keypoints":[228,143,2,230,146,2,224,143,2,226.6875,128.375,0,215,145,2,205,138,2,233,157,2,216,113,2,246,163,2,226,84,2,264,172,2,209,120,2,226,128,2,197,103,1,241,132,2,211,71,2,256,96,2],"score":0.482},{"image_id":872,"category_id":1,"keypoints":[386,138,2,353.5,294.083333333,0,379,134,2,353.5,294.083333333,0,357,144,2,353.5,294.083333333,0,355,193,2,353.5,294.083333333,0,410,216,2,353.5,294.083333333,0,442,191,2,368,319,2,345,319,1,366,432,1,296,428,1,332,526,2,206,489,2],"score":0.441},{"image_id":872,"category_id":1,"keypoints":[173,187,1,177,179,2,223.133333333,328.066666667,0,203,175,2,223.133333333,328.066666667,0,242,228,2,198,232,2,288,300,2,207,292,2,251,358,2,157,331,2,259,354,2,202,355,1,256,480,2,189,470,2,338,410,2,207,570,2],"score":0.545},{"image_id":885,"category_id":1,"keypoints":[376,110,2,380,107,2,372,106,2,384,109,2,365,105,1,386,123,2,346,119,2,379,149,2,329,146,2,372,174,2,319,175,2,357,144,2,324,141,2,370,183,2,319,190,2,386,239,1,300,247,1],"score":0.666},{"image_id":962,"category_id":1,"keypoints":[127,197,2,169.866666667,362.8,0,116,194,2,169.866666667,362.8,0,85,215,2,133,236,2,52,270,2,224,297,2,90,362,2,298,328,2,176,371,2,170,396,2,118,426,2,241,463,2,167,520,2,323,586,2,228,581,2],"score":0.334},{"image_id":969,"category_id":1,"keypoints":[239,297,2,243,292,2,232,292,2,250,287,2,225,290,2,262,306,2,213,309,2,273,348,2,204,350,2,261,337,2,214,337,2,254,369,2,221,370,2,273,406,2,243.266666667,336.466666667,0,285,457,2,243.266666667,336.466666667,0],"score":0.097},{"image_id":969,"category_id":1,"keypoints":[180,483.454545455,0,180,483.454545455,0,180,483.454545455,0,156,398,2,205,397,1,157,453,2,210,439,2,138,513,2,221,420,2,109,551,2,226,401,2,174,566,2,221,562,2,163,618,2,180,483.454545455,0,180,483.454545455,0,180,483.454545455,0],"score":0.602},{"image_id":974,"category_id":1,"keypoints":[187,78,2,191,72,2,181,74,2,199,66,2,172,70,2,207,79,1,175,88,2,228,91,2,168,114,2,237,103,2,162,136,1,216,122,2,190,127,2,202,128,2,174,132,2,211,178,2,192,176,2],"score":0.274},{"image_id":974,"category_id":1,"keypoints":[170,82,2,175,77,2,165,79,2,182,77,2,159,82,2,192,93,1,160,104,2,175.222222222,96.4444444444,0,175.222222222,96.4444444444,0,175.222222222,96.4444444444,0,175.222222222,96.4444444444,0,198,133,1,176,141,1,175.222222222,96.4444444444,0,175.222222222,96.4444444444,0,175.222222222,96.4444444444,0,175.222222222,96.4444444444,0],"score":0.895},{"image_id":974,"category_id":1,"keypoints":[134.6,89.6,0,120,70,1,134.6,89.6,0,135,67,2,134.6,89.6,0,149,95,1,116,98,1,153,118,1,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0,134.6,89.6,0],"score":0.261},{"image_id":974,"category_id":1,"keypoints":[90,91,2,96,84,2,84,87,2,105,86,2,78,94,2,125,112,2,74,127,2,145,152,2,74,166,2,118,178,2,81,183,2,131,184,1,97,190,1,162,210,1,70,225,1,102,144.6,0,102,144.6,0],"score":0.193},{"image_id":985,"category_id":1,"keypoints":[416,233,2,417,231,2,414,231,2,422,231,2,411,233,2,424,243,2,408,244,1,429,245,2,418.181818182,244.636363636,0,430,240,2,418.181818182,244.636363636,0,421,281,2,408,279,2,418.181818182,244.636363636,0,418.181818182,244.636363636,0,418.181818182,244.636363636,0,418.181818182,244.636363636,0],"score":0.319},{"image_id":985,"category_id":1,"keypoints":[321,245,2,323,243,2,318,243,2,321.428571429,257.285714286,0,321.428571429,257.285714286,0,327,257,2,311,257,2,321.428571429,257.285714286,0,321.428571429,257.285714286,0,321.428571429,257.285714286,0,321.428571429,257.285714286,0,331,276,2,319,280,2,321.428571429,257.285714286,0,321.428571429,257.285714286,0,321.428571429,257.285714286,0,321.428571429,257.285714286,0],"score":0.99},{"image_id":999,"category_id":1,"keypoints":[115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,115.714285714,134.285714286,0,16,35,2,115.714285714,134.285714286,0,102,39,2,152,40,2,97,161,2,149,151,2,104,277,2,190,237,2],"score":0.23},{"image_id":999,"category_id":1,"keypoints":[361,190,2,350,186,2,428.133333333,212.2,0,335,165,2,428.133333333,212.2,0,359,141,2,359,164,2,387,195,2,408,204,2,378,276,2,390,272,2,484,164,2,463,182,2,493,263,2,468,269,2,613,257,2,574,255,2],"score":0.427},{"image_id":1000,"category_id":1,"keypoints":[152,174,2,157,170,2,147,170,2,164,173,2,141,174,2,172,198,2,134,197,2,188,220,1,111,219,2,177,240,2,126,244,2,163,276,2,142,275,2,162,315,2,140,314,2,167,354,2,139,352,2],"score":0.044},{"image_id":1000,"category_id":1,"keypoints":[421,150,2,426,143,2,416,146,2,440,141,1,422.4,153.6,0,422.4,153.6,0,409,188,1,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0,422.4,153.6,0],"score":0.765},{"image_id":1000,"category_id":1,"keypoints":[302,127,2,309,120,2,296,120,2,319,123,2,289,124,2,332,162,2,275,160,2,342,210,1,262,204,2,326,218,2,269,212,2,318,251,2,280,250,1,314,318,2,285,312,2,312,387,2,286,379,2],"score":0.055},{"image_id":1000,"category_id":1,"keypoints":[256,199,2,260,193,2,250,195,2,268,196,2,239,203,2,270,230,2,230,229,2,285,260,2,215,258,2,297,233,2,221,263,2,267,301,2,240,299,2,274,349,2,238,346,2,273,398,2,237,395,2],"score":0.278},{"image_id":1000,"category_id":1,"keypoints":[558,239,2,567,232,2,551,229,2,590,236,2,564.230769231,324,0,595,284,2,539,278,2,632,335,2,522,321,2,595,362,2,564.230769231,324,0,563,397,2,527,387,2,568,467,2,528,445,2,564.230769231,324,0,564.230769231,324,0],"score":0.868},{"image_id":1000,"category_id":1,"keypoints":[485,257,2,497,248,2,478,245,2,512,252,2,468,243,1,519,304,2,454,290,2,520,365,2,435,332,2,484,401,2,439,384,2,487,425,2,452,415,2,477.428571429,330.571428571,0,454,467,2,477.428571429,330.571428571,0,477.428571429,330.571428571,0],"score":0.648},{"image_id":1000,"category_id":1,"keypoints":[426,200,2,433,192,2,423,192,2,454,194,2,427.928571429,303.571428571,0,461,236,1,420,233,2,427.928571429,303.571428571,0,405,288,2,427.928571429,303.571428571,0,393,338,2,450,329,1,415,325,1,444,393,1,413,393,2,440,470,1,414,467,2],"score":0.526},{"image_id":1000,"category_id":1,"keypoints":[370,184,2,378,178,2,366,178,2,389,182,2,358,181,2,401,222,2,344,218,2,404,284,1,337,268,2,383,324,2,331,317,2,379,311,2,349,306,2,373,371,2,338,375,2,369,445,2,356,433,2],"score":0.789},{"image_id":1000,"category_id":1,"keypoints":[442,138,2,450,133,2,435,132,2,461,138,2,427,137,1,476,178,2,413,178,1,459,231,1,405,224,2,439.727272727,181.818181818,0,439.727272727,181.818181818,0,454,257,1,415,254,1,439.727272727,181.818181818,0,439.727272727,181.818181818,0,439.727272727,181.818181818,0,439.727272727,181.818181818,0],"score":0.96},{"image_id":1000,"category_id":1,"keypoints":[238,148,2,245,143,2,232,142,2,253,146,2,226,141,1,260,172,2,217,171,2,279,199,1,201,221,2,271,217,2,200,261,2,257,262,1,219,259,1,248,322,1,201,317,1,245,367,2,200,368,2],"score":0.116},{"image_id":1000,"category_id":1,"keypoints":[375,148,2,383,142,2,372,142,2,398,145,2,367,147,2,408,178,1,358,178,1,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0,380.142857143,154.285714286,0],"score":0.626},{"image_id":1089,"category_id":1,"keypoints":[256,235,2,290,200,2,227,213,2,355,209,2,285.1,328.9,0,470,335,2,181,338,2,473,525,2,76,547,2,309,344,2,214,343,2,285.1,328.9,0,285.1,328.9,0,285.1,328.9,0,285.1,328.9,0,285.1,328.9,0,285.1,328.9,0],"score":0.881},{"image_id":1146,"category_id":1,"keypoints":[116,134,2,140,101,2,72,92,2,113.777777778,320.777777778,0,0,99,2,176,274,2,113.777777778,320.777777778,0,212,436,2,113.777777778,320.777777778,0,197,565,2,113.777777778,320.777777778,0,150,587,2,0,599,2,113.777777778,320.777777778,0,113.777777778,320.777777778,0,113.777777778,320.777777778,0,113.777777778,320.777777778,0],"score":0.563},{"image_id":1149,"category_id":1,"keypoints":[539,70,2,545,65,2,535,65,2,556,68,2,527,67,1,571,99,1,524,102,2,566,155,1,511,144,2,547,181,2,516,179,2,566,188,1,529,188,2,571,253,1,538,255,2,583,313,1,544,311,2],"score":0.956},{"image_id":1149,"category_id":1,"keypoints":[427,73,2,433,69,2,424,68,2,446,74,2,441.357142857,170.785714286,0,466,107,2,403,104,1,485,148,2,441.357142857,170.785714286,0,456,169,2,441.357142857,170.785714286,0,458,200,2,411,200,2,458,263,2,415,268,2,472,325,2,425,323,2],"score":0.514},{"image_id":1149,"category_id":1,"keypoints":[146,70,2,151,65,2,142,65,2,163,66,2,135,67,2,188,102,2,121,101,2,201,141,2,112,138,2,201,172,2,112,182,2,179,188,2,135,187,2,152.769230769,118.769230769,0,152.769230769,118.769230769,0,152.769230769,118.769230769,0,152.769230769,118.769230769,0],"score":0.489},{"image_id":1149,"category_id":1,"keypoints":[507,87,2,511,84,2,504,84,2,517,88,2,501,88,2,521,105,1,500,107,2,509.142857143,135.714285714,0,499,131,2,509.142857143,135.714285714,0,509.142857143,135.714285714,0,516,152,1,502,151,2,519,187,1,501,187,2,526,226,1,504,223,2],"score":0.511},{"image_id":1149,"category_id":1,"keypoints":[445,82,2,448,76,2,438,79,2,457,70,2,454.875,104.25,0,470,101,2,432,98,1,487,140,2,454.875,104.25,0,454.875,104.25,0,454.875,104.25,0,462,188,1,454.875,104.25,0,454.875,104.25,0,454.875,104.25,0,454.875,104.25,0,454.875,104.25,0],"score":0.605},{"image_id":1149,"category_id":1,"keypoints":[301,60,2,306,53,2,295,53,2,320,57,2,288,60,1,335,99,1,276,100,2,352,151,1,261,138,2,316,183,1,268,173,2,319,209,1,278,208,2,324,292,1,270,290,1,300.6,141.733333333,0,300.6,141.733333333,0],"score":0.455},{"image_id":1164,"category_id":1,"keypoints":[385.333333333,123.166666667,0,385.333333333,123.166666667,0,385.333333333,123.166666667,0,372,52,2,385.333333333,123.166666667,0,383,74,2,389,71,2,364,98,2,380,110,2,334,105,2,355,112,2,399,133,2,404,131,2,405,186,2,410,185,2,429,221,1,385.333333333,123.166666667,0],"score":0.006},{"image_id":1176,"category_id":1,"keypoints":[224,287,2,225,285,2,223,283,2,208.571428571,308.714285714,0,216,282,2,215,293,2,205,285,2,208.571428571,308.714285714,0,191,288,2,208.571428571,308.714285714,0,203,295,2,198,319,2,187,318,2,217,331,2,214,333,2,206,361,2,196,362,2],"score":0.418},{"image_id":1180,"category_id":1,"keypoints":[213,210,2,224,200,2,205,199,2,211.384615385,311.076923077,0,187,194,2,256,240,2,165,256,2,280,315,2,147,330,2,277,362,2,157,387,2,257,384,2,184,396,2,211.384615385,311.076923077,0,211.384615385,311.076923077,0,211.384615385,311.076923077,0,196,571,1],"score":0.378},{"image_id":1180,"category_id":1,"keypoints":[0,287,2,2,278,2,17.2222222222,333.333333333,0,8,276,2,17.2222222222,333.333333333,0,18,302,2,17.2222222222,333.333333333,0,37,340,2,17.2222222222,333.333333333,0,32,347,2,17.2222222222,333.333333333,0,5,363,2,17.2222222222,333.333333333,0,22,366,2,17.2222222222,333.333333333,0,34,441,2,17.2222222222,333.333333333,0],"score":0.153},{"image_id":1180,"category_id":1,"keypoints":[59,270,2,65,264,2,56,264,2,79,267,2,61.625,329.375,0,87,294,2,47,293,2,91,332,2,15,304,1,71,364,2,34,322,2,88,347,2,50,345,2,76,378,2,37,376,1,85,426,2,46,424,2],"score":0.438},{"image_id":1180,"category_id":1,"keypoints":[409,255,2,397,279.333333333,0,406,251,2,397,279.333333333,0,394,250,2,413,267,2,378,272,2,397,279.333333333,0,381,300,2,397,279.333333333,0,411,298,2,404,310,2,377,311,1,397,279.333333333,0,397,279.333333333,0,397,279.333333333,0,397,279.333333333,0],"score":0.746},{"image_id":1244,"category_id":1,"keypoints":[492,335,1,500.769230769,365.846153846,0,500.769230769,365.846153846,0,496,333,2,500.769230769,365.846153846,0,489,347,2,517,348,2,481,361,2,517,363,2,486,370,2,500.769230769,365.846153846,0,496,377,2,514,377,2,486,369,2,521,373,2,494,401,2,521,402,2],"score":0.256},{"image_id":1268,"category_id":1,"keypoints":[60.2222222222,254.888888889,0,60.2222222222,254.888888889,0,60.2222222222,254.888888889,0,60.2222222222,254.888888889,0,59,226,2,37,237,2,59,238,2,60.2222222222,254.888888889,0,75,254,2,60.2222222222,254.888888889,0,76,243,2,34,276,2,49,277,2,60.2222222222,254.888888889,0,81,258,2,60.2222222222,254.888888889,0,72,285,1],"score":0.201},{"image_id":1268,"category_id":1,"keypoints":[433.1,255,0,433.1,255,0,433.1,255,0,424,220,2,440,222,2,411,228,2,446,232,2,405,248,1,451,261,2,433.1,255,0,433.1,255,0,408,287,2,428,287,2,433.1,255,0,455,265,2,433.1,255,0,463,300,1],"score":0.937},{"image_id":1268,"category_id":1,"keypoints":[14,225,2,14.375,245,0,13,222,2,14.375,245,0,9,223,2,14.375,245,0,10,234,2,14.375,245,0,23,250,2,14.375,245,0,14.375,245,0,14.375,245,0,3,269,2,14.375,245,0,22,256,2,14.375,245,0,21,281,1],"score":0.234},{"image_id":1270,"category_id":1,"keypoints":[433,238,2,435,232,2,429,234,2,425.5625,291.5,0,414,238,2,438,255,2,403,254,2,443,278,2,403,280,2,428,308,2,410,297,2,441,296,2,419,299,2,443,341,2,406,341,2,456,387,2,408,386,2],"score":0.663},{"image_id":1270,"category_id":1,"keypoints":[364.4,159.6,0,364.4,159.6,0,364.4,159.6,0,364.4,159.6,0,364.4,159.6,0,363,103,2,353,104,2,376,123,2,364.4,159.6,0,377,144,2,364.4,159.6,0,363,152,2,353,152,2,353,190,2,371,190,2,352,222,2,383,216,1],"score":0.19},{"image_id":1270,"category_id":1,"keypoints":[195,209,2,175.2,272.666666667,0,186,204,2,175.2,272.666666667,0,177,203,2,187,228,2,165,228,2,187,246,2,152,253,2,195,260,1,180,258,2,177,286,2,163,287,2,187,329,2,158,330,2,181,384,2,138,385,2],"score":0.191},{"image_id":1270,"category_id":1,"keypoints":[179,144,2,182,141,2,175,140,2,169.5,169.857142857,0,167,141,2,184,157,2,157,155,2,186,178,1,154,181,2,183,158,2,166,185,1,173,192,1,146,190,2,169.5,169.857142857,0,159,195,2,169.5,169.857142857,0,162,221,2],"score":0.08},{"image_id":1270,"category_id":1,"keypoints":[228,146,2,227.333333333,177.8,0,225,145,2,227.333333333,177.8,0,213,147,2,233,159,2,201,159,2,257,174,2,202,180,2,236,175,2,224,174,2,236,192,2,208,192,2,255,185,2,220,186,2,253,227,1,219,226,1],"score":0.749},{"image_id":1270,"category_id":1,"keypoints":[311,156,2,315,156,2,312,152,2,321.461538462,182.076923077,0,321.461538462,182.076923077,0,337,159,2,307,159,2,337,180,2,321.461538462,182.076923077,0,314,180,2,321.461538462,182.076923077,0,331,193,2,312,193,2,334,189,2,318,190,2,329,229,1,322,231,2],"score":0.078},{"image_id":1270,"category_id":1,"keypoints":[101,170,2,101,164,2,94,165,2,90.375,244.4375,0,83,166,2,106,190,2,75,189,2,107,226,2,65,228,2,126,254,2,67,263,2,102,258,2,77,258,2,111,323,2,68,319,2,113,374,1,50,364,2],"score":0.406},{"image_id":1270,"category_id":1,"keypoints":[258.666666667,122.5,0,258.666666667,122.5,0,258.666666667,122.5,0,258.666666667,122.5,0,258.666666667,122.5,0,260,76,2,258.666666667,122.5,0,270,99,2,258.666666667,122.5,0,271,130,2,258.666666667,122.5,0,258,127,2,242,128,2,251,175,1,258.666666667,122.5,0,258.666666667,122.5,0,258.666666667,122.5,0],"score":0.448},{"image_id":1290,"category_id":1,"keypoints":[348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,404,98,2,348.5,186.5,0,293,275,1,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0,348.5,186.5,0],"score":0.067},{"image_id":1290,"category_id":1,"keypoints":[128,58,2,145,46,2,105,62,2,185,81,2,90,104,2,222,153,2,79,169,2,325,221,1,40,171,2,302,275,1,103,111,2,259,329,1,131,340,1,162.615384615,163.076923077,0,162.615384615,163.076923077,0,162.615384615,163.076923077,0,162.615384615,163.076923077,0],"score":0.397},{"image_id":1290,"category_id":1,"keypoints":[558,318,2,609,301,2,522.666666667,394.5,0,522.666666667,394.5,0,522.666666667,394.5,0,522.666666667,394.5,0,566,397,2,522.666666667,394.5,0,512,431,2,479,530,2,412,390,1,522.666666667,394.5,0,522.666666667,394.5,0,522.666666667,394.5,0,522.666666667,394.5,0,522.666666667,394.5,0,522.666666667,394.5,0],"score":0.154},{"image_id":1292,"category_id":1,"keypoints":[306,105,2,309,92,2,292,109,2,348,81,2,323.8,219.866666667,0,398,142,2,294,176,2,435,220,2,264,277,2,322,164,2,267,180,2,417,363,2,324,365,2,420,308,2,244,386,2,217,330,2,323.8,219.866666667,0],"score":0.502},{"image_id":1292,"category_id":1,"keypoints":[8,288,0,8,288,0,8,288,0,8,288,0,8,288,0,8,288,0,8,288,0,8,288,0,8,288,0,11,233,2,8,288,0,8,288,0,8,288,0,5,343,2,8,288,0,8,288,0,8,288,0],"score":0.787}] 2 | -------------------------------------------------------------------------------- /aitodpycocotools/aitodpycocotools/cocoeval.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tsungyi' 2 | 3 | import copy 4 | import datetime 5 | import time 6 | from collections import defaultdict 7 | 8 | import numpy as np 9 | 10 | from . import mask as maskUtils 11 | 12 | 13 | class COCOeval: 14 | # Interface for evaluating detection on the Microsoft COCO dataset. 15 | # 16 | # The usage for CocoEval is as follows: 17 | # cocoGt=..., cocoDt=... # load dataset and results 18 | # E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object 19 | # E.params.recThrs = ...; # set parameters as desired 20 | # E.evaluate(); # run per image evaluation 21 | # E.accumulate(); # accumulate per image results 22 | # E.summarize(); # display summary metrics of results 23 | # For example usage see evalDemo.m and http://mscoco.org/. 24 | # 25 | # The evaluation parameters are as follows (defaults in brackets): 26 | # imgIds - [all] N img ids to use for evaluation 27 | # catIds - [all] K cat ids to use for evaluation 28 | # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation 29 | # recThrs - [0:.01:1] R=101 recall thresholds for evaluation 30 | # areaRng - [...] A=4 object area ranges for evaluation 31 | # maxDets - [1 10 100] M=3 thresholds on max detections per image 32 | # iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints' 33 | # iouType replaced the now DEPRECATED useSegm parameter. 34 | # useCats - [1] if true use category labels for evaluation 35 | # Note: if useCats=0 category labels are ignored as in proposal scoring. 36 | # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. 37 | # 38 | # evaluate(): evaluates detections on every image and every category and 39 | # concats the results into the "evalImgs" with fields: 40 | # dtIds - [1xD] id for each of the D detections (dt) 41 | # gtIds - [1xG] id for each of the G ground truths (gt) 42 | # dtMatches - [TxD] matching gt id at each IoU or 0 43 | # gtMatches - [TxG] matching dt id at each IoU or 0 44 | # dtScores - [1xD] confidence of each dt 45 | # gtIgnore - [1xG] ignore flag for each gt 46 | # dtIgnore - [TxD] ignore flag for each dt at each IoU 47 | # 48 | # accumulate(): accumulates the per-image, per-category evaluation 49 | # results in "evalImgs" into the dictionary "eval" with fields: 50 | # params - parameters used for evaluation 51 | # date - date evaluation was performed 52 | # counts - [T,R,K,A,M] parameter dimensions (see above) 53 | # precision - [TxRxKxAxM] precision for every evaluation setting 54 | # recall - [TxKxAxM] max recall for every evaluation setting 55 | # Note: precision and recall==-1 for settings with no gt objects. 56 | # 57 | # See also coco, mask, pycocoDemo, pycocoEvalDemo 58 | # 59 | # Microsoft COCO Toolbox. version 2.0 60 | # Data, paper, and tutorials available at: http://mscoco.org/ 61 | # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. 62 | # Licensed under the Simplified BSD License [see coco/license.txt] 63 | def __init__(self, cocoGt=None, cocoDt=None, iouType='segm'): 64 | ''' 65 | Initialize CocoEval using coco APIs for gt and dt 66 | :param cocoGt: coco object with ground truth annotations 67 | :param cocoDt: coco object with detection results 68 | :return: None 69 | ''' 70 | if not iouType: 71 | print('iouType not specified. use default iouType segm') 72 | self.cocoGt = cocoGt # ground truth COCO API 73 | self.cocoDt = cocoDt # detections COCO API 74 | self.evalImgs = defaultdict( 75 | list) # per-image per-category evaluation results [KxAxI] elements 76 | self.eval = {} # accumulated evaluation results 77 | self._gts = defaultdict(list) # gt for evaluation 78 | self._dts = defaultdict(list) # dt for evaluation 79 | self.params = Params(iouType=iouType) # parameters 80 | self._paramsEval = {} # parameters for evaluation 81 | self.stats = [] # result summarization 82 | self.ious = {} # ious between all gts and dts 83 | if cocoGt is not None: 84 | self.params.imgIds = sorted(cocoGt.getImgIds()) 85 | self.params.catIds = sorted(cocoGt.getCatIds()) 86 | 87 | def _prepare(self): 88 | ''' 89 | Prepare ._gts and ._dts for evaluation based on params 90 | :return: None 91 | ''' 92 | def _toMask(anns, coco): 93 | # modify ann['segmentation'] by reference 94 | for ann in anns: 95 | rle = coco.annToRLE(ann) 96 | ann['segmentation'] = rle 97 | 98 | p = self.params 99 | if p.useCats: 100 | gts = self.cocoGt.loadAnns( 101 | self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 102 | dts = self.cocoDt.loadAnns( 103 | self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) 104 | else: 105 | gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds)) 106 | dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds)) 107 | 108 | # convert ground truth to mask if iouType == 'segm' 109 | if p.iouType == 'segm': 110 | _toMask(gts, self.cocoGt) 111 | _toMask(dts, self.cocoDt) 112 | # set ignore flag 113 | for gt in gts: 114 | gt['ignore'] = gt['ignore'] if 'ignore' in gt else 0 115 | gt['ignore'] = 'iscrowd' in gt and gt['iscrowd'] 116 | if p.iouType == 'keypoints': 117 | gt['ignore'] = (gt['num_keypoints'] == 0) or gt['ignore'] 118 | self._gts = defaultdict(list) # gt for evaluation 119 | self._dts = defaultdict(list) # dt for evaluation 120 | for gt in gts: 121 | self._gts[gt['image_id'], gt['category_id']].append(gt) 122 | for dt in dts: 123 | self._dts[dt['image_id'], dt['category_id']].append(dt) 124 | self.evalImgs = defaultdict( 125 | list) # per-image per-category evaluation results 126 | self.eval = {} # accumulated evaluation results 127 | 128 | def evaluate(self): 129 | ''' 130 | Run per image evaluation on given images and store results 131 | (a list of dict) in self.evalImgs 132 | :return: None 133 | ''' 134 | tic = time.time() 135 | print('Running per image evaluation...') 136 | p = self.params 137 | # add backward compatibility if useSegm is specified in params 138 | if p.useSegm is not None: 139 | p.iouType = 'segm' if p.useSegm == 1 else 'bbox' 140 | print('useSegm (deprecated) is not None. Running {} evaluation'. 141 | format(p.iouType)) 142 | print('Evaluate annotation type *{}*'.format(p.iouType)) 143 | p.imgIds = list(np.unique(p.imgIds)) 144 | if p.useCats: 145 | p.catIds = list(np.unique(p.catIds)) 146 | p.maxDets = sorted(p.maxDets) 147 | self.params = p 148 | 149 | self._prepare() 150 | # loop through images, area range, max detection number 151 | catIds = p.catIds if p.useCats else [-1] 152 | 153 | if p.iouType == 'segm' or p.iouType == 'bbox': 154 | computeIoU = self.computeIoU 155 | elif p.iouType == 'keypoints': 156 | computeIoU = self.computeOks 157 | self.ious = {(imgId, catId): computeIoU(imgId, catId) 158 | for imgId in p.imgIds for catId in catIds} 159 | 160 | evaluateImg = self.evaluateImg 161 | maxDet = p.maxDets[-1] 162 | self.evalImgs = [ 163 | evaluateImg(imgId, catId, areaRng, maxDet) for catId in catIds 164 | for areaRng in p.areaRng for imgId in p.imgIds 165 | ] 166 | self._paramsEval = copy.deepcopy(self.params) 167 | toc = time.time() 168 | print('DONE (t={:0.2f}s).'.format(toc - tic)) 169 | 170 | def computeIoU(self, imgId, catId): 171 | p = self.params 172 | if p.useCats: 173 | gt = self._gts[imgId, catId] 174 | dt = self._dts[imgId, catId] 175 | else: 176 | gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] 177 | dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] 178 | if len(gt) == 0 and len(dt) == 0: 179 | return [] 180 | inds = np.argsort([-d['score'] for d in dt], kind='mergesort') 181 | dt = [dt[i] for i in inds] 182 | if len(dt) > p.maxDets[-1]: 183 | dt = dt[0:p.maxDets[-1]] 184 | 185 | if p.iouType == 'segm': 186 | g = [g['segmentation'] for g in gt] 187 | d = [d['segmentation'] for d in dt] 188 | elif p.iouType == 'bbox': 189 | g = [g['bbox'] for g in gt] 190 | d = [d['bbox'] for d in dt] 191 | else: 192 | raise Exception('unknown iouType for iou computation') 193 | 194 | # compute iou between each dt and gt region 195 | iscrowd = [int(o['iscrowd']) for o in gt] 196 | ious = maskUtils.iou(d, g, iscrowd) 197 | return ious 198 | 199 | def computeOks(self, imgId, catId): 200 | p = self.params 201 | # dimention here should be Nxm 202 | gts = self._gts[imgId, catId] 203 | dts = self._dts[imgId, catId] 204 | inds = np.argsort([-d['score'] for d in dts], kind='mergesort') 205 | dts = [dts[i] for i in inds] 206 | if len(dts) > p.maxDets[-1]: 207 | dts = dts[0:p.maxDets[-1]] 208 | # if len(gts) == 0 and len(dts) == 0: 209 | if len(gts) == 0 or len(dts) == 0: 210 | return [] 211 | ious = np.zeros((len(dts), len(gts))) 212 | sigmas = p.kpt_oks_sigmas 213 | vars = (sigmas * 2)**2 214 | k = len(sigmas) 215 | # compute oks between each detection and ground truth object 216 | for j, gt in enumerate(gts): 217 | # create bounds for ignore regions(double the gt bbox) 218 | g = np.array(gt['keypoints']) 219 | xg = g[0::3] 220 | yg = g[1::3] 221 | vg = g[2::3] 222 | k1 = np.count_nonzero(vg > 0) 223 | bb = gt['bbox'] 224 | x0 = bb[0] - bb[2] 225 | x1 = bb[0] + bb[2] * 2 226 | y0 = bb[1] - bb[3] 227 | y1 = bb[1] + bb[3] * 2 228 | for i, dt in enumerate(dts): 229 | d = np.array(dt['keypoints']) 230 | xd = d[0::3] 231 | yd = d[1::3] 232 | if k1 > 0: 233 | # measure the per-keypoint distance if keypoints visible 234 | dx = xd - xg 235 | dy = yd - yg 236 | else: 237 | # measure minimum distance to keypoints in (x0,y0) & 238 | # (x1,y1) 239 | z = np.zeros((k)) 240 | dx = np.max((z, x0 - xd), axis=0) + np.max( 241 | (z, xd - x1), axis=0) 242 | dy = np.max((z, y0 - yd), axis=0) + np.max( 243 | (z, yd - y1), axis=0) 244 | e = (dx**2 + dy**2) / vars / (gt['area'] + np.spacing(1)) / 2 245 | if k1 > 0: 246 | e = e[vg > 0] 247 | ious[i, j] = np.sum(np.exp(-e)) / e.shape[0] 248 | return ious 249 | 250 | def evaluateImg(self, imgId, catId, aRng, maxDet): 251 | ''' 252 | perform evaluation for single category and image 253 | :return: dict (single image results) 254 | ''' 255 | p = self.params 256 | if p.useCats: 257 | gt = self._gts[imgId, catId] 258 | dt = self._dts[imgId, catId] 259 | else: 260 | gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] 261 | dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] 262 | if len(gt) == 0 and len(dt) == 0: 263 | return None 264 | 265 | for g in gt: 266 | if g['ignore'] or (g['area'] < aRng[0] or g['area'] > aRng[1]): 267 | g['_ignore'] = 1 268 | else: 269 | g['_ignore'] = 0 270 | 271 | # sort dt highest score first, sort gt ignore last 272 | gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort') 273 | gt = [gt[i] for i in gtind] 274 | dtind = np.argsort([-d['score'] for d in dt], kind='mergesort') 275 | dt = [dt[i] for i in dtind[0:maxDet]] 276 | iscrowd = [int(o['iscrowd']) for o in gt] 277 | # load computed ious 278 | ious = self.ious[imgId, catId][:, gtind] if len( 279 | self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] 280 | 281 | T = len(p.iouThrs) 282 | G = len(gt) 283 | D = len(dt) 284 | gtm = np.zeros((T, G)) 285 | dtm = np.zeros((T, D)) 286 | gtIg = np.array([g['_ignore'] for g in gt]) 287 | dtIg = np.zeros((T, D)) 288 | dtIoU = np.zeros((T, D)) 289 | if not len(ious) == 0: 290 | for tind, t in enumerate(p.iouThrs): 291 | for dind, d in enumerate(dt): 292 | # information about best match so far (m=-1 -> unmatched) 293 | iou = min([t, 1 - 1e-10]) 294 | m = -1 295 | for gind, g in enumerate(gt): 296 | # if this gt already matched, and not a crowd, continue 297 | if gtm[tind, gind] > 0 and not iscrowd[gind]: 298 | continue 299 | # if dt matched to reg gt, and on ignore gt, stop 300 | if m > -1 and gtIg[m] == 0 and gtIg[gind] == 1: 301 | break 302 | # continue to next gt unless better match made 303 | if ious[dind, gind] < iou: 304 | continue 305 | # if match successful and best so far, store 306 | # appropriately 307 | iou = ious[dind, gind] 308 | m = gind 309 | # if match made store id of match for both dt and gt 310 | if m == -1: 311 | continue 312 | dtIg[tind, dind] = gtIg[m] 313 | dtm[tind, dind] = gt[m]['id'] 314 | gtm[tind, m] = d['id'] 315 | dtIoU[tind, dind] = iou 316 | # set unmatched detections outside of area range to ignore 317 | a = np.array([d['area'] < aRng[0] or d['area'] > aRng[1] 318 | for d in dt]).reshape((1, len(dt))) 319 | dtIg = np.logical_or(dtIg, np.logical_and(dtm == 0, np.repeat(a, T, 320 | 0))) 321 | # store results for given image and category 322 | return { 323 | 'image_id': imgId, 324 | 'category_id': catId, 325 | 'aRng': aRng, 326 | 'maxDet': maxDet, 327 | 'dtIds': [d['id'] for d in dt], 328 | 'gtIds': [g['id'] for g in gt], 329 | 'dtMatches': dtm, 330 | 'gtMatches': gtm, 331 | 'dtScores': [d['score'] for d in dt], 332 | 'gtIgnore': gtIg, 333 | 'dtIgnore': dtIg, 334 | 'dtIoUs': dtIoU, 335 | } 336 | 337 | def accumulate(self, p=None, with_lrp=True): 338 | ''' 339 | Accumulate per image evaluation results and store the result in 340 | self.eval 341 | 342 | :param p: input params for evaluation 343 | :return: None 344 | ''' 345 | print('Accumulating evaluation results...') 346 | tic = time.time() 347 | if not self.evalImgs: 348 | print('Please run evaluate() first') 349 | # allows input customized parameters 350 | if p is None: 351 | p = self.params 352 | p.catIds = p.catIds if p.useCats == 1 else [-1] 353 | T = len(p.iouThrs) 354 | R = len(p.recThrs) 355 | K = len(p.catIds) if p.useCats else 1 356 | A = len(p.areaRng) 357 | M = len(p.maxDets) 358 | precision = -np.ones( 359 | (T, R, K, A, M)) # -1 for the precision of absent categories 360 | recall = -np.ones((T, K, A, M)) 361 | scores = -np.ones((T, R, K, A, M)) 362 | olrp_loc = -np.ones((K, A, M)) 363 | olrp_fp = -np.ones((K, A, M)) 364 | olrp_fn = -np.ones((K, A, M)) 365 | olrp = -np.ones((K, A, M)) 366 | lrp_opt_thr = -np.ones((K, A, M)) 367 | 368 | # create dictionary for future indexing 369 | _pe = self._paramsEval 370 | catIds = _pe.catIds if _pe.useCats else [-1] 371 | setK = set(catIds) 372 | setA = set(map(tuple, _pe.areaRng)) 373 | setM = set(_pe.maxDets) 374 | setI = set(_pe.imgIds) 375 | # get inds to evaluate 376 | k_list = [n for n, k in enumerate(p.catIds) if k in setK] 377 | m_list = [m for n, m in enumerate(p.maxDets) if m in setM] 378 | a_list = [ 379 | n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) 380 | if a in setA 381 | ] 382 | i_list = [n for n, i in enumerate(p.imgIds) if i in setI] 383 | I0 = len(_pe.imgIds) 384 | A0 = len(_pe.areaRng) 385 | # retrieve E at each category, area range, and max number of detections 386 | for k, k0 in enumerate(k_list): 387 | Nk = k0 * A0 * I0 388 | for a, a0 in enumerate(a_list): 389 | Na = a0 * I0 390 | for m, maxDet in enumerate(m_list): 391 | E = [self.evalImgs[Nk + Na + i] for i in i_list] 392 | E = [e for e in E if e is not None] 393 | if len(E) == 0: 394 | continue 395 | dtScores = np.concatenate( 396 | [e['dtScores'][0:maxDet] for e in E]) 397 | 398 | # different sorting method generates slightly different 399 | # results. mergesort is used to be consistent as Matlab 400 | # implementation. 401 | inds = np.argsort(-dtScores, kind='mergesort') 402 | dtScoresSorted = dtScores[inds] 403 | 404 | dtm = np.concatenate( 405 | [e['dtMatches'][:, 0:maxDet] for e in E], axis=1)[:, 406 | inds] 407 | dtIg = np.concatenate( 408 | [e['dtIgnore'][:, 0:maxDet] for e in E], axis=1)[:, 409 | inds] 410 | dtIoU = np.concatenate( 411 | [e['dtIoUs'][:, 0:maxDet] for e in E], axis=1)[:, inds] 412 | 413 | gtIg = np.concatenate([e['gtIgnore'] for e in E]) 414 | npig = np.count_nonzero(gtIg == 0) 415 | if npig == 0: 416 | continue 417 | tps = np.logical_and(dtm, np.logical_not(dtIg)) 418 | fps = np.logical_and(np.logical_not(dtm), 419 | np.logical_not(dtIg)) 420 | 421 | dtIoU = np.multiply(dtIoU, tps) 422 | tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float) 423 | fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float) 424 | for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): 425 | tp = np.array(tp) 426 | fp = np.array(fp) 427 | nd = len(tp) 428 | rc = tp / npig 429 | pr = tp / (fp + tp + np.spacing(1)) 430 | q = np.zeros((R, )) 431 | ss = np.zeros((R, )) 432 | 433 | if nd: 434 | recall[t, k, a, m] = rc[-1] 435 | else: 436 | recall[t, k, a, m] = 0 437 | 438 | # numpy is slow without cython optimization for 439 | # accessing elements use python array gets significant 440 | # speed improvement 441 | pr = pr.tolist() 442 | q = q.tolist() 443 | 444 | for i in range(nd - 1, 0, -1): 445 | if pr[i] > pr[i - 1]: 446 | pr[i - 1] = pr[i] 447 | 448 | inds = np.searchsorted(rc, p.recThrs, side='left') 449 | try: 450 | for ri, pi in enumerate(inds): 451 | q[ri] = pr[pi] 452 | ss[ri] = dtScoresSorted[pi] 453 | except: # noqa: E722 454 | pass 455 | precision[t, :, k, a, m] = np.array(q) 456 | scores[t, :, k, a, m] = np.array(ss) 457 | 458 | if with_lrp: 459 | # oLRP and Opt.Thr. Computation 460 | tp_num = np.cumsum(tps[0, :]) 461 | fp_num = np.cumsum(fps[0, :]) 462 | fn_num = npig - tp_num 463 | # If there is detection 464 | if tp_num.shape[0] > 0: 465 | # There is some TPs 466 | if tp_num[-1] > 0: 467 | total_loc = tp_num - np.cumsum(dtIoU[0, :]) 468 | lrps = (total_loc / (1 - _pe.iouThrs[0]) + fp_num + 469 | fn_num) / (tp_num + fp_num + fn_num) 470 | opt_pos_idx = np.argmin(lrps) 471 | olrp[k, a, m] = lrps[opt_pos_idx] 472 | olrp_loc[k, a, m] = total_loc[opt_pos_idx] / \ 473 | tp_num[opt_pos_idx] 474 | olrp_fp[k, a, m] = fp_num[opt_pos_idx] / \ 475 | (tp_num[opt_pos_idx] + fp_num[opt_pos_idx]) 476 | olrp_fn[k, a, m] = fn_num[opt_pos_idx] / npig 477 | lrp_opt_thr[k, a, m] = dtScoresSorted[opt_pos_idx] 478 | # There is No TP 479 | else: 480 | olrp_loc[k, a, m] = np.nan 481 | olrp_fp[k, a, m] = np.nan 482 | olrp_fn[k, a, m] = 1. 483 | olrp[k, a, m] = 1. 484 | lrp_opt_thr[k, a, m] = np.nan 485 | # No detection 486 | else: 487 | olrp_loc[k, a, m] = np.nan 488 | olrp_fp[k, a, m] = np.nan 489 | olrp_fn[k, a, m] = 1. 490 | olrp[k, a, m] = 1. 491 | lrp_opt_thr[k, a, m] = np.nan 492 | self.eval = { 493 | 'params': p, 494 | 'counts': [T, R, K, A, M], 495 | 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 496 | 'precision': precision, 497 | 'recall': recall, 498 | 'scores': scores, 499 | 'olrp_loc': olrp_loc, 500 | 'olrp_fp': olrp_fp, 501 | 'olrp_fn': olrp_fn, 502 | 'olrp': olrp, 503 | 'lrp_opt_thr': lrp_opt_thr, 504 | } 505 | toc = time.time() 506 | print('DONE (t={:0.2f}s).'.format(toc - tic)) 507 | 508 | def summarize(self): 509 | ''' 510 | Compute and display summary metrics for evaluation results. 511 | Note this functin can *only* be applied on the default parameter 512 | setting 513 | ''' 514 | def _summarize(ap=1, iouThr=None, areaRng='all', maxDets=100, lrp_type=None): 515 | p = self.params 516 | iStr = '{:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}' # noqa: E501 517 | titleStr = 'Average Precision' if ap == 1 else 'Average Recall' 518 | typeStr = '(AP)' if ap == 1 else '(AR)' 519 | iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \ 520 | if iouThr is None else '{:0.2f}'.format(iouThr) 521 | 522 | aind = [ 523 | i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng 524 | ] 525 | mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] 526 | if ap == 1: 527 | # dimension of precision: [TxRxKxAxM] 528 | s = self.eval['precision'] 529 | # IoU 530 | if iouThr is not None: 531 | t = np.where(iouThr == p.iouThrs)[0] 532 | s = s[t] 533 | s = s[:, :, :, aind, mind] 534 | if len(s[s > -1]) == 0: 535 | mean_s = -1 536 | else: 537 | mean_s = np.mean(s[s > -1]) 538 | elif ap == 0: 539 | # dimension of recall: [TxKxAxM] 540 | s = self.eval['recall'] 541 | if iouThr is not None: 542 | t = np.where(iouThr == p.iouThrs)[0] 543 | s = s[t] 544 | s = s[:, :, aind, mind] 545 | else: 546 | # # dimension of LRP: [KxAxM] 547 | # Person 0, Broccoli 50 548 | if lrp_type == 'oLRP': 549 | s = self.eval['olrp'][:, aind, mind] 550 | titleStr = 'Optimal LRP' 551 | typeStr = ' ' 552 | if lrp_type == 'oLRP_Localisation': 553 | s = self.eval['olrp_loc'][:, aind, mind] 554 | titleStr = 'Optimal LRP Loc' 555 | typeStr = ' ' 556 | if lrp_type == 'oLRP_false_positive': 557 | s = self.eval['olrp_fp'][:, aind, mind] 558 | titleStr = 'Optimal LRP FP' 559 | typeStr = ' ' 560 | if lrp_type == 'oLRP_false_negative': 561 | s = self.eval['olrp_fn'][:, aind, mind] 562 | titleStr = 'Optimal LRP FN' 563 | typeStr = ' ' 564 | if lrp_type == 'oLRP_thresholds': 565 | s = self.eval['lrp_opt_thr'][:, aind, mind].squeeze(axis=1) 566 | titleStr = '# Class-specific LRP-Optimal Thresholds # \n' 567 | typeStr = ' ' 568 | # Floor by using 3 decimal digits 569 | print(titleStr, np.round(s - 0.5 * 10**(-3), 3)) 570 | return s 571 | idx = (~np.isnan(s)) 572 | s = s[idx] 573 | if len(s[s > -1]) == 0: 574 | mean_s = -1 575 | else: 576 | mean_s = np.mean(s[s > -1]) 577 | print( 578 | iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets, 579 | mean_s)) 580 | return mean_s 581 | 582 | def _summarizeDets(): 583 | stats = np.zeros((19, )) 584 | stats[0] = _summarize(1, maxDets=self.params.maxDets[2]) 585 | stats[1] = _summarize(1, iouThr=.25, maxDets=self.params.maxDets[2]) 586 | stats[2] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2]) 587 | stats[3] = _summarize(1, 588 | iouThr=.75, 589 | maxDets=self.params.maxDets[2]) 590 | stats[4] = _summarize(1, 591 | areaRng='verytiny', 592 | maxDets=self.params.maxDets[2]) 593 | stats[5] = _summarize(1, 594 | areaRng='tiny', 595 | maxDets=self.params.maxDets[2]) 596 | stats[6] = _summarize(1, 597 | areaRng='small', 598 | maxDets=self.params.maxDets[2]) 599 | stats[7] = _summarize(1, 600 | areaRng='medium', 601 | maxDets=self.params.maxDets[2]) 602 | stats[8] = _summarize(0, maxDets=self.params.maxDets[0]) 603 | stats[9] = _summarize(0, maxDets=self.params.maxDets[1]) 604 | stats[10] = _summarize(0, maxDets=self.params.maxDets[2]) 605 | stats[11] = _summarize(0, 606 | areaRng='verytiny', 607 | maxDets=self.params.maxDets[2]) 608 | stats[12] = _summarize(0, 609 | areaRng='tiny', 610 | maxDets=self.params.maxDets[2]) 611 | stats[13] = _summarize(0, 612 | areaRng='small', 613 | maxDets=self.params.maxDets[2]) 614 | stats[14] = _summarize(0, 615 | areaRng='medium', 616 | maxDets=self.params.maxDets[2]) 617 | stats[15] = _summarize(-1, 618 | iouThr=.5, 619 | areaRng='all', 620 | maxDets=self.params.maxDets[2], 621 | lrp_type='oLRP') 622 | stats[16] = _summarize(-1, 623 | iouThr=.5, 624 | areaRng='all', 625 | maxDets=self.params.maxDets[2], 626 | lrp_type='oLRP_Localisation') 627 | stats[17] = _summarize(-1, 628 | iouThr=.5, 629 | areaRng='all', 630 | maxDets=self.params.maxDets[2], 631 | lrp_type='oLRP_false_positive') 632 | stats[18] = _summarize(-1, 633 | iouThr=.5, 634 | areaRng='all', 635 | maxDets=self.params.maxDets[2], 636 | lrp_type='oLRP_false_negative') 637 | _summarize(-1, 638 | iouThr=.5, 639 | areaRng='all', 640 | maxDets=self.params.maxDets[2], 641 | lrp_type='oLRP_thresholds') 642 | return stats 643 | 644 | def _summarizeKps(): 645 | stats = np.zeros((10, )) 646 | stats[0] = _summarize(1, maxDets=20) 647 | stats[1] = _summarize(1, maxDets=20, iouThr=.5) 648 | stats[2] = _summarize(1, maxDets=20, iouThr=.75) 649 | stats[3] = _summarize(1, maxDets=20, areaRng='medium') 650 | stats[4] = _summarize(1, maxDets=20, areaRng='large') 651 | stats[5] = _summarize(0, maxDets=20) 652 | stats[6] = _summarize(0, maxDets=20, iouThr=.5) 653 | stats[7] = _summarize(0, maxDets=20, iouThr=.75) 654 | stats[8] = _summarize(0, maxDets=20, areaRng='medium') 655 | stats[9] = _summarize(0, maxDets=20, areaRng='large') 656 | return stats 657 | 658 | if not self.eval: 659 | raise Exception('Please run accumulate() first') 660 | iouType = self.params.iouType 661 | if iouType == 'segm' or iouType == 'bbox': 662 | summarize = _summarizeDets 663 | elif iouType == 'keypoints': 664 | summarize = _summarizeKps 665 | self.stats = summarize() 666 | 667 | def __str__(self): 668 | self.summarize() 669 | 670 | 671 | class Params: 672 | ''' 673 | Params for coco evaluation api 674 | ''' 675 | def setDetParams(self): 676 | self.imgIds = [] 677 | self.catIds = [] 678 | # np.arange causes trouble. the data point on arange is slightly 679 | # larger than the true value 680 | self.iouThrs = np.linspace(.5, 681 | 0.95, 682 | int(np.round((0.95 - .5) / .05)) + 1, 683 | endpoint=True) 684 | self.recThrs = np.linspace(.0, 685 | 1.00, 686 | int(np.round((1.00 - .0) / .01)) + 1, 687 | endpoint=True) 688 | self.maxDets = [1, 100, 1500] 689 | self.areaRng = [[0**2, 1e5**2], [0**2, 8**2], [8**2, 16**2], [16**2, 32**2], 690 | [32**2, 1e5**2]] 691 | self.areaRngLbl = ['all', 'verytiny', 'tiny', 'small', 'medium'] 692 | self.useCats = 1 693 | 694 | def setKpParams(self): 695 | self.imgIds = [] 696 | self.catIds = [] 697 | # np.arange causes trouble. the data point on arange is slightly 698 | # larger than the true value 699 | self.iouThrs = np.linspace(.5, 700 | 0.95, 701 | int(np.round((0.95 - .5) / .05)) + 1, 702 | endpoint=True) 703 | self.recThrs = np.linspace(.0, 704 | 1.00, 705 | int(np.round((1.00 - .0) / .01)) + 1, 706 | endpoint=True) 707 | self.maxDets = [20] 708 | self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]] 709 | self.areaRngLbl = ['all', 'medium', 'large'] 710 | self.useCats = 1 711 | self.kpt_oks_sigmas = np.array([ 712 | .26, .25, .25, .35, .35, .79, .79, .72, .72, .62, .62, 1.07, 1.07, 713 | .87, .87, .89, .89 714 | ]) / 10.0 715 | 716 | def __init__(self, iouType='segm'): 717 | if iouType == 'segm' or iouType == 'bbox': 718 | self.setDetParams() 719 | elif iouType == 'keypoints': 720 | self.setKpParams() 721 | else: 722 | raise Exception('iouType not supported') 723 | self.iouType = iouType 724 | # useSegm is deprecated 725 | self.useSegm = None 726 | --------------------------------------------------------------------------------