以下操作在windows下进行,暂时还未在ubuntu下验证,因为之前ubuntu下labelme总是出问题,搞得人很崩溃
主要参考的链接有
win10下labelme的安装教程
自己导出一下conda环境
conda导出来的结果
# This file may be used to create an environment using:
# $ conda create --name --file
# platform: win-64
bzip2=1.0.6=vc14_3
certifi=2016.2.28=py36_0
colorama=0.4.5=pypi_0
cycler=0.11.0=pypi_0
decorator=4.4.2=pypi_0
distro=1.8.0=pypi_0
freetype=2.5.5=vc14_2
icu=57.1=vc14_0
imageio=2.16.0=pypi_0
imgviz=1.6.2=pypi_0
jpeg=9b=vc14_0
kiwisolver=1.3.1=pypi_0
labelme=5.1.1=pypi_0
libpng=1.6.30=vc14_1
libtiff=4.0.6=vc14_3
matplotlib=3.3.4=pypi_0
natsort=8.2.0=pypi_0
networkx=2.5.1=pypi_0
numpy=1.19.5=pypi_0
olefile=0.44=py36_0
opencv-python=3.4.2.16=pypi_0
openssl=1.0.2l=vc14_0
packaging=21.3=pypi_0
pillow=8.4.0=pypi_0
pip=9.0.1=py36_1
pyparsing=3.0.7=pypi_0
pyqt=5.6.0=py36_2
python=3.6.2=0
python-dateutil=2.8.2=pypi_0
pywavelets=1.1.1=pypi_0
pyyaml=6.0=pypi_0
qt=5.6.2=vc14_6
qtpy=2.0.1=pypi_0
scikit-build=0.16.4=pypi_0
scikit-image=0.17.2=pypi_0
scipy=1.5.4=pypi_0
setuptools=59.6.0=pypi_0
sip=4.18=py36_0
six=1.16.0=pypi_0
termcolor=1.1.0=pypi_0
tifffile=2020.9.3=pypi_0
typing-extensions=4.1.1=pypi_0
vc=14=0
vs2015_runtime=14.0.25420=0
wheel=0.37.1=pypi_0
wincertstore=0.2=py36_0
zlib=1.2.11=vc14_0
pip导出来的结果
certifi==2016.2.28
colorama==0.4.5
cycler==0.11.0
decorator==4.4.2
distro==1.8.0
imageio==2.16.0
imgviz==1.6.2
kiwisolver==1.3.1
labelme==5.1.1
matplotlib==3.3.4
natsort==8.2.0
networkx==2.5.1
numpy==1.19.5
olefile==0.44
opencv-python==3.4.2.16
packaging==21.3
Pillow==8.4.0
pyparsing==3.0.7
python-dateutil==2.8.2
PyWavelets==1.1.1
PyYAML==6.0
QtPy==2.0.1
scikit-build==0.16.4
scikit-image==0.17.2
scipy==1.5.4
six==1.16.0
termcolor==1.1.0
tifffile==2020.9.3
typing-extensions==4.1.1
wincertstore==0.2
点击那根线段即可在中间添加一个点
暂时未验证
注意python版本用3.7,不然protobuf装不上
absl-py==1.3.0
astunparse==1.6.3
cachetools==5.2.0
certifi @ file:///C:/b/abs_ac29jvt43w/croot/certifi_1665076682579/work/certifi
charset-normalizer==2.1.1
colorama==0.4.6
cycler==0.11.0
flatbuffers==22.12.6
fonttools==4.38.0
gast==0.4.0
google-auth==2.15.0
google-auth-oauthlib==0.4.6
google-pasta==0.2.0
grpcio==1.51.1
h5py==3.7.0
idna==3.4
imageio==2.23.0
importlib-metadata==5.2.0
keras==2.11.0
kiwisolver==1.4.4
libclang==14.0.6
Markdown==3.4.1
MarkupSafe==2.1.1
matplotlib==3.5.3
networkx==2.6.3
numpy==1.21.6
oauthlib==3.2.2
opencv-python==3.4.2.16
opt-einsum==3.3.0
packaging==22.0
Pillow==9.3.0
protobuf==3.19.6
pyasn1==0.4.8
pyasn1-modules==0.2.8
pyparsing==3.0.9
python-dateutil==2.8.2
PyWavelets==1.3.0
requests==2.28.1
requests-oauthlib==1.3.1
rsa==4.9
scikit-image==0.19.3
scipy==1.7.3
six==1.16.0
tensorboard==2.11.0
tensorboard-data-server==0.6.1
tensorboard-plugin-wit==1.8.1
tensorflow==2.11.0
tensorflow-estimator==2.11.0
tensorflow-intel==2.11.0
tensorflow-io-gcs-filesystem==0.29.0
termcolor==2.1.1
tifffile==2021.11.2
tqdm==4.64.1
typing_extensions==4.4.0
urllib3==1.26.13
Werkzeug==2.2.2
wincertstore==0.2
wrapt==1.14.1
zipp==3.11.0
相关代码
import shutil, base64, io, os, json, glob, math, warnings
import numpy as np
import PIL
import cv2
from PIL import (ExifTags, Image, ImageOps, ImageDraw, ImageDraw, ImageFont)
from skimage import img_as_ubyte
import tensorflow as tf
import os.path as osp
from tqdm import trange
import matplotlib.pyplot as plt
''' @定制化标签 '''
def shape_to_mask(img_shape, points, shape_type=None,
line_width=10, point_size=5):
mask = np.zeros(img_shape[:2], dtype=np.uint8)
mask = PIL.Image.fromarray(mask)
draw = PIL.ImageDraw.Draw(mask)
xy = [tuple(point) for point in points]
if shape_type == 'circle':
assert len(xy) == 2, 'Shape of shape_type=circle must have 2 points'
(cx, cy), (px, py) = xy
d = math.sqrt((cx - px) ** 2 + (cy - py) ** 2)
draw.ellipse([cx - d, cy - d, cx + d, cy + d], outline=1, fill=1)
elif shape_type == 'rectangle':
assert len(xy) == 2, 'Shape of shape_type=rectangle must have 2 points'
draw.rectangle(xy, outline=1, fill=1)
elif shape_type == 'line':
assert len(xy) == 2, 'Shape of shape_type=line must have 2 points'
draw.line(xy=xy, fill=1, width=line_width)
elif shape_type == 'linestrip':
draw.line(xy=xy, fill=1, width=line_width)
elif shape_type == 'point':
assert len(xy) == 1, 'Shape of shape_type=point must have 1 points'
cx, cy = xy[0]
r = point_size
draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=1, fill=1)
else:
assert len(xy) > 2, 'Polygon must have points more than 2'
draw.polygon(xy=xy, outline=1, fill=1) # xy 为[(x,y),(x.y),(...,...),...]
pass
'''
################### 定制化标签 ###################
此处可根据需要打标签图draw进行自定义业务标签
'''
mask = np.array(mask, dtype=bool)
return mask
def img_b64_to_arr(img_b64):
f = io.BytesIO()
f.write(base64.b64decode(img_b64))
img_arr = np.array(PIL.Image.open(f))
return img_arr
def img_arr_to_b64(img_arr): # 没调用
img_pil = PIL.Image.fromarray(img_arr)
f = io.BytesIO()
img_pil.save(f, format='PNG')
img_bin = f.getvalue()
if hasattr(base64, 'encodebytes'):
img_b64 = base64.encodebytes(img_bin)
else:
img_b64 = base64.encodestring(img_bin)
return img_b64
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:
img.save(f, 'PNG')
f.seek(0)
return f.read()
def apply_exif_orientation(image): # 没调用
try:
exif = image._getexif()
except AttributeError:
exif = None
if exif is None:
return image
exif = {
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:
# 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 polygons_to_mask(img_shape, polygons, shape_type=None):
warnings.warn(
"The 'polygons_to_mask' function is deprecated, "
"use 'shape_to_mask' instead."
)
return shape_to_mask(img_shape, points=polygons, shape_type=shape_type)
def shapes_to_label(img_shape, shapes, label_name_to_value, type='class'):
assert type in ['class', 'instance']
cls = np.zeros(img_shape[:2], dtype=np.int32)
if type == 'instance':
ins = np.zeros(img_shape[:2], dtype=np.int32)
instance_names = ['_background_']
for shape in shapes:
points = shape['points']
label = shape['label']
shape_type = shape.get('shape_type', None)
if type == 'class':
cls_name = label
elif type == 'instance':
cls_name = label.split('-')[0]
if label not in instance_names:
instance_names.append(label)
ins_id = instance_names.index(label)
cls_id = label_name_to_value[cls_name]
# mask = shape_to_mask(img_shape[:2], points, shape_type) # detail
mask = shape_to_mask(img_shape[:2], points, shape_type, line_width=10, point_size=5) # detail
cls[mask] = cls_id # 对每个label进行赋值类别
if type == 'instance':
ins[mask] = ins_id
pass
pass
if type == 'instance':
return cls, ins
return cls
def labelme_shapes_to_label(img_shape, shapes):
warnings.warn('labelme_shapes_to_label is deprecated, so please use '
'shapes_to_label.')
label_name_to_value = {'_background_': 0}
for shape in shapes:
label_name = shape['label']
if label_name in label_name_to_value:
label_value = label_name_to_value[label_name]
else:
label_value = len(label_name_to_value)
label_name_to_value[label_name] = label_value
lbl = shapes_to_label(img_shape, shapes, label_name_to_value)
return lbl, label_name_to_value
def masks_to_bboxes(masks):
if masks.ndim != 3:
raise ValueError(
'masks.ndim must be 3, but it is {}'
.format(masks.ndim)
)
if masks.dtype != bool:
raise ValueError(
'masks.dtype must be bool type, but it is {}'
.format(masks.dtype)
)
bboxes = []
for mask in masks:
where = np.argwhere(mask)
(y1, x1), (y2, x2) = where.min(0), where.max(0) + 1
bboxes.append((y1, x1, y2, x2))
bboxes = np.asarray(bboxes, dtype=np.float32)
return bboxes
def label_colormap(N=256):
def bitget(byteval, idx):
return ((byteval & (1 << idx)) != 0)
cmap = np.zeros((N, 3))
for i in range(0, N):
id = i
r, g, b = 0, 0, 0
for j in range(0, 8):
r = np.bitwise_or(r, (bitget(id, 0) << 7 - j))
g = np.bitwise_or(g, (bitget(id, 1) << 7 - j))
b = np.bitwise_or(b, (bitget(id, 2) << 7 - j))
id = (id >> 3)
cmap[i, 0] = r
cmap[i, 1] = g
cmap[i, 2] = b
cmap = cmap.astype(np.float32) / 255
return cmap
def _validate_colormap(colormap, n_labels):
if colormap is None:
colormap = label_colormap(n_labels)
else:
assert colormap.shape == (colormap.shape[0], 3), \
'colormap must be sequence of RGB values'
assert 0 <= colormap.min() and colormap.max() <= 1, \
'colormap must ranges 0 to 1'
return colormap
# similar function as skimage.color.label2rgb
def label2rgb(
lbl, img=None, n_labels=None, alpha=0.5, thresh_suppress=0, colormap=None,
):
if n_labels is None:
n_labels = len(np.unique(lbl))
colormap = _validate_colormap(colormap, n_labels)
colormap = (colormap * 255).astype(np.uint8)
lbl_viz = colormap[lbl]
lbl_viz[lbl == -1] = (0, 0, 0) # unlabeled
if img is not None:
img_gray = PIL.Image.fromarray(img).convert('LA')
img_gray = np.asarray(img_gray.convert('RGB'))
# img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# img_gray = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2RGB)
lbl_viz = alpha * lbl_viz + (1 - alpha) * img_gray
lbl_viz = lbl_viz.astype(np.uint8)
pass
return lbl_viz
def draw_label(label, img=None, label_names=None, colormap=None, **kwargs):
"""Draw pixel-wise label with colorization and label names.
label: ndarray, (H, W)
Pixel-wise labels to colorize.
img: ndarray, (H, W, 3), optional
Image on which the colorized label will be drawn.
label_names: iterable
List of label names.
"""
backend_org = plt.rcParams['backend']
plt.switch_backend('agg')
plt.subplots_adjust(left=0, right=1, top=1, bottom=0,
wspace=0, hspace=0)
plt.margins(0, 0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
if label_names is None:
label_names = [str(l) for l in range(label.max() + 1)]
colormap = _validate_colormap(colormap, len(label_names))
label_viz = label2rgb(
label, img, n_labels=len(label_names), colormap=colormap, **kwargs
)
plt.imshow(label_viz)
plt.axis('off')
plt_handlers = []
plt_titles = []
for label_value, label_name in enumerate(label_names):
if label_value not in label:
continue
fc = colormap[label_value]
p = plt.Rectangle((0, 0), 1, 1, fc=fc)
plt_handlers.append(p)
plt_titles.append('{value}: {name}'
.format(value=label_value, name=label_name))
plt.legend(plt_handlers, plt_titles, loc='lower right', framealpha=.5)
f = io.BytesIO()
plt.savefig(f, bbox_inches='tight', pad_inches=0)
plt.cla()
plt.close()
plt.switch_backend(backend_org)
out_size = (label_viz.shape[1], label_viz.shape[0])
out = PIL.Image.open(f).resize(out_size, PIL.Image.BILINEAR).convert('RGB')
out = np.asarray(out)
return out
def draw_instances(
image=None,
bboxes=None,
labels=None,
masks=None,
captions=None,
):
# TODO(wkentaro)
assert image is not None
assert bboxes is not None
assert labels is not None
assert masks is None
assert captions is not None
viz = PIL.Image.fromarray(image)
draw = PIL.ImageDraw.ImageDraw(viz)
font_path = osp.join(
osp.dirname(matplotlib.__file__),
'mpl-data/fonts/ttf/DejaVuSans.ttf'
)
font = PIL.ImageFont.truetype(font_path)
colormap = label_colormap(255)
for bbox, label, caption in zip(bboxes, labels, captions):
color = colormap[label]
color = tuple((color * 255).astype(np.uint8).tolist())
xmin, ymin, xmax, ymax = bbox
draw.rectangle((xmin, ymin, xmax, ymax), outline=color)
draw.text((xmin, ymin), caption, font=font)
return np.asarray(viz)
def lblsave(filename, lbl):
if os.path.splitext(filename)[1] != '.png':
filename += '.png'
# Assume label ranses [-1, 254] for int32,
# and [0, 255] for uint8 as VOC.
if lbl.min() >= -1 and lbl.max() < 255:
lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='P')
colormap = label_colormap(255)
lbl_pil.putpalette((colormap * 255).astype(np.uint8).flatten())
lbl_pil.save(filename)
else:
raise ValueError(
'[%s] Cannot save the pixel-wise class label as PNG. '
'Please consider using the .npy format.' % filename
)
pass
pass
def json2label(json_root="dataset\\2021-08-30", img_root="dataset\\2021-08-30", save_root="dataset\\2021-08-30",
label_name_to_value={'_background_': 0, "object": 1, "flaw": 2},
use_label=True, use_color=True, use_jpeg=True, use_visual=True,
is_show_time=1):
####### 默认输出路径 ########
save_img = os.path.join(save_root, "JPEGImages")
save_ano = os.path.join(save_root, "Annotations")
save_label = os.path.join(save_root, "SegmentationClassRaw")
save_color = os.path.join(save_root, "SegmentationClassPNG")
save_visual = os.path.join(save_root, "SegmentationClassVisualization")
if use_jpeg and not os.path.exists(save_img):
os.makedirs(save_img)
if use_label and not os.path.exists(save_ano):
os.makedirs(save_ano)
if use_color and not os.path.exists(save_color):
os.makedirs(save_color)
if use_label and not os.path.exists(save_label):
os.makedirs(save_label)
if use_visual and not os.path.exists(save_visual):
os.makedirs(save_visual)
pass
# 检索对应的json和img
print("img_root={}".format(img_root))
img_dirs = glob.glob(os.path.join(img_root, "*[jpg,JPG,JPEG,bmp,BMP]")) # list 的文件
print("当前root下有图片个数: ", len(img_dirs))
for j in trange(len(img_dirs)):
img_dir = img_dirs[j]
path, name = os.path.split(img_dir)
first, second = os.path.splitext(name)
json_dir = os.path.join(json_root, first + ".json")
if not os.path.exists(img_dir) or not os.path.isfile(img_dir):
print("no_exists: ", j, img_dir)
continue
pass
if not os.path.exists(json_dir) or not os.path.isfile(json_dir):
print("no_exists: ", j, json_dir)
continue
pass
# 进行json解析和标签转换
if os.path.isfile(json_dir):
data = json.load(open(json_dir))
pass
if data['imageData']:
imageData = data['imageData']
else:
with open(img_dir, 'rb') as f:
imageData = f.read()
imageData = base64.b64encode(imageData).decode('utf-8')
pass
pass
''' 解析图片: img : (array type) '''
img = img_b64_to_arr(imageData)
# mask the label_name
for shape in data['shapes']:
label_name = shape['label'] # 获取label名
if label_name in label_name_to_value:
label_value = label_name_to_value[label_name]
else:
label_value = len(label_name_to_value)
label_name_to_value[label_name] = label_value
label_values, label_names = [], []
for ln, lv in sorted(label_name_to_value.items(), key=lambda x: x[1]):
label_values.append(lv)
label_names.append(ln)
pass
if label_values != list(range(len(label_values))):
print("assert label_values")
''' 解析标签: "seg_label" lbl已经为0,1,2,3,的标签了 '''
lbl = shapes_to_label(img.shape, data['shapes'], label_name_to_value)
''' 掩码可视化:"draw_mask" lbl_viz '''
captions = ['{}: {}'.format(lv, ln)
for ln, lv in label_name_to_value.items()]
lbl_viz = draw_label(lbl, img, captions)
if is_show_time > 0:
is_show_time -= 1
plt.figure(figsize=(20, 20))
plt.subplot(121)
plt.imshow(lbl)
plt.subplot(122)
plt.imshow(lbl_viz)
plt.show()
pass
# 保存为统一图片
if use_jpeg:
PIL.Image.fromarray(img).save(os.path.join(save_img, first + ".jpeg"))
# 保存为数字标签图
if use_label:
with tf.io.gfile.GFile(os.path.join(save_label, first + ".png"), mode='w') as f:
Image.fromarray(lbl.astype(dtype=np.uint8)).save(f, 'PNG')
# 保存为彩色标签图
if use_color:
lblsave(os.path.join(save_color, first + ".png"), lbl)
# 保存为掩码可视化
if use_visual:
PIL.Image.fromarray(lbl_viz).save(os.path.join(save_visual, first + ".png"))
# 移动一份数据-到aon和jpg文件夹,统一输出格式 这里是移动,也可以复制吧
if use_label:
# shutil.move(img_dir, os.path.join(save_ano, name)) if os.path.exists(img_dir) else None
# shutil.move(json_dir, os.path.join(save_ano, first + ".json")) if os.path.exists(json_dir) else None
shutil.copy(img_dir, os.path.join(save_ano, name)) if os.path.exists(img_dir) else None
shutil.copy(json_dir, os.path.join(save_ano, first + ".json")) if os.path.exists(json_dir) else None
pass
pass
pass
if __name__ == "__main__":
'''
说明:
函数 shapes_to_label 可以修改线宽,支持 矩形,点
函数 shape_to_mask 标签扩充放大, 使用膨胀'''
#jsonroot和img_root可不可以是同一个文件夹,可以,他会自动判断类别
json2label(json_root="tower",
img_root="tower",
save_root="output",
label_name_to_value={'_background_': 0, "tower1": 1, "tower2": 2},
use_label=True, #
use_color=True, #
use_jpeg=True, #
use_visual=True, #
is_show_time=1)
使用方法,修改最下面的json_root和img_root,这两个可以是一个文件夹,因为他最终会判断文件的后缀名
参考链接
labelme转图片
使用的代码为,注意修改最上面的文件的路径
import json
import os
#输入路径 路径到包含.json文件为止,可以包含图片文件
file_path = ['towerSegoutput/Annotations']
outputRoot = 'txts'
#标签对应情况
name = ['background','tower','cate2'] # class names
a = os.listdir(file_path[0]) #.
#如果输出路径不存在,自动创建
if not os.path.exists(outputRoot):
os.mkdir(outputRoot)
for j in range(len(file_path)):
#对于每个.json文件
for file_name in os.listdir(file_path[j]):
if ".json" not in file_name:
print("不是json文件")
continue
print("是json文件")
print('\n文件名', file_name)
path = os.path.join(file_path[j],file_name)
print("path={}".format(path))
with open(path,'r') as load_f:
dict = json.load(load_f)
# print('解析后得json文件',dict)
#这个可能要修改,图片长度和宽度
# h, w = dict['imgHeight'], dict['imgWidth']
h, w = dict['imageHeight'], dict['imageWidth']
print("具有的物体的数量={}".format(len(dict['shapes'])))
first,last = os.path.splitext(file_name)
txt_name = first+'.txt'
path_txt = os.path.join(outputRoot,txt_name)
print("path_txt={}".format(path_txt))
f = open(path_txt, 'w')
#一个json文件里可能有多个物体
for i in range(len(dict['shapes'])):
if dict['shapes'][i]['label'] in name:
obj_id = name.index(dict['shapes'][i]['label'])
label_pts = [obj_id]
#这里的poings之前时polygons(类似)
points = dict['shapes'][i]['points']
print("多边形的点的数量为={}".format(len(points)))
for t in points:
x = t[0]/w
x = round(x,5)
y = t[1]/h
y = round(y,5)
label_pts.append(x)
label_pts.append(y)
for t in label_pts:
f.write(str(t))
f.write(' ')
f.write('\n')
print('物体的类别数量为{}'.format(name[obj_id]))
f.close() #把文件关闭