由于每张图片需要选取目标框,所需时间较长,需要工具辅助。
下面文字和代码源自wuzuyu365的博文深度学习python图像标记工具labelTool。
深度学习训练需要标记图像位置和类别,之前用的时候是叫做BBox-Label-Tool-master,遇到大图像就显示不完整了,没有自适应缩放, 这是改进后的Python脚本。
目录结构:图片目录名images, 标签目录名labels,图像目录下各类别目录名要以001,002,003,...的格式命名。
1 # -*- coding:utf-8 -*-
2 # -------------------------------------------------------------------------------
3 # Name: Object bounding box label tool
4 # Purpose: Label object bboxes for ImageNet Detection data
5 # Author: Qiushi
6 # Created: 06/06/2014
7
8 #
9 # -------------------------------------------------------------------------------
10 from __future__ import division
11 from Tkinter import *
12 import tkMessageBox
13 from PIL import Image, ImageTk
14 import os
15 import glob
16 import random
17
18 w0 = 1; # 图片原始宽度
19 h0 = 1; # 图片原始高度
20
21 # colors for the bboxes
22 COLORS = [‘red‘, ‘blue‘, ‘yellow‘, ‘pink‘, ‘cyan‘, ‘green‘, ‘black‘]
23 # image sizes for the examples
24 SIZE = 256, 256
25
26 # 指定缩放后的图像大小
27 DEST_SIZE = 500, 500
28
29
30 class LabelTool():
31 def __init__(self, master):
32 # set up the main frame
33 self.parent = master
34 self.parent.title("LabelTool")
35 self.frame = Frame(self.parent)
36 self.frame.pack(fill=BOTH, expand=1)
37 self.parent.resizable(width=TRUE, height=TRUE)
38
39 # initialize global state
40 self.imageDir = ‘‘
41 self.imageList = []
42 self.egDir = ‘‘
43 self.egList = []
44 self.outDir = ‘‘
45 self.cur = 0
46 self.total = 0
47 self.category = 0
48 self.imagename = ‘‘
49 self.labelfilename = ‘‘
50 self.tkimg = None
51
52 # initialize mouse state
53 self.STATE = {}
54 self.STATE[‘click‘] = 0
55 self.STATE[‘x‘], self.STATE[‘y‘] = 0, 0
56
57 # reference to bbox
58 self.bboxIdList = []
59 self.bboxId = None
60 self.bboxList = []
61 self.hl = None
62 self.vl = None
63
64 # ----------------- GUI stuff ---------------------
65 # dir entry & load
66 self.label = Label(self.frame, text="Image Dir:")
67 self.label.grid(row=0, column=0, sticky=E)
68 self.entry = Entry(self.frame)
69 self.entry.grid(row=0, column=1, sticky=W + E)
70 self.ldBtn = Button(self.frame, text="Load", command=self.loadDir)
71 self.ldBtn.grid(row=0, column=2, sticky=W + E)
72
73 # main panel for labeling
74 self.mainPanel = Canvas(self.frame, cursor=‘tcross‘)
75 self.mainPanel.bind("<Button-1>", self.mouseClick)
76 self.mainPanel.bind("<Motion>", self.mouseMove)
77 self.parent.bind("<Escape>", self.cancelBBox) # press <Espace> to cancel current bbox
78 self.parent.bind("s", self.cancelBBox)
79 self.parent.bind("a", self.prevImage) # press ‘a‘ to go backforward
80 self.parent.bind("d", self.nextImage) # press ‘d‘ to go forward
81 self.mainPanel.grid(row=1, column=1, rowspan=4, sticky=W + N)
82
83 # showing bbox info & delete bbox
84 self.lb1 = Label(self.frame, text=‘Bounding boxes:‘)
85 self.lb1.grid(row=1, column=2, sticky=W + N)
86
87 self.listbox = Listbox(self.frame, width=28, height=12)
88 self.listbox.grid(row=2, column=2, sticky=N)
89
90 self.btnDel = Button(self.frame, text=‘Delete‘, command=self.delBBox)
91 self.btnDel.grid(row=3, column=2, sticky=W + E + N)
92 self.btnClear = Button(self.frame, text=‘ClearAll‘, command=self.clearBBox)
93 self.btnClear.grid(row=4, column=2, sticky=W + E + N)
94
95 # control panel for image navigation
96 self.ctrPanel = Frame(self.frame)
97 self.ctrPanel.grid(row=5, column=1, columnspan=2, sticky=W + E)
98 self.prevBtn = Button(self.ctrPanel, text=‘<< Prev‘, width=10, command=self.prevImage)
99 self.prevBtn.pack(side=LEFT, padx=5, pady=3)
100 self.nextBtn = Button(self.ctrPanel, text=‘Next >>‘, width=10, command=self.nextImage)
101 self.nextBtn.pack(side=LEFT, padx=5, pady=3)
102 self.progLabel = Label(self.ctrPanel, text="Progress: / ")
103 self.progLabel.pack(side=LEFT, padx=5)
104 self.tmpLabel = Label(self.ctrPanel, text="Go to Image No.")
105 self.tmpLabel.pack(side=LEFT, padx=5)
106 self.idxEntry = Entry(self.ctrPanel, width=5)
107 self.idxEntry.pack(side=LEFT)
108 self.goBtn = Button(self.ctrPanel, text=‘Go‘, command=self.gotoImage)
109 self.goBtn.pack(side=LEFT)
110
111 # example pannel for illustration
112 self.egPanel = Frame(self.frame, border=10)
113 self.egPanel.grid(row=1, column=0, rowspan=5, sticky=N)
114 self.tmpLabel2 = Label(self.egPanel, text="Examples:")
115 self.tmpLabel2.pack(side=TOP, pady=5)
116
117 self.egLabels = []
118 for i in range(3):
119 self.egLabels.append(Label(self.egPanel))
120 self.egLabels[-1].pack(side=TOP)
121
122 # display mouse position
123 self.disp = Label(self.ctrPanel, text=‘‘)
124 self.disp.pack(side=RIGHT)
125
126 self.frame.columnconfigure(1, weight=1)
127 self.frame.rowconfigure(4, weight=1)
128
129 # for debugging
130
131 ## self.setImage()
132 ## self.loadDir()
133
134
135 def loadDir(self, dbg=False):
136 if not dbg:
137 s = self.entry.get()
138 self.parent.focus()
139 self.category = int(s)
140 else:
141 s = r‘D:\workspace\python\labelGUI‘
142 ## if not os.path.isdir(s):
143 ## tkMessageBox.showerror("Error!", message = "The specified dir doesn‘t exist!")
144 ## return
145 # get image list
146
147 print ‘self.category =%d‘ % (self.category)
148
149 self.imageDir = os.path.join(r‘./images‘, ‘%03d‘ % (self.category))
150 print(self.imageDir)
151 self.imageList = glob.glob(os.path.join(self.imageDir, ‘*.jpg‘))
152 if len(self.imageList) == 0:
153 print ‘No .jpg images found in the specified dir!‘
154 return
155 else:
156 print ‘num=%d‘ % (len(self.imageList))
157
158 # default to the 1st image in the collection
159 self.cur = 1
160 self.total = len(self.imageList)
161
162 # set up output dir
163 self.outDir = os.path.join(r‘./labels‘, ‘%03d‘ % (self.category))
164 if not os.path.exists(self.outDir):
165 os.mkdir(self.outDir)
166
167 # load example bboxes
168 self.egDir = os.path.join(r‘./Examples‘, ‘%03d‘ % (self.category))
169 # if not os.path.exists(self.egDir):
170 # return
171
172 filelist = glob.glob(os.path.join(self.egDir, ‘*.jpg‘))
173 self.tmp = []
174 self.egList = []
175 random.shuffle(filelist)
176 for (i, f) in enumerate(filelist):
177 if i == 3:
178 break
179 im = Image.open(f)
180 r = min(SIZE[0] / im.size[0], SIZE[1] / im.size[1])
181 new_size = int(r * im.size[0]), int(r * im.size[1])
182 self.tmp.append(im.resize(new_size, Image.ANTIALIAS))
183 self.egList.append(ImageTk.PhotoImage(self.tmp[-1]))
184 self.egLabels[i].config(image=self.egList[-1], width=SIZE[0], height=SIZE[1])
185
186 self.loadImage()
187 print ‘%d images loaded from %s‘ % (self.total, s)
188
189 def loadImage(self):
190 # load image
191 imagepath = self.imageList[self.cur - 1]
192 pil_image = Image.open(imagepath)
193
194 # get the size of the image
195 # 获取图像的原始大小
196 global w0, h0
197 w0, h0 = pil_image.size
198
199 # 缩放到指定大小
200 pil_image = pil_image.resize((DEST_SIZE[0], DEST_SIZE[1]), Image.ANTIALIAS)
201
202 # pil_image = imgresize(w, h, w_box, h_box, pil_image)
203 self.img = pil_image
204
205 self.tkimg = ImageTk.PhotoImage(pil_image)
206
207 self.mainPanel.config(width=max(self.tkimg.width(), 400), height=max(self.tkimg.height(), 400))
208 self.mainPanel.create_image(0, 0, image=self.tkimg, anchor=NW)
209 self.progLabel.config(text="%04d/%04d" % (self.cur, self.total))
210
211 # load labels
212 self.clearBBox()
213 self.imagename = os.path.split(imagepath)[-1].split(‘.‘)[0]
214 labelname = self.imagename + ‘.txt‘
215 self.labelfilename = os.path.join(self.outDir, labelname)
216 bbox_cnt = 0
217 if os.path.exists(self.labelfilename):
218 with open(self.labelfilename) as f:
219 for (i, line) in enumerate(f):
220 if i == 0:
221 bbox_cnt = int(line.strip())
222 continue
223 print line
224 tmp = [(t.strip()) for t in line.split()]
225
226 print "********************"
227 print DEST_SIZE
228 # tmp = (0.1, 0.3, 0.5, 0.5)
229 print "tmp[0,1,2,3]===%.2f, %.2f, %.2f, %.2f" % (
230 float(tmp[0]), float(tmp[1]), float(tmp[2]), float(tmp[3]))
231 # print "%.2f,%.2f,%.2f,%.2f" %(tmp[0] tmp[1] tmp[2] tmp[3] )
232
233 print "********************"
234
235 # tx = (10, 20, 30, 40)
236 # self.bboxList.append(tuple(tx))
237 self.bboxList.append(tuple(tmp))
238 tmp[0] = float(tmp[0])
239 tmp[1] = float(tmp[1])
240 tmp[2] = float(tmp[2])
241 tmp[3] = float(tmp[3])
242
243 tx0 = int(tmp[0] * DEST_SIZE[0])
244 ty0 = int(tmp[1] * DEST_SIZE[1])
245
246 tx1 = int(tmp[2] * DEST_SIZE[0])
247 ty1 = int(tmp[3] * DEST_SIZE[1])
248 print "tx0, ty0, tx1, ty1"
249 print tx0, ty0, tx1, ty1
250
251 tmpId = self.mainPanel.create_rectangle(tx0, ty0, tx1, ty1, 252 width=2, 253 outline=COLORS[(len(self.bboxList) - 1) % len(COLORS)])
254
255 self.bboxIdList.append(tmpId)
256 self.listbox.insert(END, ‘(%.2f,%.2f)-(%.2f,%.2f)‘ % (tmp[0], tmp[1], tmp[2], tmp[3]))
257
258 # self.listbox.insert(END, ‘(%d, %d) -> (%d, %d)‘ %(tmp[0], tmp[1], tmp[2], tmp[3]))
259 self.listbox.itemconfig(len(self.bboxIdList) - 1,
260 fg=COLORS[(len(self.bboxIdList) - 1) % len(COLORS)])
261
262 def saveImage(self):
263 # print "-----1--self.bboxList---------"
264 print self.bboxList
265 # print "-----2--self.bboxList---------"
266
267 with open(self.labelfilename, ‘w‘) as f:
268 f.write(‘%d\n‘ % len(self.bboxList))
269 for bbox in self.bboxList:
270 f.write(‘ ‘.join(map(str, bbox)) + ‘\n‘)
271 print ‘Image No. %d saved‘ % (self.cur)
272
273 def mouseClick(self, event):
274 if self.STATE[‘click‘] == 0:
275 self.STATE[‘x‘], self.STATE[‘y‘] = event.x, event.y
276 else:
277 x1, x2 = min(self.STATE[‘x‘], event.x), max(self.STATE[‘x‘], event.x)
278 y1, y2 = min(self.STATE[‘y‘], event.y), max(self.STATE[‘y‘], event.y)
279
280 x1, x2 = x1 / DEST_SIZE[0], x2 / DEST_SIZE[0];
281 y1, y2 = y1 / DEST_SIZE[1], y2 / DEST_SIZE[1];
282
283 self.bboxList.append((x1, y1, x2, y2))
284 self.bboxIdList.append(self.bboxId)
285 self.bboxId = None
286 self.listbox.insert(END, ‘(%.2f, %.2f)-(%.2f, %.2f)‘ % (x1, y1, x2, y2))
287 self.listbox.itemconfig(len(self.bboxIdList) - 1, fg=COLORS[(len(self.bboxIdList) - 1) % len(COLORS)])
288 self.STATE[‘click‘] = 1 - self.STATE[‘click‘]
289
290 def mouseMove(self, event):
291 self.disp.config(text=‘x: %.2f, y: %.2f‘ % (event.x / DEST_SIZE[0], event.y / DEST_SIZE[1]))
292 if self.tkimg:
293 if self.hl:
294 self.mainPanel.delete(self.hl)
295 self.hl = self.mainPanel.create_line(0, event.y, self.tkimg.width(), event.y, width=2)
296 if self.vl:
297 self.mainPanel.delete(self.vl)
298 self.vl = self.mainPanel.create_line(event.x, 0, event.x, self.tkimg.height(), width=2)
299 if 1 == self.STATE[‘click‘]:
300 if self.bboxId:
301 self.mainPanel.delete(self.bboxId)
302 self.bboxId = self.mainPanel.create_rectangle(self.STATE[‘x‘], self.STATE[‘y‘], 303 event.x, event.y, 304 width=2, 305 outline=COLORS[len(self.bboxList) % len(COLORS)])
306
307 def cancelBBox(self, event):
308 if 1 == self.STATE[‘click‘]:
309 if self.bboxId:
310 self.mainPanel.delete(self.bboxId)
311 self.bboxId = None
312 self.STATE[‘click‘] = 0
313
314 def delBBox(self):
315 sel = self.listbox.curselection()
316 if len(sel) != 1:
317 return
318 idx = int(sel[0])
319 self.mainPanel.delete(self.bboxIdList[idx])
320 self.bboxIdList.pop(idx)
321 self.bboxList.pop(idx)
322 self.listbox.delete(idx)
323
324 def clearBBox(self):
325 for idx in range(len(self.bboxIdList)):
326 self.mainPanel.delete(self.bboxIdList[idx])
327 self.listbox.delete(0, len(self.bboxList))
328 self.bboxIdList = []
329 self.bboxList = []
330
331 def prevImage(self, event=None):
332 self.saveImage()
333 if self.cur > 1:
334 self.cur -= 1
335 self.loadImage()
336
337 def nextImage(self, event=None):
338 self.saveImage()
339 if self.cur < self.total:
340 self.cur += 1
341 self.loadImage()
342
343 def gotoImage(self):
344 idx = int(self.idxEntry.get())
345 if 1 <= idx and idx <= self.total:
346 self.saveImage()
347 self.cur = idx
348 self.loadImage()
349
350 ## def setImage(self, imagepath = r‘test2.png‘):
351 ## self.img = Image.open(imagepath)
352 ## self.tkimg = ImageTk.PhotoImage(self.img)
353 ## self.mainPanel.config(width = self.tkimg.width())
354 ## self.mainPanel.config(height = self.tkimg.height())
355 ## self.mainPanel.create_image(0, 0, image = self.tkimg, anchor=NW)
356
357 def imgresize(w, h, w_box, h_box, pil_image):
358 ‘‘‘
359 resize a pil_image object so it will fit into
360 a box of size w_box times h_box, but retain aspect ratio
361 ‘‘‘
362 f1 = 1.0 * w_box / w # 1.0 forces float division in Python2
363 f2 = 1.0 * h_box / h
364 factor = min([f1, f2])
365 # print(f1, f2, factor) # test
366 # use best down-sizing filter
367 width = int(w * factor)
368 height = int(h * factor)
369 return pil_image.resize((width, height), Image.ANTIALIAS)
370
371
372 if __name__ == ‘__main__‘:
373 root = Tk()
374 tool = LabelTool(root)
375 root.mainloop()
快捷键a,d前后切换图像,esc取消当前框,使用效果如下。

