Merge branch 'main' into package

This commit is contained in:
Wang Xin 2022-11-25 10:24:21 +08:00 committed by GitHub
commit 051c019e3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 141 additions and 109 deletions

11
.github/dependabot.yml vendored Normal file
View File

@ -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"

2
.gitignore vendored
View File

@ -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/

View File

@ -3,16 +3,25 @@
# Labelme2YOLO # Labelme2YOLO
[![PyPI - Version](https://img.shields.io/pypi/v/labelme2yolo.svg)](https://pypi.org/project/labelme2yolo) [![PyPI - Version](https://img.shields.io/pypi/v/labelme2yolo.svg)](https://pypi.org/project/labelme2yolo)
![PyPI - Downloads](https://img.shields.io/pypi/dm/labelme2yolo?style=flat)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/labelme2yolo.svg)](https://pypi.org/project/labelme2yolo) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/labelme2yolo.svg)](https://pypi.org/project/labelme2yolo)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/12122fe86f8643c4aa5667c20d528f61)](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

View File

@ -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"

View File

@ -1,3 +1,4 @@
opencv-python>=4.1.2 opencv-python
Pillow Pillow
scikit-learn scikit-learn
numpy

View File

@ -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'

View File

@ -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())

View File

@ -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

View File

@ -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)