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
# 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.
#.idea/
.idea/

View File

@ -3,16 +3,25 @@
# 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)
[![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.
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
- export data as yolo polygon annotation (for YOLOv5 v7.0 segmentation)
## Installation
```console
pip install labelme2yolo
```
## Parameters Explain
**--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
Put all LabelMe JSON files under **labelme_json_dir**, and run this python command.
```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,
```bash
/home/username/labelme_json_dir/YOLODataset/labels/train/
/home/username/labelme_json_dir/YOLODataset/labels/test/
/home/username/labelme_json_dir/YOLODataset/labels/val/
/home/username/labelme_json_dir/YOLODataset/images/train/
/home/username/labelme_json_dir/YOLODataset/images/test/
/home/username/labelme_json_dir/YOLODataset/images/val/
/path/to/labelme_json_dir/YOLODataset/labels/train/
/path/to/labelme_json_dir/YOLODataset/labels/test/
/path/to/labelme_json_dir/YOLODataset/labels/val/
/path/to/labelme_json_dir/YOLODataset/images/train/
/path/to/labelme_json_dir/YOLODataset/images/test/
/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
If you already split train dataset and validation dataset for LabelMe by yourself, please put these folder under labelme_json_dir, for example,
```bash
/home/username/labelme_json_dir/train/
/home/username/labelme_json_dir/val/
/path/to/labelme_json_dir/train/
/path/to/labelme_json_dir/val/
```
Put all LabelMe JSON files under **labelme_json_dir**.
Script would read train and validation dataset by folder.
Run this python command.
```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,
```bash
/home/username/labelme_json_dir/YOLODataset/labels/train/
/home/username/labelme_json_dir/YOLODataset/labels/val/
/home/username/labelme_json_dir/YOLODataset/images/train/
/home/username/labelme_json_dir/YOLODataset/images/val/
/path/to/labelme_json_dir/YOLODataset/labels/train/
/path/to/labelme_json_dir/YOLODataset/labels/val/
/path/to/labelme_json_dir/YOLODataset/images/train/
/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
Put LabelMe JSON file under **labelme_json_dir**. , and run this python command.
```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,
```bash
/home/username/labelme_json_dir/2.text
/home/username/labelme_json_dir/2.png
```
## Installation
```console
pip install labelme2yolo
/path/to/labelme_json_dir/2.text
/path/to/labelme_json_dir/2.png
```
## License

View File

@ -24,15 +24,16 @@ classifiers = [
]
dependencies = [
"opencv-python>=4.1.2",
"Pillow",
"scikit-learn"
"Pillow>=9.2,<9.4",
"scikit-learn~=1.1.1",
"numpy~=1.23.1"
]
dynamic = ["version"]
[project.urls]
Documentation = "https://github.com/unknown/labelme2yolo#readme"
Issues = "https://github.com/unknown/labelme2yolo/issues"
Source = "https://github.com/unknown/labelme2yolo"
Documentation = "https://github.com/greatv/labelme2yolo#readme"
Issues = "https://github.com/greatv/labelme2yolo/issues"
Source = "https://github.com/greatv/labelme2yolo"
[tool.hatch.version]
path = "src/labelme2yolo/__about__.py"

View File

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

View File

@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2022-present Wang Xin <xinwang614@gmail.com>
#
# SPDX-License-Identifier: MIT
__version__ = '0.0.5'

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: MIT
import sys
if __name__ == '__main__':
if __name__ == "__main__":
from .cli import run
sys.exit(run())

View File

@ -2,26 +2,41 @@
#
# SPDX-License-Identifier: MIT
import argparse
import sys
# from labelme2yolo.__about__ import version
from labelme2yolo.l2y import Labelme2YOLO
def run():
parser = argparse.ArgumentParser("labelme2yolo")
parser.add_argument('--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('--test_size',type=float, nargs='?', default=0.0,
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.')
parser.add_argument(
"--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(
"--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()
if not args.json_dir:
parser.print_help(sys.stderr)
sys.exit(1)
parser.print_help()
return 0
convertor = Labelme2YOLO(args.json_dir)
@ -29,3 +44,5 @@ def run():
convertor.convert(val_size=args.val_size, test_size=args.test_size)
else:
convertor.convert_one(args.json_name)
return 0

View File

@ -1,26 +1,28 @@
'''
"""
Created on Aug 18, 2021
@author: xiaosonh
@author: GreatV(Wang Xin)
'''
import os
import sys
import argparse
import shutil
import math
"""
import base64
import io
import json
import math
import os
import shutil
from collections import OrderedDict
from multiprocessing import Pool
import json
import cv2
from sklearn.model_selection import train_test_split
import numpy as np
import PIL.ExifTags
import PIL.Image
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
@ -68,60 +70,54 @@ def img_arr_to_b64(img_arr):
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
def img_data_to_png_data(img_data):
with io.BytesIO() as f:
f.write(img_data)
img = PIL.Image.open(f)
with io.BytesIO() as f_out:
f_out.write(img_data)
img = PIL.Image.open(f_out)
with io.BytesIO() as f:
img.save(f, "PNG")
f.seek(0)
return f.read()
with io.BytesIO() as f_in:
img.save(f_in, "PNG")
f_in.seek(0)
return f_in.read()
# copy form https://github.com/wkentaro/labelme/blob/main/labelme/utils/image.py
def apply_exif_orientation(image):
try:
exif = image._getexif()
except AttributeError:
exif = None
def get_label_id_map(json_dir):
label_set = set()
if exif is None:
return image
for file_name in os.listdir(json_dir):
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 = {
PIL.ExifTags.TAGS[k]: v
for k, v in exif.items()
if k in PIL.ExifTags.TAGS
}
return OrderedDict([(label, label_id) for label_id, label in enumerate(label_set)])
orientation = exif.get("Orientation", None)
if orientation == 1:
# do nothing
return image
elif orientation == 2:
# left-to-right mirror
return PIL.ImageOps.mirror(image)
elif orientation == 3:
# rotate 180
return image.transpose(PIL.Image.ROTATE_180)
elif orientation == 4:
# top-to-bottom mirror
return PIL.ImageOps.flip(image)
elif orientation == 5:
# top-to-left mirror
return PIL.ImageOps.mirror(image.transpose(PIL.Image.ROTATE_270))
elif orientation == 6:
# rotate 270
return image.transpose(PIL.Image.ROTATE_270)
elif orientation == 7:
# top-to-right mirror
return PIL.ImageOps.mirror(image.transpose(PIL.Image.ROTATE_90))
elif orientation == 8:
# rotate 90
return image.transpose(PIL.Image.ROTATE_90)
else:
return image
def save_yolo_label(json_name, label_dir_path, target_dir, yolo_obj_list):
txt_path = os.path.join(
label_dir_path, target_dir, json_name.replace(".json", ".txt")
)
with open(txt_path, "w+") as f:
for yolo_obj_idx, yolo_obj in enumerate(yolo_obj_list):
yolo_obj_line = (
"%s %s %s %s %s\n" % yolo_obj
if yolo_obj_idx + 1 != len(yolo_obj_list)
else "%s %s %s %s %s" % yolo_obj
)
f.write(yolo_obj_line)
def save_yolo_image(json_data, json_name, image_dir_path, target_dir):
img_name = json_name.replace(".json", ".png")
img_path = os.path.join(image_dir_path, target_dir, img_name)
if not os.path.exists(img_path):
img = img_b64_to_arr(json_data["imageData"])
PIL.Image.fromarray(img).save(img_path)
return img_path
class Labelme2YOLO(object):
@ -209,6 +205,7 @@ class Labelme2YOLO(object):
for target_dir, json_names in zip(('train/', 'val/', 'test/'),
(train_json_names, val_json_names, test_json_names)):
pool = Pool(os.cpu_count() - 1)
for json_name in json_names:
pool.apply_async(self.covert_json_to_text,
args=(target_dir, json_name))
@ -252,7 +249,7 @@ class Labelme2YOLO(object):
yolo_obj_list = []
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
# it only has 2 points, 1st is circle center, 2nd is drag end point
if shape['shape_type'] == 'circle':
@ -329,7 +326,8 @@ class Labelme2YOLO(object):
yaml_file.write('nc: %i\n' % len(self._label_id_map))
names_str = ''
for label, _ in self._label_id_map.items():
names_str += "'%s', " % label
names_str = names_str.rstrip(', ')
yaml_file.write('names: [%s]' % names_str)
names_str = names_str.rstrip(", ")
yaml_file.write("names: [%s]" % names_str)