Merge branch 'main' into package
This commit is contained in:
commit
051c019e3f
|
@ -0,0 +1,11 @@
|
||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "pip" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
|
@ -157,4 +157,4 @@ cython_debug/
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
.idea/
|
||||||
|
|
53
README.md
53
README.md
|
@ -3,16 +3,25 @@
|
||||||
# Labelme2YOLO
|
# Labelme2YOLO
|
||||||
|
|
||||||
[](https://pypi.org/project/labelme2yolo)
|
[](https://pypi.org/project/labelme2yolo)
|
||||||
|

|
||||||
[](https://pypi.org/project/labelme2yolo)
|
[](https://pypi.org/project/labelme2yolo)
|
||||||
|
[](https://www.codacy.com/gh/GreatV/labelme2yolo/dashboard?utm_source=github.com&utm_medium=referral&utm_content=GreatV/labelme2yolo&utm_campaign=Badge_Grade)
|
||||||
|
|
||||||
Help converting LabelMe Annotation Tool JSON format to YOLO text file format.
|
Help converting LabelMe Annotation Tool JSON format to YOLO text file format.
|
||||||
If you've already marked your segmentation dataset by LabelMe, it's easy to use this tool to help converting to YOLO format dataset.
|
If you've already marked your segmentation dataset by LabelMe, it's easy to use this tool to help converting to YOLO format dataset.
|
||||||
|
|
||||||
---------
|
---------
|
||||||
|
|
||||||
## New
|
## New
|
||||||
|
|
||||||
- export data as yolo polygon annotation (for YOLOv5 v7.0 segmentation)
|
- export data as yolo polygon annotation (for YOLOv5 v7.0 segmentation)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```console
|
||||||
|
pip install labelme2yolo
|
||||||
|
```
|
||||||
|
|
||||||
## Parameters Explain
|
## Parameters Explain
|
||||||
**--json_dir** LabelMe JSON files folder path.
|
**--json_dir** LabelMe JSON files folder path.
|
||||||
|
|
||||||
|
@ -27,57 +36,51 @@ If you've already marked your segmentation dataset by LabelMe, it's easy to use
|
||||||
### 1. Convert JSON files, split training, validation and test dataset by --val_size and --test_size
|
### 1. Convert JSON files, split training, validation and test dataset by --val_size and --test_size
|
||||||
Put all LabelMe JSON files under **labelme_json_dir**, and run this python command.
|
Put all LabelMe JSON files under **labelme_json_dir**, and run this python command.
|
||||||
```bash
|
```bash
|
||||||
python labelme2yolo.py --json_dir /home/username/labelme_json_dir/ --val_size 0.15 --test_size 0.15
|
labelme2yolo --json_dir /path/to/labelme_json_dir/ --val_size 0.15 --test_size 0.15
|
||||||
```
|
```
|
||||||
Script would generate YOLO format dataset labels and images under different folders, for example,
|
Script would generate YOLO format dataset labels and images under different folders, for example,
|
||||||
```bash
|
```bash
|
||||||
/home/username/labelme_json_dir/YOLODataset/labels/train/
|
/path/to/labelme_json_dir/YOLODataset/labels/train/
|
||||||
/home/username/labelme_json_dir/YOLODataset/labels/test/
|
/path/to/labelme_json_dir/YOLODataset/labels/test/
|
||||||
/home/username/labelme_json_dir/YOLODataset/labels/val/
|
/path/to/labelme_json_dir/YOLODataset/labels/val/
|
||||||
/home/username/labelme_json_dir/YOLODataset/images/train/
|
/path/to/labelme_json_dir/YOLODataset/images/train/
|
||||||
/home/username/labelme_json_dir/YOLODataset/images/test/
|
/path/to/labelme_json_dir/YOLODataset/images/test/
|
||||||
/home/username/labelme_json_dir/YOLODataset/images/val/
|
/path/to/labelme_json_dir/YOLODataset/images/val/
|
||||||
|
|
||||||
/home/username/labelme_json_dir/YOLODataset/dataset.yaml
|
/path/to/labelme_json_dir/YOLODataset/dataset.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Convert JSON files, split training and validation dataset by folder
|
### 2. Convert JSON files, split training and validation dataset by folder
|
||||||
If you already split train dataset and validation dataset for LabelMe by yourself, please put these folder under labelme_json_dir, for example,
|
If you already split train dataset and validation dataset for LabelMe by yourself, please put these folder under labelme_json_dir, for example,
|
||||||
```bash
|
```bash
|
||||||
/home/username/labelme_json_dir/train/
|
/path/to/labelme_json_dir/train/
|
||||||
/home/username/labelme_json_dir/val/
|
/path/to/labelme_json_dir/val/
|
||||||
```
|
```
|
||||||
Put all LabelMe JSON files under **labelme_json_dir**.
|
Put all LabelMe JSON files under **labelme_json_dir**.
|
||||||
Script would read train and validation dataset by folder.
|
Script would read train and validation dataset by folder.
|
||||||
Run this python command.
|
Run this python command.
|
||||||
```bash
|
```bash
|
||||||
python labelme2yolo.py --json_dir /home/username/labelme_json_dir/
|
labelme2yolo --json_dir /path/to/labelme_json_dir/
|
||||||
```
|
```
|
||||||
Script would generate YOLO format dataset labels and images under different folders, for example,
|
Script would generate YOLO format dataset labels and images under different folders, for example,
|
||||||
```bash
|
```bash
|
||||||
/home/username/labelme_json_dir/YOLODataset/labels/train/
|
/path/to/labelme_json_dir/YOLODataset/labels/train/
|
||||||
/home/username/labelme_json_dir/YOLODataset/labels/val/
|
/path/to/labelme_json_dir/YOLODataset/labels/val/
|
||||||
/home/username/labelme_json_dir/YOLODataset/images/train/
|
/path/to/labelme_json_dir/YOLODataset/images/train/
|
||||||
/home/username/labelme_json_dir/YOLODataset/images/val/
|
/path/to/labelme_json_dir/YOLODataset/images/val/
|
||||||
|
|
||||||
/home/username/labelme_json_dir/YOLODataset/dataset.yaml
|
/path/to/labelme_json_dir/YOLODataset/dataset.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Convert single JSON file
|
### 3. Convert single JSON file
|
||||||
Put LabelMe JSON file under **labelme_json_dir**. , and run this python command.
|
Put LabelMe JSON file under **labelme_json_dir**. , and run this python command.
|
||||||
```bash
|
```bash
|
||||||
python labelme2yolo.py --json_dir /home/username/labelme_json_dir/ --json_name 2.json
|
labelme2yolo --json_dir /path/to/labelme_json_dir/ --json_name 2.json
|
||||||
```
|
```
|
||||||
Script would generate YOLO format text label and image under **labelme_json_dir**, for example,
|
Script would generate YOLO format text label and image under **labelme_json_dir**, for example,
|
||||||
```bash
|
```bash
|
||||||
/home/username/labelme_json_dir/2.text
|
/path/to/labelme_json_dir/2.text
|
||||||
/home/username/labelme_json_dir/2.png
|
/path/to/labelme_json_dir/2.png
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```console
|
|
||||||
pip install labelme2yolo
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
@ -24,15 +24,16 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"opencv-python>=4.1.2",
|
"opencv-python>=4.1.2",
|
||||||
"Pillow",
|
"Pillow>=9.2,<9.4",
|
||||||
"scikit-learn"
|
"scikit-learn~=1.1.1",
|
||||||
|
"numpy~=1.23.1"
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Documentation = "https://github.com/unknown/labelme2yolo#readme"
|
Documentation = "https://github.com/greatv/labelme2yolo#readme"
|
||||||
Issues = "https://github.com/unknown/labelme2yolo/issues"
|
Issues = "https://github.com/greatv/labelme2yolo/issues"
|
||||||
Source = "https://github.com/unknown/labelme2yolo"
|
Source = "https://github.com/greatv/labelme2yolo"
|
||||||
|
|
||||||
[tool.hatch.version]
|
[tool.hatch.version]
|
||||||
path = "src/labelme2yolo/__about__.py"
|
path = "src/labelme2yolo/__about__.py"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
opencv-python>=4.1.2
|
opencv-python
|
||||||
Pillow
|
Pillow
|
||||||
scikit-learn
|
scikit-learn
|
||||||
|
numpy
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# SPDX-FileCopyrightText: 2022-present Wang Xin <xinwang614@gmail.com>
|
# SPDX-FileCopyrightText: 2022-present Wang Xin <xinwang614@gmail.com>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
__version__ = '0.0.5'
|
__version__ = '0.0.5'
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
from .cli import run
|
from .cli import run
|
||||||
|
|
||||||
sys.exit(run())
|
sys.exit(run())
|
||||||
|
|
|
@ -2,30 +2,47 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
|
||||||
# from labelme2yolo.__about__ import version
|
|
||||||
from labelme2yolo.l2y import Labelme2YOLO
|
from labelme2yolo.l2y import Labelme2YOLO
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
parser = argparse.ArgumentParser("labelme2yolo")
|
parser = argparse.ArgumentParser("labelme2yolo")
|
||||||
parser.add_argument('--json_dir',type=str,
|
parser.add_argument(
|
||||||
help='Please input the path of the labelme json files.')
|
"--json_dir", type=str, help="Please input the path of the labelme json files."
|
||||||
parser.add_argument('--val_size',type=float, nargs='?', default=None,
|
)
|
||||||
help='Please input the validation dataset size, for example 0.1 ')
|
parser.add_argument(
|
||||||
parser.add_argument('--test_size',type=float, nargs='?', default=0.0,
|
"--val_size",
|
||||||
help='Please input the validation dataset size, for example 0.1 ')
|
type=float,
|
||||||
parser.add_argument('--json_name',type=str, nargs='?', default=None,
|
nargs="?",
|
||||||
help='If you put json name, it would convert only one json file to YOLO.')
|
default=None,
|
||||||
|
help="Please input the validation dataset size, for example 0.1 ",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--test_size",
|
||||||
|
type=float,
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="Please input the validation dataset size, for example 0.1 ",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--json_name",
|
||||||
|
type=str,
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="If you put json name, it would convert only one json file to YOLO.",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if not args.json_dir:
|
if not args.json_dir:
|
||||||
parser.print_help(sys.stderr)
|
parser.print_help()
|
||||||
sys.exit(1)
|
return 0
|
||||||
|
|
||||||
convertor = Labelme2YOLO(args.json_dir)
|
convertor = Labelme2YOLO(args.json_dir)
|
||||||
|
|
||||||
if args.json_name is None:
|
if args.json_name is None:
|
||||||
convertor.convert(val_size=args.val_size, test_size=args.test_size)
|
convertor.convert(val_size=args.val_size, test_size=args.test_size)
|
||||||
else:
|
else:
|
||||||
convertor.convert_one(args.json_name)
|
convertor.convert_one(args.json_name)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
'''
|
"""
|
||||||
Created on Aug 18, 2021
|
Created on Aug 18, 2021
|
||||||
|
|
||||||
@author: xiaosonh
|
@author: xiaosonh
|
||||||
@author: GreatV(Wang Xin)
|
@author: GreatV(Wang Xin)
|
||||||
'''
|
"""
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
import shutil
|
|
||||||
import math
|
|
||||||
import base64
|
import base64
|
||||||
import io
|
import io
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
import json
|
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
from sklearn.model_selection import train_test_split
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import PIL.ExifTags
|
import PIL.ExifTags
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import PIL.ImageOps
|
import PIL.ImageOps
|
||||||
|
from sklearn.model_selection import train_test_split
|
||||||
|
|
||||||
|
|
||||||
|
# number of LabelMe2YOLO multiprocessing threads
|
||||||
|
NUM_THREADS = max(1, os.cpu_count() - 1)
|
||||||
|
|
||||||
|
|
||||||
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
|
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
|
||||||
|
@ -68,60 +70,54 @@ def img_arr_to_b64(img_arr):
|
||||||
|
|
||||||
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
|
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
|
||||||
def img_data_to_png_data(img_data):
|
def img_data_to_png_data(img_data):
|
||||||
with io.BytesIO() as f:
|
with io.BytesIO() as f_out:
|
||||||
f.write(img_data)
|
f_out.write(img_data)
|
||||||
img = PIL.Image.open(f)
|
img = PIL.Image.open(f_out)
|
||||||
|
|
||||||
with io.BytesIO() as f:
|
with io.BytesIO() as f_in:
|
||||||
img.save(f, "PNG")
|
img.save(f_in, "PNG")
|
||||||
f.seek(0)
|
f_in.seek(0)
|
||||||
return f.read()
|
return f_in.read()
|
||||||
|
|
||||||
|
|
||||||
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
|
def get_label_id_map(json_dir):
|
||||||
def apply_exif_orientation(image):
|
label_set = set()
|
||||||
try:
|
|
||||||
exif = image._getexif()
|
|
||||||
except AttributeError:
|
|
||||||
exif = None
|
|
||||||
|
|
||||||
if exif is None:
|
for file_name in os.listdir(json_dir):
|
||||||
return image
|
if file_name.endswith("json"):
|
||||||
|
json_path = os.path.join(json_dir, file_name)
|
||||||
|
data = json.load(open(json_path))
|
||||||
|
for shape in data["shapes"]:
|
||||||
|
label_set.add(shape["label"])
|
||||||
|
|
||||||
exif = {
|
return OrderedDict([(label, label_id) for label_id, label in enumerate(label_set)])
|
||||||
PIL.ExifTags.TAGS[k]: v
|
|
||||||
for k, v in exif.items()
|
|
||||||
if k in PIL.ExifTags.TAGS
|
|
||||||
}
|
|
||||||
|
|
||||||
orientation = exif.get("Orientation", None)
|
|
||||||
|
|
||||||
if orientation == 1:
|
def save_yolo_label(json_name, label_dir_path, target_dir, yolo_obj_list):
|
||||||
# do nothing
|
txt_path = os.path.join(
|
||||||
return image
|
label_dir_path, target_dir, json_name.replace(".json", ".txt")
|
||||||
elif orientation == 2:
|
)
|
||||||
# left-to-right mirror
|
|
||||||
return PIL.ImageOps.mirror(image)
|
with open(txt_path, "w+") as f:
|
||||||
elif orientation == 3:
|
for yolo_obj_idx, yolo_obj in enumerate(yolo_obj_list):
|
||||||
# rotate 180
|
yolo_obj_line = (
|
||||||
return image.transpose(PIL.Image.ROTATE_180)
|
"%s %s %s %s %s\n" % yolo_obj
|
||||||
elif orientation == 4:
|
if yolo_obj_idx + 1 != len(yolo_obj_list)
|
||||||
# top-to-bottom mirror
|
else "%s %s %s %s %s" % yolo_obj
|
||||||
return PIL.ImageOps.flip(image)
|
)
|
||||||
elif orientation == 5:
|
f.write(yolo_obj_line)
|
||||||
# top-to-left mirror
|
|
||||||
return PIL.ImageOps.mirror(image.transpose(PIL.Image.ROTATE_270))
|
|
||||||
elif orientation == 6:
|
def save_yolo_image(json_data, json_name, image_dir_path, target_dir):
|
||||||
# rotate 270
|
img_name = json_name.replace(".json", ".png")
|
||||||
return image.transpose(PIL.Image.ROTATE_270)
|
img_path = os.path.join(image_dir_path, target_dir, img_name)
|
||||||
elif orientation == 7:
|
|
||||||
# top-to-right mirror
|
if not os.path.exists(img_path):
|
||||||
return PIL.ImageOps.mirror(image.transpose(PIL.Image.ROTATE_90))
|
img = img_b64_to_arr(json_data["imageData"])
|
||||||
elif orientation == 8:
|
PIL.Image.fromarray(img).save(img_path)
|
||||||
# rotate 90
|
|
||||||
return image.transpose(PIL.Image.ROTATE_90)
|
return img_path
|
||||||
else:
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class Labelme2YOLO(object):
|
class Labelme2YOLO(object):
|
||||||
|
@ -209,6 +205,7 @@ class Labelme2YOLO(object):
|
||||||
for target_dir, json_names in zip(('train/', 'val/', 'test/'),
|
for target_dir, json_names in zip(('train/', 'val/', 'test/'),
|
||||||
(train_json_names, val_json_names, test_json_names)):
|
(train_json_names, val_json_names, test_json_names)):
|
||||||
pool = Pool(os.cpu_count() - 1)
|
pool = Pool(os.cpu_count() - 1)
|
||||||
|
|
||||||
for json_name in json_names:
|
for json_name in json_names:
|
||||||
pool.apply_async(self.covert_json_to_text,
|
pool.apply_async(self.covert_json_to_text,
|
||||||
args=(target_dir, json_name))
|
args=(target_dir, json_name))
|
||||||
|
@ -252,7 +249,7 @@ class Labelme2YOLO(object):
|
||||||
yolo_obj_list = []
|
yolo_obj_list = []
|
||||||
|
|
||||||
img_h, img_w, _ = cv2.imread(img_path).shape
|
img_h, img_w, _ = cv2.imread(img_path).shape
|
||||||
for shape in json_data['shapes']:
|
for shape in json_data["shapes"]:
|
||||||
# labelme circle shape is different from others
|
# labelme circle shape is different from others
|
||||||
# it only has 2 points, 1st is circle center, 2nd is drag end point
|
# it only has 2 points, 1st is circle center, 2nd is drag end point
|
||||||
if shape['shape_type'] == 'circle':
|
if shape['shape_type'] == 'circle':
|
||||||
|
@ -329,7 +326,8 @@ class Labelme2YOLO(object):
|
||||||
yaml_file.write('nc: %i\n' % len(self._label_id_map))
|
yaml_file.write('nc: %i\n' % len(self._label_id_map))
|
||||||
|
|
||||||
names_str = ''
|
names_str = ''
|
||||||
|
|
||||||
for label, _ in self._label_id_map.items():
|
for label, _ in self._label_id_map.items():
|
||||||
names_str += "'%s', " % label
|
names_str += "'%s', " % label
|
||||||
names_str = names_str.rstrip(', ')
|
names_str = names_str.rstrip(", ")
|
||||||
yaml_file.write('names: [%s]' % names_str)
|
yaml_file.write("names: [%s]" % names_str)
|
||||||
|
|
Loading…
Reference in New Issue