├── .github ├── CONTRIBUTING.md ├── FUNDING.yml └── workflows │ ├── push-pr-ci.yml │ ├── python-publish-pre.yml │ └── python-publish.yml ├── .gitignore ├── .readthedocs.yaml ├── .vscode └── settings.json ├── LICENSE.txt ├── README.md ├── build.sh ├── docs ├── Makefile ├── _build │ ├── doctrees │ │ ├── environment.pickle │ │ ├── index.doctree │ │ ├── modules.doctree │ │ └── pylabel.doctree │ └── html │ │ ├── .buildinfo │ │ ├── .doctrees │ │ ├── index.doctree │ │ ├── modules.doctree │ │ └── pylabel.doctree │ │ ├── _modules │ │ ├── index.html │ │ └── pylabel │ │ │ ├── analyze.html │ │ │ ├── dataset.html │ │ │ ├── exporter.html │ │ │ ├── importer.html │ │ │ ├── labeler.html │ │ │ ├── splitter.html │ │ │ └── visualize.html │ │ ├── _sources │ │ ├── index.rst.txt │ │ ├── modules.rst.txt │ │ └── pylabel.rst.txt │ │ ├── _static │ │ ├── basic.css │ │ ├── css │ │ │ ├── badge_only.css │ │ │ ├── fonts │ │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── lato-bold-italic.woff │ │ │ │ ├── lato-bold-italic.woff2 │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-normal-italic.woff │ │ │ │ ├── lato-normal-italic.woff2 │ │ │ │ ├── lato-normal.woff │ │ │ │ └── lato-normal.woff2 │ │ │ └── theme.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── jquery-3.5.1.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── badge_only.js │ │ │ ├── html5shiv-printshiv.min.js │ │ │ ├── html5shiv.min.js │ │ │ └── theme.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.13.1.js │ │ └── underscore.js │ │ ├── genindex.html │ │ ├── index.html │ │ ├── modules.html │ │ ├── objects.inv │ │ ├── py-modindex.html │ │ ├── pylabel.html │ │ ├── search.html │ │ └── searchindex.js ├── conf.py ├── index.rst ├── make.bat ├── modules.rst ├── pylabel.rst └── requirements.txt ├── pylabel ├── __init__.py ├── analyze.py ├── dataset.py ├── exporter.py ├── importer.py ├── labeler.py ├── shared.py ├── splitter.py └── visualize.py ├── requirements.txt ├── setup.py └── tests ├── README.md ├── __init__.py └── test_main.py /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PyLabel 2 | Thank you for your interest in contributing to PyLabel. 3 | 4 | **Run Validation Tests**
5 | Before making your pull request, please run the validation tests localy to ensure that your change has not impacted other areas. See instructions on running the tests here https://github.com/pylabel-project/pylabel/blob/dev/tests/README.md. 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | #These are supported funding model platforms 2 | open_collective: pylabel 3 | -------------------------------------------------------------------------------- /.github/workflows/push-pr-ci.yml: -------------------------------------------------------------------------------- 1 | name: CI on push or pull request for python 3.9 2 | 3 | on: 4 | push: 5 | branches: [ main, dev ] 6 | pull_request: 7 | branches: [ main, dev ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python 3.9 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: "3.9" 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 24 | - name: Test with pytest 25 | run: | 26 | pytest 27 | - name: Test notebooks with pytest + nbmake 28 | run: | 29 | pytest --nbmake "dataset_splitting.ipynb" "coco2voc.ipynb" "yolo2coco.ipynb" "yolo2voc.ipynb" 30 | -------------------------------------------------------------------------------- /.github/workflows/python-publish-pre.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | 10 | name: Publish Pre-Release Python Package 11 | 12 | on: 13 | release: 14 | types: [published] 15 | branches: 16 | - dev 17 | jobs: 18 | deploy: 19 | 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Set up Python 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: '3.8' 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install build 32 | - name: Build package 33 | run: python -m build 34 | - name: Publish package 35 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 36 | with: 37 | user: __token__ 38 | password: ${{ secrets.PYPI_API_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | 10 | name: Publish Python Package 11 | 12 | on: 13 | release: 14 | types: [published] 15 | branches: [ main ] 16 | 17 | jobs: 18 | deploy: 19 | 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Set up Python 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: '3.8' 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install build 32 | - name: Build package 33 | run: python -m build 34 | - name: Publish package 35 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 36 | with: 37 | user: __token__ 38 | password: ${{ secrets.PYPI_API_TOKEN }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | penv/ 2 | env/ 3 | venv/ 4 | .vscode/settings.json 5 | coco_instances_val2017.json 6 | mypythonlib.egg-info/ 7 | dist/ 8 | ./build/ 9 | data/ 10 | .eggs/ 11 | pylabel.egg-info/ 12 | *.ipynb 13 | pylabel/__pycache__/ 14 | notebooks.sh 15 | training/ 16 | annotations/ 17 | images/ 18 | roadsign_splitdata.zip 19 | road_sign_data.yaml 20 | BCCD_Dataset/ 21 | model_training/ 22 | __pycache__ 23 | samples -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | python: 21 | install: 22 | - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "githubPullRequests.ignoredPullRequestBranches": [ 3 | "dev" 4 | ] 5 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 PyLabel Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyLabel 2 | 3 | 4 | PyPI   5 | 6 |    7 | 8 | 9 | Documentation Status 10 | 11 |   Open In Colab 12 |   13 | 14 |

15 | PyLabel is a Python package to help you prepare image datasets for computer vision models including PyTorch and YOLOv5. It can translate bounding box annotations between different formats. (For example, COCO to YOLO.) And it includes an AI-assisted labeling tool that runs in a Jupyter notebook. 16 | 17 | - **Translate:** Convert annotation formats with a single line of code: 18 | ``` 19 | importer.ImportCoco(path_to_annotations).export.ExportToYoloV5() 20 | ``` 21 | - **Analyze:** PyLabel stores annotatations in a pandas dataframe so you can easily perform analysis on image datasets. 22 | - **Split:** Divide image datasets into train, test, and val with stratification to get consistent class distribution.
23 | - **Label:** PyLabel also includes an image labeling tool that runs in a Jupyter notebook that can annotate images manually or perform automatic labeling using a pre-trained model.

