├── .DS_Store
├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── albumentations.ipynb
├── coco2voc.ipynb
├── coco2yolosegmentation.ipynb
├── coco2yolov5.ipynb
├── coco_extract_subset.ipynb
├── dataset_splitting.ipynb
├── label_new_dataset.ipynb
├── pylabel2azure_custom_vision.ipynb
├── pylabeler.ipynb
├── voc2coco.ipynb
├── yolo2coco.ipynb
├── yolo2pylabeler.ipynb
├── yolo2voc.ipynb
├── yolo_with_yaml_importer.ipynb
└── yolov5_training.ipynb
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pylabel-project/samples/a152a56b3f7a9b8db4d5ce9de2163ce7360aab1c/.DS_Store
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | open_collective: pylabel
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # My Files
2 | sample.ipynb
3 |
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 | *$py.class
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | pip-wheel-metadata/
27 | share/python-wheels/
28 | *.egg-info/
29 | .installed.cfg
30 | *.egg
31 | MANIFEST
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Unit test / coverage reports
44 | htmlcov/
45 | .tox/
46 | .nox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | *.py,cover
54 | .hypothesis/
55 | .pytest_cache/
56 |
57 | # Translations
58 | *.mo
59 | *.pot
60 |
61 | # Django stuff:
62 | *.log
63 | local_settings.py
64 | db.sqlite3
65 | db.sqlite3-journal
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | target/
79 |
80 | # Jupyter Notebook
81 | .ipynb_checkpoints
82 |
83 | # IPython
84 | profile_default/
85 | ipython_config.py
86 |
87 | # pyenv
88 | .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 PyLabel Project
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Notebooks and code samples to help you use the PyLabel Python package and the PyLabeler Jupyter-based labeling tool.
2 |
3 | ## Annotation Format Conversion
4 | Use PyLabel to translate bounding box annotations between different formats-for example, from coco to yolo.
5 | - [coco2voc.ipynb](https://github.com/pylabel-project/samples/blob/main/coco2voc.ipynb)
6 | - [coco2yolov5.ipynb](https://github.com/pylabel-project/samples/blob/main/coco2yolov5.ipynb)
7 | - [voc2coco.ipynb](https://github.com/pylabel-project/samples/blob/main/voc2coco.ipynb)
8 | - [yolo2coco.ipynb](https://github.com/pylabel-project/samples/blob/main/yolo2coco.ipynb)
9 | - [yolo2voc.ipynb](https://github.com/pylabel-project/samples/blob/main/yolo2voc.ipynb)
10 | - [yolo_with_yaml_importer.ipynb](https://github.com/pylabel-project/samples/blob/main/yolo_with_yaml_importer.ipynb)
11 |
12 | ## PyLabeler
13 | PyLabeler is a Jupyter-based labeling tool that you can use to annotate images and edit bounding box annotations within a Jupyter notebook.
14 | - [**label_new_dataset.ipynb**](https://github.com/pylabel-project/samples/blob/main/label_new_dataset.ipynb) 
This notebook is a labeling tool that can be used to annotate image datasets with bounding boxes, automatically suggest bounding boxes using an object detection model, and save the annotations in YOCO, COCO, or VOC format.
15 | - [**yolo2pylabeler.ipynb**](https://github.com/pylabel-project/samples/blob/main/yolo2pylabeler.ipynb) 
This notebook uses PyLabeler to edit an existing dataset of Yolo annotations and save the new annotations back to Yolo format.
16 |
17 | ## Integrations with Other Tools
18 | PyLabel can help you use other tools that take bounding box annotations as an input or output. PyLabel stores annotations as a Pandas dataframe, which you can access directly to support your particular use case.
19 |
20 | - [**albumentations.ipynb**](https://github.com/pylabel-project/samples/blob/main/albumentations.ipynb) 
If you don't have enough images to train a model well, you can use image augmenation to create more samples for training and validation.
21 | [Albumentations](https://albumentations.ai/) is a popular open-source library for creating additional, augmented images as well as the annotations for those images.
22 |
23 | - [**azure_custom_vision.ipynb**](https://github.com/pylabel-project/samples/blob/main/pylabel2azure_custom_vision.ipynb) 
Using PyLabel you can import existing labels in COCO, YOLOv5, or VOC format and then upload the dataset to Azure Custom Vision.
24 |
--------------------------------------------------------------------------------
/coco2yolosegmentation.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {
7 | "id": "Cxi8K9mXwl5t"
8 | },
9 | "source": [
10 | "
\n",
11 | "\n",
12 | "# Convert Coco Segmentation Annotations to YOLO \n",
13 | "Yolo now supports segmentation. Use this notebook to try converting segmented annotations from Coco to Yolo format. "
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": 1,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import logging\n",
23 | "logging.getLogger().setLevel(logging.CRITICAL)\n",
24 | "!pip install pylabel > /dev/null"
25 | ]
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": 2,
30 | "metadata": {
31 | "id": "AYwWkeF4z1Sc"
32 | },
33 | "outputs": [],
34 | "source": [
35 | "from pylabel import importer"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "## Import coco annotations \n",
43 | "First we will import annotations from the coco dataset, which are in coco json format. "
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": 3,
49 | "metadata": {},
50 | "outputs": [
51 | {
52 | "name": "stdout",
53 | "output_type": "stream",
54 | "text": [
55 | "--2023-01-15 17:32:37-- https://github.com/pylabel-project/pylabel/files/10416366/dataset.zip?raw=true\n",
56 | "Resolving github.com (github.com)... 192.30.255.112\n",
57 | "Connecting to github.com (github.com)|192.30.255.112|:443... connected.\n",
58 | "HTTP request sent, awaiting response... 302 Found\n",
59 | "Location: https://objects.githubusercontent.com/github-production-repository-file-5c1aeb/419178479/10416366?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230116%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230116T013237Z&X-Amz-Expires=300&X-Amz-Signature=4f0bface806d84718180781a43838c441714eb0d0fc360b69de6c70b0e1eaf12&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=419178479&response-content-disposition=attachment%3Bfilename%3Ddataset.zip&response-content-type=application%2Fzip [following]\n",
60 | "--2023-01-15 17:32:37-- https://objects.githubusercontent.com/github-production-repository-file-5c1aeb/419178479/10416366?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230116%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230116T013237Z&X-Amz-Expires=300&X-Amz-Signature=4f0bface806d84718180781a43838c441714eb0d0fc360b69de6c70b0e1eaf12&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=419178479&response-content-disposition=attachment%3Bfilename%3Ddataset.zip&response-content-type=application%2Fzip\n",
61 | "Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.111.133, 185.199.108.133, 185.199.109.133, ...\n",
62 | "Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.111.133|:443... connected.\n",
63 | "HTTP request sent, awaiting response... 200 OK\n",
64 | "Length: 15091452 (14M) [application/zip]\n",
65 | "Saving to: ‘data/dataset.zip’\n",
66 | "\n",
67 | "data/dataset.zip 100%[===================>] 14.39M 16.0MB/s in 0.9s \n",
68 | "\n",
69 | "2023-01-15 17:32:39 (16.0 MB/s) - ‘data/dataset.zip’ saved [15091452/15091452]\n",
70 | "\n"
71 | ]
72 | }
73 | ],
74 | "source": [
75 | "import os \n",
76 | "import zipfile\n",
77 | "\n",
78 | "#Download sample dataset \n",
79 | "os.makedirs(\"data\", exist_ok=True)\n",
80 | "!wget \"https://github.com/pylabel-project/pylabel/files/10416366/dataset.zip?raw=true\" -O data/dataset.zip\n",
81 | "with zipfile.ZipFile(\"data/dataset.zip\", 'r') as zip_ref:\n",
82 | " zip_ref.extractall(\"data\")"
83 | ]
84 | },
85 | {
86 | "cell_type": "code",
87 | "execution_count": 4,
88 | "metadata": {},
89 | "outputs": [
90 | {
91 | "data": {
92 | "text/html": [
93 | "
\n",
94 | "\n",
107 | "
\n",
108 | " \n",
109 | " \n",
110 | " | \n",
111 | " img_folder | \n",
112 | " img_filename | \n",
113 | " img_path | \n",
114 | " img_id | \n",
115 | " img_width | \n",
116 | " img_height | \n",
117 | " img_depth | \n",
118 | " ann_segmented | \n",
119 | " ann_bbox_xmin | \n",
120 | " ann_bbox_ymin | \n",
121 | " ... | \n",
122 | " ann_segmentation | \n",
123 | " ann_iscrowd | \n",
124 | " ann_pose | \n",
125 | " ann_truncated | \n",
126 | " ann_difficult | \n",
127 | " cat_id | \n",
128 | " cat_name | \n",
129 | " cat_supercategory | \n",
130 | " split | \n",
131 | " annotated | \n",
132 | "
\n",
133 | " \n",
134 | " id | \n",
135 | " | \n",
136 | " | \n",
137 | " | \n",
138 | " | \n",
139 | " | \n",
140 | " | \n",
141 | " | \n",
142 | " | \n",
143 | " | \n",
144 | " | \n",
145 | " | \n",
146 | " | \n",
147 | " | \n",
148 | " | \n",
149 | " | \n",
150 | " | \n",
151 | " | \n",
152 | " | \n",
153 | " | \n",
154 | " | \n",
155 | " | \n",
156 | "
\n",
157 | " \n",
158 | " \n",
159 | " \n",
160 | " 0 | \n",
161 | " | \n",
162 | " IMG20221222121516.jpg | \n",
163 | " | \n",
164 | " 1 | \n",
165 | " 3072 | \n",
166 | " 4096 | \n",
167 | " | \n",
168 | " | \n",
169 | " 259.284603 | \n",
170 | " 1465.132193 | \n",
171 | " ... | \n",
172 | " [[792.7838258164849, 2337.045101088646, 673.34... | \n",
173 | " 0 | \n",
174 | " | \n",
175 | " | \n",
176 | " | \n",
177 | " 1 | \n",
178 | " Hanger | \n",
179 | " | \n",
180 | " | \n",
181 | " 1 | \n",
182 | "
\n",
183 | " \n",
184 | " 1 | \n",
185 | " | \n",
186 | " IMG20221222121516.jpg | \n",
187 | " | \n",
188 | " 1 | \n",
189 | " 3072 | \n",
190 | " 4096 | \n",
191 | " | \n",
192 | " | \n",
193 | " 390.668740 | \n",
194 | " 1373.561431 | \n",
195 | " ... | \n",
196 | " [[1174.9922239502328, 2305.194401244167, 1131.... | \n",
197 | " 0 | \n",
198 | " | \n",
199 | " | \n",
200 | " | \n",
201 | " 1 | \n",
202 | " Hanger | \n",
203 | " | \n",
204 | " | \n",
205 | " 1 | \n",
206 | "
\n",
207 | " \n",
208 | " 2 | \n",
209 | " | \n",
210 | " IMG20221222121516.jpg | \n",
211 | " | \n",
212 | " 1 | \n",
213 | " 3072 | \n",
214 | " 4096 | \n",
215 | " | \n",
216 | " | \n",
217 | " 896.298600 | \n",
218 | " 1485.038880 | \n",
219 | " ... | \n",
220 | " [[1624.8833592534984, 2185.7542768273706, 1541... | \n",
221 | " 0 | \n",
222 | " | \n",
223 | " | \n",
224 | " | \n",
225 | " 1 | \n",
226 | " Hanger | \n",
227 | " | \n",
228 | " | \n",
229 | " 1 | \n",
230 | "
\n",
231 | " \n",
232 | " 3 | \n",
233 | " | \n",
234 | " IMG20221222121543.jpg | \n",
235 | " | \n",
236 | " 2 | \n",
237 | " 3072 | \n",
238 | " 4096 | \n",
239 | " | \n",
240 | " | \n",
241 | " 1855.800933 | \n",
242 | " 1409.393468 | \n",
243 | " ... | \n",
244 | " [[1871.7262830482105, 2090.202177293934, 1915.... | \n",
245 | " 0 | \n",
246 | " | \n",
247 | " | \n",
248 | " | \n",
249 | " 1 | \n",
250 | " Hanger | \n",
251 | " | \n",
252 | " | \n",
253 | " 1 | \n",
254 | "
\n",
255 | " \n",
256 | " 4 | \n",
257 | " | \n",
258 | " IMG20221222121543.jpg | \n",
259 | " | \n",
260 | " 2 | \n",
261 | " 3072 | \n",
262 | " 4096 | \n",
263 | " | \n",
264 | " | \n",
265 | " 2438.171073 | \n",
266 | " 1388.690513 | \n",
267 | " ... | \n",
268 | " [[3019.977190254017, 1809.119751166407, 2935.0... | \n",
269 | " 0 | \n",
270 | " | \n",
271 | " | \n",
272 | " | \n",
273 | " 1 | \n",
274 | " Hanger | \n",
275 | " | \n",
276 | " | \n",
277 | " 1 | \n",
278 | "
\n",
279 | " \n",
280 | "
\n",
281 | "
5 rows × 25 columns
\n",
282 | "
"
283 | ],
284 | "text/plain": [
285 | " img_folder img_filename img_path img_id img_width img_height \\\n",
286 | "id \n",
287 | "0 IMG20221222121516.jpg 1 3072 4096 \n",
288 | "1 IMG20221222121516.jpg 1 3072 4096 \n",
289 | "2 IMG20221222121516.jpg 1 3072 4096 \n",
290 | "3 IMG20221222121543.jpg 2 3072 4096 \n",
291 | "4 IMG20221222121543.jpg 2 3072 4096 \n",
292 | "\n",
293 | " img_depth ann_segmented ann_bbox_xmin ann_bbox_ymin ... \\\n",
294 | "id ... \n",
295 | "0 259.284603 1465.132193 ... \n",
296 | "1 390.668740 1373.561431 ... \n",
297 | "2 896.298600 1485.038880 ... \n",
298 | "3 1855.800933 1409.393468 ... \n",
299 | "4 2438.171073 1388.690513 ... \n",
300 | "\n",
301 | " ann_segmentation ann_iscrowd ann_pose \\\n",
302 | "id \n",
303 | "0 [[792.7838258164849, 2337.045101088646, 673.34... 0 \n",
304 | "1 [[1174.9922239502328, 2305.194401244167, 1131.... 0 \n",
305 | "2 [[1624.8833592534984, 2185.7542768273706, 1541... 0 \n",
306 | "3 [[1871.7262830482105, 2090.202177293934, 1915.... 0 \n",
307 | "4 [[3019.977190254017, 1809.119751166407, 2935.0... 0 \n",
308 | "\n",
309 | " ann_truncated ann_difficult cat_id cat_name cat_supercategory split \\\n",
310 | "id \n",
311 | "0 1 Hanger \n",
312 | "1 1 Hanger \n",
313 | "2 1 Hanger \n",
314 | "3 1 Hanger \n",
315 | "4 1 Hanger \n",
316 | "\n",
317 | " annotated \n",
318 | "id \n",
319 | "0 1 \n",
320 | "1 1 \n",
321 | "2 1 \n",
322 | "3 1 \n",
323 | "4 1 \n",
324 | "\n",
325 | "[5 rows x 25 columns]"
326 | ]
327 | },
328 | "execution_count": 4,
329 | "metadata": {},
330 | "output_type": "execute_result"
331 | }
332 | ],
333 | "source": [
334 | "#Specify path to the coco.json file\n",
335 | "path_to_annotations = \"data/project-2023-01-14-07-55-43.json\"\n",
336 | "#Specify the path to the images (if they are in a different folder than the annotations)\n",
337 | "path_to_images = \"\"\n",
338 | "\n",
339 | "#Import the dataset into the pylable schema \n",
340 | "dataset = importer.ImportCoco(path_to_annotations, path_to_images=path_to_images, name=\"Segmentation\")\n",
341 | "dataset.df.head(5)"
342 | ]
343 | },
344 | {
345 | "attachments": {},
346 | "cell_type": "markdown",
347 | "metadata": {},
348 | "source": [
349 | "# Export to Yolo v5 with Segmenttation \n",
350 | "The PyLabel exporter will export all of the annotations in the dataframe to the desired target format.\n",
351 | "Yolo creates one text file for each image in the dataset. Set segmentation=True to export segmentation annotations. "
352 | ]
353 | },
354 | {
355 | "cell_type": "code",
356 | "execution_count": 5,
357 | "metadata": {},
358 | "outputs": [
359 | {
360 | "data": {
361 | "text/plain": [
362 | "'training/labels/IMG20221222121516.txt'"
363 | ]
364 | },
365 | "execution_count": 5,
366 | "metadata": {},
367 | "output_type": "execute_result"
368 | }
369 | ],
370 | "source": [
371 | "dataset.path_to_annotations = \"data/yolo\"\n",
372 | "dataset.export.ExportToYoloV5(segmentation=True)[1]"
373 | ]
374 | },
375 | {
376 | "cell_type": "code",
377 | "execution_count": 6,
378 | "metadata": {},
379 | "outputs": [
380 | {
381 | "name": "stdout",
382 | "output_type": "stream",
383 | "text": [
384 | "1 0.25807 0.57057 0.21919 0.53169 0.19068 0.49572 0.16476 0.44907 0.14402 0.40047 0.12458 0.37033 0.08699 0.3577 0.0844 0.45879 0.20882 0.58418 \n",
385 | "1 0.38248 0.56279 0.36823 0.53558 0.32028 0.50544 0.27492 0.47823 0.24252 0.44421 0.21271 0.40047 0.18549 0.35089 0.17124 0.33631 0.15568 0.33534 0.13235 0.34603 0.12717 0.35089 0.13106 0.3577 0.15698 0.38783 0.18938 0.45393 0.2166 0.49086 0.26325 0.51905 0.30213 0.54335 0.34879 0.58029 \n",
386 | "1 0.52893 0.53363 0.50172 0.54821 0.4408 0.51614 0.38119 0.486 0.32805 0.43351 0.29176 0.38297 0.3138 0.36353 0.33712 0.36256 0.37082 0.37325 0.39804 0.41505 0.42007 0.44518 0.44988 0.47142 0.48876 0.49961 \n"
387 | ]
388 | }
389 | ],
390 | "source": [
391 | "#View a sample of the segmentation annotations\n",
392 | "!cat 'training/labels/IMG20221222121516.txt'"
393 | ]
394 | }
395 | ],
396 | "metadata": {
397 | "colab": {
398 | "collapsed_sections": [],
399 | "name": "import2.ipynb",
400 | "provenance": []
401 | },
402 | "kernelspec": {
403 | "display_name": "Python 3.8.5 ('my-new-project')",
404 | "language": "python",
405 | "name": "python3"
406 | },
407 | "language_info": {
408 | "codemirror_mode": {
409 | "name": "ipython",
410 | "version": 3
411 | },
412 | "file_extension": ".py",
413 | "mimetype": "text/x-python",
414 | "name": "python",
415 | "nbconvert_exporter": "python",
416 | "pygments_lexer": "ipython3",
417 | "version": "3.8.5"
418 | },
419 | "vscode": {
420 | "interpreter": {
421 | "hash": "b9d2b2088005b129b6cb174ba487de37c3e3acf6fca20d7f1b3d5131adeb2740"
422 | }
423 | }
424 | },
425 | "nbformat": 4,
426 | "nbformat_minor": 2
427 | }
428 |
--------------------------------------------------------------------------------
/coco_extract_subset.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": []
7 | },
8 | {
9 | "cell_type": "markdown",
10 | "metadata": {
11 | "id": "Cxi8K9mXwl5t"
12 | },
13 | "source": [
14 | "# Extract annotations from COCO Dataset annotation file\n",
15 | "This notebook was created to answer a question from stackoverflow: [https://stackoverflow.com/questions/69722538/extract-annotations-from-coco-dataset-annotation-file](https://stackoverflow.com/questions/69722538/extract-annotations-from-coco-dataset-annotation-file)\n",
16 | "\n",
17 | "> I want to train on a subset of COCO dataset. For the images, I have created a folder of first 30k images of train2017 folder. Now I need annotations of those 30k images (extracted from instances_train2017.json) in a separate json file so that I can train it. How can I do it?\n",
18 | "\n",
19 | "The reason for the question is that Coco stores all of the annotations in one long json file, so there is no simple way to extract only the ones that you need. PyLabel can help with this task by importing the dataset, filtering the annotations to the images you care about, and then exporting back to a coco json file. \n"
20 | ]
21 | },
22 | {
23 | "cell_type": "markdown",
24 | "metadata": {},
25 | "source": []
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": 5,
30 | "metadata": {},
31 | "outputs": [],
32 | "source": [
33 | "import logging\n",
34 | "logging.getLogger().setLevel(logging.CRITICAL)\n",
35 | "!pip install pylabel > /dev/null"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": 6,
41 | "metadata": {
42 | "id": "AYwWkeF4z1Sc"
43 | },
44 | "outputs": [],
45 | "source": [
46 | "from pylabel import importer"
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "# Download sample dataset \n",
54 | "For this example we can use a sample dataset stored in coco format. The general approach can later be applied to the full coco dataset."
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": 7,
60 | "metadata": {},
61 | "outputs": [
62 | {
63 | "name": "stdout",
64 | "output_type": "stream",
65 | "text": [
66 | "--2021-11-01 07:52:48-- https://github.com/pylabelalpha/notebook/blob/main/BCCD_coco.zip?raw=true\n",
67 | "Resolving github.com (github.com)... 192.30.255.112\n",
68 | "Connecting to github.com (github.com)|192.30.255.112|:443... connected.\n",
69 | "HTTP request sent, awaiting response... 302 Found\n",
70 | "Location: https://github.com/pylabelalpha/notebook/raw/main/BCCD_coco.zip [following]\n",
71 | "--2021-11-01 07:52:48-- https://github.com/pylabelalpha/notebook/raw/main/BCCD_coco.zip\n",
72 | "Reusing existing connection to github.com:443.\n",
73 | "HTTP request sent, awaiting response... 302 Found\n",
74 | "Location: https://raw.githubusercontent.com/pylabelalpha/notebook/main/BCCD_coco.zip [following]\n",
75 | "--2021-11-01 07:52:48-- https://raw.githubusercontent.com/pylabelalpha/notebook/main/BCCD_coco.zip\n",
76 | "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n",
77 | "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n",
78 | "HTTP request sent, awaiting response... 200 OK\n",
79 | "Length: 7625693 (7.3M) [application/zip]\n",
80 | "Saving to: ‘data/BCCD_coco.zip’\n",
81 | "\n",
82 | "data/BCCD_coco.zip 100%[===================>] 7.27M 21.2MB/s in 0.3s \n",
83 | "\n",
84 | "2021-11-01 07:52:50 (21.2 MB/s) - ‘data/BCCD_coco.zip’ saved [7625693/7625693]\n",
85 | "\n"
86 | ]
87 | },
88 | {
89 | "data": {
90 | "text/html": [
91 | "\n",
92 | "\n",
105 | "
\n",
106 | " \n",
107 | " \n",
108 | " | \n",
109 | " img_folder | \n",
110 | " img_filename | \n",
111 | " img_path | \n",
112 | " img_id | \n",
113 | " img_width | \n",
114 | " img_height | \n",
115 | " img_depth | \n",
116 | " ann_segmented | \n",
117 | " ann_bbox_xmin | \n",
118 | " ann_bbox_ymin | \n",
119 | " ... | \n",
120 | " ann_area | \n",
121 | " ann_segmentation | \n",
122 | " ann_iscrowd | \n",
123 | " ann_pose | \n",
124 | " ann_truncated | \n",
125 | " ann_difficult | \n",
126 | " cat_id | \n",
127 | " cat_name | \n",
128 | " cat_supercategory | \n",
129 | " split | \n",
130 | "
\n",
131 | " \n",
132 | " id | \n",
133 | " | \n",
134 | " | \n",
135 | " | \n",
136 | " | \n",
137 | " | \n",
138 | " | \n",
139 | " | \n",
140 | " | \n",
141 | " | \n",
142 | " | \n",
143 | " | \n",
144 | " | \n",
145 | " | \n",
146 | " | \n",
147 | " | \n",
148 | " | \n",
149 | " | \n",
150 | " | \n",
151 | " | \n",
152 | " | \n",
153 | " | \n",
154 | "
\n",
155 | " \n",
156 | " \n",
157 | " \n",
158 | " 0 | \n",
159 | " | \n",
160 | " BloodImage_00315.jpg | \n",
161 | " None | \n",
162 | " 0 | \n",
163 | " 640 | \n",
164 | " 480 | \n",
165 | " 3 | \n",
166 | " 0 | \n",
167 | " 164.0 | \n",
168 | " 261.0 | \n",
169 | " ... | \n",
170 | " 13699.0 | \n",
171 | " None | \n",
172 | " None | \n",
173 | " Unspecified | \n",
174 | " 0 | \n",
175 | " 0 | \n",
176 | " 0 | \n",
177 | " RBC | \n",
178 | " None | \n",
179 | " | \n",
180 | "
\n",
181 | " \n",
182 | " 1 | \n",
183 | " | \n",
184 | " BloodImage_00315.jpg | \n",
185 | " None | \n",
186 | " 0 | \n",
187 | " 640 | \n",
188 | " 480 | \n",
189 | " 3 | \n",
190 | " 0 | \n",
191 | " 15.0 | \n",
192 | " 66.0 | \n",
193 | " ... | \n",
194 | " 13699.0 | \n",
195 | " None | \n",
196 | " None | \n",
197 | " Unspecified | \n",
198 | " 0 | \n",
199 | " 0 | \n",
200 | " 0 | \n",
201 | " RBC | \n",
202 | " None | \n",
203 | " | \n",
204 | "
\n",
205 | " \n",
206 | " 2 | \n",
207 | " | \n",
208 | " BloodImage_00315.jpg | \n",
209 | " None | \n",
210 | " 0 | \n",
211 | " 640 | \n",
212 | " 480 | \n",
213 | " 3 | \n",
214 | " 0 | \n",
215 | " 13.0 | \n",
216 | " 234.0 | \n",
217 | " ... | \n",
218 | " 11781.0 | \n",
219 | " None | \n",
220 | " None | \n",
221 | " Unspecified | \n",
222 | " 0 | \n",
223 | " 0 | \n",
224 | " 0 | \n",
225 | " RBC | \n",
226 | " None | \n",
227 | " | \n",
228 | "
\n",
229 | " \n",
230 | " 3 | \n",
231 | " | \n",
232 | " BloodImage_00315.jpg | \n",
233 | " None | \n",
234 | " 0 | \n",
235 | " 640 | \n",
236 | " 480 | \n",
237 | " 3 | \n",
238 | " 0 | \n",
239 | " 239.0 | \n",
240 | " 3.0 | \n",
241 | " ... | \n",
242 | " 11960.0 | \n",
243 | " None | \n",
244 | " None | \n",
245 | " Unspecified | \n",
246 | " 0 | \n",
247 | " 0 | \n",
248 | " 0 | \n",
249 | " RBC | \n",
250 | " None | \n",
251 | " | \n",
252 | "
\n",
253 | " \n",
254 | " 4 | \n",
255 | " | \n",
256 | " BloodImage_00315.jpg | \n",
257 | " None | \n",
258 | " 0 | \n",
259 | " 640 | \n",
260 | " 480 | \n",
261 | " 3 | \n",
262 | " 0 | \n",
263 | " 542.0 | \n",
264 | " 109.0 | \n",
265 | " ... | \n",
266 | " 10290.0 | \n",
267 | " None | \n",
268 | " None | \n",
269 | " Unspecified | \n",
270 | " 1 | \n",
271 | " 0 | \n",
272 | " 0 | \n",
273 | " RBC | \n",
274 | " None | \n",
275 | " | \n",
276 | "
\n",
277 | " \n",
278 | "
\n",
279 | "
5 rows × 24 columns
\n",
280 | "
"
281 | ],
282 | "text/plain": [
283 | " img_folder img_filename img_path img_id img_width img_height \\\n",
284 | "id \n",
285 | "0 BloodImage_00315.jpg None 0 640 480 \n",
286 | "1 BloodImage_00315.jpg None 0 640 480 \n",
287 | "2 BloodImage_00315.jpg None 0 640 480 \n",
288 | "3 BloodImage_00315.jpg None 0 640 480 \n",
289 | "4 BloodImage_00315.jpg None 0 640 480 \n",
290 | "\n",
291 | " img_depth ann_segmented ann_bbox_xmin ann_bbox_ymin ... ann_area \\\n",
292 | "id ... \n",
293 | "0 3 0 164.0 261.0 ... 13699.0 \n",
294 | "1 3 0 15.0 66.0 ... 13699.0 \n",
295 | "2 3 0 13.0 234.0 ... 11781.0 \n",
296 | "3 3 0 239.0 3.0 ... 11960.0 \n",
297 | "4 3 0 542.0 109.0 ... 10290.0 \n",
298 | "\n",
299 | " ann_segmentation ann_iscrowd ann_pose ann_truncated ann_difficult \\\n",
300 | "id \n",
301 | "0 None None Unspecified 0 0 \n",
302 | "1 None None Unspecified 0 0 \n",
303 | "2 None None Unspecified 0 0 \n",
304 | "3 None None Unspecified 0 0 \n",
305 | "4 None None Unspecified 1 0 \n",
306 | "\n",
307 | " cat_id cat_name cat_supercategory split \n",
308 | "id \n",
309 | "0 0 RBC None \n",
310 | "1 0 RBC None \n",
311 | "2 0 RBC None \n",
312 | "3 0 RBC None \n",
313 | "4 0 RBC None \n",
314 | "\n",
315 | "[5 rows x 24 columns]"
316 | ]
317 | },
318 | "execution_count": 7,
319 | "metadata": {},
320 | "output_type": "execute_result"
321 | }
322 | ],
323 | "source": [
324 | "import os \n",
325 | "import zipfile\n",
326 | "\n",
327 | "#Download and import sample coco dataset \n",
328 | "os.makedirs(\"data\", exist_ok=True)\n",
329 | "!wget \"https://github.com/pylabelalpha/notebook/blob/main/BCCD_coco.zip?raw=true\" -O data/BCCD_coco.zip\n",
330 | "with zipfile.ZipFile(\"data/BCCD_coco.zip\", 'r') as zip_ref:\n",
331 | " zip_ref.extractall(\"data\")\n",
332 | "\n",
333 | "#Specify path to the coco.json file\n",
334 | "path_to_annotations = \"data/BCCD_Dataset.json\"\n",
335 | "#Specify the path to the images (if they are in a different folder than the annotations)\n",
336 | "path_to_images = \"\"\n",
337 | "\n",
338 | "#Import the dataset into the pylable schema \n",
339 | "dataset = importer.ImportCoco(path_to_annotations, path_to_images=path_to_images, name=\"BCCD_coco\")\n",
340 | "dataset.df.head(5)"
341 | ]
342 | },
343 | {
344 | "cell_type": "markdown",
345 | "metadata": {},
346 | "source": [
347 | "PyLabel imports the annotations into a pandas dataframe. Now you can filter this dataframe to the rows related to the images that you care about. There are 364 images in this dataset."
348 | ]
349 | },
350 | {
351 | "cell_type": "code",
352 | "execution_count": 8,
353 | "metadata": {
354 | "colab": {
355 | "base_uri": "https://localhost:8080/"
356 | },
357 | "id": "5R1rOJVL420b",
358 | "outputId": "ae94525b-c6d5-4a4f-d56d-7f1bd29a7411"
359 | },
360 | "outputs": [
361 | {
362 | "name": "stdout",
363 | "output_type": "stream",
364 | "text": [
365 | "Number of images: 364\n",
366 | "Class counts:\n",
367 | "RBC 4155\n",
368 | "WBC 372\n",
369 | "Platelets 361\n",
370 | "Name: cat_name, dtype: int64\n"
371 | ]
372 | }
373 | ],
374 | "source": [
375 | "print(f\"Number of images: {dataset.analyze.num_images}\")\n",
376 | "print(f\"Class counts:\\n{dataset.analyze.class_counts}\")"
377 | ]
378 | },
379 | {
380 | "cell_type": "markdown",
381 | "metadata": {},
382 | "source": [
383 | "## Extract images\n",
384 | "Lets copy some images to another directory to to represent the images that we care about. "
385 | ]
386 | },
387 | {
388 | "cell_type": "code",
389 | "execution_count": 9,
390 | "metadata": {},
391 | "outputs": [
392 | {
393 | "name": "stdout",
394 | "output_type": "stream",
395 | "text": [
396 | "mkdir: data/100Images/: File exists\n"
397 | ]
398 | }
399 | ],
400 | "source": [
401 | "#Copy 100 images from the BCCD_Dataset/BCCD/JPEGImages/ to BCCD_Dataset/BCCD/100Images/ \n",
402 | "!mkdir data/100Images/ \n",
403 | "!ls data/*.jpg | head -100 | xargs -I{} cp {} data/100Images/ "
404 | ]
405 | },
406 | {
407 | "cell_type": "markdown",
408 | "metadata": {},
409 | "source": [
410 | "Create a list with all of the files in this directory. "
411 | ]
412 | },
413 | {
414 | "cell_type": "code",
415 | "execution_count": 10,
416 | "metadata": {},
417 | "outputs": [
418 | {
419 | "name": "stdout",
420 | "output_type": "stream",
421 | "text": [
422 | "100 files including BloodImage_00000.jpg\n"
423 | ]
424 | }
425 | ],
426 | "source": [
427 | "#Store a list of all of the files in the directory \n",
428 | "files = sorted(os.listdir('data/100Images/'))\n",
429 | "print(f\"{len(files)} files including {files[0]}\")"
430 | ]
431 | },
432 | {
433 | "cell_type": "markdown",
434 | "metadata": {},
435 | "source": [
436 | "Now filter the dataframe to only images in the list of files."
437 | ]
438 | },
439 | {
440 | "cell_type": "code",
441 | "execution_count": 11,
442 | "metadata": {},
443 | "outputs": [
444 | {
445 | "name": "stdout",
446 | "output_type": "stream",
447 | "text": [
448 | "Number of images 100\n"
449 | ]
450 | }
451 | ],
452 | "source": [
453 | "dataset.df = dataset.df[dataset.df.img_filename.isin(files)].reset_index()\n",
454 | "print(f\"Number of images {dataset.df.img_filename.nunique()}\")\n"
455 | ]
456 | },
457 | {
458 | "cell_type": "markdown",
459 | "metadata": {},
460 | "source": [
461 | "# Export annotations back as a coso json file"
462 | ]
463 | },
464 | {
465 | "cell_type": "code",
466 | "execution_count": 12,
467 | "metadata": {},
468 | "outputs": [
469 | {
470 | "data": {
471 | "text/plain": [
472 | "['data/100Images/100Images_coco.json']"
473 | ]
474 | },
475 | "execution_count": 12,
476 | "metadata": {},
477 | "output_type": "execute_result"
478 | }
479 | ],
480 | "source": [
481 | "dataset.path_to_annotations = 'data/100Images/'\n",
482 | "dataset.name = '100Images_coco'\n",
483 | "\n",
484 | "dataset.export.ExportToCoco()"
485 | ]
486 | }
487 | ],
488 | "metadata": {
489 | "colab": {
490 | "collapsed_sections": [],
491 | "name": "import2.ipynb",
492 | "provenance": []
493 | },
494 | "interpreter": {
495 | "hash": "de5e368ab5494158da905fb8b1c2e4fef8844fbfaace7cceca0beab983df3a80"
496 | },
497 | "kernelspec": {
498 | "display_name": "Python 3.8.5 64-bit ('penv': venv)",
499 | "name": "python3"
500 | },
501 | "language_info": {
502 | "codemirror_mode": {
503 | "name": "ipython",
504 | "version": 3
505 | },
506 | "file_extension": ".py",
507 | "mimetype": "text/x-python",
508 | "name": "python",
509 | "nbconvert_exporter": "python",
510 | "pygments_lexer": "ipython3",
511 | "version": "3.8.5"
512 | }
513 | },
514 | "nbformat": 4,
515 | "nbformat_minor": 2
516 | }
517 |
--------------------------------------------------------------------------------
/label_new_dataset.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "
\n",
8 | "\n",
9 | "
\n",
10 | ""
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "metadata": {
16 | "id": "Cxi8K9mXwl5t"
17 | },
18 | "source": [
19 | "# Autolabel images with PyLabel, YOLOv5, and jupyter-bbox-widget\n",
20 | "This notebook is labeling tool that can used to annotate image datasets with bounding boxes, automatically suggest bounding boxes using an object detection model, and save the annotations in YOCO, COCO, or VOC format. \n",
21 | "\n",
22 | "The annotation interface uses the [jupyter-bbox-widget](https://github.com/gereleth/jupyter-bbox-widget). The bounding box detection uses PyTorch and a [VOLOv5](https://github.com/ultralytics/yolov5) model."
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": 1,
28 | "metadata": {},
29 | "outputs": [
30 | {
31 | "name": "stdout",
32 | "output_type": "stream",
33 | "text": [
34 | "Note: you may need to restart the kernel to use updated packages.\n"
35 | ]
36 | }
37 | ],
38 | "source": [
39 | "import logging\n",
40 | "logging.getLogger().setLevel(logging.CRITICAL)\n",
41 | "%pip install pylabel > /dev/null"
42 | ]
43 | },
44 | {
45 | "cell_type": "code",
46 | "execution_count": 2,
47 | "metadata": {
48 | "id": "AYwWkeF4z1Sc"
49 | },
50 | "outputs": [],
51 | "source": [
52 | "from pylabel import importer"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "## Import Images to Create a New Dataset\n",
60 | "In this example there are no annotations created yet. The path should be the path to a directory with the images that you want to annotate. For this demonstration we will download a subset of the coco dataset. "
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 3,
66 | "metadata": {},
67 | "outputs": [
68 | {
69 | "name": "stdout",
70 | "output_type": "stream",
71 | "text": [
72 | "--2022-01-10 20:59:01-- https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip\n",
73 | "Resolving github.com (github.com)... 192.30.255.112\n",
74 | "Connecting to github.com (github.com)|192.30.255.112|:443... connected.\n",
75 | "HTTP request sent, awaiting response... 302 Found\n",
76 | "Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/264818686/7a208a00-e19d-11eb-94cf-5222600cc665?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20220111%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220111T045901Z&X-Amz-Expires=300&X-Amz-Signature=be5a6a8e9e904069734e102690defe3169e6bd5fabf6f590baad342825534153&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=264818686&response-content-disposition=attachment%3B%20filename%3Dcoco128.zip&response-content-type=application%2Foctet-stream [following]\n",
77 | "--2022-01-10 20:59:01-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/264818686/7a208a00-e19d-11eb-94cf-5222600cc665?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20220111%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220111T045901Z&X-Amz-Expires=300&X-Amz-Signature=be5a6a8e9e904069734e102690defe3169e6bd5fabf6f590baad342825534153&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=264818686&response-content-disposition=attachment%3B%20filename%3Dcoco128.zip&response-content-type=application%2Foctet-stream\n",
78 | "Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n",
79 | "Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.108.133|:443... connected.\n",
80 | "HTTP request sent, awaiting response... 200 OK\n",
81 | "Length: 6984509 (6.7M) [application/octet-stream]\n",
82 | "Saving to: ‘data/coco128.zip’\n",
83 | "\n",
84 | "data/coco128.zip 100%[===================>] 6.66M 11.1MB/s in 0.6s \n",
85 | "\n",
86 | "2022-01-10 20:59:02 (11.1 MB/s) - ‘data/coco128.zip’ saved [6984509/6984509]\n",
87 | "\n"
88 | ]
89 | }
90 | ],
91 | "source": [
92 | "import os, zipfile\n",
93 | "\n",
94 | "#Download sample yolo dataset \n",
95 | "os.makedirs(\"data\", exist_ok=True)\n",
96 | "!wget \"https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip\" -O data/coco128.zip\n",
97 | "with zipfile.ZipFile(\"data/coco128.zip\", 'r') as zip_ref:\n",
98 | " zip_ref.extractall(\"data\")"
99 | ]
100 | },
101 | {
102 | "cell_type": "code",
103 | "execution_count": 4,
104 | "metadata": {},
105 | "outputs": [
106 | {
107 | "data": {
108 | "text/html": [
109 | "\n",
110 | "\n",
123 | "
\n",
124 | " \n",
125 | " \n",
126 | " | \n",
127 | " img_folder | \n",
128 | " img_filename | \n",
129 | " img_path | \n",
130 | " img_id | \n",
131 | " img_width | \n",
132 | " img_height | \n",
133 | " img_depth | \n",
134 | " ann_segmented | \n",
135 | " ann_bbox_xmin | \n",
136 | " ann_bbox_ymin | \n",
137 | " ... | \n",
138 | " ann_segmentation | \n",
139 | " ann_iscrowd | \n",
140 | " ann_pose | \n",
141 | " ann_truncated | \n",
142 | " ann_difficult | \n",
143 | " cat_id | \n",
144 | " cat_name | \n",
145 | " cat_supercategory | \n",
146 | " split | \n",
147 | " annotated | \n",
148 | "
\n",
149 | " \n",
150 | " id | \n",
151 | " | \n",
152 | " | \n",
153 | " | \n",
154 | " | \n",
155 | " | \n",
156 | " | \n",
157 | " | \n",
158 | " | \n",
159 | " | \n",
160 | " | \n",
161 | " | \n",
162 | " | \n",
163 | " | \n",
164 | " | \n",
165 | " | \n",
166 | " | \n",
167 | " | \n",
168 | " | \n",
169 | " | \n",
170 | " | \n",
171 | " | \n",
172 | "
\n",
173 | " \n",
174 | " \n",
175 | " \n",
176 | " 0 | \n",
177 | " | \n",
178 | " 000000000612.jpg | \n",
179 | " NaN | \n",
180 | " 0 | \n",
181 | " 640 | \n",
182 | " 480 | \n",
183 | " 3 | \n",
184 | " NaN | \n",
185 | " NaN | \n",
186 | " NaN | \n",
187 | " ... | \n",
188 | " NaN | \n",
189 | " NaN | \n",
190 | " NaN | \n",
191 | " NaN | \n",
192 | " NaN | \n",
193 | " NaN | \n",
194 | " | \n",
195 | " NaN | \n",
196 | " NaN | \n",
197 | " NaN | \n",
198 | "
\n",
199 | " \n",
200 | " 1 | \n",
201 | " | \n",
202 | " 000000000404.jpg | \n",
203 | " NaN | \n",
204 | " 1 | \n",
205 | " 426 | \n",
206 | " 640 | \n",
207 | " 3 | \n",
208 | " NaN | \n",
209 | " NaN | \n",
210 | " NaN | \n",
211 | " ... | \n",
212 | " NaN | \n",
213 | " NaN | \n",
214 | " NaN | \n",
215 | " NaN | \n",
216 | " NaN | \n",
217 | " NaN | \n",
218 | " | \n",
219 | " NaN | \n",
220 | " NaN | \n",
221 | " NaN | \n",
222 | "
\n",
223 | " \n",
224 | " 2 | \n",
225 | " | \n",
226 | " 000000000438.jpg | \n",
227 | " NaN | \n",
228 | " 2 | \n",
229 | " 640 | \n",
230 | " 480 | \n",
231 | " 3 | \n",
232 | " NaN | \n",
233 | " NaN | \n",
234 | " NaN | \n",
235 | " ... | \n",
236 | " NaN | \n",
237 | " NaN | \n",
238 | " NaN | \n",
239 | " NaN | \n",
240 | " NaN | \n",
241 | " NaN | \n",
242 | " | \n",
243 | " NaN | \n",
244 | " NaN | \n",
245 | " NaN | \n",
246 | "
\n",
247 | " \n",
248 | "
\n",
249 | "
3 rows × 25 columns
\n",
250 | "
"
251 | ],
252 | "text/plain": [
253 | " img_folder img_filename img_path img_id img_width img_height \\\n",
254 | "id \n",
255 | "0 000000000612.jpg NaN 0 640 480 \n",
256 | "1 000000000404.jpg NaN 1 426 640 \n",
257 | "2 000000000438.jpg NaN 2 640 480 \n",
258 | "\n",
259 | " img_depth ann_segmented ann_bbox_xmin ann_bbox_ymin ... ann_segmentation \\\n",
260 | "id ... \n",
261 | "0 3 NaN NaN NaN ... NaN \n",
262 | "1 3 NaN NaN NaN ... NaN \n",
263 | "2 3 NaN NaN NaN ... NaN \n",
264 | "\n",
265 | " ann_iscrowd ann_pose ann_truncated ann_difficult cat_id cat_name \\\n",
266 | "id \n",
267 | "0 NaN NaN NaN NaN NaN \n",
268 | "1 NaN NaN NaN NaN NaN \n",
269 | "2 NaN NaN NaN NaN NaN \n",
270 | "\n",
271 | " cat_supercategory split annotated \n",
272 | "id \n",
273 | "0 NaN NaN NaN \n",
274 | "1 NaN NaN NaN \n",
275 | "2 NaN NaN NaN \n",
276 | "\n",
277 | "[3 rows x 25 columns]"
278 | ]
279 | },
280 | "execution_count": 4,
281 | "metadata": {},
282 | "output_type": "execute_result"
283 | }
284 | ],
285 | "source": [
286 | "path_to_images = \"data/coco128/images/train2017\"\n",
287 | "dataset = importer.ImportImagesOnly(path=path_to_images, name=\"coco128\")\n",
288 | "dataset.df.head(3)"
289 | ]
290 | },
291 | {
292 | "cell_type": "markdown",
293 | "metadata": {},
294 | "source": [
295 | "## Predict and Edit Annotations\n",
296 | "Use the jupyter_bbox_widget to inspect, edit, and save annotations without leaving the Jupyter notebook. Press predict to autolabel images using a pretrained model. For instructions and keyboard shortcuts for using this widget see https://github.com/gereleth/jupyter-bbox-widget#Usage."
297 | ]
298 | },
299 | {
300 | "cell_type": "code",
301 | "execution_count": 5,
302 | "metadata": {},
303 | "outputs": [
304 | {
305 | "data": {
306 | "application/vnd.jupyter.widget-view+json": {
307 | "model_id": "eef0a9d413534c74ada849ee51608e55",
308 | "version_major": 2,
309 | "version_minor": 0
310 | },
311 | "text/plain": [
312 | "VBox(children=(HBox(children=(Label(value='000000000612.jpg (not annotated)'),)), HBox(children=(Button(icon='…"
313 | ]
314 | },
315 | "metadata": {},
316 | "output_type": "display_data"
317 | }
318 | ],
319 | "source": [
320 | "classes = ['person','boat', 'bear', \"car\"]\n",
321 | "dataset.labeler.StartPyLaber(new_classes=classes)"
322 | ]
323 | },
324 | {
325 | "cell_type": "markdown",
326 | "metadata": {},
327 | "source": [
328 | "# Instructions \n",
329 | "- The first image (000000000612.jpg) should show some bears. Select the bear cleass draw some some boxes around the bears and then save.\n",
330 | "- The next image should be a boat. (000000000404.jpg) Select the boat class, draw boxes around the boats, and save.\n",
331 | "- When you see an image with an object that is not in the current list of classes, add it as new class, draw boxes on the image using that class and save. \n",
332 | "At anytime, run the cell below to see how many classes you have labeled in the dataset. "
333 | ]
334 | },
335 | {
336 | "cell_type": "code",
337 | "execution_count": 6,
338 | "metadata": {},
339 | "outputs": [
340 | {
341 | "data": {
342 | "text/plain": [
343 | " 128\n",
344 | "Name: cat_name, dtype: int64"
345 | ]
346 | },
347 | "execution_count": 6,
348 | "metadata": {},
349 | "output_type": "execute_result"
350 | }
351 | ],
352 | "source": [
353 | "dataset.analyze.class_counts"
354 | ]
355 | },
356 | {
357 | "cell_type": "code",
358 | "execution_count": 8,
359 | "metadata": {},
360 | "outputs": [
361 | {
362 | "data": {
363 | "text/plain": [
364 | "['training/dataset.yaml',\n",
365 | " 'training/labels/000000000612.txt',\n",
366 | " 'training/labels/000000000404.txt']"
367 | ]
368 | },
369 | "execution_count": 8,
370 | "metadata": {},
371 | "output_type": "execute_result"
372 | }
373 | ],
374 | "source": [
375 | "#Export the annotations in Yolo format\n",
376 | "dataset.path_to_annotations = 'data/coco128/labels/newlabels/'\n",
377 | "os.makedirs(dataset.path_to_annotations, exist_ok=True)\n",
378 | "dataset.export.ExportToYoloV5()"
379 | ]
380 | },
381 | {
382 | "cell_type": "code",
383 | "execution_count": null,
384 | "metadata": {},
385 | "outputs": [],
386 | "source": []
387 | }
388 | ],
389 | "metadata": {
390 | "colab": {
391 | "collapsed_sections": [],
392 | "name": "import2.ipynb",
393 | "provenance": []
394 | },
395 | "interpreter": {
396 | "hash": "de5e368ab5494158da905fb8b1c2e4fef8844fbfaace7cceca0beab983df3a80"
397 | },
398 | "kernelspec": {
399 | "display_name": "Python 3.8.5 64-bit ('penv': venv)",
400 | "name": "python3"
401 | },
402 | "language_info": {
403 | "codemirror_mode": {
404 | "name": "ipython",
405 | "version": 3
406 | },
407 | "file_extension": ".py",
408 | "mimetype": "text/x-python",
409 | "name": "python",
410 | "nbconvert_exporter": "python",
411 | "pygments_lexer": "ipython3",
412 | "version": "3.8.5"
413 | }
414 | },
415 | "nbformat": 4,
416 | "nbformat_minor": 2
417 | }
418 |
--------------------------------------------------------------------------------
/pylabel2azure_custom_vision.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "
\n",
8 | "# Upload Annotations to Azure Custom Vision \n",
9 | "Custom Vision, part of the Azure Cognitive Services family, is a solution for training and deploying custom computer vision models. Custom Vision includes an API to upload images and annotations to train a custom model. Using PyLabel you can import existing labels in COCO, YOLOv5, or VOC format and then upload the dataset to Custom Vision. \n",
10 | "\n",
11 | "This notebook demonstrates how to import a custom dataset in YOLO format to Custom Vision. To complete the steps you will need an Azure Account and a Custom Vision subscription. Follow [this tutorial on the the Custom Vision site](https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/quickstarts/object-detection?tabs=visual-studio&pivots=programming-language-python) to setup your account and make sure it is working before using this notebook to import a custom dataset. When you are ready to use this notebook to upload a custom dataset, it is recommended to open https://www.customvision.ai/ so you can see the results of the commands you are performing through the API. "
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 1,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "%pip install azure-cognitiveservices-vision-customvision\n",
21 | "%pip install pylabel\n",
22 | "\n",
23 | "#Import Azure cognitive services libraries \n",
24 | "from azure.cognitiveservices.vision.customvision.training import CustomVisionTrainingClient\n",
25 | "from azure.cognitiveservices.vision.customvision.prediction import CustomVisionPredictionClient\n",
26 | "from azure.cognitiveservices.vision.customvision.training.models import ImageFileCreateBatch, ImageFileCreateEntry, Region\n",
27 | "from msrest.authentication import ApiKeyCredentials\n",
28 | "\n",
29 | "#Import other libraries used in this notebook \n",
30 | "import os, zipfile\n",
31 | "from pathlib import PurePath\n",
32 | "from os.path import exists\n",
33 | "from decimal import *\n",
34 | "\n",
35 | "from pylabel import importer"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": 2,
41 | "metadata": {},
42 | "outputs": [],
43 | "source": [
44 | "# Replace with your Azure endpoint and subscription keys.\n",
45 | "ENDPOINT = \"\"\n",
46 | "training_key = \"\"\n",
47 | "prediction_key = \"\"\n",
48 | "prediction_resource_id = \"\""
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": 3,
54 | "metadata": {},
55 | "outputs": [],
56 | "source": [
57 | "#Initialize objects used by Azure Congitive vision\n",
58 | "credentials = ApiKeyCredentials(in_headers={\"Training-key\": training_key})\n",
59 | "trainer = CustomVisionTrainingClient(ENDPOINT, credentials)\n",
60 | "prediction_credentials = ApiKeyCredentials(in_headers={\"Prediction-key\": prediction_key})\n",
61 | "predictor = CustomVisionPredictionClient(ENDPOINT, prediction_credentials)"
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": 4,
67 | "metadata": {},
68 | "outputs": [],
69 | "source": [
70 | "#Create a new project\n",
71 | "publish_iteration_name = \"detectModel\"\n",
72 | "obj_detection_domain = next(domain for domain in trainer.get_domains() if domain.type == \"ObjectDetection\" and domain.name == \"General\")\n",
73 | "project = trainer.create_project(\"PyLabel Sample Dataset\", domain_id=obj_detection_domain.id)\n",
74 | "#If you browse to https://www.customvision.ai/ you should see a new project called \"PyLabel Sample Dataset\""
75 | ]
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "metadata": {},
80 | "source": [
81 | "## Download Custom Dataset \n",
82 | "For this demonstration we will download 100 images from the squirrels and nuts dataset with annotations in YOLOv5 format. PyLabel can also import datasets in COCO and PASCAL VOC format. "
83 | ]
84 | },
85 | {
86 | "cell_type": "code",
87 | "execution_count": 5,
88 | "metadata": {},
89 | "outputs": [],
90 | "source": [
91 | "%%capture\n",
92 | "os.makedirs(\"data/\", exist_ok=True)\n",
93 | "!wget \"https://github.com/pylabel-project/datasets_models/blob/main/squirrelsandnuts/squirrelsandnuts_train.zip?raw=true\" -O data/squirrelsandnuts_train.zip\n",
94 | "with zipfile.ZipFile(\"data/squirrelsandnuts_train.zip\", 'r') as zip_ref:\n",
95 | " zip_ref.extractall(\"data/\")"
96 | ]
97 | },
98 | {
99 | "cell_type": "code",
100 | "execution_count": 6,
101 | "metadata": {},
102 | "outputs": [
103 | {
104 | "data": {
105 | "text/html": [
106 | "\n",
107 | "\n",
120 | "
\n",
121 | " \n",
122 | " \n",
123 | " | \n",
124 | " img_folder | \n",
125 | " img_filename | \n",
126 | " img_path | \n",
127 | " img_id | \n",
128 | " img_width | \n",
129 | " img_height | \n",
130 | " img_depth | \n",
131 | " ann_segmented | \n",
132 | " ann_bbox_xmin | \n",
133 | " ann_bbox_ymin | \n",
134 | " ... | \n",
135 | " ann_segmentation | \n",
136 | " ann_iscrowd | \n",
137 | " ann_pose | \n",
138 | " ann_truncated | \n",
139 | " ann_difficult | \n",
140 | " cat_id | \n",
141 | " cat_name | \n",
142 | " cat_supercategory | \n",
143 | " split | \n",
144 | " annotated | \n",
145 | "
\n",
146 | " \n",
147 | " id | \n",
148 | " | \n",
149 | " | \n",
150 | " | \n",
151 | " | \n",
152 | " | \n",
153 | " | \n",
154 | " | \n",
155 | " | \n",
156 | " | \n",
157 | " | \n",
158 | " | \n",
159 | " | \n",
160 | " | \n",
161 | " | \n",
162 | " | \n",
163 | " | \n",
164 | " | \n",
165 | " | \n",
166 | " | \n",
167 | " | \n",
168 | " | \n",
169 | "
\n",
170 | " \n",
171 | " \n",
172 | " \n",
173 | " 0 | \n",
174 | " ../../images/train | \n",
175 | " 2021-07-03T06-30-10-frame_0001.jpeg | \n",
176 | " NaN | \n",
177 | " 0 | \n",
178 | " 960 | \n",
179 | " 540 | \n",
180 | " 3 | \n",
181 | " NaN | \n",
182 | " 255.024 | \n",
183 | " 170.991 | \n",
184 | " ... | \n",
185 | " NaN | \n",
186 | " NaN | \n",
187 | " NaN | \n",
188 | " NaN | \n",
189 | " NaN | \n",
190 | " 0 | \n",
191 | " Squirrel | \n",
192 | " NaN | \n",
193 | " NaN | \n",
194 | " 1 | \n",
195 | "
\n",
196 | " \n",
197 | " 1 | \n",
198 | " ../../images/train | \n",
199 | " 2021-07-03T06-47-39-frame_0004.jpeg | \n",
200 | " NaN | \n",
201 | " 1 | \n",
202 | " 960 | \n",
203 | " 540 | \n",
204 | " 3 | \n",
205 | " NaN | \n",
206 | " 650.016 | \n",
207 | " 447.984 | \n",
208 | " ... | \n",
209 | " NaN | \n",
210 | " NaN | \n",
211 | " NaN | \n",
212 | " NaN | \n",
213 | " NaN | \n",
214 | " 1 | \n",
215 | " Nut | \n",
216 | " NaN | \n",
217 | " NaN | \n",
218 | " 1 | \n",
219 | "
\n",
220 | " \n",
221 | " 2 | \n",
222 | " ../../images/train | \n",
223 | " 2021-07-03T06-47-39-frame_0004.jpeg | \n",
224 | " NaN | \n",
225 | " 1 | \n",
226 | " 960 | \n",
227 | " 540 | \n",
228 | " 3 | \n",
229 | " NaN | \n",
230 | " 690.480 | \n",
231 | " 422.010 | \n",
232 | " ... | \n",
233 | " NaN | \n",
234 | " NaN | \n",
235 | " NaN | \n",
236 | " NaN | \n",
237 | " NaN | \n",
238 | " 1 | \n",
239 | " Nut | \n",
240 | " NaN | \n",
241 | " NaN | \n",
242 | " 1 | \n",
243 | "
\n",
244 | " \n",
245 | "
\n",
246 | "
3 rows × 25 columns
\n",
247 | "
"
248 | ],
249 | "text/plain": [
250 | " img_folder img_filename img_path img_id \\\n",
251 | "id \n",
252 | "0 ../../images/train 2021-07-03T06-30-10-frame_0001.jpeg NaN 0 \n",
253 | "1 ../../images/train 2021-07-03T06-47-39-frame_0004.jpeg NaN 1 \n",
254 | "2 ../../images/train 2021-07-03T06-47-39-frame_0004.jpeg NaN 1 \n",
255 | "\n",
256 | " img_width img_height img_depth ann_segmented ann_bbox_xmin \\\n",
257 | "id \n",
258 | "0 960 540 3 NaN 255.024 \n",
259 | "1 960 540 3 NaN 650.016 \n",
260 | "2 960 540 3 NaN 690.480 \n",
261 | "\n",
262 | " ann_bbox_ymin ... ann_segmentation ann_iscrowd ann_pose \\\n",
263 | "id ... \n",
264 | "0 170.991 ... NaN NaN NaN \n",
265 | "1 447.984 ... NaN NaN NaN \n",
266 | "2 422.010 ... NaN NaN NaN \n",
267 | "\n",
268 | " ann_truncated ann_difficult cat_id cat_name cat_supercategory split \\\n",
269 | "id \n",
270 | "0 NaN NaN 0 Squirrel NaN NaN \n",
271 | "1 NaN NaN 1 Nut NaN NaN \n",
272 | "2 NaN NaN 1 Nut NaN NaN \n",
273 | "\n",
274 | " annotated \n",
275 | "id \n",
276 | "0 1 \n",
277 | "1 1 \n",
278 | "2 1 \n",
279 | "\n",
280 | "[3 rows x 25 columns]"
281 | ]
282 | },
283 | "execution_count": 6,
284 | "metadata": {},
285 | "output_type": "execute_result"
286 | }
287 | ],
288 | "source": [
289 | "#Import annotations as a PyLabel dataset\n",
290 | "dataset = importer.ImportYoloV5(path=\"data/squirrelsandnuts_train/labels/train\",\n",
291 | " path_to_images=\"../../images/train\", \n",
292 | " img_ext=\"jpeg\",\n",
293 | " cat_names=['Squirrel','Nut']\n",
294 | " )\n",
295 | "dataset.df.head(3)"
296 | ]
297 | },
298 | {
299 | "cell_type": "markdown",
300 | "metadata": {},
301 | "source": [
302 | "## Import to Azure Custom Vision\n",
303 | "PyLabel stores the annotations as a pandas dataframe. Now you can use extract the annotations from the dataframe and use it as inputs to the Custom Vision APIs. \n",
304 | "\n",
305 | "The first step is to create tags for each of the classes in your custom dataset. A list of class names is available in the dataset.analyze.classes property. "
306 | ]
307 | },
308 | {
309 | "cell_type": "code",
310 | "execution_count": 7,
311 | "metadata": {},
312 | "outputs": [
313 | {
314 | "name": "stdout",
315 | "output_type": "stream",
316 | "text": [
317 | "['Squirrel', 'Nut']\n"
318 | ]
319 | }
320 | ],
321 | "source": [
322 | "print(dataset.analyze.classes)\n",
323 | "#Create a tag for each class and store then in a dict where the class name is the key\n",
324 | "tags = {}\n",
325 | "for class_name in dataset.analyze.classes:\n",
326 | " tag = trainer.create_tag(project.id, class_name)\n",
327 | " tags[class_name] = tag"
328 | ]
329 | },
330 | {
331 | "cell_type": "markdown",
332 | "metadata": {},
333 | "source": [
334 | "Now if you check your account on https://www.customvision.ai/ you should see a new project called \"PyLabel Sample Dataset\" with 2 tags added: Squirrels and Nuts. \n",
335 | "\n",
336 | "You are ready to upload your images and annotations. For each image in your dataset you will need to add \"Regions\" for each bounding box and then upload the image and annotations. "
337 | ]
338 | },
339 | {
340 | "cell_type": "code",
341 | "execution_count": 14,
342 | "metadata": {},
343 | "outputs": [
344 | {
345 | "name": "stdout",
346 | "output_type": "stream",
347 | "text": [
348 | "Upload complete\n"
349 | ]
350 | }
351 | ],
352 | "source": [
353 | "#Iterate the rows for each image in the dataframe\n",
354 | "for img_filename, img_df in dataset.df.groupby('img_filename'):\n",
355 | " img_path = str(PurePath(dataset.path_to_annotations, str(img_df.iloc[0].img_folder), img_filename))\n",
356 | " assert exists(img_path), f\"File does not exist: {img_path}\"\n",
357 | "\n",
358 | " #Create a region object for each bounding box in the dataset \n",
359 | " regions = []\n",
360 | " for index, row in img_df.iterrows():\n",
361 | "\n",
362 | " #Normalize the boundings box coordinates between 0 and 1\n",
363 | " x = Decimal(row.ann_bbox_xmin / row.img_width).min(1)\n",
364 | " y = Decimal(row.ann_bbox_ymin / row.img_height).min(1)\n",
365 | " w = Decimal(row.ann_bbox_width / row.img_width).min(1-x)\n",
366 | " h = Decimal(row.ann_bbox_height / row.img_height).min(1-y)\n",
367 | " \n",
368 | " regions.append(Region(\n",
369 | " tag_id=tags[row.cat_name].id, \n",
370 | " left=x,\n",
371 | " top=y,\n",
372 | " width=w,\n",
373 | " height=h\n",
374 | " )\n",
375 | " )\n",
376 | "\n",
377 | " #Create an object with the image and all of the annotations for that image\n",
378 | " with open(img_path, mode=\"rb\") as image_contents:\n",
379 | " image_and_annotations = [ImageFileCreateEntry(name=img_filename, contents=image_contents.read(), regions=regions)]\n",
380 | "\n",
381 | " #Upload the image and all annnotations for that image\n",
382 | " upload_result = trainer.create_images_from_files(\n",
383 | " project.id, \n",
384 | " ImageFileCreateBatch(images=image_and_annotations)\n",
385 | " )\n",
386 | " \n",
387 | " #If upload is not successful, print details about that image for debugging \n",
388 | " if not upload_result.is_batch_successful:\n",
389 | " print(\"Image upload failed.\")\n",
390 | " for image in upload_result.images:\n",
391 | " print(img_path)\n",
392 | " print(\"Image status: \", image.status)\n",
393 | " print(regions)\n",
394 | "\n",
395 | "#This will take a few minutes \n",
396 | "print(\"Upload complete\")\n"
397 | ]
398 | },
399 | {
400 | "cell_type": "markdown",
401 | "metadata": {},
402 | "source": [
403 | "Now you should see all of your images uploaded to https://www.customvision.ai/.\n",
404 | "\n",
405 | "
\n",
406 | "
\n",
407 | "Click and image to see the bounding boxes.\n",
408 | "
\n",
409 | "
\n",
410 | "
\n",
411 | "\n",
412 | "Now you are ready to train a model, which you can do at https://www.customvision.ai/. \n",
413 | "- If find a problem with this notebook, please report it as an issue here: https://github.com/pylabel-project/pylabel/issues \n",
414 | "- If have other questions, please start a discussion here: https://github.com/pylabel-project/pylabel/discussions. "
415 | ]
416 | }
417 | ],
418 | "metadata": {
419 | "interpreter": {
420 | "hash": "224f10583582cbeb83347e66d7d5874fb4e3ef8613e486088287d7c0b66e9aac"
421 | },
422 | "kernelspec": {
423 | "display_name": "Python 3.8.5 64-bit ('venv': venv)",
424 | "language": "python",
425 | "name": "python3"
426 | },
427 | "language_info": {
428 | "codemirror_mode": {
429 | "name": "ipython",
430 | "version": 3
431 | },
432 | "file_extension": ".py",
433 | "mimetype": "text/x-python",
434 | "name": "python",
435 | "nbconvert_exporter": "python",
436 | "pygments_lexer": "ipython3",
437 | "version": "3.8.5"
438 | },
439 | "orig_nbformat": 4
440 | },
441 | "nbformat": 4,
442 | "nbformat_minor": 2
443 | }
444 |
--------------------------------------------------------------------------------
/yolo_with_yaml_importer.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "Pylabel_YoloWithYAML_Importer",
7 | "provenance": [],
8 | "collapsed_sections": []
9 | },
10 | "kernelspec": {
11 | "name": "python3",
12 | "display_name": "Python 3"
13 | },
14 | "language_info": {
15 | "name": "python"
16 | }
17 | },
18 | "cells": [
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {
22 | "id": "zC-dG7dt6Ks-"
23 | },
24 | "source": [
25 | "This shows how PyLabel can import data when given a YAML File and a dataset that has already had its images and annotations/labels folders split into train, test and val. This is our expected use case."
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "metadata": {
31 | "id": "ZlG6Nnec0EHi"
32 | },
33 | "source": [
34 | "import torch\n",
35 | "from IPython.display import Image # for displaying images\n",
36 | "import os, zipfile\n",
37 | "import random\n",
38 | "import shutil\n",
39 | "from sklearn.model_selection import train_test_split\n",
40 | "import xml.etree.ElementTree as ET\n",
41 | "from xml.dom import minidom\n",
42 | "from tqdm import tqdm\n",
43 | "from PIL import Image, ImageDraw\n",
44 | "import numpy as np\n",
45 | "import matplotlib.pyplot as plt\n",
46 | "\n",
47 | "random.seed(108)\n",
48 | "\n",
49 | "import logging\n",
50 | "logging.getLogger().setLevel(logging.CRITICAL)\n",
51 | "!pip install pylabel > /dev/null\n",
52 | "\n",
53 | "#!pip install pylabel\n",
54 | "\n",
55 | "from pylabel import importer\n",
56 | "from pylabel import *\n",
57 | "\n",
58 | "from pathlib import PurePath\n",
59 | "\n",
60 | "import yaml"
61 | ],
62 | "execution_count": 1,
63 | "outputs": []
64 | },
65 | {
66 | "cell_type": "code",
67 | "metadata": {
68 | "colab": {
69 | "base_uri": "https://localhost:8080/"
70 | },
71 | "id": "REHUX3HHhSeP",
72 | "outputId": "c286f4e2-9e83-48ab-8ef7-716ffc1cf01c"
73 | },
74 | "source": [
75 | "#A random dataset that we found online.\n",
76 | "!wget -O roadsign_splitdata.zip https://raw.githubusercontent.com/pylabel-project/datasets_models/main/roadsign_splitdata.zip\n",
77 | "!unzip roadsign_splitdata.zip\n"
78 | ],
79 | "execution_count": 2,
80 | "outputs": [
81 | {
82 | "output_type": "stream",
83 | "name": "stdout",
84 | "text": [
85 | "--2021-11-30 01:46:42-- https://raw.githubusercontent.com/pylabel-project/datasets_models/main/roadsign_splitdata.zip\n",
86 | "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n",
87 | "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n",
88 | "HTTP request sent, awaiting response... 200 OK\n",
89 | "Length: 22454439 (21M) [application/zip]\n",
90 | "Saving to: ‘roadsign_splitdata.zip’\n",
91 | "\n",
92 | "roadsign_splitdata. 100%[===================>] 21.41M 137MB/s in 0.2s \n",
93 | "\n",
94 | "2021-11-30 01:46:44 (137 MB/s) - ‘roadsign_splitdata.zip’ saved [22454439/22454439]\n",
95 | "\n",
96 | "Archive: roadsign_splitdata.zip\n",
97 | " inflating: annotations/test/road10.txt \n",
98 | " inflating: annotations/test/road29.txt \n",
99 | " inflating: annotations/test/road30.txt \n",
100 | " inflating: annotations/test/road38.txt \n",
101 | " inflating: annotations/test/road41.txt \n",
102 | " inflating: annotations/train/road0.txt \n",
103 | " inflating: annotations/train/road1.txt \n",
104 | " inflating: annotations/train/road12.txt \n",
105 | " inflating: annotations/train/road13.txt \n",
106 | " inflating: annotations/train/road14.txt \n",
107 | " inflating: annotations/train/road15.txt \n",
108 | " inflating: annotations/train/road16.txt \n",
109 | " inflating: annotations/train/road17.txt \n",
110 | " inflating: annotations/train/road18.txt \n",
111 | " inflating: annotations/train/road19.txt \n",
112 | " inflating: annotations/train/road2.txt \n",
113 | " inflating: annotations/train/road20.txt \n",
114 | " inflating: annotations/train/road21.txt \n",
115 | " inflating: annotations/train/road22.txt \n",
116 | " inflating: annotations/train/road23.txt \n",
117 | " inflating: annotations/train/road25.txt \n",
118 | " inflating: annotations/train/road26.txt \n",
119 | " inflating: annotations/train/road27.txt \n",
120 | " inflating: annotations/train/road28.txt \n",
121 | " inflating: annotations/train/road3.txt \n",
122 | " inflating: annotations/train/road31.txt \n",
123 | " inflating: annotations/train/road32.txt \n",
124 | " inflating: annotations/train/road33.txt \n",
125 | " inflating: annotations/train/road34.txt \n",
126 | " inflating: annotations/train/road35.txt \n",
127 | " inflating: annotations/train/road37.txt \n",
128 | " inflating: annotations/train/road39.txt \n",
129 | " inflating: annotations/train/road4.txt \n",
130 | " inflating: annotations/train/road40.txt \n",
131 | " inflating: annotations/train/road42.txt \n",
132 | " inflating: annotations/train/road5.txt \n",
133 | " inflating: annotations/train/road6.txt \n",
134 | " inflating: annotations/train/road8.txt \n",
135 | " inflating: annotations/train/road9.txt \n",
136 | " inflating: annotations/val/road11.txt \n",
137 | " inflating: annotations/val/road24.txt \n",
138 | " inflating: annotations/val/road36.txt \n",
139 | " inflating: annotations/val/road7.txt \n",
140 | " creating: images/test/\n",
141 | " inflating: images/test/road10.png \n",
142 | " inflating: images/test/road29.png \n",
143 | " inflating: images/test/road30.png \n",
144 | " inflating: images/test/road38.png \n",
145 | " inflating: images/test/road41.png \n",
146 | " creating: images/train/\n",
147 | " inflating: images/train/road0.png \n",
148 | " inflating: images/train/road1.png \n",
149 | " inflating: images/train/road12.png \n",
150 | " inflating: images/train/road13.png \n",
151 | " inflating: images/train/road14.png \n",
152 | " inflating: images/train/road15.png \n",
153 | " inflating: images/train/road16.png \n",
154 | " inflating: images/train/road17.png \n",
155 | " inflating: images/train/road18.png \n",
156 | " inflating: images/train/road19.png \n",
157 | " inflating: images/train/road2.png \n",
158 | " inflating: images/train/road20.png \n",
159 | " inflating: images/train/road21.png \n",
160 | " inflating: images/train/road22.png \n",
161 | " inflating: images/train/road23.png \n",
162 | " inflating: images/train/road25.png \n",
163 | " inflating: images/train/road26.png \n",
164 | " inflating: images/train/road27.png \n",
165 | " inflating: images/train/road28.png \n",
166 | " inflating: images/train/road3.png \n",
167 | " inflating: images/train/road31.png \n",
168 | " inflating: images/train/road32.png \n",
169 | " inflating: images/train/road33.png \n",
170 | " inflating: images/train/road34.png \n",
171 | " inflating: images/train/road35.png \n",
172 | " inflating: images/train/road37.png \n",
173 | " inflating: images/train/road39.png \n",
174 | " inflating: images/train/road4.png \n",
175 | " inflating: images/train/road40.png \n",
176 | " inflating: images/train/road42.png \n",
177 | " inflating: images/train/road5.png \n",
178 | " inflating: images/train/road6.png \n",
179 | " inflating: images/train/road8.png \n",
180 | " inflating: images/train/road9.png \n",
181 | " creating: images/val/\n",
182 | " inflating: images/val/road11.png \n",
183 | " inflating: images/val/road24.png \n",
184 | " inflating: images/val/road36.png \n",
185 | " inflating: images/val/road7.png \n",
186 | " inflating: road_sign_data.yaml \n"
187 | ]
188 | }
189 | ]
190 | },
191 | {
192 | "cell_type": "code",
193 | "metadata": {
194 | "colab": {
195 | "base_uri": "https://localhost:8080/"
196 | },
197 | "id": "nxYw2FbmhhQJ",
198 | "outputId": "64fad070-0e90-4a01-99ce-ea495994b60f"
199 | },
200 | "source": [
201 | "#An example annotation\n",
202 | "!cat /content/annotations/train/road4.txt"
203 | ],
204 | "execution_count": 3,
205 | "outputs": [
206 | {
207 | "output_type": "stream",
208 | "name": "stdout",
209 | "text": [
210 | "1 0.1891 0.4325 0.2285 0.3200\n",
211 | "1 0.5225 0.5425 0.1760 0.2750\n",
212 | "1 0.7903 0.6050 0.1648 0.2650\n"
213 | ]
214 | }
215 | ]
216 | },
217 | {
218 | "cell_type": "code",
219 | "metadata": {
220 | "colab": {
221 | "base_uri": "https://localhost:8080/"
222 | },
223 | "id": "L3lYwlImhhMN",
224 | "outputId": "d6513479-beb2-4fa7-9833-9dd89fe2fdee"
225 | },
226 | "source": [
227 | "#An example YAML file\n",
228 | "!cat /content/road_sign_data.yaml"
229 | ],
230 | "execution_count": 4,
231 | "outputs": [
232 | {
233 | "output_type": "stream",
234 | "name": "stdout",
235 | "text": [
236 | "train: images/train/ \n",
237 | "val: images/val/\n",
238 | "test: images/test/\n",
239 | "\n",
240 | "# number of classes\n",
241 | "nc: 4\n",
242 | "\n",
243 | "# class names\n",
244 | "names: [\"trafficlight\",\"stop\", \"speedlimit\",\"crosswalk\"]"
245 | ]
246 | }
247 | ]
248 | },
249 | {
250 | "cell_type": "code",
251 | "metadata": {
252 | "id": "XFzuoZYjiR__"
253 | },
254 | "source": [
255 | "#Import the data from a YAML file and convert it to a dataset\n",
256 | "data0 = importer.ImportYoloV5WithYaml(yaml_file=\"/content/road_sign_data.yaml\", \n",
257 | " path_to_annotations=None, \n",
258 | " image_ext='png',\n",
259 | " name_of_annotations_folder=\"annotations\")"
260 | ],
261 | "execution_count": 7,
262 | "outputs": []
263 | },
264 | {
265 | "cell_type": "code",
266 | "metadata": {
267 | "colab": {
268 | "base_uri": "https://localhost:8080/",
269 | "height": 258
270 | },
271 | "id": "shMQ1ebViMHH",
272 | "outputId": "af4cfb26-40ab-42e4-8fae-df51dbccdb32"
273 | },
274 | "source": [
275 | "#An example of what the data looks like\n",
276 | "data0.df.head(5)"
277 | ],
278 | "execution_count": 10,
279 | "outputs": [
280 | {
281 | "output_type": "execute_result",
282 | "data": {
283 | "text/html": [
284 | "
\n",
285 | "\n",
298 | "
\n",
299 | " \n",
300 | " \n",
301 | " | \n",
302 | " img_folder | \n",
303 | " img_filename | \n",
304 | " img_path | \n",
305 | " img_id | \n",
306 | " img_width | \n",
307 | " img_height | \n",
308 | " img_depth | \n",
309 | " ann_segmented | \n",
310 | " ann_bbox_xmin | \n",
311 | " ann_bbox_ymin | \n",
312 | " ann_bbox_xmax | \n",
313 | " ann_bbox_ymax | \n",
314 | " ann_bbox_width | \n",
315 | " ann_bbox_height | \n",
316 | " ann_area | \n",
317 | " ann_segmentation | \n",
318 | " ann_iscrowd | \n",
319 | " ann_pose | \n",
320 | " ann_truncated | \n",
321 | " ann_difficult | \n",
322 | " cat_id | \n",
323 | " cat_name | \n",
324 | " cat_supercategory | \n",
325 | " split | \n",
326 | " annotated | \n",
327 | "
\n",
328 | " \n",
329 | " id | \n",
330 | " | \n",
331 | " | \n",
332 | " | \n",
333 | " | \n",
334 | " | \n",
335 | " | \n",
336 | " | \n",
337 | " | \n",
338 | " | \n",
339 | " | \n",
340 | " | \n",
341 | " | \n",
342 | " | \n",
343 | " | \n",
344 | " | \n",
345 | " | \n",
346 | " | \n",
347 | " | \n",
348 | " | \n",
349 | " | \n",
350 | " | \n",
351 | " | \n",
352 | " | \n",
353 | " | \n",
354 | " | \n",
355 | "
\n",
356 | " \n",
357 | " \n",
358 | " \n",
359 | " 0 | \n",
360 | " ../../images/train | \n",
361 | " road23.png | \n",
362 | " NaN | \n",
363 | " 0 | \n",
364 | " 266 | \n",
365 | " 400 | \n",
366 | " 3 | \n",
367 | " NaN | \n",
368 | " 216.00530 | \n",
369 | " 125.9800 | \n",
370 | " 241.99350 | \n",
371 | " 178.9800 | \n",
372 | " 25.9882 | \n",
373 | " 53.0000 | \n",
374 | " 1377.3746 | \n",
375 | " NaN | \n",
376 | " NaN | \n",
377 | " NaN | \n",
378 | " NaN | \n",
379 | " NaN | \n",
380 | " 1 | \n",
381 | " stop | \n",
382 | " NaN | \n",
383 | " train | \n",
384 | " 1 | \n",
385 | "
\n",
386 | " \n",
387 | " 1 | \n",
388 | " ../../images/train | \n",
389 | " road33.png | \n",
390 | " NaN | \n",
391 | " 1 | \n",
392 | " 267 | \n",
393 | " 400 | \n",
394 | " 3 | \n",
395 | " NaN | \n",
396 | " 23.00205 | \n",
397 | " 6.0200 | \n",
398 | " 177.99555 | \n",
399 | " 387.0200 | \n",
400 | " 154.9935 | \n",
401 | " 381.0000 | \n",
402 | " 59052.5235 | \n",
403 | " NaN | \n",
404 | " NaN | \n",
405 | " NaN | \n",
406 | " NaN | \n",
407 | " NaN | \n",
408 | " 1 | \n",
409 | " stop | \n",
410 | " NaN | \n",
411 | " train | \n",
412 | " 1 | \n",
413 | "
\n",
414 | " \n",
415 | " 2 | \n",
416 | " ../../images/train | \n",
417 | " road8.png | \n",
418 | " NaN | \n",
419 | " 2 | \n",
420 | " 400 | \n",
421 | " 300 | \n",
422 | " 3 | \n",
423 | " NaN | \n",
424 | " 91.00000 | \n",
425 | " 72.0150 | \n",
426 | " 129.00000 | \n",
427 | " 136.0050 | \n",
428 | " 38.0000 | \n",
429 | " 63.9900 | \n",
430 | " 2431.6200 | \n",
431 | " NaN | \n",
432 | " NaN | \n",
433 | " NaN | \n",
434 | " NaN | \n",
435 | " NaN | \n",
436 | " 1 | \n",
437 | " stop | \n",
438 | " NaN | \n",
439 | " train | \n",
440 | " 1 | \n",
441 | "
\n",
442 | " \n",
443 | " 3 | \n",
444 | " ../../images/train | \n",
445 | " road8.png | \n",
446 | " NaN | \n",
447 | " 2 | \n",
448 | " 400 | \n",
449 | " 300 | \n",
450 | " 3 | \n",
451 | " NaN | \n",
452 | " 245.02000 | \n",
453 | " 103.9950 | \n",
454 | " 274.02000 | \n",
455 | " 163.0050 | \n",
456 | " 29.0000 | \n",
457 | " 59.0100 | \n",
458 | " 1711.2900 | \n",
459 | " NaN | \n",
460 | " NaN | \n",
461 | " NaN | \n",
462 | " NaN | \n",
463 | " NaN | \n",
464 | " 1 | \n",
465 | " stop | \n",
466 | " NaN | \n",
467 | " train | \n",
468 | " 1 | \n",
469 | "
\n",
470 | " \n",
471 | " 4 | \n",
472 | " ../../images/train | \n",
473 | " road16.png | \n",
474 | " NaN | \n",
475 | " 3 | \n",
476 | " 400 | \n",
477 | " 248 | \n",
478 | " 3 | \n",
479 | " NaN | \n",
480 | " 225.00000 | \n",
481 | " 42.9908 | \n",
482 | " 241.00000 | \n",
483 | " 82.0012 | \n",
484 | " 16.0000 | \n",
485 | " 39.0104 | \n",
486 | " 624.1664 | \n",
487 | " NaN | \n",
488 | " NaN | \n",
489 | " NaN | \n",
490 | " NaN | \n",
491 | " NaN | \n",
492 | " 1 | \n",
493 | " stop | \n",
494 | " NaN | \n",
495 | " train | \n",
496 | " 1 | \n",
497 | "
\n",
498 | " \n",
499 | "
\n",
500 | "
"
501 | ],
502 | "text/plain": [
503 | " img_folder img_filename ... split annotated\n",
504 | "id ... \n",
505 | "0 ../../images/train road23.png ... train 1\n",
506 | "1 ../../images/train road33.png ... train 1\n",
507 | "2 ../../images/train road8.png ... train 1\n",
508 | "3 ../../images/train road8.png ... train 1\n",
509 | "4 ../../images/train road16.png ... train 1\n",
510 | "\n",
511 | "[5 rows x 25 columns]"
512 | ]
513 | },
514 | "metadata": {},
515 | "execution_count": 10
516 | }
517 | ]
518 | },
519 | {
520 | "cell_type": "code",
521 | "metadata": {
522 | "colab": {
523 | "base_uri": "https://localhost:8080/",
524 | "height": 195
525 | },
526 | "id": "sUfbY-NxhhGn",
527 | "outputId": "8ac631dd-5dbd-4b94-bb42-ad6f44373624"
528 | },
529 | "source": [
530 | "#A sample of what the groupings look like\n",
531 | "data0.df.groupby('split').count()"
532 | ],
533 | "execution_count": 8,
534 | "outputs": [
535 | {
536 | "output_type": "execute_result",
537 | "data": {
538 | "text/html": [
539 | "\n",
540 | "\n",
553 | "
\n",
554 | " \n",
555 | " \n",
556 | " | \n",
557 | " img_folder | \n",
558 | " img_filename | \n",
559 | " img_path | \n",
560 | " img_id | \n",
561 | " img_width | \n",
562 | " img_height | \n",
563 | " img_depth | \n",
564 | " ann_segmented | \n",
565 | " ann_bbox_xmin | \n",
566 | " ann_bbox_ymin | \n",
567 | " ann_bbox_xmax | \n",
568 | " ann_bbox_ymax | \n",
569 | " ann_bbox_width | \n",
570 | " ann_bbox_height | \n",
571 | " ann_area | \n",
572 | " ann_segmentation | \n",
573 | " ann_iscrowd | \n",
574 | " ann_pose | \n",
575 | " ann_truncated | \n",
576 | " ann_difficult | \n",
577 | " cat_id | \n",
578 | " cat_name | \n",
579 | " cat_supercategory | \n",
580 | " annotated | \n",
581 | "
\n",
582 | " \n",
583 | " split | \n",
584 | " | \n",
585 | " | \n",
586 | " | \n",
587 | " | \n",
588 | " | \n",
589 | " | \n",
590 | " | \n",
591 | " | \n",
592 | " | \n",
593 | " | \n",
594 | " | \n",
595 | " | \n",
596 | " | \n",
597 | " | \n",
598 | " | \n",
599 | " | \n",
600 | " | \n",
601 | " | \n",
602 | " | \n",
603 | " | \n",
604 | " | \n",
605 | " | \n",
606 | " | \n",
607 | " | \n",
608 | "
\n",
609 | " \n",
610 | " \n",
611 | " \n",
612 | " test | \n",
613 | " 8 | \n",
614 | " 8 | \n",
615 | " 0 | \n",
616 | " 8 | \n",
617 | " 8 | \n",
618 | " 8 | \n",
619 | " 8 | \n",
620 | " 0 | \n",
621 | " 8 | \n",
622 | " 8 | \n",
623 | " 8 | \n",
624 | " 8 | \n",
625 | " 8 | \n",
626 | " 8 | \n",
627 | " 8 | \n",
628 | " 0 | \n",
629 | " 0 | \n",
630 | " 0 | \n",
631 | " 0 | \n",
632 | " 0 | \n",
633 | " 8 | \n",
634 | " 8 | \n",
635 | " 0 | \n",
636 | " 8 | \n",
637 | "
\n",
638 | " \n",
639 | " train | \n",
640 | " 55 | \n",
641 | " 55 | \n",
642 | " 0 | \n",
643 | " 55 | \n",
644 | " 55 | \n",
645 | " 55 | \n",
646 | " 55 | \n",
647 | " 0 | \n",
648 | " 55 | \n",
649 | " 55 | \n",
650 | " 55 | \n",
651 | " 55 | \n",
652 | " 55 | \n",
653 | " 55 | \n",
654 | " 55 | \n",
655 | " 0 | \n",
656 | " 0 | \n",
657 | " 0 | \n",
658 | " 0 | \n",
659 | " 0 | \n",
660 | " 55 | \n",
661 | " 55 | \n",
662 | " 0 | \n",
663 | " 55 | \n",
664 | "
\n",
665 | " \n",
666 | " val | \n",
667 | " 7 | \n",
668 | " 7 | \n",
669 | " 0 | \n",
670 | " 7 | \n",
671 | " 7 | \n",
672 | " 7 | \n",
673 | " 7 | \n",
674 | " 0 | \n",
675 | " 7 | \n",
676 | " 7 | \n",
677 | " 7 | \n",
678 | " 7 | \n",
679 | " 7 | \n",
680 | " 7 | \n",
681 | " 7 | \n",
682 | " 0 | \n",
683 | " 0 | \n",
684 | " 0 | \n",
685 | " 0 | \n",
686 | " 0 | \n",
687 | " 7 | \n",
688 | " 7 | \n",
689 | " 0 | \n",
690 | " 7 | \n",
691 | "
\n",
692 | " \n",
693 | "
\n",
694 | "
"
695 | ],
696 | "text/plain": [
697 | " img_folder img_filename ... cat_supercategory annotated\n",
698 | "split ... \n",
699 | "test 8 8 ... 0 8\n",
700 | "train 55 55 ... 0 55\n",
701 | "val 7 7 ... 0 7\n",
702 | "\n",
703 | "[3 rows x 24 columns]"
704 | ]
705 | },
706 | "metadata": {},
707 | "execution_count": 8
708 | }
709 | ]
710 | },
711 | {
712 | "cell_type": "code",
713 | "metadata": {
714 | "id": "AEuFW-vZhhA9"
715 | },
716 | "source": [
717 | ""
718 | ],
719 | "execution_count": null,
720 | "outputs": []
721 | },
722 | {
723 | "cell_type": "code",
724 | "metadata": {
725 | "id": "PUISOppWhg7x"
726 | },
727 | "source": [
728 | ""
729 | ],
730 | "execution_count": null,
731 | "outputs": []
732 | },
733 | {
734 | "cell_type": "code",
735 | "metadata": {
736 | "id": "UNXcq-QFhg5t"
737 | },
738 | "source": [
739 | ""
740 | ],
741 | "execution_count": null,
742 | "outputs": []
743 | },
744 | {
745 | "cell_type": "code",
746 | "metadata": {
747 | "id": "maPU3HdE5Hj6"
748 | },
749 | "source": [
750 | ""
751 | ],
752 | "execution_count": null,
753 | "outputs": []
754 | },
755 | {
756 | "cell_type": "code",
757 | "metadata": {
758 | "id": "3NyTds8a9sLw"
759 | },
760 | "source": [
761 | ""
762 | ],
763 | "execution_count": null,
764 | "outputs": []
765 | },
766 | {
767 | "cell_type": "code",
768 | "metadata": {
769 | "id": "68jpf37TnHlX"
770 | },
771 | "source": [
772 | ""
773 | ],
774 | "execution_count": null,
775 | "outputs": []
776 | }
777 | ]
778 | }
779 |
--------------------------------------------------------------------------------
/yolov5_training.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "accelerator": "GPU",
6 | "colab": {
7 | "name": "pylabeler.ipynb",
8 | "provenance": [],
9 | "collapsed_sections": []
10 | },
11 | "interpreter": {
12 | "hash": "de5e368ab5494158da905fb8b1c2e4fef8844fbfaace7cceca0beab983df3a80"
13 | },
14 | "kernelspec": {
15 | "display_name": "Python 3.8.5 64-bit ('penv': venv)",
16 | "name": "python3"
17 | },
18 | "language_info": {
19 | "codemirror_mode": {
20 | "name": "ipython",
21 | "version": 3
22 | },
23 | "file_extension": ".py",
24 | "mimetype": "text/x-python",
25 | "name": "python",
26 | "nbconvert_exporter": "python",
27 | "pygments_lexer": "ipython3",
28 | "version": "3.8.5"
29 | }
30 | },
31 | "cells": [
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {
35 | "id": "5UDUKQvj-I_j"
36 | },
37 | "source": [
38 | "
"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {
44 | "id": "Cxi8K9mXwl5t"
45 | },
46 | "source": [
47 | "## Simple YoloV5 Training Example\n",
48 | "This notebook demonstrates how to use your own labeled dataset to train a YoloV5 model and then use it to make predictions. For this demonstration we will download 100 images from the squirrels and nuts dataset. (Which I created from images of squirrels in my yard.)\n"
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "metadata": {
54 | "id": "_NcGRjmC-I_m"
55 | },
56 | "source": [
57 | "import logging\n",
58 | "logging.getLogger().setLevel(logging.CRITICAL)\n",
59 | "import os \n",
60 | "import zipfile"
61 | ],
62 | "execution_count": 1,
63 | "outputs": []
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "metadata": {
68 | "id": "dkFFxJFn-I_m"
69 | },
70 | "source": [
71 | "## Download Images and Labels"
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "metadata": {
77 | "colab": {
78 | "base_uri": "https://localhost:8080/"
79 | },
80 | "id": "dvT4Ykxb-I_n",
81 | "outputId": "ddf2e748-2e2f-40f2-a3ab-ae882687a506"
82 | },
83 | "source": [
84 | "os.makedirs(\"data/\", exist_ok=True)\n",
85 | "!wget \"https://github.com/pylabel-project/datasets_models/blob/main/squirrelsandnuts/squirrelsandnuts_train.zip?raw=true\" -O data/squirrelsandnuts_train.zip\n",
86 | "with zipfile.ZipFile(\"data/squirrelsandnuts_train.zip\", 'r') as zip_ref:\n",
87 | " zip_ref.extractall(\"data/\")"
88 | ],
89 | "execution_count": 2,
90 | "outputs": [
91 | {
92 | "output_type": "stream",
93 | "name": "stdout",
94 | "text": [
95 | "--2021-12-07 04:42:36-- https://github.com/pylabel-project/datasets_models/blob/main/squirrelsandnuts/squirrelsandnuts_train.zip?raw=true\n",
96 | "Resolving github.com (github.com)... 140.82.121.3\n",
97 | "Connecting to github.com (github.com)|140.82.121.3|:443... connected.\n",
98 | "HTTP request sent, awaiting response... 302 Found\n",
99 | "Location: https://github.com/pylabel-project/datasets_models/raw/main/squirrelsandnuts/squirrelsandnuts_train.zip [following]\n",
100 | "--2021-12-07 04:42:36-- https://github.com/pylabel-project/datasets_models/raw/main/squirrelsandnuts/squirrelsandnuts_train.zip\n",
101 | "Reusing existing connection to github.com:443.\n",
102 | "HTTP request sent, awaiting response... 302 Found\n",
103 | "Location: https://raw.githubusercontent.com/pylabel-project/datasets_models/main/squirrelsandnuts/squirrelsandnuts_train.zip [following]\n",
104 | "--2021-12-07 04:42:37-- https://raw.githubusercontent.com/pylabel-project/datasets_models/main/squirrelsandnuts/squirrelsandnuts_train.zip\n",
105 | "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.110.133, ...\n",
106 | "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n",
107 | "HTTP request sent, awaiting response... 200 OK\n",
108 | "Length: 10368708 (9.9M) [application/zip]\n",
109 | "Saving to: ‘data/squirrelsandnuts_train.zip’\n",
110 | "\n",
111 | "data/squirrelsandnu 100%[===================>] 9.89M --.-KB/s in 0.1s \n",
112 | "\n",
113 | "2021-12-07 04:42:37 (96.5 MB/s) - ‘data/squirrelsandnuts_train.zip’ saved [10368708/10368708]\n",
114 | "\n"
115 | ]
116 | }
117 | ]
118 | },
119 | {
120 | "cell_type": "markdown",
121 | "metadata": {
122 | "id": "OO6wuzVuaXO3"
123 | },
124 | "source": [
125 | "Now you should have 100 images and annotation files in the folder data/squirrelsandnuts_train/. There is also a YAML file that you need to tell YOLO where to find the images and folders. View the Yaml file. Note that there are 2 classes: squirrels and nuts. "
126 | ]
127 | },
128 | {
129 | "cell_type": "code",
130 | "metadata": {
131 | "id": "i-PP611TbBRD",
132 | "outputId": "592f1f68-a411-4488-9657-5348b60be286",
133 | "colab": {
134 | "base_uri": "https://localhost:8080/"
135 | }
136 | },
137 | "source": [
138 | "!cat data/squirrelsandnuts_train/yolo_data.yaml"
139 | ],
140 | "execution_count": 3,
141 | "outputs": [
142 | {
143 | "output_type": "stream",
144 | "name": "stdout",
145 | "text": [
146 | "path: ../data/squirrelsandnuts_train/\n",
147 | "train: images/train # train images (relative to 'path') 128 images\n",
148 | "val: images/train # val images (relative to 'path') 128 images\n",
149 | "\n",
150 | "# Classes\n",
151 | "nc: 2 # number of classes\n",
152 | "names: [ 'Squirrel','Nut'] # class names"
153 | ]
154 | }
155 | ]
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "metadata": {
160 | "id": "rlVIf5qj-I_o"
161 | },
162 | "source": [
163 | "# Install Yolo Dependencies\n",
164 | "This is known to work on Google Colab, it may or may not work in other environments. "
165 | ]
166 | },
167 | {
168 | "cell_type": "code",
169 | "metadata": {
170 | "colab": {
171 | "base_uri": "https://localhost:8080/"
172 | },
173 | "id": "1-EeN4ZX-I_o",
174 | "outputId": "79e8966c-8210-4d6f-cdd1-30eebe56e96a"
175 | },
176 | "source": [
177 | "!git clone https://github.com/ultralytics/yolov5 # clone\n",
178 | "%cd yolov5\n",
179 | "%pip install -qr requirements.txt # install\n",
180 | "import torch\n",
181 | "from yolov5 import utils"
182 | ],
183 | "execution_count": 4,
184 | "outputs": [
185 | {
186 | "output_type": "stream",
187 | "name": "stdout",
188 | "text": [
189 | "fatal: destination path 'yolov5' already exists and is not an empty directory.\n",
190 | "/content/yolov5\n"
191 | ]
192 | }
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "metadata": {
198 | "id": "kCL9U0lHOnuB"
199 | },
200 | "source": [
201 | "Now you are ready to train the model. Below is the standard code that is used for training which I customized for this particulat dataset. Some of the key parameters: \n",
202 | "- **img** This should be the longest dimension of your images in pixels. The squirrel photos are 960 pixels wide.\n",
203 | "- **data** The path to the yaml file, from the yolov5 directory. \n",
204 | "- **weights** Start from the standard yolo pretraining model. \n",
205 | "- **epochs** It is set to 10 so it goes faster while you are still testing things. I would suggest doing at least 100 epochs for your actual model training. "
206 | ]
207 | },
208 | {
209 | "cell_type": "code",
210 | "metadata": {
211 | "colab": {
212 | "base_uri": "https://localhost:8080/"
213 | },
214 | "id": "_ulnXfSqU9A5",
215 | "outputId": "760da9fd-e364-4599-d9ed-c9fbe6ddbb1a"
216 | },
217 | "source": [
218 | "!python train.py --img 960 --batch 16 --epochs 10 --data ../data/squirrelsandnuts_train/yolo_data.yaml --weights yolov5s.pt --cache --exist-ok\n"
219 | ],
220 | "execution_count": 5,
221 | "outputs": [
222 | {
223 | "output_type": "stream",
224 | "name": "stdout",
225 | "text": [
226 | "\u001b[34m\u001b[1mtrain: \u001b[0mweights=yolov5s.pt, cfg=, data=../data/squirrelsandnuts_train/yolo_data.yaml, hyp=data/hyps/hyp.scratch.yaml, epochs=10, batch_size=16, imgsz=960, rect=False, resume=False, nosave=False, noval=False, noautoanchor=False, evolve=None, bucket=, cache=ram, image_weights=False, device=, multi_scale=False, single_cls=False, adam=False, sync_bn=False, workers=8, project=runs/train, name=exp, exist_ok=True, quad=False, linear_lr=False, label_smoothing=0.0, patience=100, freeze=0, save_period=-1, local_rank=-1, entity=None, upload_dataset=False, bbox_interval=-1, artifact_alias=latest\n",
227 | "\u001b[34m\u001b[1mgithub: \u001b[0mup to date with https://github.com/ultralytics/yolov5 ✅\n",
228 | "YOLOv5 🚀 v6.0-124-g1075488 torch 1.10.0+cu111 CUDA:0 (Tesla K80, 11441MiB)\n",
229 | "\n",
230 | "\u001b[34m\u001b[1mhyperparameters: \u001b[0mlr0=0.01, lrf=0.1, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=0.05, cls=0.5, cls_pw=1.0, obj=1.0, obj_pw=1.0, iou_t=0.2, anchor_t=4.0, fl_gamma=0.0, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.0, copy_paste=0.0\n",
231 | "\u001b[34m\u001b[1mWeights & Biases: \u001b[0mrun 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs (RECOMMENDED)\n",
232 | "\u001b[34m\u001b[1mTensorBoard: \u001b[0mStart with 'tensorboard --logdir runs/train', view at http://localhost:6006/\n",
233 | "Overriding model.yaml nc=80 with nc=2\n",
234 | "\n",
235 | " from n params module arguments \n",
236 | " 0 -1 1 3520 models.common.Conv [3, 32, 6, 2, 2] \n",
237 | " 1 -1 1 18560 models.common.Conv [32, 64, 3, 2] \n",
238 | " 2 -1 1 18816 models.common.C3 [64, 64, 1] \n",
239 | " 3 -1 1 73984 models.common.Conv [64, 128, 3, 2] \n",
240 | " 4 -1 2 115712 models.common.C3 [128, 128, 2] \n",
241 | " 5 -1 1 295424 models.common.Conv [128, 256, 3, 2] \n",
242 | " 6 -1 3 625152 models.common.C3 [256, 256, 3] \n",
243 | " 7 -1 1 1180672 models.common.Conv [256, 512, 3, 2] \n",
244 | " 8 -1 1 1182720 models.common.C3 [512, 512, 1] \n",
245 | " 9 -1 1 656896 models.common.SPPF [512, 512, 5] \n",
246 | " 10 -1 1 131584 models.common.Conv [512, 256, 1, 1] \n",
247 | " 11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
248 | " 12 [-1, 6] 1 0 models.common.Concat [1] \n",
249 | " 13 -1 1 361984 models.common.C3 [512, 256, 1, False] \n",
250 | " 14 -1 1 33024 models.common.Conv [256, 128, 1, 1] \n",
251 | " 15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
252 | " 16 [-1, 4] 1 0 models.common.Concat [1] \n",
253 | " 17 -1 1 90880 models.common.C3 [256, 128, 1, False] \n",
254 | " 18 -1 1 147712 models.common.Conv [128, 128, 3, 2] \n",
255 | " 19 [-1, 14] 1 0 models.common.Concat [1] \n",
256 | " 20 -1 1 296448 models.common.C3 [256, 256, 1, False] \n",
257 | " 21 -1 1 590336 models.common.Conv [256, 256, 3, 2] \n",
258 | " 22 [-1, 10] 1 0 models.common.Concat [1] \n",
259 | " 23 -1 1 1182720 models.common.C3 [512, 512, 1, False] \n",
260 | " 24 [17, 20, 23] 1 18879 models.yolo.Detect [2, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]\n",
261 | "Model Summary: 270 layers, 7025023 parameters, 7025023 gradients, 15.9 GFLOPs\n",
262 | "\n",
263 | "Transferred 343/349 items from yolov5s.pt\n",
264 | "Scaled weight_decay = 0.0005\n",
265 | "\u001b[34m\u001b[1moptimizer:\u001b[0m SGD with parameter groups 57 weight, 60 weight (no decay), 60 bias\n",
266 | "\u001b[34m\u001b[1malbumentations: \u001b[0mversion 1.0.3 required by YOLOv5, but version 0.1.12 is currently installed\n",
267 | "\u001b[34m\u001b[1mtrain: \u001b[0mScanning '../data/squirrelsandnuts_train/labels/train.cache' images and labels... 100 found, 0 missing, 0 empty, 0 corrupted: 100% 100/100 [00:00, ?it/s]\n",
268 | "\u001b[34m\u001b[1mtrain: \u001b[0mCaching images (0.2GB ram): 100% 100/100 [00:00<00:00, 123.33it/s]\n",
269 | "\u001b[34m\u001b[1mval: \u001b[0mScanning '../data/squirrelsandnuts_train/labels/train.cache' images and labels... 100 found, 0 missing, 0 empty, 0 corrupted: 100% 100/100 [00:00, ?it/s]\n",
270 | "\u001b[34m\u001b[1mval: \u001b[0mCaching images (0.2GB ram): 100% 100/100 [00:02<00:00, 41.96it/s]\n",
271 | "Plotting labels to runs/train/exp/labels.jpg... \n",
272 | "\n",
273 | "\u001b[34m\u001b[1mAutoAnchor: \u001b[0m5.02 anchors/target, 1.000 Best Possible Recall (BPR). Current anchors are a good fit to dataset ✅\n",
274 | "Image sizes 960 train, 960 val\n",
275 | "Using 2 dataloader workers\n",
276 | "Logging results to \u001b[1mruns/train/exp\u001b[0m\n",
277 | "Starting training for 10 epochs...\n",
278 | "\n",
279 | " Epoch gpu_mem box obj cls labels img_size\n",
280 | " 0/9 7.13G 0.1201 0.06202 0.02789 21 960: 100% 7/7 [00:24<00:00, 3.44s/it]\n",
281 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:07<00:00, 1.78s/it]\n",
282 | " all 100 223 0.0101 0.218 0.00821 0.00156\n",
283 | "\n",
284 | " Epoch gpu_mem box obj cls labels img_size\n",
285 | " 1/9 7.72G 0.1123 0.05519 0.02483 9 960: 100% 7/7 [00:21<00:00, 3.09s/it]\n",
286 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:05<00:00, 1.48s/it]\n",
287 | " all 100 223 0.0156 0.122 0.0117 0.00254\n",
288 | "\n",
289 | " Epoch gpu_mem box obj cls labels img_size\n",
290 | " 2/9 7.72G 0.1127 0.05003 0.02494 17 960: 100% 7/7 [00:21<00:00, 3.09s/it]\n",
291 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:05<00:00, 1.30s/it]\n",
292 | " all 100 223 0.0233 0.0996 0.0155 0.00312\n",
293 | "\n",
294 | " Epoch gpu_mem box obj cls labels img_size\n",
295 | " 3/9 7.72G 0.1094 0.05003 0.02274 34 960: 100% 7/7 [00:21<00:00, 3.10s/it]\n",
296 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:04<00:00, 1.21s/it]\n",
297 | " all 100 223 0.0263 0.17 0.0207 0.0036\n",
298 | "\n",
299 | " Epoch gpu_mem box obj cls labels img_size\n",
300 | " 4/9 7.72G 0.105 0.04328 0.0205 15 960: 100% 7/7 [00:21<00:00, 3.09s/it]\n",
301 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:04<00:00, 1.13s/it]\n",
302 | " all 100 223 0.055 0.0932 0.0297 0.00547\n",
303 | "\n",
304 | " Epoch gpu_mem box obj cls labels img_size\n",
305 | " 5/9 7.72G 0.1018 0.0439 0.01921 22 960: 100% 7/7 [00:21<00:00, 3.11s/it]\n",
306 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:04<00:00, 1.08s/it]\n",
307 | " all 100 223 0.0615 0.147 0.0413 0.00759\n",
308 | "\n",
309 | " Epoch gpu_mem box obj cls labels img_size\n",
310 | " 6/9 7.72G 0.09698 0.0436 0.01738 16 960: 100% 7/7 [00:21<00:00, 3.09s/it]\n",
311 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:04<00:00, 1.04s/it]\n",
312 | " all 100 223 0.136 0.132 0.0755 0.0148\n",
313 | "\n",
314 | " Epoch gpu_mem box obj cls labels img_size\n",
315 | " 7/9 7.72G 0.09413 0.04511 0.01702 19 960: 100% 7/7 [00:21<00:00, 3.09s/it]\n",
316 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:04<00:00, 1.01s/it]\n",
317 | " all 100 223 0.142 0.162 0.0968 0.0226\n",
318 | "\n",
319 | " Epoch gpu_mem box obj cls labels img_size\n",
320 | " 8/9 7.72G 0.09503 0.0483 0.01637 27 960: 100% 7/7 [00:21<00:00, 3.08s/it]\n",
321 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:03<00:00, 1.01it/s]\n",
322 | " all 100 223 0.223 0.137 0.103 0.024\n",
323 | "\n",
324 | " Epoch gpu_mem box obj cls labels img_size\n",
325 | " 9/9 7.72G 0.09281 0.04637 0.01476 11 960: 100% 7/7 [00:21<00:00, 3.10s/it]\n",
326 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:03<00:00, 1.02it/s]\n",
327 | " all 100 223 0.145 0.195 0.103 0.0228\n",
328 | "\n",
329 | "10 epochs completed in 0.076 hours.\n",
330 | "Optimizer stripped from runs/train/exp/weights/last.pt, 14.5MB\n",
331 | "Optimizer stripped from runs/train/exp/weights/best.pt, 14.5MB\n",
332 | "\n",
333 | "Validating runs/train/exp/weights/best.pt...\n",
334 | "Fusing layers... \n",
335 | "Model Summary: 213 layers, 7015519 parameters, 0 gradients, 15.8 GFLOPs\n",
336 | " Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:06<00:00, 1.66s/it]\n",
337 | " all 100 223 0.218 0.137 0.102 0.0238\n",
338 | " Squirrel 100 78 0.326 0.205 0.175 0.0427\n",
339 | " Nut 100 145 0.111 0.069 0.029 0.00497\n",
340 | "Results saved to \u001b[1mruns/train/exp\u001b[0m\n"
341 | ]
342 | }
343 | ]
344 | },
345 | {
346 | "cell_type": "markdown",
347 | "metadata": {
348 | "id": "XlsuBRc3QBHR"
349 | },
350 | "source": [
351 | "After the model has completed training: \n",
352 | "- Review the model summary above. Check the precision, recall, and mAP values for all of the classes. If there is a class that has worse values than the others, annotate more samples for that class. \n",
353 | "- The model weights will be saved to **runs/train/exp/weights/best.pt**. \n",
354 | "\n",
355 | "Now you can use that model to make predictions."
356 | ]
357 | },
358 | {
359 | "cell_type": "code",
360 | "metadata": {
361 | "id": "ruNw8Ze5e6a9",
362 | "outputId": "1c948094-3a5b-4159-cb67-051247bdf2c9",
363 | "colab": {
364 | "base_uri": "https://localhost:8080/",
365 | "height": 171
366 | }
367 | },
368 | "source": [
369 | "model = torch.hub.load('ultralytics/yolov5', 'custom', path='runs/train/exp/weights/best.pt', force_reload=True) \n",
370 | "imgs = ['../data/squirrelsandnuts_train/images/train/2021-07-03T06-30-10-frame_0001.jpeg'] # batch of images\n",
371 | "results = model(imgs)\n",
372 | "\n",
373 | "results.save()\n",
374 | "results.pandas().xyxy[0]\n"
375 | ],
376 | "execution_count": 6,
377 | "outputs": [
378 | {
379 | "output_type": "stream",
380 | "name": "stderr",
381 | "text": [
382 | "Downloading: \"https://github.com/ultralytics/yolov5/archive/master.zip\" to /root/.cache/torch/hub/master.zip\n",
383 | "YOLOv5 🚀 2021-12-7 torch 1.10.0+cu111 CUDA:0 (Tesla K80, 11441MiB)\n",
384 | "\n",
385 | "Fusing layers... \n",
386 | "Model Summary: 213 layers, 7015519 parameters, 0 gradients, 15.8 GFLOPs\n",
387 | "Adding AutoShape... \n",
388 | "Saved 1 image to \u001b[1mruns/detect/exp4\u001b[0m\n"
389 | ]
390 | },
391 | {
392 | "output_type": "execute_result",
393 | "data": {
394 | "text/html": [
395 | "\n",
396 | "\n",
409 | "
\n",
410 | " \n",
411 | " \n",
412 | " | \n",
413 | " xmin | \n",
414 | " ymin | \n",
415 | " xmax | \n",
416 | " ymax | \n",
417 | " confidence | \n",
418 | " class | \n",
419 | " name | \n",
420 | "
\n",
421 | " \n",
422 | " \n",
423 | " \n",
424 | "
\n",
425 | "
"
426 | ],
427 | "text/plain": [
428 | "Empty DataFrame\n",
429 | "Columns: [xmin, ymin, xmax, ymax, confidence, class, name]\n",
430 | "Index: []"
431 | ]
432 | },
433 | "metadata": {},
434 | "execution_count": 6
435 | }
436 | ]
437 | },
438 | {
439 | "cell_type": "markdown",
440 | "metadata": {
441 | "id": "FwHyG9WdKX8o"
442 | },
443 | "source": [
444 | "Look at the output above. Look in the runs/detect folder. If there are no classes detected then the model is not working well and you need more data, more epochs, or to try other things. "
445 | ]
446 | }
447 | ]
448 | }
--------------------------------------------------------------------------------