MakeLabel是一款用Python编写的,用于给图片区域内容作标注的工具软件。它的目的是给YOLOV5样本图片作标注,并生成可供YOLOV5训练使用的标签文件。
label.txt文件
标签类别文件,声明训练所要标注的类别。一行一个类别,第1个是类别名称,第2个是序号。中间用英文逗号分隔。
人,0
猫,1
狗,2
makelabel.py文件
主程序,程序很简单,所以没有做标注。主要是用Tkinter和PIL来实现。要和label.txt放在同一个目录内。
import tkinter as tk
from tkinter import ttk
import sys,os
#import win32api,win32con
from tkinter import filedialog
from PIL import Image,ImageTk
import tkinter.font
#=======================================================
def get_img(nid):
global Values
c = 0
for key,value in Values["Img_File"].items():
if(nid == c):
return key
c+=1
return ""
#=======================================================
def show_img():
global Values,bottom_Canvas,colors
if(Values["Img_Dir"]=="" or Values["Label_Dir"]==""):
Values["nID"] = 0
return
nid = Values["nID"]
if(nid >= len(Values["Img_File"])):
nid = len(Values["Img_File"])-1
if(nid<0):
nid = 0
Values["nID"] = nid
fn = get_img(nid)
if(fn==""):
return
fname = Values["Img_Dir"]+"\\"+fn
label_F = Values["Label_Dir"] + "\\" + Values["Img_File"][fn]
if(os.path.exists(label_F)==False):
file = open(label_F,'w')
file.close()
Values["Cur_Rt"] = []
else:
Values["Cur_Rt"] = []
file = open(label_F,'r')
cline = file.readline()
while(cline):
cline = cline.split()
T = [0,0,0,0,0]
T[0] = int(cline[0])
T[1] = float(cline[1])
T[2] = float(cline[2])
T[3] = float(cline[3])
T[4] = float(cline[4])
T[1] = T[1] - T[3]/2
T[2] = T[2] - T[4]/2
Values["Cur_Rt"].append(T)
cline = file.readline()
file.close()
img = Image.open(fname)
w = img.size[0]
h = img.size[1]
v1 = 1
if(w<h and h>600):
v1 = h / 600
h /= v1
w /= v1
elif(w>h and w>1100):
v1 = w / 1100
w /= v1
h /= v1
w = int(w)
h = int(h)
if(Values["Img_Data"]!=0):
bottom_Canvas.delete(Values["Img_Data"])
img = img.resize((w,h))
Values["Img_Data"] = ImageTk.PhotoImage(img)
sx = (1200 - w ) //2
sy = (650 - h) //2
Values["Cur_Size"] = [sx,sy,w,h]
bottom_Canvas.delete(tk.ALL)
bottom_Canvas.create_image(sx,sy,anchor='nw', image=Values["Img_Data"])
for rt in Values["Cur_Rt"]:
color = colors[rt[0]]
x1 = int(w * rt[1])+sx
y1 = int(h * rt[2])+sy
x2 = int(w * rt[3] + x1)
y2 = int(h * rt[4] + y1)
bottom_Canvas.create_rectangle(x1,y1,x2,y2,width=2,outline=color)
#=======================================================
def find_jpg():
global Values
Values["Img_File"] = {}
Values["nID"] = 0
for root,dirs,files in os.walk(Values["Img_Dir"]):
for file in files:
if(file.endswith(".jpg") or file.endswith(".JPG") or
file.endswith(".jpeg") or file.endswith(".JPEG")):
f1 = os.path.splitext(file)[0]
Values["Img_File"][file] = f1+".txt"
show_img()
#=======================================================
def prev_bn():
global Values
Values["nID"] -=1
show_img()
#=======================================================
def next_bn():
global Values
Values["nID"] +=1
show_img()
#=======================================================
def clear_bn():
Values["Cur_Rt"] = []
nid = Values["nID"]
if(nid >= len(Values["Img_File"])):
nid = len(Values["Img_File"])-1
if(nid<0):
return
fn = get_img(nid)
label_F = Values["Label_Dir"] + "\\" + Values["Img_File"][fn]
os.remove(label_F)
show_img()
#=======================================================
def OnMouseMove(event):
global Values,bottom_Canvas,Ctrl_List,colors
if(Values["Sel_Mode"]<0):
return
if(Values["Tmp_Rt"][0]==0):
Values["Tmp_Rt"][0] = 1
Values["Tmp_Rt"][1] = event.x
Values["Tmp_Rt"][2] = event.y
else:
Values["Tmp_Rt"][3] = event.x
Values["Tmp_Rt"][4] = event.y
if(Values["Tmp_Rt"][5]!=0):
bottom_Canvas.delete(Values["Tmp_Rt"][5])
ncolor = colors[Values["Sel_Mode"]]
Values["Tmp_Rt"][5] = bottom_Canvas.create_rectangle(Values["Tmp_Rt"][1],Values["Tmp_Rt"][2],
Values["Tmp_Rt"][3],Values["Tmp_Rt"][4],width=2,outline=ncolor)
#=======================================================
def OnRelease(event):
global Values,Ctrl_List,Label_List
if(Values["Tmp_Rt"][0] ==0):
return
fn = get_img(Values["nID"])
if(fn==""):
return
label_F = Values["Label_Dir"] + "\\" + Values["Img_File"][fn]
item = Ctrl_List.get()
pID = -1
pID = Label_List[item]
if(pID==-1):
return
if(Values["Tmp_Rt"][1] > Values["Tmp_Rt"][3]):
t = Values["Tmp_Rt"][1]
Values["Tmp_Rt"][1] = Values["Tmp_Rt"][3]
Values["Tmp_Rt"][3] = t
if(Values["Tmp_Rt"][2] > Values["Tmp_Rt"][4]):
t = Values["Tmp_Rt"][1]
Values["Tmp_Rt"][2] = Values["Tmp_Rt"][4]
Values["Tmp_Rt"][4] = t
ff = open(label_F,"a")
sx =round((Values["Tmp_Rt"][1]-Values["Cur_Size"][0]) / Values["Cur_Size"][2],4)
sy = round((Values["Tmp_Rt"][2]-Values["Cur_Size"][1]) / Values["Cur_Size"][3],4)
w = round((Values["Tmp_Rt"][3]-Values["Tmp_Rt"][1] ) / Values["Cur_Size"][2],4)
h = round((Values["Tmp_Rt"][4]-Values["Tmp_Rt"][2] ) / Values["Cur_Size"][3],4)
sx += w/2
sy += h/2
if(sx<0):
sx=0
if(sx>1):
sx=1
if(sy<0):
sy=0
if(sy>1):
sy=1
if(w>1):
w=1
if(h>1):
h=1
ff.write(str(pID)+" "+str(sx)+" "+str(sy)+" "+ str(w)+" "+ str(h)+"\n")
ff.close()
Values["Tmp_Rt"][0] = 0
Values["Tmp_Rt"][5] = 0
#=======================================================
def ch_Img_Dir(event):
global Values,Img_Dir_Label
Values["Img_Dir"] = filedialog.askdirectory()
Values["Img_Dir"] = Values["Img_Dir"].replace("/","\\")
Img_Dir_Label["text"] = Values["Img_Dir"]
find_jpg()
#======================================================
def ch_Label_Dir(event):
global Values,Label_Dir_Label
Values["Label_Dir"] = filedialog.askdirectory()
Values["Label_Dir"] = Values["Label_Dir"].replace("/","\\")
Label_Dir_Label["text"] = Values["Label_Dir"]
show_img()
#======================================================
def OnSelect(event):
global Ctrl_List,Values,Label_List
item = Ctrl_List.get()
if(item=="清除标签"):
Values["Sel_Mode"] = -1
else:
Values["Sel_Mode"] = Label_List[item]
#======================================================
def Open_Label():
global Label_List
Label_List = {}
f = open("label.txt",'r',encoding='utf-8')
while True:
line = f.readline()
if(len(line)==0):
break
tmp = line.split(",")
tmp[1] = tmp[1].replace('\n','')
Label_List[tmp[0]] = int(tmp[1])
f.close()
#======================================================
def Init_GUI(root):
global Img_Dir_Label,Label_Dir_Label,bottom_Canvas,Ctrl_List,Label_List
font1 = tk.font.Font(family='黑体',size=12);
top_Frame = tk.PanedWindow(height=50,bg='#3f3f3f')
top_Frame.pack(fill=tk.BOTH,expand=1)
Img_Dir_Label = tk.Label(top_Frame,text="图片目录",bg='#7f7f7f',font=font1,fg='white',
width=30,height=2)
Label_Dir_Label = tk.Label(top_Frame,text="标签目录",bg='#7f7f7f',font=font1,fg='white',
width=30,height=2)
Img_Dir_Label.bind("<Button-1>",ch_Img_Dir)
Label_Dir_Label.bind("<Button-1>",ch_Label_Dir)
Prev_Button = tk.Button(top_Frame,text="前一张",bg='#7f7f7f',font=font1,fg='white',
width=10,height=2,command=prev_bn)
Next_Button = tk.Button(top_Frame,text="后一张",bg='#7f7f7f',font=font1,fg='white',
width=10,height=2,command=next_bn)
Clear_Button = tk.Button(top_Frame,text="清除标记",bg='#7f7f7f',font=font1,fg='white',width=10,
height=2,command=clear_bn)
Open_Label()
Ctrl_List = ttk.Combobox(top_Frame,font=font1,width=5,state="readonly")
TM = []
for key,value in Label_List.items():
TM.append(key)
Ctrl_List['value'] = TM
Ctrl_List.bind("<ComboboxSelected>",OnSelect)
Label_TIP_Label = tk.Label(top_Frame,text="MakeLabel V2021",bg='#7f7f7f',font=font1,
fg='white',width=20,height=2)
top_Frame.add(Img_Dir_Label)
top_Frame.add(Label_Dir_Label)
top_Frame.add(Prev_Button)
top_Frame.add(Next_Button)
top_Frame.add(Ctrl_List)
top_Frame.add(Clear_Button)
top_Frame.add(Label_TIP_Label)
bottom_Canvas = tk.Canvas(root,height=650,bg='#3f3f3f')
bottom_Canvas.pack(fill=tk.BOTH,expand=1)
bottom_Canvas.bind("<B1-Motion>",OnMouseMove)
bottom_Canvas.bind("<ButtonRelease-1>",OnRelease)
#================================================
if __name__ == '__main__':
colors =["#ffffff","#ff0000","#00ff00","#0000ff","#7f0000","#007f00","#00007f"]
Values = {"nID":0,"Img_Dir":"","Label_Dir":"","Img_File":{},"Img_Data":0,
"Tmp_Rt":[0,-1,-1,-1,-1,0],"Cur_Rt":[],"Sel_Mode":0,"Cur_Size":[]}
Img_Dir_Label = 0
Label_Dir_Label = 0
bottom_Canvas = 0
Ctrl_List = 0
Label_List = 0;
#---------------------
root = tk.Tk()
root.title("Make Label")
root.resizable(0,0)
root.geometry("1200x700+0+0")
#s_Width = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
#s_Height = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
#sx = (s_Width-1200)//2
#sy = (s_Height-700)//2
sx=0
sy=0
root.geometry("%sx%s+%s+%s" % (1200,700,sx,sy))
Init_GUI(root)
root.mainloop()