Name | jupyter_bbox_widget JSON |
Version |
0.6.0
JSON |
| download |
home_page | None |
Summary | A Jupyter widget for annotating images with bounding boxes |
upload_time | 2024-08-25 13:11:59 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.7 |
license | Copyright (c) 2021 gereleth
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
keywords |
annotation
image
jupyter
labeling
widgets
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/gereleth/jupyter-bbox-widget/HEAD?filepath=examples%2Fintroduction.ipynb&urlpath=tree%2Fexamples%2Fintroduction.ipynb)
# jupyter\_bbox\_widget
A Jupyter widget for annotating images with bounding boxes. **Try a [live demo on Binder](https://mybinder.org/v2/gh/gereleth/jupyter-bbox-widget/HEAD?filepath=examples%2Fintroduction.ipynb)**.
```python
from jupyter_bbox_widget import BBoxWidget
widget = BBoxWidget(
image='fruit.jpg',
classes=['apple', 'orange', 'pear'],
)
widget
```
![UI example](https://raw.githubusercontent.com/gereleth/jupyter-bbox-widget/main/examples/ui_example.jpg)
Create, edit, move, resize and delete bounding box annotations using the mouse.
Use `widget.bboxes` to get current annotations values:
```python
widget.bboxes
# [{'x': 377, 'y': 177, 'width': 181, 'height': 201, 'label': 'apple'},
# {'x': 219, 'y': 142, 'width': 169, 'height': 171, 'label': 'orange'},
# {'x': 43, 'y': 174, 'width': 234, 'height': 195, 'label': 'pear'}]
```
You can also assign to `widget.bboxes` to display any annotations. For example, use the output of an object detection model to do model-assisted labeling.
```python
widget.bboxes = [
{'x': 377, 'y': 177, 'width': 181, 'height': 201, 'label': 'apple'},
{'x': 219, 'y': 142, 'width': 169, 'height': 171, 'label': 'orange'},
{'x': 43, 'y': 174, 'width': 234, 'height': 195, 'label': 'pear'}
]
```
*Fruit photo by <a href="https://unsplash.com/@umanoide?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Umanoide</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>*
## Installation
You can install using `pip`:
```bash
pip install jupyter_bbox_widget
```
A Jupyter server restart may be necessary for the widget to be properly discovered.
## Usage
### Create and edit annotations with the mouse
- click and drag to create a bbox
- click and drag corners or edges to resize a bbox
- click and drag inside a bbox to move it
- in order to change a label select a new label below the image and click on label text
### Keyboard shortcuts
When you click inside the widget area it will gain focus and start receiving keyboard events. An outline usually indicates that the element is focused. Normal Jupyter keyboard shortcuts won't work in this state. To unfocus the widget area click outside it or press `Esc`.
Some shortcuts act on the selected bbox. New bboxes are selected automatically when created. You can also select a bbox by clicking on it. Selected bbox is displayed on top of others and with a thicker border.
- Digit keys 1-9 and 0 select a class label.
- `Esc` unfocuses the widget
- `Enter` is the same as pressing Submit button
- `Space` is the same as pressing Skip button
- `Tab` / `Shift-Tab` select next/previous bbox.
- Keys acting on the selected bbox (assuming a QWERTY keyboard, other layouts should use any keys in the same locations):
- `W` move up
- `A` move left
- `S` move down
- `D` move right
- `Q` shrink width
- `E` grow width
- `R` grow height
- `F` shrink height
- `C` assign selected class label
- Holding `Shift` while pressing movement keys will increase step size
### Skip and Submit events
You can define functions that will be called automatically when you press Skip or Submit buttons. This is useful for creating a workflow for annotating multiple images.
```python
@widget.on_skip
def skip():
# move on to the next image
@widget.on_submit
def save():
# do stuff to save current annotations and move on
```
There is an example of a simple annotation workflow in [`examples/introduction.ipynb`](https://github.com/gereleth/jupyter-bbox-widget/blob/main/examples/introduction.ipynb) notebook.
### Widget traits
Get or set widget state by using these traits.
- `image` - path to a local image file or a web link
- `bboxes` - list of bounding boxes
- `classes` - list of class labels
- `colors` - list of colors to use for different classes
- `label` - currently selected class label
- `selected_index` - index of currently selected bbox. Is `-1` when nothing is selected.
- `hide_buttons` - default False, remove Skip and Submit buttons
- `view_only` - default False, make bboxes non-editable. This is useful for viewing annotation outputs without accidentally changing them.
- `image_bytes` - binary data from the image file
It's also possible to react to state changes by using the widget's [`observe`](https://ipywidgets.readthedocs.io/en/8.1.5/examples/Widget%20Events.html#registering-callbacks-to-trait-changes-in-the-kernel) method. For example, the following code will make the function `on_bbox_change` run every time the user edits bounding boxes:
```python
def on_bbox_change(change):
new_bboxes = change['new']
# do whatever with them
widget.observe(on_bbox_change, names=['bboxes'])
```
### Displaying special images
Maybe your images aren't files in a common format or require special handling to load. One way to show them is to save the image into an in-memory bytes buffer and feed that to `widget.image_bytes`. I'm open to suggestions on how to make this more user-friendly.
```python
from io import BytesIO
bytes_io = BytesIO()
# for example, say img is a PIL image
img.save(bytes_io, format='png')
# feed that data to the widget
widget.image_bytes = bytes_io.getvalue()
```
### Recording additional data
Sometimes you need to record more info about an object than just a location and a class label. For example, you might want to specify whether the object is in focus or blurred, record its size or other properties.
Let's say we want to apply a rating on a scale from 1 to 5 to every object in the image. We create a slider widget to edit the rating values:
```python
w_rating = widgets.IntSlider(value=3, min=1, max=5, description='Rating')
```
And we attach it to the bbox widget.
```python
widget.attach(w_rating, name='rating')
```
As a result all bboxes created afterwards will have a `rating` property and the `w_rating` widget can be used to display and manipulate the rating of the currently selected bbox.
Any number and any kind of `ipywidgets` widgets may be used in this way for creating richer annotations - number inputs, text inputs, checkboxes and so on (see [widget list](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html)).
The notebook in [`examples/introduction.ipynb`](https://github.com/gereleth/jupyter-bbox-widget/blob/main/examples/introduction.ipynb) has an example and a more detailed explanation of this feature.
## Changelog
- v0.6.0
- rewritten to use [`anywidget`](https://github.com/manzt/anywidget) under the hood
- improved the way images are sent to frontend - it's no longer necessary to base64-encode local files in order to show them in Jupyter Lab
- v0.5.0
- enabled use of `widget.on_skip` and `widget.on_submit` methods as decorators
- v0.4.0
- exposed selected class label to the python side as `widget.label`
- v0.3.4
- set max-width: 100% on image so that it respects layout
- v0.3.3
- fixed bboxes not updating after class change by keyboard shortcut
- v0.3.2
- added `hide_buttons` option
- fixed bbox delete icon not displayed properly
- v0.3.1
- unselect a bbox on click outside in view only mode
- fixed a bug with overwriting attached properties on unselect
- v0.3.0
- added `view_only` mode
- v0.2.0
- added Skip and Submit buttons
- added attach widget functionality for recording extra properties
- multiple fixes and improvements
- v0.1.0
- initial release
## Development Installation
This project was originally inspired by a blogpost [Creating Reactive Jupyter Widgets With Svelte](https://cabreraalex.medium.com/creating-reactive-jupyter-widgets-with-svelte-ef2fb580c05) but is currently based on [`anywidget`](https://github.com/manzt/anywidget) which provides a very nice developer experience.
Follow the steps below to make changes to this widget.
1. Clone the repo, cd into its folder
2. Activate the python environment you will use
3. Install the python package with dev dependencies `pip install -e .[dev]`
4. Run `npm install` to install JS dependencies.
5. Run `npm run dev` to launch vite dev server. It will watch for any changes you make to the files in `svelte` folder.
6. In `src/jupyter_bbox_widget/bbox.py` change the `DEV` variable to `True` so that the widget gets its JS code from the vite dev server.
7. Launch jupyter lab with `ANYWIDGET_HMR=1 jupyter lab` to turn on anywidget's hot module reloading.
8. Open a notebook with the widget and have fun making changes - you should see them in the UI as soon as you save a file.
9. Run `hatch build` to build your widget when it's ready.
Raw data
{
"_id": null,
"home_page": null,
"name": "jupyter_bbox_widget",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "Annotation, Image, Jupyter, Labeling, Widgets",
"author": null,
"author_email": "gereleth <daria.voznyak@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/3e/36/64e5af7d9f4552ecee4be1f077b4891d8e0aa3cdb5f51c3daaab93bfb970/jupyter_bbox_widget-0.6.0.tar.gz",
"platform": null,
"description": "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/gereleth/jupyter-bbox-widget/HEAD?filepath=examples%2Fintroduction.ipynb&urlpath=tree%2Fexamples%2Fintroduction.ipynb)\n\n# jupyter\\_bbox\\_widget\n\nA Jupyter widget for annotating images with bounding boxes. **Try a [live demo on Binder](https://mybinder.org/v2/gh/gereleth/jupyter-bbox-widget/HEAD?filepath=examples%2Fintroduction.ipynb)**.\n\n```python\nfrom jupyter_bbox_widget import BBoxWidget\nwidget = BBoxWidget(\n image='fruit.jpg',\n classes=['apple', 'orange', 'pear'],\n)\nwidget\n```\n\n![UI example](https://raw.githubusercontent.com/gereleth/jupyter-bbox-widget/main/examples/ui_example.jpg)\n\nCreate, edit, move, resize and delete bounding box annotations using the mouse.\n\nUse `widget.bboxes` to get current annotations values:\n\n```python\nwidget.bboxes\n# [{'x': 377, 'y': 177, 'width': 181, 'height': 201, 'label': 'apple'},\n# {'x': 219, 'y': 142, 'width': 169, 'height': 171, 'label': 'orange'},\n# {'x': 43, 'y': 174, 'width': 234, 'height': 195, 'label': 'pear'}]\n```\n\nYou can also assign to `widget.bboxes` to display any annotations. For example, use the output of an object detection model to do model-assisted labeling.\n\n```python\nwidget.bboxes = [\n {'x': 377, 'y': 177, 'width': 181, 'height': 201, 'label': 'apple'},\n {'x': 219, 'y': 142, 'width': 169, 'height': 171, 'label': 'orange'},\n {'x': 43, 'y': 174, 'width': 234, 'height': 195, 'label': 'pear'}\n]\n```\n\n*Fruit photo by <a href=\"https://unsplash.com/@umanoide?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Umanoide</a> on <a href=\"https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Unsplash</a>*\n \n## Installation\n\nYou can install using `pip`:\n\n```bash\npip install jupyter_bbox_widget\n```\n\nA Jupyter server restart may be necessary for the widget to be properly discovered.\n\n## Usage\n\n### Create and edit annotations with the mouse\n\n- click and drag to create a bbox\n- click and drag corners or edges to resize a bbox\n- click and drag inside a bbox to move it\n- in order to change a label select a new label below the image and click on label text\n\n### Keyboard shortcuts\n\nWhen you click inside the widget area it will gain focus and start receiving keyboard events. An outline usually indicates that the element is focused. Normal Jupyter keyboard shortcuts won't work in this state. To unfocus the widget area click outside it or press `Esc`.\n\nSome shortcuts act on the selected bbox. New bboxes are selected automatically when created. You can also select a bbox by clicking on it. Selected bbox is displayed on top of others and with a thicker border.\n\n- Digit keys 1-9 and 0 select a class label.\n- `Esc` unfocuses the widget\n- `Enter` is the same as pressing Submit button\n- `Space` is the same as pressing Skip button\n- `Tab` / `Shift-Tab` select next/previous bbox.\n- Keys acting on the selected bbox (assuming a QWERTY keyboard, other layouts should use any keys in the same locations):\n - `W` move up\n - `A` move left\n - `S` move down\n - `D` move right\n - `Q` shrink width\n - `E` grow width\n - `R` grow height\n - `F` shrink height\n - `C` assign selected class label\n - Holding `Shift` while pressing movement keys will increase step size\n\n### Skip and Submit events\n\nYou can define functions that will be called automatically when you press Skip or Submit buttons. This is useful for creating a workflow for annotating multiple images. \n\n```python\n@widget.on_skip\ndef skip():\n # move on to the next image\n\n@widget.on_submit\ndef save():\n # do stuff to save current annotations and move on\n```\n\nThere is an example of a simple annotation workflow in [`examples/introduction.ipynb`](https://github.com/gereleth/jupyter-bbox-widget/blob/main/examples/introduction.ipynb) notebook.\n\n### Widget traits\n\nGet or set widget state by using these traits.\n\n- `image` - path to a local image file or a web link\n- `bboxes` - list of bounding boxes\n- `classes` - list of class labels\n- `colors` - list of colors to use for different classes\n- `label` - currently selected class label\n- `selected_index` - index of currently selected bbox. Is `-1` when nothing is selected.\n- `hide_buttons` - default False, remove Skip and Submit buttons\n- `view_only` - default False, make bboxes non-editable. This is useful for viewing annotation outputs without accidentally changing them.\n- `image_bytes` - binary data from the image file\n\nIt's also possible to react to state changes by using the widget's [`observe`](https://ipywidgets.readthedocs.io/en/8.1.5/examples/Widget%20Events.html#registering-callbacks-to-trait-changes-in-the-kernel) method. For example, the following code will make the function `on_bbox_change` run every time the user edits bounding boxes:\n\n```python\ndef on_bbox_change(change):\n new_bboxes = change['new']\n # do whatever with them\n\nwidget.observe(on_bbox_change, names=['bboxes'])\n```\n\n### Displaying special images\n\nMaybe your images aren't files in a common format or require special handling to load. One way to show them is to save the image into an in-memory bytes buffer and feed that to `widget.image_bytes`. I'm open to suggestions on how to make this more user-friendly.\n\n```python\nfrom io import BytesIO\nbytes_io = BytesIO()\n# for example, say img is a PIL image\nimg.save(bytes_io, format='png')\n# feed that data to the widget\nwidget.image_bytes = bytes_io.getvalue()\n```\n\n### Recording additional data\n\nSometimes you need to record more info about an object than just a location and a class label. For example, you might want to specify whether the object is in focus or blurred, record its size or other properties.\n\nLet's say we want to apply a rating on a scale from 1 to 5 to every object in the image. We create a slider widget to edit the rating values:\n\n```python\nw_rating = widgets.IntSlider(value=3, min=1, max=5, description='Rating')\n```\n\nAnd we attach it to the bbox widget.\n\n```python\nwidget.attach(w_rating, name='rating')\n```\n\nAs a result all bboxes created afterwards will have a `rating` property and the `w_rating` widget can be used to display and manipulate the rating of the currently selected bbox.\n\nAny number and any kind of `ipywidgets` widgets may be used in this way for creating richer annotations - number inputs, text inputs, checkboxes and so on (see [widget list](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html)). \n\nThe notebook in [`examples/introduction.ipynb`](https://github.com/gereleth/jupyter-bbox-widget/blob/main/examples/introduction.ipynb) has an example and a more detailed explanation of this feature.\n\n## Changelog\n\n- v0.6.0\n - rewritten to use [`anywidget`](https://github.com/manzt/anywidget) under the hood\n - improved the way images are sent to frontend - it's no longer necessary to base64-encode local files in order to show them in Jupyter Lab\n- v0.5.0 \n - enabled use of `widget.on_skip` and `widget.on_submit` methods as decorators\n- v0.4.0\n - exposed selected class label to the python side as `widget.label`\n- v0.3.4\n - set max-width: 100% on image so that it respects layout\n- v0.3.3\n - fixed bboxes not updating after class change by keyboard shortcut\n- v0.3.2\n - added `hide_buttons` option\n - fixed bbox delete icon not displayed properly\n- v0.3.1\n - unselect a bbox on click outside in view only mode\n - fixed a bug with overwriting attached properties on unselect\n- v0.3.0\n - added `view_only` mode\n- v0.2.0\n - added Skip and Submit buttons\n - added attach widget functionality for recording extra properties\n - multiple fixes and improvements\n- v0.1.0\n - initial release\n\n\n## Development Installation\n\nThis project was originally inspired by a blogpost [Creating Reactive Jupyter Widgets With Svelte](https://cabreraalex.medium.com/creating-reactive-jupyter-widgets-with-svelte-ef2fb580c05) but is currently based on [`anywidget`](https://github.com/manzt/anywidget) which provides a very nice developer experience.\n\nFollow the steps below to make changes to this widget.\n\n1. Clone the repo, cd into its folder\n2. Activate the python environment you will use\n3. Install the python package with dev dependencies `pip install -e .[dev]`\n4. Run `npm install` to install JS dependencies.\n5. Run `npm run dev` to launch vite dev server. It will watch for any changes you make to the files in `svelte` folder.\n6. In `src/jupyter_bbox_widget/bbox.py` change the `DEV` variable to `True` so that the widget gets its JS code from the vite dev server.\n7. Launch jupyter lab with `ANYWIDGET_HMR=1 jupyter lab` to turn on anywidget's hot module reloading.\n8. Open a notebook with the widget and have fun making changes - you should see them in the UI as soon as you save a file.\n9. Run `hatch build` to build your widget when it's ready.",
"bugtrack_url": null,
"license": "Copyright (c) 2021 gereleth\n All rights reserved.\n \n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions are met:\n \n 1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n \n 2. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n \n 3. Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n \n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"summary": "A Jupyter widget for annotating images with bounding boxes",
"version": "0.6.0",
"project_urls": {
"Homepage": "https://github.com/gereleth/jupyter_bbox_widget"
},
"split_keywords": [
"annotation",
" image",
" jupyter",
" labeling",
" widgets"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "b4e4bfab95d47c97ec1316ca78f11ae02b4bf9d877b16014f4151d6467fbc448",
"md5": "7449e14bfde687f8dd9d423fefe68c16",
"sha256": "033464236976a77725dd70c2467173ced26a718e842a14bbc92896afddc5bd13"
},
"downloads": -1,
"filename": "jupyter_bbox_widget-0.6.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "7449e14bfde687f8dd9d423fefe68c16",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 24295,
"upload_time": "2024-08-25T13:11:58",
"upload_time_iso_8601": "2024-08-25T13:11:58.447811Z",
"url": "https://files.pythonhosted.org/packages/b4/e4/bfab95d47c97ec1316ca78f11ae02b4bf9d877b16014f4151d6467fbc448/jupyter_bbox_widget-0.6.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "3e3664e5af7d9f4552ecee4be1f077b4891d8e0aa3cdb5f51c3daaab93bfb970",
"md5": "dc50b0ad74f58e4056e84e3d8ec0edfd",
"sha256": "758440b20579049854df5ddf12c3d535b6f718d8ffdd9205f4595dab856a0e33"
},
"downloads": -1,
"filename": "jupyter_bbox_widget-0.6.0.tar.gz",
"has_sig": false,
"md5_digest": "dc50b0ad74f58e4056e84e3d8ec0edfd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 22564,
"upload_time": "2024-08-25T13:11:59",
"upload_time_iso_8601": "2024-08-25T13:11:59.882101Z",
"url": "https://files.pythonhosted.org/packages/3e/36/64e5af7d9f4552ecee4be1f077b4891d8e0aa3cdb5f51c3daaab93bfb970/jupyter_bbox_widget-0.6.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-25 13:11:59",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "gereleth",
"github_project": "jupyter_bbox_widget",
"github_not_found": true,
"lcname": "jupyter_bbox_widget"
}