labelme转coco数据集的那些事·完整版
1. 因为发现布置的任务的具体要求没有被组员理解,导致所有180反转后的标签都变为了:
一个json文件有好几个这样的标签
那如果人手一个个这样处理,真的是要累死,所以就写了这个代码
import os
import json
path = 'J:\\final_tomato_data\\test'
json_files = sorted([pos_json for pos_json in os.listdir(path) if pos_json.endswith('.json')])
for index, js in enumerate(json_files):
with open(os.path.join(path, js)) as json_file:
data = json.load(json_file)
if "shapes" in data:
data['shapes']['label'] = "Bacterial_spot"
with open(os.path.join(path, js), 'w') as json_file:
json.dump(data, json_file)
但是会运行报错,然后我们看下报错信息:
接下来定位到位置单步调试:
发现在第二次进入时出现了报错,
那么查看data的结构,
找到了data
点开shape
点开0
综上所述:
label在dict_data的shapes键值为0,1两个dict组成的一个list内的一对key-value对内
那么问题就很明显了,改为
就可以指定好对应的位置。
啊哈,原来问题是看json文件内容虽然只有两层
但是实际上是经过python处理后又多了一层
所见非真,此见即幻!这便是code的奥秒
再次运行:
不再报错!
看看json:
处理成功!
因为我们只是指定了第一个,
下面所有都没变,那么就在加层循环。
大功告成!
运行成功!
所有的都被修改了:
这里放上最终的代码:
import os
import json
path = '*****'
json_files = sorted([pos_json for pos_json in os.listdir(path) if pos_json.endswith('.json')])
for index, js in enumerate(json_files):
with open(os.path.join(path, js)) as json_file:
data = json.load(json_file)
if "shapes" in data:
data1= data['shapes']
for i in data1:
i['label']= "Bacterial_spot"
with open(os.path.join(path, js), 'w') as json_file:
json.dump(data, json_file)
2. 首先了解一下,coco数据集的转换程序的结构
|-- images
| |--- 1.jpg
| |--- 1.json
| |--- 2.jpg
| |--- 2.json
| |--- .......
|-- labelme2coco.py
|-- labels.txt
1️⃣ imges目录下就是你的数据集原始图片,加上labelme标注的json文件。
2️⃣ labelme2coco.py源码放到最后。
3️⃣ labels.txt就是你的类别标签,假设我有两个类(lm,ls),那么对应的labels.txt内容如下:
ignore
background
lm
ls
在labelme2coco.py文件的目录下,打开命令行执行:
python labelme2coco.py --input_dir images --output_dir coco --labels labels.txt
1️⃣ --input_dir:指定images文件夹
2️⃣ --output_dir:指定你的输出文件夹
3️⃣ --labels:指定你的labels.txt文件
执行结果如下图:
生成的coco数据集目录结构如下:
|-- annotations
| |--- instances_train2017.json
| |--- instances_val2017.json
|-- train2017
| |--- 2.jpg
| |--- 5.jpg
| |--- .......
|-- val2017
| |--- 1.jpg
| |--- 3.jpg
| |--- .......
|-- visualization
| |--- 1.jpg
| |--- 2.jpg
| |--- .......
如果想调整训练集验证集的比例,可以在labelme2coco.py源码中搜索 test_size
labelme2coco.py源码:
# 命令行执行: python labelme2coco.py --input_dir images --output_dir coco --labels labels.txt
# 输出文件夹必须为空文件夹
import argparse
import collections
import datetime
import glob
import json
import os
import os.path as osp
import sys
import uuid
import imgviz
import numpy as np
import labelme
from sklearn.model_selection import train_test_split
try:
import pycocotools.mask
except ImportError:
print("Please install pycocotools:\n\n pip install pycocotools\n")
sys.exit(1)
def to_coco(args, label_files, train):
# 创建 总标签data
now = datetime.datetime.now()
data = dict(
info=dict(
description=None,
url=None,
version=None,
year=now.year,
contributor=None,
date_created=now.strftime("%Y-%m-%d %H:%M:%S.%f"),
),
licenses=[dict(url=None, id=0, name=None, )],
images=[
# license, url, file_name, height, width, date_captured, id
],
type="instances",
annotations=[
# segmentation, area, iscrowd, image_id, bbox, category_id, id
],
categories=[
# supercategory, id, name
],
)
# 创建一个 {类名 : id} 的字典,并保存到 总标签data 字典中。
class_name_to_id = {}
for i, line in enumerate(open(args.labels).readlines()):
class_id = i - 1 # starts with -1
class_name = line.strip() # strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
if class_id == -1:
assert class_name == "__ignore__" # background:0, class1:1, ,,
continue
class_name_to_id[class_name] = class_id
data["categories"].append(
dict(supercategory=None, id=class_id, name=class_name, )
)
if train:
out_ann_file = osp.join(args.output_dir, "annotations", "instances_train2017.json")
else:
out_ann_file = osp.join(args.output_dir, "annotations", "instances_val2017.json")
for image_id, filename in enumerate(label_files):
label_file = labelme.LabelFile(filename=filename)
base = osp.splitext(osp.basename(filename))[0] # 文件名不带后缀
if train:
out_img_file = osp.join(args.output_dir, "train2017", base + ".jpg")
else:
out_img_file = osp.join(args.output_dir, "val2017", base + ".jpg")
print("| ", out_img_file)
# ************************** 对图片的处理开始 *******************************************
# 将标签文件对应的图片进行保存到对应的 文件夹。train保存到 train2017/ test保存到 val2017/
img = labelme.utils.img_data_to_arr(label_file.imageData) # .json文件中包含图像,用函数提出来
imgviz.io.imsave(out_img_file, img) # 将图像保存到输出路径
# ************************** 对图片的处理结束 *******************************************
# ************************** 对标签的处理开始 *******************************************
data["images"].append(
dict(
license=0,
url=None,
file_name=osp.relpath(out_img_file, osp.dirname(out_ann_file)),
# out_img_file = "/coco/train2017/1.jpg"
# out_ann_file = "/coco/annotations/annotations_train2017.json"
# osp.dirname(out_ann_file) = "/coco/annotations"
# file_name = ..\train2017\1.jpg out_ann_file文件所在目录下 找 out_img_file 的相对路径
height=img.shape[0],
width=img.shape[1],
date_captured=None,
id=image_id,
)
)
masks = {} # for area
segmentations = collections.defaultdict(list) # for segmentation
for shape in label_file.shapes:
points = shape["points"]
label = shape["label"]
group_id = shape.get("group_id")
shape_type = shape.get("shape_type", "polygon")
mask = labelme.utils.shape_to_mask(
img.shape[:2], points, shape_type
)
if group_id is None:
group_id = uuid.uuid1()
instance = (label, group_id)
if instance in masks:
masks[instance] = masks[instance] | mask
else:
masks[instance] = mask
if shape_type == "rectangle":
(x1, y1), (x2, y2) = points
x1, x2 = sorted([x1, x2])
y1, y2 = sorted([y1, y2])
points = [x1, y1, x2, y1, x2, y2, x1, y2]
else:
points = np.asarray(points).flatten().tolist()
segmentations[instance].append(points)
segmentations = dict(segmentations)
for instance, mask in masks.items():
cls_name, group_id = instance
if cls_name not in class_name_to_id:
continue
cls_id = class_name_to_id[cls_name]
mask = np.asfortranarray(mask.astype(np.uint8))
mask = pycocotools.mask.encode(mask)
area = float(pycocotools.mask.area(mask))
bbox = pycocotools.mask.toBbox(mask).flatten().tolist()
data["annotations"].append(
dict(
id=len(data["annotations"]),
image_id=image_id,
category_id=cls_id,
segmentation=segmentations[instance],
area=area,
bbox=bbox,
iscrowd=0,
)
)
# ************************** 对标签的处理结束 *******************************************
# ************************** 可视化的处理开始 *******************************************
if not args.noviz:
labels, captions, masks = zip(
*[
(class_name_to_id[cnm], cnm, msk)
for (cnm, gid), msk in masks.items()
if cnm in class_name_to_id
]
)
viz = imgviz.instances2rgb(
image=img,
labels=labels,
masks=masks,
captions=captions,
font_size=15,
line_width=2,
)
out_viz_file = osp.join(
args.output_dir, "visualization", base + ".jpg"
)
imgviz.io.imsave(out_viz_file, viz)
# ************************** 可视化的处理结束 *******************************************
with open(out_ann_file, "w") as f: # 将每个标签文件汇总成data后,保存总标签data文件
json.dump(data, f)
# 主程序执行
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("--input_dir", help="input annotated directory")
parser.add_argument("--output_dir", help="output dataset directory")
parser.add_argument("--labels", help="labels file", required=True)
parser.add_argument("--noviz", help="no visualization", action="store_true")
args = parser.parse_args()
if osp.exists(args.output_dir):
print("Output directory already exists:", args.output_dir)
sys.exit(1)
os.makedirs(args.output_dir)
print("| Creating dataset dir:", args.output_dir)
if not args.noviz:
os.makedirs(osp.join(args.output_dir, "visualization"))
# 创建保存的文件夹
if not os.path.exists(osp.join(args.output_dir, "annotations")):
os.makedirs(osp.join(args.output_dir, "annotations"))
if not os.path.exists(osp.join(args.output_dir, "train2017")):
os.makedirs(osp.join(args.output_dir, "train2017"))
if not os.path.exists(osp.join(args.output_dir, "val2017")):
os.makedirs(osp.join(args.output_dir, "val2017"))
# 获取目录下所有的.jpg文件列表
feature_files = glob.glob(osp.join(args.input_dir, "*.jpg"))
print('| Image number: ', len(feature_files))
# 获取目录下所有的joson文件列表
label_files = glob.glob(osp.join(args.input_dir, "*.json"))
print('| Json number: ', len(label_files))
# feature_files:待划分的样本特征集合 label_files:待划分的样本标签集合 test_size:测试集所占比例
# x_train:划分出的训练集特征 x_test:划分出的测试集特征 y_train:划分出的训练集标签 y_test:划分出的测试集标签
x_train, x_test, y_train, y_test = train_test_split(feature_files, label_files, test_size=0.3)
print("| Train number:", len(y_train), '\t Value number:', len(y_test))
# 把训练集标签转化为COCO的格式,并将标签对应的图片保存到目录 /train2017/
print("—" * 50)
print("| Train images:")
to_coco(args, y_train, train=True)
# 把测试集标签转化为COCO的格式,并将标签对应的图片保存到目录 /val2017/
print("—" * 50)
print("| Test images:")
to_coco(args, y_test, train=False)
if __name__ == "__main__":
print("—" * 50)
main()
print("—" * 50)
作者:E_sheep
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3. 接下来将我们的数据集转成需要的形式
因为我们有很多个文件夹,但是要求的是每个图片必须对应一个json文件,所以进行文件名改写.
改写文件名
我写了一个改名的脚本,开源给各位:
import os
folder_path = ''
for filename in os.listdir(folder_path):
new_filename = '1' + filename
source = os.path.join(folder_path, filename)
destination = os.path.join(folder_path, new_filename)
os.rename(source, destination)
作者:E_sheep
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这个的作用就是每个文件最前面都会加上1
那么这个就是加上2:
import os
folder_path = ''
for filename in os.listdir(folder_path):
new_filename = '2' + filename
source = os.path.join(folder_path, filename)
destination = os.path.join(folder_path, new_filename)
os.rename(source, destination)
作者:E_sheep
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这样就可以做到不同的文件夹内虽然文件名相同
整合到一个文件夹内所有文件名不会重复
改写json文件图片path
因为修改了文件名,但是json文件内容不会随之修改,所以要再操作一遍
我就又写了这个改名程序:
import os
import json
path = ''
json_files = sorted([pos_json for pos_json in os.listdir(path) if pos_json.endswith('.json')])
for index, js in enumerate(json_files):
with open(os.path.join(path, js)) as json_file:
data = json.load(json_file)
filename_without_ext = os.path.splitext(js)[0]
data["imagePath"] = ".\\" + filename_without_ext + ".JPG"
with open(os.path.join(path, js), 'w') as json_file:
json.dump(data, json_file)
这个可以做到先获取文件名,然后后缀定死为.jpg,这个格式的字符串
对每个文件夹都操作一遍就这样就大功告成了!
已解决问题:
26000+文件你怎么快速找出是哪个不配对,
答:
每个文件夹按类型分类
查看每组数量,不一样的就是问题文件夹
找到问题文件夹后,去按默认排序,最小图标,重复的就是多的那个
遗留问题:
已经确定这个不是编码问题!
再第7个文件夹中遇到了这个问题,不确定是哪里错了,搜了很久都没解决,
最后重新对原始文件夹复制品处理(缺失版),发现可以,进而确定是新增的问题,最后用新增的重新写脚本处理好了。脚本就不放出来了。