24 | - **Visualize:** Render images from your dataset with bounding boxes overlaid so you can confirm the accuracy of the annotations. 25 | 26 | 27 | ## Tutorial Notebooks 28 | See PyLabel in action in these [sample Jupyter notebooks](https://github.com/pylabel-project/samples/):
29 | - [Convert COCO to YOLO](https://github.com/pylabel-project/samples/blob/main/coco2yolov5.ipynb) 30 | - [Convert COCO to VOC](https://github.com/pylabel-project/samples/blob/main/coco2voc.ipynb) 31 | - [Convert VOC to COCO](https://github.com/pylabel-project/samples/blob/main/voc2coco.ipynb) 32 | - [Convert YOLO to COCO](https://github.com/pylabel-project/samples/blob/main/yolo2coco.ipynb) 33 | - [Convert YOLO to VOC](https://github.com/pylabel-project/samples/blob/main/yolo2voc.ipynb) 34 | - [Import a YOLO YAML File](https://github.com/pylabel-project/samples/blob/main/yolo_with_yaml_importer.ipynb) 35 | - [Splitting Images Datasets into Train, Test, Val](https://github.com/pylabel-project/samples/blob/main/dataset_splitting.ipynb) 36 | - [Labeling Tool Demo with AI Assisted Labeling](https://github.com/pylabel-project/samples/blob/main/pylabeler.ipynb) 37 | 38 | Find more docs at https://pylabel.readthedocs.io. 39 | 40 | ## About PyLabel 41 | PyLabel was developed by Jeremy Fraenkel, Alex Heaton, and Derek Topper as the Capstope project for the Master of Information and Data Science (MIDS) at the UC Berkeley School of Information. If you have any questions or feedback please [create an issue](https://github.com/pylabel-project/pylabel/issues). Please let us know how we can make PyLabel more useful. 42 | 43 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | rm -r build/ 2 | rm -r pylabel.egg-info/ 3 | rm -r dist/ 4 | Python setup.py sdist bdist_wheel 5 | twine upload --repository-url https://upload.pypi.org/legacy/ dist/* 6 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/pylabel.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/doctrees/pylabel.doctree -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 1313101fa6f04cd3f284c7e8344acc16 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/.doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/.doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/pylabel.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/.doctrees/pylabel.doctree -------------------------------------------------------------------------------- /docs/_build/html/_modules/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview: module code — PyLabel 0.1.30 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

24 | 63 | 64 |
68 | 69 |
70 |
71 |
72 |
    73 |
  • »
  • 74 |
  • Overview: module code
  • 75 |
  • 76 |
  • 77 |
78 |
79 |
80 |
81 |
82 | 83 |

All modules for which code is available

84 | 92 | 93 |
94 |
95 | 109 |
110 |
111 |
112 |
113 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /docs/_build/html/_modules/pylabel/dataset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | pylabel.dataset — PyLabel 0.1.30 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 58 | 59 |
63 | 64 |
65 |
66 |
67 |
    68 |
  • »
  • 69 |
  • Module code »
  • 70 |
  • pylabel.dataset
  • 71 |
  • 72 |
  • 73 |
74 |
75 |
76 |
77 |
78 | 79 |

Source code for pylabel.dataset

 80 | """The dataset is the primary object that you will interactive with when using PyLabel.
 81 | All other modules are sub-modules of the dataset object. 
 82 | """
 83 | 
 84 | from pylabel.analyze import Analyze
 85 | from pylabel.exporter import Export
 86 | from pylabel.visualize import Visualize
 87 | from pylabel.labeler import Labeler
 88 | from pylabel.splitter import Split
 89 | from pylabel.shared import _ReindexCatIds
 90 | 
 91 | import numpy as np
 92 | 
 93 | 
 94 | 
[docs]class Dataset: 95 | def __init__(self, df): 96 | self.df = df 97 | """Pandas Dataframe: The dataframe where annotations are stored. This dataframe can be read directly 98 | to query the contents of the dataset. You can also edit this dataframe to filter records or edit the 99 | annotations directly. 100 | 101 | Example: 102 | >>> dataset.df 103 | """ 104 | self.name = "dataset" 105 | """string: Default is 'dataset'. A friendly name for your dataset that is used as part of the filename(s) 106 | when exporting annotation files. 107 | """ 108 | self.path_to_annotations = "" 109 | """string: Default is ''. The path to the annotation files associated with the dataset. When importing, 110 | this will be path to the directory where the annotations are stored. By default, annotations will be exported 111 | to the same directory. Changing this value will change where the annotations are exported to. 112 | """ 113 | self.export = Export(dataset=self) 114 | """See pylabel.export module.""" 115 | self.visualize = Visualize(dataset=self) 116 | """See pylabel.visualize module.""" 117 | self.analyze = Analyze(dataset=self) 118 | """See pylabel.analyze module.""" 119 | self.labeler = Labeler(self) 120 | """See pylabel.labeler module.""" 121 | self.splitter = Split(dataset=self) 122 | """See pylabel.splitter module.""" 123 | 124 |
[docs] def ReindexCatIds(self, cat_id_index=0): 125 | """ 126 | Reindex the values of the cat_id column so that that they start from an int (usually 0 or 1) and 127 | then increment the cat_ids to index + number of categories. 128 | It's useful if the cat_ids are not continuous, especially for dataset subsets, 129 | or combined multiple datasets. Some models like Yolo require starting from 0 and others 130 | like Detectron require starting from 1. 131 | 132 | Args: 133 | cat_id_index (int): Defaults to 0. 134 | The cat ids will increment sequentially the cat_index value. For example if there are 10 135 | classes then the cat_ids will be a range from 0-9. 136 | 137 | Example: 138 | >>> dataset.analyze.class_ids 139 | [1,2,4,5,6,7,8,9,11,12] 140 | >>> dataset.ReindexCatIds(cat_id_index) = 0 141 | >>> dataset.analyze.class_ids 142 | [0,1,2,3,4,5,6,7,8,9] 143 | """ 144 | assert isinstance(cat_id_index, int), "cat_id_index must be an int." 145 | _ReindexCatIds(self.df, cat_id_index)
146 |
147 | 148 |
149 |
150 | 164 |
165 |
166 |
167 |
168 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/_build/html/_modules/pylabel/visualize.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | pylabel.visualize — PyLabel 0.1.30 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 58 | 59 |
63 | 64 |
65 |
66 |
67 |
    68 |
  • »
  • 69 |
  • Module code »
  • 70 |
  • pylabel.visualize
  • 71 |
  • 72 |
  • 73 |
74 |
75 |
76 |
77 |
78 | 79 |

Source code for pylabel.visualize

 80 | import bbox_visualizer as bbv
 81 | import cv2
 82 | from PIL import Image
 83 | from pathlib import Path
 84 | 
 85 | 
[docs]class Visualize: 86 | def __init__(self, dataset=None): 87 | self.dataset = dataset 88 | 89 | """Functions to visualize inspect images and annotations.""" 90 |
[docs] def ShowBoundingBoxes(self, img_id:int=0, img_filename:str="") -> Image: 91 | """Enter a filename or index number and return the image with the bounding boxes drawn.""" 92 | 93 | ds = self.dataset 94 | 95 | #Handle cases where user enters image name in default field 96 | if type(img_id) == str: 97 | img_filename = img_id 98 | 99 | if img_filename == "": 100 | df_single_img_annots = ds.df.loc[ds.df.img_id == img_id] 101 | else: 102 | df_single_img_annots = ds.df.loc[ds.df.img_filename == img_filename] 103 | 104 | full_image_path = str(Path(ds.path_to_annotations, df_single_img_annots.iloc[0].img_folder, df_single_img_annots.iloc[0].img_filename)) 105 | img = cv2.imread(str(full_image_path)) 106 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 107 | 108 | labels = [] 109 | bboxes = [] 110 | 111 | for index, row in df_single_img_annots.iterrows(): 112 | labels.append(row['cat_name']) 113 | bboxes.append([int(row['ann_bbox_xmin']),int(row['ann_bbox_ymin']),int(row['ann_bbox_xmax']),int(row['ann_bbox_ymax'])]) 114 | 115 | img_with_boxes = bbv.draw_multiple_rectangles(img, bboxes) 116 | img_with_boxes = bbv.add_multiple_labels(img_with_boxes, labels, bboxes) 117 | 118 | rendered_img = Image.fromarray(img_with_boxes) 119 | #rendered_img.save("bbox-visualizer/jpeg.jpg") 120 | return rendered_img
121 | 122 |
123 | 124 |
125 |
126 | 140 |
141 |
142 |
143 |
144 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | Welcome 2 | ================== 3 | 4 | Documentation for the the PyLabel package at https://github.com/pylabel-project/pylabel/ 5 | 6 | .. toctree:: 7 | :maxdepth: 3 8 | :caption: Documentation : 9 | 10 | pylabel 11 | 12 | .. toctree:: 13 | :maxdepth: 3 14 | :caption: Integrations : 15 | 16 | Albumentations 17 | Azure Custom Vision 18 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/modules.rst.txt: -------------------------------------------------------------------------------- 1 | pylabel package 2 | =============== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | pylabel 8 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/pylabel.rst.txt: -------------------------------------------------------------------------------- 1 | pylabel package 2 | =============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pylabel.analyze module 8 | ---------------------- 9 | 10 | .. automodule:: pylabel.analyze 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pylabel.dataset module 16 | ---------------------- 17 | 18 | .. automodule:: pylabel.dataset 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pylabel.exporter module 24 | ----------------------- 25 | 26 | .. automodule:: pylabel.exporter 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pylabel.importer module 32 | ----------------------- 33 | 34 | .. automodule:: pylabel.importer 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pylabel.labeler module 40 | ---------------------- 41 | 42 | .. automodule:: pylabel.labeler 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pylabel.shared module 48 | --------------------- 49 | 50 | .. automodule:: pylabel.shared 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | pylabel.splitter module 56 | ----------------------- 57 | 58 | .. automodule:: pylabel.splitter 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | pylabel.visualize module 64 | ------------------------ 65 | 66 | .. automodule:: pylabel.visualize 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | Module contents 72 | --------------- 73 | 74 | .. automodule:: pylabel 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | -------------------------------------------------------------------------------- /docs/_build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | * 33 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 34 | */ 35 | jQuery.urldecode = function(x) { 36 | if (!x) { 37 | return x 38 | } 39 | return decodeURIComponent(x.replace(/\+/g, ' ')); 40 | }; 41 | 42 | /** 43 | * small helper function to urlencode strings 44 | */ 45 | jQuery.urlencode = encodeURIComponent; 46 | 47 | /** 48 | * This function returns the parsed url parameters of the 49 | * current request. Multiple values per key are supported, 50 | * it will always return arrays of strings for the value parts. 51 | */ 52 | jQuery.getQueryParameters = function(s) { 53 | if (typeof s === 'undefined') 54 | s = document.location.search; 55 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 56 | var result = {}; 57 | for (var i = 0; i < parts.length; i++) { 58 | var tmp = parts[i].split('=', 2); 59 | var key = jQuery.urldecode(tmp[0]); 60 | var value = jQuery.urldecode(tmp[1]); 61 | if (key in result) 62 | result[key].push(value); 63 | else 64 | result[key] = [value]; 65 | } 66 | return result; 67 | }; 68 | 69 | /** 70 | * highlight a given string on a jquery object by wrapping it in 71 | * span elements with the given class name. 72 | */ 73 | jQuery.fn.highlightText = function(text, className) { 74 | function highlight(node, addItems) { 75 | if (node.nodeType === 3) { 76 | var val = node.nodeValue; 77 | var pos = val.toLowerCase().indexOf(text); 78 | if (pos >= 0 && 79 | !jQuery(node.parentNode).hasClass(className) && 80 | !jQuery(node.parentNode).hasClass("nohighlight")) { 81 | var span; 82 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 83 | if (isInSVG) { 84 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 85 | } else { 86 | span = document.createElement("span"); 87 | span.className = className; 88 | } 89 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 90 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 91 | document.createTextNode(val.substr(pos + text.length)), 92 | node.nextSibling)); 93 | node.nodeValue = val.substr(0, pos); 94 | if (isInSVG) { 95 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 96 | var bbox = node.parentElement.getBBox(); 97 | rect.x.baseVal.value = bbox.x; 98 | rect.y.baseVal.value = bbox.y; 99 | rect.width.baseVal.value = bbox.width; 100 | rect.height.baseVal.value = bbox.height; 101 | rect.setAttribute('class', className); 102 | addItems.push({ 103 | "parent": node.parentNode, 104 | "target": rect}); 105 | } 106 | } 107 | } 108 | else if (!jQuery(node).is("button, select, textarea")) { 109 | jQuery.each(node.childNodes, function() { 110 | highlight(this, addItems); 111 | }); 112 | } 113 | } 114 | var addItems = []; 115 | var result = this.each(function() { 116 | highlight(this, addItems); 117 | }); 118 | for (var i = 0; i < addItems.length; ++i) { 119 | jQuery(addItems[i].parent).before(addItems[i].target); 120 | } 121 | return result; 122 | }; 123 | 124 | /* 125 | * backward compatibility for jQuery.browser 126 | * This will be supported until firefox bug is fixed. 127 | */ 128 | if (!jQuery.browser) { 129 | jQuery.uaMatch = function(ua) { 130 | ua = ua.toLowerCase(); 131 | 132 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 133 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 134 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 135 | /(msie) ([\w.]+)/.exec(ua) || 136 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 137 | []; 138 | 139 | return { 140 | browser: match[ 1 ] || "", 141 | version: match[ 2 ] || "0" 142 | }; 143 | }; 144 | jQuery.browser = {}; 145 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 146 | } 147 | 148 | /** 149 | * Small JavaScript module for the documentation. 150 | */ 151 | var Documentation = { 152 | 153 | init : function() { 154 | this.fixFirefoxAnchorBug(); 155 | this.highlightSearchWords(); 156 | this.initIndexTable(); 157 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 158 | this.initOnKeyListeners(); 159 | } 160 | }, 161 | 162 | /** 163 | * i18n support 164 | */ 165 | TRANSLATIONS : {}, 166 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 167 | LOCALE : 'unknown', 168 | 169 | // gettext and ngettext don't access this so that the functions 170 | // can safely bound to a different name (_ = Documentation.gettext) 171 | gettext : function(string) { 172 | var translated = Documentation.TRANSLATIONS[string]; 173 | if (typeof translated === 'undefined') 174 | return string; 175 | return (typeof translated === 'string') ? translated : translated[0]; 176 | }, 177 | 178 | ngettext : function(singular, plural, n) { 179 | var translated = Documentation.TRANSLATIONS[singular]; 180 | if (typeof translated === 'undefined') 181 | return (n == 1) ? singular : plural; 182 | return translated[Documentation.PLURALEXPR(n)]; 183 | }, 184 | 185 | addTranslations : function(catalog) { 186 | for (var key in catalog.messages) 187 | this.TRANSLATIONS[key] = catalog.messages[key]; 188 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 189 | this.LOCALE = catalog.locale; 190 | }, 191 | 192 | /** 193 | * add context elements like header anchor links 194 | */ 195 | addContextElements : function() { 196 | $('div[id] > :header:first').each(function() { 197 | $('\u00B6'). 198 | attr('href', '#' + this.id). 199 | attr('title', _('Permalink to this headline')). 200 | appendTo(this); 201 | }); 202 | $('dt[id]').each(function() { 203 | $('\u00B6'). 204 | attr('href', '#' + this.id). 205 | attr('title', _('Permalink to this definition')). 206 | appendTo(this); 207 | }); 208 | }, 209 | 210 | /** 211 | * workaround a firefox stupidity 212 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 213 | */ 214 | fixFirefoxAnchorBug : function() { 215 | if (document.location.hash && $.browser.mozilla) 216 | window.setTimeout(function() { 217 | document.location.href += ''; 218 | }, 10); 219 | }, 220 | 221 | /** 222 | * highlight the search words provided in the url in the text 223 | */ 224 | highlightSearchWords : function() { 225 | var params = $.getQueryParameters(); 226 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 227 | if (terms.length) { 228 | var body = $('div.body'); 229 | if (!body.length) { 230 | body = $('body'); 231 | } 232 | window.setTimeout(function() { 233 | $.each(terms, function() { 234 | body.highlightText(this.toLowerCase(), 'highlighted'); 235 | }); 236 | }, 10); 237 | $('') 239 | .appendTo($('#searchbox')); 240 | } 241 | }, 242 | 243 | /** 244 | * init the domain index toggle buttons 245 | */ 246 | initIndexTable : function() { 247 | var togglers = $('img.toggler').click(function() { 248 | var src = $(this).attr('src'); 249 | var idnum = $(this).attr('id').substr(7); 250 | $('tr.cg-' + idnum).toggle(); 251 | if (src.substr(-9) === 'minus.png') 252 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 253 | else 254 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 255 | }).css('display', ''); 256 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 257 | togglers.click(); 258 | } 259 | }, 260 | 261 | /** 262 | * helper function to hide the search marks again 263 | */ 264 | hideSearchWords : function() { 265 | $('#searchbox .highlight-link').fadeOut(300); 266 | $('span.highlighted').removeClass('highlighted'); 267 | }, 268 | 269 | /** 270 | * make the url absolute 271 | */ 272 | makeURL : function(relativeURL) { 273 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 274 | }, 275 | 276 | /** 277 | * get the current relative url 278 | */ 279 | getCurrentURL : function() { 280 | var path = document.location.pathname; 281 | var parts = path.split(/\//); 282 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 283 | if (this === '..') 284 | parts.pop(); 285 | }); 286 | var url = parts.join('/'); 287 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 288 | }, 289 | 290 | initOnKeyListeners: function() { 291 | $(document).keydown(function(event) { 292 | var activeElementType = document.activeElement.tagName; 293 | // don't navigate when in search box, textarea, dropdown or button 294 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' 295 | && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey 296 | && !event.shiftKey) { 297 | switch (event.keyCode) { 298 | case 37: // left 299 | var prevHref = $('link[rel="prev"]').prop('href'); 300 | if (prevHref) { 301 | window.location.href = prevHref; 302 | return false; 303 | } 304 | break; 305 | case 39: // right 306 | var nextHref = $('link[rel="next"]').prop('href'); 307 | if (nextHref) { 308 | window.location.href = nextHref; 309 | return false; 310 | } 311 | break; 312 | } 313 | } 314 | }); 315 | } 316 | }; 317 | 318 | // quick alias for translations 319 | _ = Documentation.gettext; 320 | 321 | $(document).ready(function() { 322 | Documentation.init(); 323 | }); 324 | -------------------------------------------------------------------------------- /docs/_build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.1.30', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /docs/_build/html/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_build/html/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | var splitChars = (function() { 204 | var result = {}; 205 | var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, 206 | 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, 207 | 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, 208 | 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, 209 | 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, 210 | 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, 211 | 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, 212 | 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, 213 | 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, 214 | 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; 215 | var i, j, start, end; 216 | for (i = 0; i < singles.length; i++) { 217 | result[singles[i]] = true; 218 | } 219 | var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], 220 | [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], 221 | [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], 222 | [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], 223 | [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], 224 | [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], 225 | [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], 226 | [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], 227 | [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], 228 | [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], 229 | [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], 230 | [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], 231 | [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], 232 | [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], 233 | [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], 234 | [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], 235 | [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], 236 | [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], 237 | [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], 238 | [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], 239 | [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], 240 | [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], 241 | [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], 242 | [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], 243 | [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], 244 | [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], 245 | [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], 246 | [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], 247 | [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], 248 | [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], 249 | [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], 250 | [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], 251 | [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], 252 | [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], 253 | [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], 254 | [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], 255 | [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], 256 | [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], 257 | [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], 258 | [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], 259 | [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], 260 | [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], 261 | [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], 262 | [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], 263 | [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], 264 | [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], 265 | [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], 266 | [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], 267 | [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; 268 | for (i = 0; i < ranges.length; i++) { 269 | start = ranges[i][0]; 270 | end = ranges[i][1]; 271 | for (j = start; j <= end; j++) { 272 | result[j] = true; 273 | } 274 | } 275 | return result; 276 | })(); 277 | 278 | function splitQuery(query) { 279 | var result = []; 280 | var start = -1; 281 | for (var i = 0; i < query.length; i++) { 282 | if (splitChars[query.charCodeAt(i)]) { 283 | if (start !== -1) { 284 | result.push(query.slice(start, i)); 285 | start = -1; 286 | } 287 | } else if (start === -1) { 288 | start = i; 289 | } 290 | } 291 | if (start !== -1) { 292 | result.push(query.slice(start)); 293 | } 294 | return result; 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #408080; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #408080; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 23 | .highlight .go { color: #888888 } /* Generic.Output */ 24 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 34 | .highlight .m { color: #666666 } /* Literal.Number */ 35 | .highlight .s { color: #BA2121 } /* Literal.String */ 36 | .highlight .na { color: #7D9029 } /* Name.Attribute */ 37 | .highlight .nb { color: #008000 } /* Name.Builtin */ 38 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #880000 } /* Name.Constant */ 40 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 41 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 43 | .highlight .nf { color: #0000FF } /* Name.Function */ 44 | .highlight .nl { color: #A0A000 } /* Name.Label */ 45 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #19177C } /* Name.Variable */ 48 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 58 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 61 | .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 65 | .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 67 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */ 70 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 71 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 72 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */ 74 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_build/html/_static/underscore.js: -------------------------------------------------------------------------------- 1 | !function(n,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define("underscore",r):(n="undefined"!=typeof globalThis?globalThis:n||self,function(){var t=n._,e=n._=r();e.noConflict=function(){return n._=t,e}}())}(this,(function(){ 2 | // Underscore.js 1.13.1 3 | // https://underscorejs.org 4 | // (c) 2009-2021 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors 5 | // Underscore may be freely distributed under the MIT license. 6 | var n="1.13.1",r="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||Function("return this")()||{},t=Array.prototype,e=Object.prototype,u="undefined"!=typeof Symbol?Symbol.prototype:null,o=t.push,i=t.slice,a=e.toString,f=e.hasOwnProperty,c="undefined"!=typeof ArrayBuffer,l="undefined"!=typeof DataView,s=Array.isArray,p=Object.keys,v=Object.create,h=c&&ArrayBuffer.isView,y=isNaN,d=isFinite,g=!{toString:null}.propertyIsEnumerable("toString"),b=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],m=Math.pow(2,53)-1;function j(n,r){return r=null==r?n.length-1:+r,function(){for(var t=Math.max(arguments.length-r,0),e=Array(t),u=0;u=0&&t<=m}}function J(n){return function(r){return null==r?void 0:r[n]}}var G=J("byteLength"),H=K(G),Q=/\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;var X=c?function(n){return h?h(n)&&!q(n):H(n)&&Q.test(a.call(n))}:C(!1),Y=J("length");function Z(n,r){r=function(n){for(var r={},t=n.length,e=0;e":">",'"':""","'":"'","`":"`"},Cn=Ln($n),Kn=Ln(_n($n)),Jn=tn.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},Gn=/(.)^/,Hn={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},Qn=/\\|'|\r|\n|\u2028|\u2029/g;function Xn(n){return"\\"+Hn[n]}var Yn=/^\s*(\w|\$)+\s*$/;var Zn=0;function nr(n,r,t,e,u){if(!(e instanceof r))return n.apply(t,u);var o=Mn(n.prototype),i=n.apply(o,u);return _(i)?i:o}var rr=j((function(n,r){var t=rr.placeholder,e=function(){for(var u=0,o=r.length,i=Array(o),a=0;a1)ur(a,r-1,t,e),u=e.length;else for(var f=0,c=a.length;f0&&(t=r.apply(this,arguments)),n<=1&&(r=null),t}}var lr=rr(cr,2);function sr(n,r,t){r=qn(r,t);for(var e,u=nn(n),o=0,i=u.length;o0?0:u-1;o>=0&&o0?a=o>=0?o:Math.max(o+f,a):f=o>=0?Math.min(o+1,f):o+f+1;else if(t&&o&&f)return e[o=t(e,u)]===u?o:-1;if(u!=u)return(o=r(i.call(e,a,f),$))>=0?o+a:-1;for(o=n>0?a:f-1;o>=0&&o0?0:i-1;for(u||(e=r[o?o[a]:a],a+=n);a>=0&&a=3;return r(n,Fn(t,u,4),e,o)}}var Ar=wr(1),xr=wr(-1);function Sr(n,r,t){var e=[];return r=qn(r,t),jr(n,(function(n,t,u){r(n,t,u)&&e.push(n)})),e}function Or(n,r,t){r=qn(r,t);for(var e=!er(n)&&nn(n),u=(e||n).length,o=0;o=0}var Br=j((function(n,r,t){var e,u;return D(r)?u=r:(r=Nn(r),e=r.slice(0,-1),r=r[r.length-1]),_r(n,(function(n){var o=u;if(!o){if(e&&e.length&&(n=In(n,e)),null==n)return;o=n[r]}return null==o?o:o.apply(n,t)}))}));function Nr(n,r){return _r(n,Rn(r))}function Ir(n,r,t){var e,u,o=-1/0,i=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=er(n)?n:jn(n)).length;ao&&(o=e);else r=qn(r,t),jr(n,(function(n,t,e){((u=r(n,t,e))>i||u===-1/0&&o===-1/0)&&(o=n,i=u)}));return o}function Tr(n,r,t){if(null==r||t)return er(n)||(n=jn(n)),n[Wn(n.length-1)];var e=er(n)?En(n):jn(n),u=Y(e);r=Math.max(Math.min(r,u),0);for(var o=u-1,i=0;i1&&(e=Fn(e,r[1])),r=an(n)):(e=qr,r=ur(r,!1,!1),n=Object(n));for(var u=0,o=r.length;u1&&(t=r[1])):(r=_r(ur(r,!1,!1),String),e=function(n,t){return!Er(r,t)}),Ur(n,e,t)}));function zr(n,r,t){return i.call(n,0,Math.max(0,n.length-(null==r||t?1:r)))}function Lr(n,r,t){return null==n||n.length<1?null==r||t?void 0:[]:null==r||t?n[0]:zr(n,n.length-r)}function $r(n,r,t){return i.call(n,null==r||t?1:r)}var Cr=j((function(n,r){return r=ur(r,!0,!0),Sr(n,(function(n){return!Er(r,n)}))})),Kr=j((function(n,r){return Cr(n,r)}));function Jr(n,r,t,e){A(r)||(e=t,t=r,r=!1),null!=t&&(t=qn(t,e));for(var u=[],o=[],i=0,a=Y(n);ir?(e&&(clearTimeout(e),e=null),a=c,i=n.apply(u,o),e||(u=o=null)):e||!1===t.trailing||(e=setTimeout(f,l)),i};return c.cancel=function(){clearTimeout(e),a=0,e=u=o=null},c},debounce:function(n,r,t){var e,u,o,i,a,f=function(){var c=zn()-u;r>c?e=setTimeout(f,r-c):(e=null,t||(i=n.apply(a,o)),e||(o=a=null))},c=j((function(c){return a=this,o=c,u=zn(),e||(e=setTimeout(f,r),t&&(i=n.apply(a,o))),i}));return c.cancel=function(){clearTimeout(e),e=o=a=null},c},wrap:function(n,r){return rr(r,n)},negate:fr,compose:function(){var n=arguments,r=n.length-1;return function(){for(var t=r,e=n[r].apply(this,arguments);t--;)e=n[t].call(this,e);return e}},after:function(n,r){return function(){if(--n<1)return r.apply(this,arguments)}},before:cr,once:lr,findKey:sr,findIndex:vr,findLastIndex:hr,sortedIndex:yr,indexOf:gr,lastIndexOf:br,find:mr,detect:mr,findWhere:function(n,r){return mr(n,Dn(r))},each:jr,forEach:jr,map:_r,collect:_r,reduce:Ar,foldl:Ar,inject:Ar,reduceRight:xr,foldr:xr,filter:Sr,select:Sr,reject:function(n,r,t){return Sr(n,fr(qn(r)),t)},every:Or,all:Or,some:Mr,any:Mr,contains:Er,includes:Er,include:Er,invoke:Br,pluck:Nr,where:function(n,r){return Sr(n,Dn(r))},max:Ir,min:function(n,r,t){var e,u,o=1/0,i=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=er(n)?n:jn(n)).length;ae||void 0===t)return 1;if(t 2 | 3 | 4 | 5 | 6 | Index — PyLabel 0.1.30 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 63 | 64 |
68 | 69 |
70 |
71 |
72 |
    73 |
  • »
  • 74 |
  • Index
  • 75 |
  • 76 |
  • 77 |
78 |
79 |
80 |
81 |
82 | 83 | 84 |

Index

85 | 86 |
87 | A 88 | | C 89 | | D 90 | | E 91 | | G 92 | | I 93 | | L 94 | | M 95 | | N 96 | | P 97 | | R 98 | | S 99 | | U 100 | | V 101 | 102 |
103 |

A

104 | 105 | 109 | 113 |
114 | 115 |

C

116 | 117 | 121 | 127 |
128 | 129 |

D

130 | 131 | 135 | 139 |
140 | 141 |

E

142 | 143 | 149 | 157 |
158 | 159 |

G

160 | 161 | 165 |
166 | 167 |

I

168 | 169 | 175 | 183 |
184 | 185 |

L

186 | 187 | 191 | 195 |
196 | 197 |

M

198 | 199 | 224 |
225 | 226 |

N

227 | 228 | 232 | 238 |
239 | 240 |

P

241 | 242 | 274 | 311 |
    275 |
  • 276 | pylabel.importer 277 | 278 |
  • 282 |
  • 283 | pylabel.labeler 284 | 285 |
  • 289 |
  • 290 | pylabel.shared 291 | 292 |
  • 296 |
  • 297 | pylabel.splitter 298 | 299 |
  • 303 |
  • 304 | pylabel.visualize 305 | 306 |
  • 310 |
312 | 313 |

R

314 | 315 | 319 |
320 | 321 |

S

322 | 323 | 331 | 339 |
340 | 341 |

U

342 | 343 | 347 |
348 | 349 |

V

350 | 351 | 355 | 359 |
360 | 361 | 362 | 363 |
364 |
365 |
366 | 367 |
368 | 369 |
370 |

© Copyright 2021, PyLabel Project.

371 |
372 | 373 | Built with Sphinx using a 374 | theme 375 | provided by Read the Docs. 376 | 377 | 378 |
379 |
380 |
381 |
382 |
383 | 388 | 389 | 390 | -------------------------------------------------------------------------------- /docs/_build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome — PyLabel 0.1.30 documentation 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 65 | 66 |
70 | 71 |
72 |
73 |
74 | 81 |
82 |
83 |
84 |
85 | 86 |
87 |

Welcome

88 |

Documentation for the the PyLabel package at https://github.com/pylabel-project/pylabel/

89 | 107 |
108 |

Integrations :

109 | 113 |
114 |
115 | 116 | 117 |
118 |
119 |
122 | 123 |
124 | 125 |
126 |

© Copyright 2021, PyLabel Project.

127 |
128 | 129 | Built with Sphinx using a 130 | theme 131 | provided by Read the Docs. 132 | 133 | 134 |
135 |
136 |
137 |
138 |
139 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /docs/_build/html/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | pylabel package — PyLabel 0.1.30 documentation 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 64 | 65 |
69 | 70 |
71 |
72 |
73 | 80 |
81 |
82 |
83 |
84 | 85 |
86 |

pylabel package

87 | 104 |
105 | 106 | 107 |
108 |
109 |
110 | 111 |
112 | 113 |
114 |

© Copyright 2021, PyLabel Project.

115 |
116 | 117 | Built with Sphinx using a 118 | theme 119 | provided by Read the Docs. 120 | 121 | 122 |
123 |
124 |
125 |
126 |
127 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pylabel-project/pylabel/6e815ffd61b7d6ba3a1ab3d510b1e6d551e21d93/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_build/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Python Module Index — PyLabel 0.1.30 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 66 | 67 |
71 | 72 |
73 |
74 |
75 |
    76 |
  • »
  • 77 |
  • Python Module Index
  • 78 |
  • 79 |
  • 80 |
81 |
82 |
83 |
84 |
85 | 86 | 87 |

Python Module Index

88 | 89 |
90 | p 91 |
92 | 93 | 94 | 95 | 97 | 98 | 100 | 103 | 104 | 105 | 108 | 109 | 110 | 113 | 114 | 115 | 118 | 119 | 120 | 123 | 124 | 125 | 128 | 129 | 130 | 133 | 134 | 135 | 138 | 139 | 140 | 143 |
 
96 | p
101 | pylabel 102 |
    106 | pylabel.analyze 107 |
    111 | pylabel.dataset 112 |
    116 | pylabel.exporter 117 |
    121 | pylabel.importer 122 |
    126 | pylabel.labeler 127 |
    131 | pylabel.shared 132 |
    136 | pylabel.splitter 137 |
    141 | pylabel.visualize 142 |
144 | 145 | 146 |
147 |
148 |
149 | 150 |
151 | 152 |
153 |

© Copyright 2021, PyLabel Project.

154 |
155 | 156 | Built with Sphinx using a 157 | theme 158 | provided by Read the Docs. 159 | 160 | 161 |
162 |
163 |
164 |
165 |
166 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /docs/_build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search — PyLabel 0.1.30 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 66 | 67 |
71 | 72 |
73 |
74 |
75 |
    76 |
  • »
  • 77 |
  • Search
  • 78 |
  • 79 |
  • 80 |
81 |
82 |
83 |
84 |
85 | 86 | 93 | 94 | 95 |
96 | 97 |
98 | 99 |
100 |
101 |
102 | 103 |
104 | 105 |
106 |

© Copyright 2021, PyLabel Project.

107 |
108 | 109 | Built with Sphinx using a 110 | theme 111 | provided by Read the Docs. 112 | 113 | 114 |
115 |
116 |
117 |
118 |
119 | 124 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/_build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({docnames:["index","modules","pylabel"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["index.rst","modules.rst","pylabel.rst"],objects:{"":[[2,0,0,"-","pylabel"]],"pylabel.analyze":[[2,1,1,"","Analyze"]],"pylabel.analyze.Analyze":[[2,2,1,"","ShowClassSplits"],[2,3,1,"","class_counts"],[2,3,1,"","class_ids"],[2,3,1,"","classes"],[2,3,1,"","num_classes"],[2,3,1,"","num_images"]],"pylabel.dataset":[[2,1,1,"","Dataset"]],"pylabel.dataset.Dataset":[[2,2,1,"","ReindexCatIds"],[2,4,1,"","analyze"],[2,4,1,"","df"],[2,4,1,"","export"],[2,4,1,"","labeler"],[2,4,1,"","name"],[2,4,1,"","path_to_annotations"],[2,4,1,"","splitter"],[2,4,1,"","visualize"]],"pylabel.exporter":[[2,1,1,"","Export"]],"pylabel.exporter.Export":[[2,2,1,"","ExportToCoco"],[2,2,1,"","ExportToVoc"],[2,2,1,"","ExportToYoloV5"]],"pylabel.importer":[[2,5,1,"","ImportCoco"],[2,5,1,"","ImportImagesOnly"],[2,5,1,"","ImportVOC"],[2,5,1,"","ImportYoloV5"],[2,5,1,"","ImportYoloV5WithYaml"]],"pylabel.labeler":[[2,1,1,"","Labeler"]],"pylabel.labeler.Labeler":[[2,2,1,"","StartPyLaber"]],"pylabel.splitter":[[2,1,1,"","Split"]],"pylabel.splitter.Split":[[2,2,1,"","GroupShuffleSplit"],[2,2,1,"","StratifiedGroupShuffleSplit"],[2,2,1,"","UnSplit"]],"pylabel.visualize":[[2,1,1,"","Visualize"]],"pylabel.visualize.Visualize":[[2,2,1,"","ShowBoundingBoxes"]],pylabel:[[2,0,0,"-","analyze"],[2,0,0,"-","dataset"],[2,0,0,"-","exporter"],[2,0,0,"-","importer"],[2,0,0,"-","labeler"],[2,0,0,"-","shared"],[2,0,0,"-","splitter"],[2,0,0,"-","visualize"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","property","Python property"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:property","4":"py:attribute","5":"py:function"},terms:{"0":2,"000000000322":2,"01":2,"1":2,"10":2,"100":2,"11":2,"12":2,"2":2,"25":2,"3":2,"34":2,"35":2,"37":2,"4":2,"5":2,"50":2,"6":2,"63":2,"64":2,"65":2,"66":2,"7":2,"8":2,"9":2,"boolean":2,"class":2,"default":2,"do":2,"export":[0,1],"function":2,"import":[0,1],"int":2,"null":2,"return":2,"true":2,A:2,By:2,For:2,If:2,It:2,The:2,Then:2,Will:2,_:[],about:2,absolut:2,across:2,albemen:[],albument:0,alex:2,all:2,alreadi:2,also:2,an:2,analyz:[0,1],annot:2,anot:2,anoth:2,appli:2,ar:2,associ:2,automat:2,azur:0,base:2,batch_siz:2,bbox:2,becaus:2,between:2,bmp:2,bool:2,bound:2,box:2,call:2,can:2,cat:2,cat_col:2,cat_id:2,cat_id_index:2,cat_index:2,cat_nam:2,categori:2,chang:2,choos:2,class_count:2,class_id:2,cnn:[],coco:2,coco_annot:2,code:2,collumn:2,column:2,com:0,combin:2,command:2,contain:2,content:[0,1],continu:2,convert:2,copi:2,copy_imag:2,correspend:2,count:2,creat:2,current:2,custom:0,data:2,databas:2,database_:2,datafram:2,dataset:[0,1],deriv:2,describ:2,descript:2,detectron:2,dev:2,df:2,differ:2,directli:2,directori:2,disk:2,displai:2,distribut:2,doe:2,drawn:2,each:2,edit:2,empti:2,enter:2,entri:2,equal:2,especi:2,etc:2,exampl:2,exist:2,exporttococo:2,exporttovoc:2,exporttoyolov5:2,extens:2,extract:2,fals:2,field:2,file:2,filenam:2,filter:2,first:2,folder:2,folder_:2,format:2,frame_0002:2,frequenc:2,friendli:2,from:2,gif:2,github:0,group:2,group_col:2,groupshufflesplit:2,ha:2,have:2,height:2,http:0,id:2,imag:2,image_ext:2,img_ext:2,img_filenam:2,img_id:2,importcoco:2,importimagesonli:2,importvoc:2,importyolov5:2,importyolov5withyaml:2,includ:2,increment:2,index:2,inform:2,input:2,instanc:2,integr:[],interact:2,item:2,jpeg:2,jpg:2,json:2,label:[0,1],level:2,lib:2,like:2,list:2,load:2,locat:2,manual:2,method:2,model:2,modul:[0,1],more:2,move:2,much:2,multipl:2,must:2,name:2,name_of_annotations_fold:2,new_class:2,next:2,none:2,normal:2,num_class:2,num_imag:2,number:2,nut:2,object:2,occlud:2,occluded_:2,omit:2,one:2,onli:2,option:2,origin:2,other:2,output:2,output_path:2,packag:0,panda:2,paramet:2,parent:2,part:2,pascal:2,path:2,path_:2,path_to_annot:2,path_to_imag:2,penv:2,pil:2,png:2,prepar:2,preserv:2,primari:2,project:0,properti:2,provid:2,py:2,pylabel:0,python3:2,queri:2,random_st:2,rang:2,read:2,recommend:2,record:2,reindex:2,reindexcatid:2,rel:2,replac:2,repres:2,requir:2,row:2,s:2,same:2,schema:2,see:2,segment:2,segmented_:2,seper:2,sequenti:2,seri:2,set:2,share:[0,1],should:2,show:2,showboundingbox:2,showclasssplit:2,site:2,sklearn:2,so:2,solut:2,some:2,sort:2,sourc:2,specifi:2,specifici:2,split:2,splitter:[0,1],squirrel:2,start:2,startpylab:2,statist:2,store:2,str:2,stratifiedgroupshufflesplit:2,string:2,sub:2,submodul:[0,1],subset:2,summari:2,support:2,tag:2,take:2,test:2,test_pct:2,thei:2,them:2,thi:2,tiff:2,tool:2,train:2,train_pct:2,twice:2,txt:2,type:2,uniqu:2,unsplit:2,us:2,use_split:2,user:2,usual:2,val:2,val_pct:2,valid:2,valu:2,value_count:2,vision:0,visual:[0,1],voc:2,voc_annot:2,weight:2,when:2,where:2,which:2,widget:2,width:2,without:2,would:2,write:2,written:2,xml:2,yaml:2,yaml_fil:2,yet:2,yolo:2,yolo_model:2,yolov5:2,you:2,your:2},titles:["Welcome","pylabel package","pylabel package"],titleterms:{"export":2,"import":2,analyz:2,content:2,dataset:2,document:0,integr:0,label:2,modul:2,packag:[1,2],pylabel:[1,2],share:2,splitter:2,submodul:2,visual:2,welcom:0}}) -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | 16 | sys.path.insert(0, os.path.abspath("..")) 17 | import pylabel 18 | import sphinx_rtd_theme 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = "PyLabel" 24 | copyright = "2021, PyLabel Project" 25 | author = "PyLabel Project" 26 | 27 | # The full version, including alpha/beta/rc tags 28 | release = "0.1.30" 29 | 30 | # -- General configuration --------------------------------------------------- 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx.ext.viewcode"] 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ["_templates"] 38 | 39 | # List of patterns, relative to source directory, that match files and 40 | # directories to ignore when looking for source files. 41 | # This pattern also affects html_static_path and html_extra_path. 42 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 43 | 44 | source_suffix = ".rst" 45 | 46 | # The master toctree document. 47 | master_doc = "index" 48 | 49 | # -- Options for HTML output ------------------------------------------------- 50 | 51 | # The theme to use for HTML and HTML Help pages. See the documentation for 52 | # a list of builtin themes. 53 | # 54 | html_theme = "sphinx_rtd_theme" 55 | # html_theme = 'alabaster' 56 | # html_theme = "furo" 57 | 58 | # Add any paths that contain custom static files (such as style sheets) here, 59 | # relative to this directory. They are copied after the builtin static files, 60 | # so a file named "default.css" will overwrite the builtin "default.css". 61 | html_static_path = ["_static"] 62 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 63 | html_logo = "https://raw.githubusercontent.com/pylabel-project/datasets_models/main/arrow_logo.png" 64 | 65 | html_theme_options = { 66 | "collapse_navigation": False, 67 | } 68 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome 2 | ================== 3 | 4 | Documentation for the the PyLabel package at https://github.com/pylabel-project/pylabel/ 5 | 6 | .. toctree:: 7 | :maxdepth: 3 8 | :caption: Documentation : 9 | 10 | pylabel 11 | 12 | .. toctree:: 13 | :maxdepth: 3 14 | :caption: Integrations : 15 | 16 | Albumentations 17 | Azure Custom Vision 18 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | pylabel package 2 | =============== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | pylabel 8 | -------------------------------------------------------------------------------- /docs/pylabel.rst: -------------------------------------------------------------------------------- 1 | pylabel package 2 | =============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pylabel.analyze module 8 | ---------------------- 9 | 10 | .. automodule:: pylabel.analyze 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pylabel.dataset module 16 | ---------------------- 17 | 18 | .. automodule:: pylabel.dataset 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pylabel.exporter module 24 | ----------------------- 25 | 26 | .. automodule:: pylabel.exporter 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pylabel.importer module 32 | ----------------------- 33 | 34 | .. automodule:: pylabel.importer 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pylabel.labeler module 40 | ---------------------- 41 | 42 | .. automodule:: pylabel.labeler 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pylabel.shared module 48 | --------------------- 49 | 50 | .. automodule:: pylabel.shared 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | pylabel.splitter module 56 | ----------------------- 57 | 58 | .. automodule:: pylabel.splitter 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | pylabel.visualize module 64 | ------------------------ 65 | 66 | .. automodule:: pylabel.visualize 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | Module contents 72 | --------------- 73 | 74 | .. automodule:: pylabel 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | bbox_visualizer 3 | matplotlib 4 | opencv-python 5 | scikit-learn 6 | jupyter_bbox_widget 7 | pyyaml 8 | pytest 9 | pytest-lazy-fixture 10 | black 11 | nbmake 12 | sphinx_rtd_theme 13 | tqdm 14 | -------------------------------------------------------------------------------- /pylabel/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pandas as pd 3 | import os 4 | import matplotlib 5 | -------------------------------------------------------------------------------- /pylabel/analyze.py: -------------------------------------------------------------------------------- 1 | """The analyze module analyzes the contents of the dataset and provides summary statistics 2 | such as the count of images and classes. """ 3 | 4 | import pandas as pd 5 | import numpy as np 6 | 7 | 8 | class Analyze: 9 | def __init__(self, dataset=None): 10 | self.dataset = dataset 11 | # self.split_counts = ds.df["split"].value_counts(dropna=False) 12 | # self.split_pct = ds.df["split"].value_counts(normalize=True, dropna=False) 13 | 14 | @property 15 | def classes(self): 16 | """Returns list of all cat names in the dataset sorted by cat_id value. 17 | 18 | Returns: 19 | List 20 | 21 | Example: 22 | >>> dataset.analyze.classes 23 | ["Squirrel", "Nut"] 24 | """ 25 | 26 | self.dataset.df.cat_id = self.dataset.df.cat_id.replace( 27 | r"^\s*$", np.nan, regex=True 28 | ) 29 | pd.to_numeric(self.dataset.df["cat_id"]) 30 | 31 | filtered_df = self.dataset.df[self.dataset.df["cat_id"].notnull()] 32 | categories = dict(zip(filtered_df.cat_name, filtered_df.cat_id.astype("int"))) 33 | categories = sorted(categories.items(), key=lambda x: x[1]) 34 | return [c[0] for c in categories if str(c[0]).strip() != ""] 35 | 36 | @property 37 | def class_ids(self): 38 | """Returns a sorted list of all cat ids in the dataset. 39 | 40 | Returns: 41 | List 42 | 43 | Example: 44 | >>> dataset.analyze.class_ids 45 | [0,1] 46 | """ 47 | filtered_df = self.dataset.df[self.dataset.df["cat_id"].notnull()] 48 | cat_ids = list(filtered_df.cat_id.astype("int").unique()) 49 | cat_ids.sort() 50 | return cat_ids 51 | 52 | @property 53 | def class_counts(self): 54 | """Counts the numbers of instances of each class in the dataset. Uses the Pandas value_counts 55 | method and returns a Pandas Series. 56 | 57 | Returns: 58 | Pandas Series 59 | Example: 60 | >>> dataset.analyze.class_counts 61 | squirrel 50 62 | nut 100 63 | """ 64 | return self.dataset.df["cat_name"].value_counts(dropna=False) 65 | 66 | @property 67 | def num_classes(self): 68 | """Counts the unique number of classes in the dataset. 69 | 70 | Returns: 71 | Int 72 | Example: 73 | >>> dataset.analyze.num_classes 74 | 2 75 | """ 76 | cat_names = list(self.dataset.df.cat_name.unique()) 77 | return len([i for i in cat_names if str(i).strip() != ""]) 78 | 79 | @property 80 | def num_images(self): 81 | """Counts the number of images in the dataset. 82 | 83 | Returns: 84 | Int 85 | Example: 86 | >>> dataset.analyze.num_images 87 | 100 88 | """ 89 | return self.dataset.df["img_filename"].nunique() 90 | 91 | @property 92 | def class_name_id_map(self): 93 | """Returns a dict where the class name is the key and class id is the value. 94 | 95 | Returns: 96 | Dict 97 | 98 | Example: 99 | >>> dataset.analyze.class_name_id_map 100 | {('Squirrel', 0),('Nut', 1)} 101 | """ 102 | return dict(zip(self.dataset.df.cat_name, self.dataset.df.cat_id)) 103 | 104 | def ShowClassSplits(self, normalize=True): 105 | """Show the distribution of classes across train, val, and 106 | test splits of the dataset. 107 | 108 | Args: 109 | normalize (bool): Defaults to True. 110 | If True, then will return the relative frequencies of the classes between 0 and 1. 111 | If False, then will return the absolute counts of each class. 112 | 113 | Returns: 114 | Pandas Dataframe 115 | 116 | Examples: 117 | >>> dataset.analyze.ShowClassSplits(normalize=True) 118 | cat_name all train test dev 119 | squirrel .66 .64 .65 .63 120 | nut .34 .34 .35 .37 121 | 122 | >>> dataset.analyze.ShowClassSplits(normalize=False) 123 | cat_name all train test dev 124 | squirrel 66 64 65 63 125 | nut 34 34 35 37 126 | 127 | """ 128 | ds = self.dataset 129 | 130 | def move_column_inplace(df, col, pos): 131 | """ 132 | Assists to rearrange columns to a desired order. 133 | """ 134 | col = df.pop(col) 135 | df.insert(pos, col.name, col) 136 | 137 | df_value_counts = pd.DataFrame( 138 | ds.df["cat_name"].value_counts(normalize=normalize), columns=["cat_name"] 139 | ) 140 | 141 | df_value_counts.index.name = "cat_name" 142 | df_value_counts.columns = ["all"] 143 | 144 | split_df = ds.df.groupby("split") 145 | 146 | if split_df.ngroups == 1: 147 | return df_value_counts 148 | 149 | for name, group in split_df: 150 | group_df = pd.DataFrame(group) 151 | df_split_value_counts = pd.DataFrame( 152 | group_df["cat_name"].value_counts(normalize=normalize), 153 | columns=["cat_name"], 154 | ) 155 | df_split_value_counts.index.name = "cat_name" 156 | df_split_value_counts.columns = [name] 157 | df_value_counts = pd.merge( 158 | df_value_counts, df_split_value_counts, how="left", on=["cat_name"] 159 | ) 160 | 161 | # Move 'train' to the left of the table since that is the usual convention. 162 | if "train" in df_value_counts.columns: 163 | move_column_inplace(df_value_counts, "train", 1) 164 | 165 | return df_value_counts 166 | -------------------------------------------------------------------------------- /pylabel/dataset.py: -------------------------------------------------------------------------------- 1 | """The dataset is the primary object that you will interactive with when using PyLabel. 2 | All other modules are sub-modules of the dataset object. 3 | """ 4 | 5 | from pylabel.analyze import Analyze 6 | from pylabel.exporter import Export 7 | from pylabel.visualize import Visualize 8 | from pylabel.labeler import Labeler 9 | from pylabel.splitter import Split 10 | from pylabel.shared import _ReindexCatIds 11 | 12 | import numpy as np 13 | 14 | 15 | class Dataset: 16 | def __init__(self, df): 17 | self.df = df 18 | """Pandas Dataframe: The dataframe where annotations are stored. This dataframe can be read directly 19 | to query the contents of the dataset. You can also edit this dataframe to filter records or edit the 20 | annotations directly. 21 | 22 | Example: 23 | >>> dataset.df 24 | """ 25 | self.name = "dataset" 26 | """string: Default is 'dataset'. A friendly name for your dataset that is used as part of the filename(s) 27 | when exporting annotation files. 28 | """ 29 | self.path_to_annotations = "" 30 | """string: Default is ''. The path to the annotation files associated with the dataset. When importing, 31 | this will be path to the directory where the annotations are stored. By default, annotations will be exported 32 | to the same directory. Changing this value will change where the annotations are exported to. 33 | """ 34 | self.export = Export(dataset=self) 35 | """See pylabel.export module.""" 36 | self.visualize = Visualize(dataset=self) 37 | """See pylabel.visualize module.""" 38 | self.analyze = Analyze(dataset=self) 39 | """See pylabel.analyze module.""" 40 | self.labeler = Labeler(self) 41 | """See pylabel.labeler module.""" 42 | self.splitter = Split(dataset=self) 43 | """See pylabel.splitter module.""" 44 | 45 | def ReindexCatIds(self, cat_id_index=0): 46 | """ 47 | Reindex the values of the cat_id column so that that they start from an int (usually 0 or 1) and 48 | then increment the cat_ids to index + number of categories. 49 | It's useful if the cat_ids are not continuous, especially for dataset subsets, 50 | or combined multiple datasets. Some models like Yolo require starting from 0 and others 51 | like Detectron require starting from 1. 52 | 53 | Args: 54 | cat_id_index (int): Defaults to 0. 55 | The cat ids will increment sequentially the cat_index value. For example if there are 10 56 | classes then the cat_ids will be a range from 0-9. 57 | 58 | Example: 59 | >>> dataset.analyze.class_ids 60 | [1,2,4,5,6,7,8,9,11,12] 61 | >>> dataset.ReindexCatIds(cat_id_index) = 0 62 | >>> dataset.analyze.class_ids 63 | [0,1,2,3,4,5,6,7,8,9] 64 | """ 65 | assert isinstance(cat_id_index, int), "cat_id_index must be an int." 66 | _ReindexCatIds(self.df, cat_id_index) 67 | -------------------------------------------------------------------------------- /pylabel/labeler.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from types import new_class 3 | import ipywidgets as widgets 4 | from pathlib import PurePath 5 | from jupyter_bbox_widget import BBoxWidget 6 | from ipywidgets import Layout 7 | import numpy as np 8 | 9 | 10 | class Labeler: 11 | def __init__(self, dataset=None): 12 | self.dataset = dataset 13 | 14 | def StartPyLaber(self, new_classes=None, image=None, yolo_model=None): 15 | """Display the bbox widget loaded with images and annotations from this dataset.""" 16 | 17 | if "google.colab" in str(get_ipython()): 18 | from google.colab import output 19 | 20 | output.enable_custom_widget_manager() 21 | 22 | dataset = self.dataset 23 | widget_output = None 24 | 25 | files = dataset.df.img_filename.unique() 26 | files = files.tolist() 27 | 28 | global file_index 29 | 30 | if image == None: 31 | file_index = 0 32 | image = files[0] 33 | else: 34 | file_index = files.index(image) 35 | 36 | def GetBBOXs(image): 37 | # Make a dataframe with the annotations for a single image 38 | img_df = dataset.df.loc[dataset.df["img_filename"] == image] 39 | img_df_subset = img_df[ 40 | [ 41 | "cat_name", 42 | "ann_bbox_height", 43 | "ann_bbox_width", 44 | "ann_bbox_xmin", 45 | "ann_bbox_ymin", 46 | ] 47 | ] 48 | # Rename the columns to match the format used by jupyter_bbox_widget 49 | img_df_subset.columns = ["label", "height", "width", "x", "y"] 50 | 51 | # Drop rows that have NaN, invalid bounding boxes 52 | img_df_subset = img_df_subset.dropna() 53 | bboxes_dict = img_df_subset.to_dict(orient="records") 54 | return bboxes_dict 55 | 56 | def GetImageLabel(image): 57 | """Returns the imagename or imagename + (not annotated)""" 58 | # Make a dataframe with the annotations for a single image 59 | img_df = dataset.df.loc[dataset.df["img_filename"] == image] 60 | 61 | if img_df.iloc[0].annotated == 1: 62 | return image 63 | else: 64 | return f"{image} (not annotated)" 65 | return 66 | 67 | bboxes_dict = GetBBOXs(image) 68 | 69 | img_folder = dataset.df.loc[dataset.df["img_filename"] == image].iloc[0][ 70 | "img_folder" 71 | ] 72 | 73 | file_paths = [ 74 | str(PurePath(dataset.path_to_annotations, img_folder, file)) 75 | for file in files 76 | ] 77 | 78 | def encode_image(filepath): 79 | with open(filepath, "rb") as f: 80 | image_bytes = f.read() 81 | encoded = str(base64.b64encode(image_bytes), "utf-8") 82 | return "data:image/jpg;base64," + encoded 83 | 84 | def UpdateCategoryList(cat_dict, new_categories): 85 | from math import isnan 86 | 87 | # Remove invalid entries 88 | cat_dict.pop("", None) 89 | # cat_dict = {k: v for k, v in cat_dict.items() if not isnan(v)} 90 | 91 | for cat in new_categories: 92 | if len(cat_dict) == 0: 93 | cat_dict[cat] = "0" 94 | elif cat in list(cat_dict.keys()): 95 | continue 96 | else: 97 | # Create a new cat id that 1+ the highest cat id value 98 | new_cat_id = max([int(v) for v in cat_dict.values()]) + 1 99 | cat_dict[cat] = str(new_cat_id) 100 | 101 | return cat_dict 102 | 103 | def on_submit(b): 104 | # save annotations for current image 105 | import pandas as pd 106 | 107 | global widget_output 108 | global file_index 109 | 110 | img_filename = files[file_index] 111 | 112 | widget_output = pd.DataFrame.from_dict(w_bbox.bboxes) 113 | 114 | # Check if there are any bounding boxes for the current image: 115 | if not widget_output.empty: 116 | # If there are bounding boxes then add them to the dataset 117 | widget_output = widget_output.rename( 118 | columns={ 119 | "label": "cat_name", 120 | "height": "ann_bbox_height", 121 | "width": "ann_bbox_width", 122 | "x": "ann_bbox_xmin", 123 | "y": "ann_bbox_ymin", 124 | } 125 | ) 126 | 127 | widget_output["ann_area"] = ( 128 | widget_output["ann_bbox_height"] * widget_output["ann_bbox_width"] 129 | ) 130 | widget_output["cat_name"] = widget_output["cat_name"].astype("string") 131 | categories = dict(zip(dataset.df.cat_name, dataset.df.cat_id)) 132 | categories = UpdateCategoryList( 133 | categories, list(widget_output.cat_name) 134 | ) 135 | widget_output["cat_id"] = widget_output["cat_name"].map(categories) 136 | 137 | else: 138 | # Build a entry in the table with empty bounding box columns 139 | widget_output = pd.DataFrame( 140 | [], 141 | columns=[ 142 | "cat_name", 143 | "cat_id", 144 | "ann_bbox_height", 145 | "ann_bbox_width", 146 | "ann_bbox_xmin", 147 | "ann_bbox_ymin", 148 | "ann_area", 149 | ], 150 | ) 151 | 152 | widget_output.loc[0] = [ 153 | "", 154 | np.NaN, 155 | np.NaN, 156 | np.NaN, 157 | np.NaN, 158 | np.NaN, 159 | np.NaN, 160 | ] 161 | 162 | widget_output["img_filename"] = str(img_filename) 163 | widget_output["img_filename"] = widget_output["img_filename"].astype( 164 | "string" 165 | ) 166 | widget_output.index.name = "id" 167 | 168 | img_df = dataset.df.loc[dataset.df["img_filename"] == img_filename] 169 | 170 | # Get the metadata associated with this image from the dataset 171 | metadata = img_df.iloc[0].to_frame().T 172 | 173 | metadata["img_filename"] = metadata["img_filename"].astype("string") 174 | 175 | # Drop the fields that are in the widget_output dataframe 176 | metadata.drop( 177 | [ 178 | "cat_name", 179 | "cat_id", 180 | "ann_area", 181 | "ann_bbox_height", 182 | "ann_bbox_width", 183 | "ann_bbox_xmin", 184 | "ann_bbox_ymin", 185 | ], 186 | axis=1, 187 | inplace=True, 188 | ) 189 | 190 | widget_output = widget_output.merge( 191 | metadata, left_on="img_filename", right_on="img_filename" 192 | ) 193 | widget_output = widget_output[dataset.df.columns] 194 | 195 | # Set annotated = 1, which means the annotates have been reviewed and accepted 196 | widget_output["annotated"] = 1 197 | 198 | # Now we have a dataframe with output of the bbox widget 199 | # Drop the current annotations for the image and add the the new ones 200 | dataset.df.drop( 201 | dataset.df[dataset.df["img_filename"] == img_filename].index, 202 | inplace=True, 203 | ) 204 | dataset.df.reset_index(drop=True, inplace=True) 205 | 206 | dataset.df = dataset.df.append(widget_output).reset_index(drop=True) 207 | 208 | # move on to the next file 209 | on_next(b) 210 | 211 | def on_next(b): 212 | global file_index 213 | 214 | file_index += 1 215 | # open new image in the widget 216 | image_file = file_paths[file_index] 217 | w_bbox.image = encode_image(image_file) 218 | w_bbox.bboxes = GetBBOXs(files[file_index]) 219 | progress_label.value = f"{file_index+1} / {len(files)}" 220 | current_img_name_label.value = GetImageLabel(files[file_index]) 221 | 222 | def on_back(b): 223 | global file_index 224 | file_index -= 1 225 | # open new image in the widget 226 | image_file = file_paths[file_index] 227 | w_bbox.image = encode_image(image_file) 228 | w_bbox.bboxes = GetBBOXs(files[file_index]) 229 | progress_label.value = f"{file_index+1} / {len(files)}" 230 | current_img_name_label.value = GetImageLabel(files[file_index]) 231 | 232 | def on_add_class(b): 233 | if new_class_text.value.strip() != "": 234 | class_list = list(w_bbox.classes) 235 | class_list.append(new_class_text.value) 236 | w_bbox.classes = list(set(class_list)) 237 | new_class_text.value = "" 238 | 239 | def on_predict(b): 240 | global file_index 241 | image_file = file_paths[file_index] 242 | 243 | result = yolo_model(image_file) 244 | result = result.pandas().xyxy[0] 245 | result["width"] = result.xmax - result.xmin 246 | result["height"] = result.ymax - result.ymin 247 | result.drop(["class", "confidence", "xmax", "ymax"], axis=1, inplace=True) 248 | result.columns = ["x", "y", "label", "width", "height"] 249 | result = result[["label", "height", "width", "x", "y"]] 250 | bboxes_dict = result.to_dict(orient="records") 251 | w_bbox.bboxes = bboxes_dict 252 | # Add the predicted classes to the widget so they can be selected by the user 253 | w_bbox.classes = list(set(w_bbox.classes + list(result["label"]))) 254 | 255 | if new_classes: 256 | classes = dataset.analyze.classes + new_classes 257 | else: 258 | classes = dataset.analyze.classes 259 | 260 | # remove empty labels and duplicate labels 261 | classes = list( 262 | set([str(c).strip() for c in classes if len(str(c).strip()) > 0]) 263 | ) 264 | 265 | # Load BBoxWidget for first load on page 266 | w_bbox = BBoxWidget( 267 | image=encode_image(file_paths[file_index]), 268 | classes=classes, 269 | bboxes=bboxes_dict, 270 | hide_buttons=True, 271 | ) 272 | 273 | progress_txt = f"{file_index+1} / {len(files)}" 274 | 275 | left_arrow_btn = widgets.Button( 276 | icon="fa-arrow-left", layout=Layout(width="35px") 277 | ) 278 | progress_label = widgets.Label(value=progress_txt) 279 | current_img_name_label = widgets.Label(value=GetImageLabel(image)) 280 | 281 | right_arrow_btn = widgets.Button( 282 | icon="fa-arrow-right", layout=Layout(width="35px") 283 | ) 284 | save_btn = widgets.Button( 285 | icon="fa-check", description="Save", layout=Layout(width="70px") 286 | ) 287 | predict_btn = widgets.Button( 288 | icon="fa-eye", description="Predict", layout=Layout(width="100px") 289 | ) 290 | train_btn = widgets.Button( 291 | icon="fa-refresh", description="Train", layout=Layout(width="100px") 292 | ) 293 | add_class_label = widgets.Label(value="Add class:") 294 | new_class_text = widgets.Text(layout=Layout(width="200px")) 295 | plus_btn = widgets.Button(icon="fa-plus", layout=Layout(width="35px")) 296 | 297 | button_row_list = [left_arrow_btn, progress_label, right_arrow_btn, save_btn] 298 | 299 | # If model arg is empty hide predict and train buttons 300 | if yolo_model != None: 301 | button_row_list = button_row_list + [predict_btn] # train_btn 302 | 303 | button_row = widgets.HBox(button_row_list) 304 | current_img_details_row = widgets.HBox([current_img_name_label]) 305 | 306 | bottom_row = widgets.HBox([add_class_label, new_class_text, plus_btn]) 307 | 308 | pylabler = widgets.VBox( 309 | [current_img_details_row, button_row, w_bbox, bottom_row] 310 | ) 311 | 312 | save_btn.on_click(on_submit) 313 | left_arrow_btn.on_click(on_back) 314 | right_arrow_btn.on_click(on_next) 315 | predict_btn.on_click(on_predict) 316 | plus_btn.on_click(on_add_class) 317 | new_class_text.on_submit(on_add_class) 318 | 319 | # Returning the container will show the widgets 320 | return pylabler 321 | -------------------------------------------------------------------------------- /pylabel/shared.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | ######### 5 | # This file has variables and functions that are used by the rest of the package 6 | ######### 7 | 8 | # These are the valid columns in the pylabel annotations table. 9 | schema = [ 10 | "img_folder", 11 | "img_filename", 12 | "img_path", 13 | "img_id", 14 | "img_width", 15 | "img_height", 16 | "img_depth", 17 | "ann_segmented", 18 | "ann_bbox_xmin", 19 | "ann_bbox_ymin", 20 | "ann_bbox_xmax", 21 | "ann_bbox_ymax", 22 | "ann_bbox_width", 23 | "ann_bbox_height", 24 | "ann_area", 25 | "ann_segmentation", 26 | "ann_iscrowd", 27 | "ann_keypoints", 28 | "ann_pose", 29 | "ann_truncated", 30 | "ann_difficult", 31 | "cat_id", 32 | "cat_name", 33 | "cat_supercategory", 34 | "split", 35 | "annotated", 36 | ] 37 | # schema = ['id','img_folder','img_filename','img_path','img_id','img_width','img_height','img_depth','ann_segmented','ann_bbox_xmin','ann_bbox_ymin','ann_bbox_xmax','ann_bbox_ymax','ann_bbox_width','ann_bbox_height','ann_area','ann_segmentation','ann_iscrowd','ann_pose','ann_truncated','ann_difficult','cat_id','cat_name','cat_supercategory','split'] 38 | 39 | 40 | def _ReindexCatIds(df, cat_id_index=0): 41 | """ 42 | Reindex the values of the cat_id column so that that they start from an int (usually 0 or 1) and 43 | then increment the cat_ids to index + number of categories. 44 | It's useful if the cat_ids are not continuous, especially for dataset subsets, 45 | or combined multiple datasets. Some models like Yolo require starting from 0 and others 46 | like Detectron require starting from 1. 47 | """ 48 | assert isinstance(cat_id_index, int), "cat_id_index must be an int." 49 | 50 | # Convert empty strings to NaN and drop rows with NaN cat_id 51 | df_copy = df.replace(r"^\s*$", np.nan, regex=True) 52 | df_copy = df_copy[df.cat_id.notnull()] 53 | # Coerce cat_id to int 54 | df_copy["cat_id"] = pd.to_numeric(df_copy["cat_id"]) 55 | 56 | # Map cat_ids to the range [cat_id_index, cat_id_index + num_cats) 57 | unique_ids = np.sort(df_copy["cat_id"].unique()) 58 | ids_dict = dict((v, k) 59 | for k, v in enumerate(unique_ids, start=cat_id_index)) 60 | df_copy["cat_id"] = df_copy["cat_id"].map(ids_dict) 61 | 62 | # Write back to the original dataframe 63 | df["cat_id"] = df_copy["cat_id"] 64 | -------------------------------------------------------------------------------- /pylabel/splitter.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from sklearn.model_selection import GroupShuffleSplit as sklearnGroupShuffleSplit 4 | from pylabel.shared import schema 5 | from tqdm import tqdm 6 | 7 | 8 | class Split: 9 | def __init__(self, dataset=None): 10 | self.dataset = dataset 11 | 12 | def UnSplit(self): 13 | """Unsplit the dataset by setting all values of the split column to null.""" 14 | self.dataset.df["split"] = np.nan 15 | 16 | def GroupShuffleSplit( 17 | self, 18 | train_pct=0.5, 19 | test_pct=0.25, 20 | val_pct=0.25, 21 | group_col="img_filename", 22 | random_state=None, 23 | ): 24 | """ 25 | This function uses the GroupShuffleSplit command from sklearn. It can split into 3 groups (train, 26 | test, and val) by applying the command twice. If you want to split into only 2 groups (train and test), 27 | then set val_pct to 0. 28 | """ 29 | 30 | # Check inputs and raise errors if needed 31 | assert 0 < float(train_pct) < 1, "train_pct must be between 0 and 1" 32 | assert 0 < float(test_pct) < 1, "test_pct must be between 0 and 1" 33 | # check that the sum of train_pct, test_pct, and val_pct is equal to 1 34 | assert ( 35 | round(train_pct + test_pct + val_pct, 1) == 1 36 | ), "Sum of train_pct, test_pct, and val_pct must equal 1." 37 | 38 | df_main = self.dataset.df 39 | gss = sklearnGroupShuffleSplit( 40 | n_splits=1, train_size=train_pct, random_state=random_state 41 | ) 42 | train_indexes, test_indexes = next( 43 | gss.split(X=df_main, y=df_main[group_col], groups=df_main.index.values) 44 | ) 45 | 46 | df_main.loc[train_indexes, "split"] = "train" 47 | df_main.loc[test_indexes, "split"] = "test" 48 | self.dataset.df = df_main 49 | 50 | if val_pct: 51 | df_train = df_main.loc[df_main["split"] == "train"] 52 | df_test = df_main.loc[df_main["split"] == "test"] 53 | df_test = df_test.reset_index() 54 | second_split_pct = float(test_pct / (test_pct + val_pct)) 55 | gss2 = sklearnGroupShuffleSplit( 56 | n_splits=1, train_size=second_split_pct, random_state=random_state 57 | ) 58 | test_indexes_2, val_indexes_2 = next( 59 | gss2.split(X=df_test, y=df_test[group_col], groups=df_test.index.values) 60 | ) 61 | df_test.loc[test_indexes_2, "split"] = "test" 62 | df_test.loc[val_indexes_2, "split"] = "val" 63 | self.dataset.df = pd.concat([df_train, df_test]) 64 | self.dataset.df = self.dataset.df.reset_index(drop=True) 65 | self.dataset.df = self.dataset.df[schema] 66 | 67 | # Written with the help of https://stackoverflow.com/questions/56872664/complex-dataset-split-stratifiedgroupshufflesplit 68 | def StratifiedGroupShuffleSplit( 69 | self, 70 | train_pct=0.7, 71 | test_pct=0.3, 72 | val_pct=0.0, 73 | weight=0.01, 74 | group_col="img_filename", 75 | cat_col="cat_name", 76 | batch_size=1, 77 | ): 78 | """ 79 | This function will 'split" the dataframe by setting the split collumn equal to 80 | train, test, or val. When a split dataset is exported the annotations will be split into 81 | seperate groups so that can be used used in model training, testing, and validation. 82 | """ 83 | 84 | # Check inputs and raise errors if needed 85 | assert ( 86 | 0 <= float(train_pct) <= 1 87 | ), "train_pct must be greater than or equal to 0 and less than or equal to 1" 88 | assert ( 89 | 0 <= float(test_pct) <= 1 90 | ), "test_pct must be greater than or equal to 0 and less than or equal to 1" 91 | assert ( 92 | 0 <= float(val_pct) <= 1 93 | ), "val_pct must be greater than or equal to 0 and less than or equal to 1" 94 | # check that the sum of train_pct, test_pct, and val_pct is equal to 1 95 | assert ( 96 | round(train_pct + test_pct + val_pct, 1) == 1 97 | ), "Sum of train_pct, test_pct, and val_pct must equal 1." 98 | 99 | df_main = self.dataset.df 100 | df_main = df_main.reindex( 101 | np.random.permutation(df_main.index) 102 | ) # shuffle dataset 103 | 104 | # create empty train, val and test datasets 105 | df_train = pd.DataFrame() 106 | df_val = pd.DataFrame() 107 | df_test = pd.DataFrame() 108 | 109 | subject_grouped_df_main = df_main.groupby( 110 | [group_col], sort=False, as_index=False 111 | ) 112 | category_grouped_df_main = ( 113 | df_main.groupby(cat_col).count()[[group_col]] / len(df_main) * 100 114 | ) 115 | 116 | # Check inputs 117 | assert 0 <= weight <= 1, "Weight must be between 0 and 1" 118 | total_splits = round((train_pct) + float(test_pct) + float(val_pct), 1) 119 | assert ( 120 | total_splits == 1 121 | ), "Sum of train_pct, test_pct, and val_pct must equal 1." 122 | assert ( 123 | batch_size >= 1 and batch_size <= subject_grouped_df_main.ngroups / 10 124 | ), "Batch must be greater than 1 and less than 1/10 count of groups" 125 | 126 | def calc_mse_loss(df): 127 | grouped_df = df.groupby(cat_col).count()[[group_col]] / len(df) * 100 128 | df_temp = category_grouped_df_main.join( 129 | grouped_df, on=cat_col, how="left", lsuffix="_main" 130 | ) 131 | df_temp.fillna(0, inplace=True) 132 | df_temp["diff"] = (df_temp["img_filename_main"] - df_temp[group_col]) ** 2 133 | mse_loss = np.mean(df_temp["diff"]) 134 | return mse_loss 135 | 136 | i = 0 # counter for all items in dataset 137 | b = 0 # counter for the batches 138 | batch_df = df_main[0:0] 139 | 140 | # Use tqdm in the for loop to show progress bar and iterate through the groups 141 | pbar = tqdm(total=subject_grouped_df_main.ngroups, desc="Splitting dataset") 142 | 143 | for _, group in subject_grouped_df_main: 144 | if i < 3: 145 | if i == 0: 146 | df_train = pd.concat( 147 | [df_train, pd.DataFrame(group)], ignore_index=True 148 | ) 149 | i += 1 150 | continue 151 | elif i == 1: 152 | df_val = pd.concat([df_val, pd.DataFrame(group)], ignore_index=True) 153 | i += 1 154 | continue 155 | else: 156 | df_test = pd.concat( 157 | [df_test, pd.DataFrame(group)], ignore_index=True 158 | ) 159 | i += 1 160 | continue 161 | 162 | # Add groups to the batch 163 | batch_df = pd.concat([batch_df, group]) 164 | b += 1 165 | if b < batch_size and i < subject_grouped_df_main.ngroups - 3: 166 | i += 1 167 | continue 168 | 169 | mse_loss_diff_train = calc_mse_loss(df_train) - calc_mse_loss( 170 | pd.concat([df_train, batch_df], ignore_index=True) 171 | ) 172 | mse_loss_diff_val = calc_mse_loss(df_val) - calc_mse_loss( 173 | pd.concat([df_train, batch_df], ignore_index=True) 174 | ) 175 | mse_loss_diff_test = calc_mse_loss(df_test) - calc_mse_loss( 176 | pd.concat([df_train, batch_df], ignore_index=True) 177 | ) 178 | 179 | total_records = len(df_train) + len(df_val) + len(df_test) 180 | 181 | len_diff_train = train_pct - (len(df_train) / total_records) 182 | len_diff_val = val_pct - (len(df_val) / total_records) 183 | len_diff_test = test_pct - (len(df_test) / total_records) 184 | 185 | len_loss_diff_train = len_diff_train * abs(len_diff_train) 186 | len_loss_diff_val = len_diff_val * abs(len_diff_val) 187 | len_loss_diff_test = len_diff_test * abs(len_diff_test) 188 | 189 | loss_train = (weight * mse_loss_diff_train) + ( 190 | (1 - weight) * len_loss_diff_train 191 | ) 192 | loss_val = (weight * mse_loss_diff_val) + ((1 - weight) * len_loss_diff_val) 193 | loss_test = (weight * mse_loss_diff_test) + ( 194 | (1 - weight) * len_loss_diff_test 195 | ) 196 | 197 | if max(loss_train, loss_val, loss_test) == loss_train: 198 | df_train = pd.concat([df_train, batch_df], ignore_index=True) 199 | elif max(loss_train, loss_val, loss_test) == loss_val: 200 | df_val = pd.concat([df_val, batch_df], ignore_index=True) 201 | else: 202 | df_test = pd.concat([df_test, batch_df], ignore_index=True) 203 | 204 | # print ("Group " + str(i) + ". loss_train: " + str(loss_train) + " | " + "loss_val: " + str(loss_val) + " | " + "loss_test: " + str(loss_test) + " | ") 205 | i += 1 206 | 207 | # update the progress bar 208 | pbar.update(b) 209 | 210 | # Reset the batch 211 | b = 0 212 | batch_df = df_main[0:0] 213 | 214 | ###### 215 | # Final prep tasks before returning the split dataframe 216 | 217 | # Sometimes the algo will put some rows in the val set even if the split percent was set to zero 218 | # In those cases move the rows from val to test 219 | if round(val_pct, 1) == round(0, 1): 220 | # Move the rows from val to test and remove the rows from val 221 | df_test = pd.concat([df_test, df_val]) 222 | df_val = df_val[0:0] # remove the values from val 223 | 224 | # Apply train, split, val labels to the split collumn 225 | df_train["split"] = "train" 226 | df_test["split"] = "test" 227 | df_val["split"] = "val" 228 | 229 | # Combine the train, test, and val dataframes 230 | df = pd.concat([df_train, pd.concat([df_test, df_val])]) 231 | 232 | assert ( 233 | df.shape == df_main.shape 234 | ), "Output shape does not match input shape. Data loss has occured." 235 | 236 | self.dataset.df = df 237 | self.dataset.df = self.dataset.df.reset_index(drop=True) 238 | self.dataset.df = self.dataset.df[schema] 239 | 240 | # set progtess bar to 100% 241 | pbar.update(subject_grouped_df_main.ngroups) 242 | pbar.close() 243 | -------------------------------------------------------------------------------- /pylabel/visualize.py: -------------------------------------------------------------------------------- 1 | import bbox_visualizer as bbv 2 | import cv2 3 | from PIL import Image 4 | from pathlib import Path 5 | 6 | class Visualize: 7 | def __init__(self, dataset=None): 8 | self.dataset = dataset 9 | 10 | """Functions to visualize inspect images and annotations.""" 11 | def ShowBoundingBoxes(self, img_id:int=0, img_filename:str="") -> Image: 12 | """Enter a filename or index number and return the image with the bounding boxes drawn.""" 13 | 14 | ds = self.dataset 15 | 16 | #Handle cases where user enters image name in default field 17 | if type(img_id) == str: 18 | img_filename = img_id 19 | 20 | if img_filename == "": 21 | df_single_img_annots = ds.df.loc[ds.df.img_id == img_id] 22 | else: 23 | df_single_img_annots = ds.df.loc[ds.df.img_filename == img_filename] 24 | 25 | full_image_path = str(Path(ds.path_to_annotations, df_single_img_annots.iloc[0].img_folder, df_single_img_annots.iloc[0].img_filename)) 26 | img = cv2.imread(str(full_image_path)) 27 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 28 | 29 | labels = [] 30 | bboxes = [] 31 | 32 | for index, row in df_single_img_annots.iterrows(): 33 | # If there are no annotations, then skip because there are no boxes to draw 34 | if row["ann_bbox_xmin"] != "": 35 | labels.append(row["cat_name"]) 36 | bboxes.append( 37 | [ 38 | int(row["ann_bbox_xmin"]), 39 | int(row["ann_bbox_ymin"]), 40 | int(row["ann_bbox_xmax"]), 41 | int(row["ann_bbox_ymax"]), 42 | ] 43 | ) 44 | 45 | img_with_boxes = bbv.draw_multiple_rectangles(img, bboxes) 46 | img_with_boxes = bbv.add_multiple_labels(img_with_boxes, labels, bboxes) 47 | 48 | rendered_img = Image.fromarray(img_with_boxes) 49 | #rendered_img.save("bbox-visualizer/jpeg.jpg") 50 | return rendered_img 51 | 52 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | #pip install -r requirements.txt 2 | pandas 3 | bbox_visualizer 4 | matplotlib 5 | opencv-python 6 | scikit-learn 7 | jupyter_bbox_widget 8 | pyyaml 9 | pytest 10 | pytest-lazy-fixture 11 | black 12 | nbmake 13 | sphinx_rtd_theme 14 | tqdm -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Test 2 | from setuptools import setup 3 | 4 | 5 | with open("README.md", "r", encoding="utf-8") as fh: 6 | long_description = fh.read() 7 | 8 | setup( 9 | name="pylabel", 10 | packages=["pylabel"], 11 | version="0.1.55", 12 | description="Transform, analyze, and visualize computer vision annotations.", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://github.com/pylabel-project/pylabel", 16 | author="PyLabel Project", 17 | license="MIT", 18 | install_requires=[ 19 | "pandas", 20 | "bbox_visualizer", 21 | "matplotlib", 22 | "opencv-python", 23 | "scikit-learn", 24 | "jupyter_bbox_widget", 25 | "pyyaml", 26 | "tqdm", 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Install the requirements to run the tests by running `pip install -r requirements.txt` from the root of this repo. 2 | 3 | Commands to run tests in pytest 4 | ``` 5 | pytest 6 | pytest --nbmake "dataset_splitting.ipynb" "coco2voc.ipynb" "yolo2coco.ipynb" "yolo2voc.ipynb" 7 | ``` 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import zipfile 3 | 4 | # Download the datasets during the setup phase so they are available when the tests are run 5 | 6 | # Download sample coco dataset 7 | os.makedirs("data", exist_ok=True) 8 | # Download sample dataset 9 | os.system( 10 | "wget 'https://github.com/pylabel-project/datasets_models/raw/main/BCCD/BCCD_coco.zip' -O data/BCCD_coco.zip" 11 | ) 12 | with zipfile.ZipFile("data/BCCD_coco.zip", "r") as zip_ref: 13 | zip_ref.extractall("data") 14 | 15 | # Download sample yolo dataset 16 | os.system( 17 | "wget 'https://github.com/pylabel-project/datasets_models/blob/main/coco128.zip?raw=true' -O data/coco128.zip" 18 | ) 19 | with zipfile.ZipFile("data/coco128.zip", "r") as zip_ref: 20 | zip_ref.extractall("data") 21 | 22 | # Download sample notebooks so they can be tested using nbmake 23 | os.system("rm -rf samples") 24 | os.system("git clone https://github.com/pylabel-project/samples.git") 25 | os.system("cp samples/*.ipynb .") 26 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pylabel import importer 3 | import pandas as pd 4 | import copy 5 | 6 | # Test the importing 7 | # Create a dataset 8 | @pytest.fixture() 9 | def coco_dataset(): 10 | """Returns a dataset object imported from a COCO dataset""" 11 | # Specify path to the coco.json file 12 | path_to_annotations = "data/BCCD_Dataset.json" 13 | # Specify the path to the images (if they are in a different folder than the annotations) 14 | path_to_images = "" 15 | # Import the dataset into the pylable schema 16 | dataset = importer.ImportCoco( 17 | path_to_annotations, path_to_images=path_to_images, name="BCCD_coco" 18 | ) 19 | return dataset 20 | 21 | 22 | @pytest.fixture() 23 | def unlabeled_dataset(): 24 | """Returns a dataset object imported from a COCO dataset""" 25 | # Specify path to the coco.json file 26 | # Specify the path to the images (if they are in a different folder than the annotations) 27 | path_to_images = "data" 28 | 29 | # Import the dataset into the pylable schema 30 | dataset = importer.ImportImagesOnly(path=path_to_images) 31 | return dataset 32 | 33 | 34 | @pytest.fixture() 35 | def yolo_dataset(): 36 | """Returns a dataset object imported from a COCO dataset""" 37 | 38 | path_to_annotations = "data/coco128/labels/train2017/" 39 | 40 | # Identify the path to get from the annotations to the images 41 | path_to_images = "../../images/train2017/" 42 | 43 | # Import the dataset into the pylable schema 44 | # Class names are defined here https://github.com/ultralytics/yolov5/blob/master/data/coco128.yaml 45 | yoloclasses = [ 46 | "person", 47 | "bicycle", 48 | "car", 49 | "motorcycle", 50 | "airplane", 51 | "bus", 52 | "train", 53 | "truck", 54 | "boat", 55 | "traffic light", 56 | "fire hydrant", 57 | "stop sign", 58 | "parking meter", 59 | "bench", 60 | "bird", 61 | "cat", 62 | "dog", 63 | "horse", 64 | "sheep", 65 | "cow", 66 | "elephant", 67 | "bear", 68 | "zebra", 69 | "giraffe", 70 | "backpack", 71 | "umbrella", 72 | "handbag", 73 | "tie", 74 | "suitcase", 75 | "frisbee", 76 | "skis", 77 | "snowboard", 78 | "sports ball", 79 | "kite", 80 | "baseball bat", 81 | "baseball glove", 82 | "skateboard", 83 | "surfboard", 84 | "tennis racket", 85 | "bottle", 86 | "wine glass", 87 | "cup", 88 | "fork", 89 | "knife", 90 | "spoon", 91 | "bowl", 92 | "banana", 93 | "apple", 94 | "sandwich", 95 | "orange", 96 | "broccoli", 97 | "carrot", 98 | "hot dog", 99 | "pizza", 100 | "donut", 101 | "cake", 102 | "chair", 103 | "couch", 104 | "potted plant", 105 | "bed", 106 | "dining table", 107 | "toilet", 108 | "tv", 109 | "laptop", 110 | "mouse", 111 | "remote", 112 | "keyboard", 113 | "cell phone", 114 | "microwave", 115 | "oven", 116 | "toaster", 117 | "sink", 118 | "refrigerator", 119 | "book", 120 | "clock", 121 | "vase", 122 | "scissors", 123 | "teddy bear", 124 | "hair drier", 125 | "toothbrush", 126 | ] 127 | 128 | dataset = importer.ImportYoloV5( 129 | path=path_to_annotations, 130 | path_to_images=path_to_images, 131 | cat_names=yoloclasses, 132 | img_ext="jpg", 133 | name="coco128", 134 | ) 135 | 136 | return dataset 137 | 138 | 139 | @pytest.mark.parametrize( 140 | "dataset", 141 | [pytest.lazy_fixture("coco_dataset"), pytest.lazy_fixture("unlabeled_dataset")], 142 | ) 143 | def test_num_images(dataset): 144 | assert isinstance( 145 | dataset.analyze.num_images, int 146 | ), "analyze.num_images should return an int" 147 | 148 | 149 | def test_df_is_dataframe(coco_dataset): 150 | assert isinstance( 151 | coco_dataset.df, pd.DataFrame 152 | ), "dataset.df should be a pandas dataframe" 153 | 154 | 155 | def test_num_classes(coco_dataset): 156 | assert isinstance( 157 | coco_dataset.analyze.num_classes, int 158 | ), "analyze.num_images should return an int" 159 | 160 | 161 | def test_classes_coco(coco_dataset): 162 | assert isinstance( 163 | coco_dataset.analyze.classes, list 164 | ), "analyze.classes should return a list" 165 | 166 | 167 | def test_classes_unlabeled(unlabeled_dataset): 168 | assert isinstance( 169 | unlabeled_dataset.analyze.classes, list 170 | ), "analyze.classes should return a list" 171 | 172 | 173 | def test_export_coco(coco_dataset): 174 | path_to_coco_export = coco_dataset.export.ExportToCoco() 175 | assert isinstance( 176 | path_to_coco_export[0], str 177 | ), "ExportToCoco should return a list with one or more strings." 178 | 179 | 180 | def test_ReindexCatIds(coco_dataset): 181 | # Check if the ReindexCatIds function is working by checking the 182 | # cat ids after the function is called. The cat ids should be continuous 183 | # starting with the index 184 | index = 5 185 | ds_copy = copy.deepcopy(coco_dataset) 186 | ds_copy.ReindexCatIds(index) 187 | 188 | assert ( 189 | min(ds_copy.analyze.class_ids) == index 190 | ), "The min value should equal the index" 191 | 192 | assert ( 193 | max(ds_copy.analyze.class_ids) == index + len(ds_copy.analyze.class_ids) - 1 194 | ), "ReindexCatIds: The max value should equal the index + number of classes" 195 | 196 | assert ( 197 | list(range(index, index + len(ds_copy.analyze.class_ids))) 198 | == ds_copy.analyze.class_ids 199 | ), "ReindexCatIds: The class ids should be continuous" 200 | 201 | 202 | # Add tests for non-labeled datasets 203 | --------------------------------------------------------------------------------