个人是在conda的虚拟环境里安装的labelme,Python是3.6版本的,但是3.x应该问题都不大。因为之前没有查到很合适的解决方法,所以自己摸索了半天多才搞完。写个blog给遇到相同问题的hxd快速解决!
我需要png格式文件的模型是TensorFlow2.x+Deeplabv3+的语义分割模型,官方的TF1.x版本好像是需要xml。但是无论如何,拿到手的数据是json也没办法,开整!
ps:labelimg应该是可以直接标记出xml文件的,需要的xd也可以直接用labelimg,安装的时候记得用虚拟环境哦哦!
在这个地方走了一点弯路,这个TensorFlow2.x+Deeplabv3+的模型我只是下下来训练过然后跑了demo,并没有仔细去看用于训练的VOC数据集就想当然地以为是xml文件(YOLO用的是xml)。所以我还先完成了json转xml的工作(完全白干)。但是做都做了一定要发上来看看,说不定有人需要呢。
借鉴了这个博主的方法:Labelme标注的json文件转成xml文件(voc2007所需格式)
这个代码还能顺便生成ImageSet文件夹下需要的训练集和测试集文件
代码在这:
#json_to_xml.py
"""
Created on Sun May 31 10:19:23 2020
@author: ywx
"""
import os
from typing import List, Any
import numpy as np
import codecs
import json
from glob import glob
import cv2
import shutil
from sklearn.model_selection import train_test_split
# 1.标签路径
labelme_path = "./labels/"
# 原始labelme标注数据路径
saved_path = "./VOC2007/"
# 保存路径
isUseTest = True # 是否创建test集
# 2.创建要求文件夹
if not os.path.exists(saved_path + "Annotations"):
os.makedirs(saved_path + "Annotations")
if not os.path.exists(saved_path + "JPEGImages/"):
os.makedirs(saved_path + "JPEGImages/")
if not os.path.exists(saved_path + "ImageSets/Main/"):
os.makedirs(saved_path + "ImageSets/Main/")
# 3.获取待处理文件
files = glob(labelme_path + "*.json")
files = [i.replace("\\", "/").split("/")[-1].split(".json")[0] for i in files]
print(files)
# 4.读取标注信息并写入 xml
for json_file_ in files:
json_filename = labelme_path + json_file_ + ".json"
json_file = json.load(open(json_filename, "r", encoding="utf-8"))
height, width, channels = cv2.imread('./images/' + json_file_ + ".jpg").shape
with codecs.open(saved_path + "Annotations/" + json_file_ + ".xml", "w", "utf-8") as xml:
xml.write('\n' )
xml.write('\t' + 'WH_data' + '\n')
xml.write('\t' + json_file_ + ".jpg" + '\n')
xml.write('\t)
xml.write('\t\tWH Data \n')
xml.write('\t\tWH \n')
xml.write('\t\tflickr \n')
xml.write('\t\tNULL \n')
xml.write('\t\n')
xml.write('\t\n' )
xml.write('\t\tNULL \n')
xml.write('\t\tWH \n')
xml.write('\t\n')
xml.write('\t\n' )
xml.write('\t\t' + str(width) + '\n')
xml.write('\t\t' + str(height) + '\n')
xml.write('\t\t' + str(channels) + '\n')
xml.write('\t\n')
xml.write('\t\t0 \n')
for multi in json_file["shapes"]:
points = np.array(multi["points"])
labelName = multi["label"]
xmin = min(points[:, 0])
xmax = max(points[:, 0])
ymin = min(points[:, 1])
ymax = max(points[:, 1])
label = multi["label"]
if xmax <= xmin:
pass
elif ymax <= ymin:
pass
else:
xml.write('\t)
xml.write('\t\t' + labelName + '\n')
xml.write('\t\tUnspecified \n')
xml.write('\t\t1 \n')
xml.write('\t\t0 \n')
xml.write('\t\t\n' )
xml.write('\t\t\t' + str(int(xmin)) + '\n')
xml.write('\t\t\t' + str(int(ymin)) + '\n')
xml.write('\t\t\t' + str(int(xmax)) + '\n')
xml.write('\t\t\t' + str(int(ymax)) + '\n')
xml.write('\t\t\n')
xml.write('\t\n')
print(json_filename, xmin, ymin, xmax, ymax, label)
xml.write('')
# 5.复制图片到 VOC2007/JPEGImages/下
image_files = glob("./images/" + "*.jpg")
print("copy image files to VOC007/JPEGImages/")
for image in image_files:
shutil.copy(image, saved_path + "JPEGImages/")
# 6.split files for txt
txtsavepath = saved_path + "ImageSets/Main/"
ftrainval = open(txtsavepath + '/trainval.txt', 'w')
ftest = open(txtsavepath + '/test.txt', 'w')
ftrain = open(txtsavepath + '/train.txt', 'w')
fval = open(txtsavepath + '/val.txt', 'w')
total_files = glob("./VOC2007/Annotations/*.xml")
total_files = [i.replace("\\", "/").split("/")[-1].split(".xml")[0] for i in total_files]
trainval_files = []
test_files = []
if isUseTest:
trainval_files, test_files = train_test_split(total_files, test_size=0.15, random_state=55)
else:
trainval_files = total_files
for file in trainval_files:
ftrainval.write(file + "\n")
# split
train_files, val_files = train_test_split(trainval_files, test_size=0.15, random_state=55)
# train
for file in train_files:
ftrain.write(file + "\n")
# val
for file in val_files:
fval.write(file + "\n")
for file in test_files:
print(file)
ftest.write(file + "\n")
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
这个自带的程序可以帮你转换单个json文件为png文件,不过我觉得直接使用应该不能满足大部分人的需求,所以在下面写了一个脚本,用来批量处理json文件。
先把批量处理的代码挂出来:
#json_to_png.py
import os
# Linux环境下下面这句去掉r
json_folder = r"./labels/"
# 获取文件夹内的文件名
FileNameList = os.listdir(json_folder)
# 激活labelme环境,如果不是用conda虚拟环境来配置的话,就把下面这句备注掉
os.system("activate labelme")
for i in range(len(FileNameList)):
# 判断当前文件是否为json文件
if(os.path.splitext(FileNameList[i])[1] == ".json"):
json_file = json_folder + "\\" + FileNameList[i]
# 将该json文件转为png
os.system("labelme_json_to_dataset " + json_file)
在目录下运行python json_to_png.py就可以啦(Linux用python3)
这边如果没有使用conda虚拟环境activate labelme的话可能就要把文件放到labelme的文件夹下操作了…因为labelme_json_to_dataset是在labelme文件夹下的exe,如果要用具体路径的话我这里是"~\labelme\Scripts/",这个故事告诉我们,conda真好用!
想象中批量处理之后就会得到一整个文件夹我需要的png图片然后放上服务器快乐训练,事实上…
真的要吐槽一下为什么是exe啊!!!!包装起来的后果就是,它为每一个json文件生成了一整个文件夹,每一个文件夹下面有四个png文件…实际上我只需要这四个中的其中一个,而且目录完全不对。一个一个文件改名然后拖出来的话又费时又无聊。
所以这里我又写了一个脚本,把每个文件夹里的我需要的png批量改名并且挪出来,这样我就可以真的扔去训练了。
把批量改名+换地址的代码放在这:
#change_name.py
import os
import string
import shutil
# Linux系统的把r去了,改自己的路径, '\'要改成'/'
dst = r'C:\Users\Administrator\Desktop\tobacco0819\labels/'
os.chdir(dst)
files = os.listdir(dst)
for file in files:
if os.path.isdir(dst + file):
os.chdir(dst + file)
new_name = str(file).strip('_json') +'.png'
# 由需要的文件不一样的记得改原文件的名字
old_name = 'label.png'
os.rename(old_name, new_name)
shutil.move(dst+file+"/"+new_name, dst)
os.chdir('../')
然后运行python change_name.py(Linux用python3)
最后放进模型里训练吧!
有什么问题发评论就好,第一次写,之后应该还会更tf,yolo,deeplab的,整一两个系列什么的。有问题可以评论探讨!