上一步完成后得到的结果是TXT文件。(我没有先重命名)

需要的XML文件格式如下。
<annotation>
<folder>VOC2007</folder>
<filename>000002.jpg</filename>
<source>
<database>My Database</database>
<annotation>VOC2007</annotation>
<image>flickr</image>
<flickrid>NULL</flickrid>
</source>
<owner>
<flickrid>NULL</flickrid>
<name>J</name>
</owner>
<size>
<width>256</width>
<height>256</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>2</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>31</xmin>
<ymin>20</ymin>
<xmax>241</xmax>
<ymax>250</ymax>
</bndbox>
</object>
</annotation>
生成代码如下。
1 import glob
2
3 s1=""" <object>
4 <name>{0}</name>
5 <pose>Unspecified</pose>
6 <truncated>0</truncated>
7 <difficult>0</difficult>
8 <bndbox>
9 <xmin>{1}</xmin>
10 <ymin>{2}</ymin>
11 <xmax>{3}</xmax>
12 <ymax>{4}</ymax>
13 </bndbox>
14 </object>"""
15
16 s2="""<annotation>
17 <folder>VOC2007</folder>
18 <filename>{0}</filename>
19 <source>
20 <database>My Database</database>
21 <annotation>VOC2007</annotation>
22 <image>flickr</image>
23 <flickrid>NULL</flickrid>
24 </source>
25 <owner>
26 <flickrid>NULL</flickrid>
27 <name>J</name>
28 </owner>
29 <size>
30 <width>256</width>
31 <height>256</height>
32 <depth>3</depth>
33 </size>
34 <segmented>0</segmented>
35 <object>
36 <name>{1}</name>
37 <pose>Unspecified</pose>
38 <truncated>0</truncated>
39 <difficult>0</difficult>
40 <bndbox>
41 <xmin>{2}</xmin>
42 <ymin>{3}</ymin>
43 <xmax>{4}</xmax>
44 <ymax>{5}</ymax>
45 </bndbox>
46 </object>{6}
47 </annotation>
48 """
49
50 textlist=glob.glob(‘labels\\001\*.txt‘)
51 for text_ in textlist:
52 flabel = open(text_, ‘r‘)
53 lb = flabel.readlines()
54 flabel.close()
55 ob2 = ""
56 if len(lb)<2:
57 continue # no annotation
58 x1=2
59 x2=lb[1].split(‘ ‘)
60 x3 = [int(float(i) * 256) for i in x2]
61 if len(lb)>2: # extra annotation
62 for i in range(2,len(lb)):
63 y2 = lb[i].split(‘ ‘)
64 y3 = [int(float(i) * 256) for i in y2]
65 ob2+=‘\n‘ + s1.format(x1,y3[0],y3[1],y3[2],y3[3])
66 imgname=(‘%06d‘ % (int(text_[13:-4])))+‘.jpg‘
67 savename=‘Annotations\\‘+str(‘%06d‘ % (int(text_[13:-4])))+‘.xml‘
68 f = open(savename, ‘w‘)
69 ob1=s2.format(imgname, x1, x3[0],x3[1],x3[2],x3[3], ob2)
70 f.write(ob1)
71 f.close()
test.txt是测试集,train.txt是训练集,val.txt是验证集,trainval.txt是训练和验证集。内容为文件名。
VOC2007中,trainval大概是整个数据集的50%,test也大概是整个数据集的50%;train大概是trainval的50%,val大概是trainval的50%。
由于本次数据较少,两类共计约400张,train、val、test各占33%。
1 import os 2 import random 3 4 trainval_percent = 0.66 5 train_percent = 0.5 6 xmlfilepath = ‘Annotations‘ 7 txtsavepath = ‘ImageSets\Main‘ 8 total_xml = os.listdir(xmlfilepath) 9 10 num=len(total_xml) 11 list=range(num) 12 tv=int(num*trainval_percent) 13 tr=int(tv*train_percent) 14 trainval= random.sample(list,tv) 15 train=random.sample(trainval,tr) 16 17 ftrainval = open(‘ImageSets\Main\\trainval.txt‘, ‘w‘) 18 ftest = open(‘ImageSets\Main\\test.txt‘, ‘w‘) 19 ftrain = open(‘ImageSets\Main\\train.txt‘, ‘w‘) 20 fval = open(‘ImageSets\Main\\val.txt‘, ‘w‘) 21 22 for i in list: 23 name=total_xml[i][:-4]+‘\n‘ 24 if i in trainval: 25 ftrainval.write(name) 26 if i in train: 27 ftrain.write(name) 28 else: 29 fval.write(name) 30 else: 31 ftest.write(name) 32 33 ftrainval.close() 34 ftrain.close() 35 fval.close() 36 ftest .close()
数据制作就完成了。
将数据集做成VOC2007格式用于Faster-RCNN训练
原文:https://www.cnblogs.com/wind-chaser/p/11347517.html