├── requirements.txt ├── README.md └── main.py /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dchment/Dataset_labelling/HEAD/requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dataset_labelling 2 | A program designed for the manual labelling of image datasets 3 | 4 | Running guide: 5 | 1. Drag the datasets folder to the same path as main.py. 6 | 2. Create a JSON file for the storage of labels. 7 | 3. Run main.py. 8 | 4. Select the JSON file. 9 | 5. Select the datasets folder. 10 | 6. Now you can manually label the dataset. 11 | 7. You can start labelling from the first unlabelled image by clicking the lowest button. 12 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PyQt5.QtCore import * # 主要包含了我们常用的一些类,汇总到了一块 3 | from PyQt5.QtGui import * 4 | from PyQt5.QtWidgets import * 5 | import os 6 | import cv2 7 | import numpy as np 8 | import json 9 | class Thread_1(QThread): # 线程1 10 | st = pyqtSignal(int) 11 | def __init__(self): 12 | super(Thread_1, self).__init__() 13 | self.label_dict_now=None 14 | self.label_dict=[] 15 | 16 | def accept(self,label_dict_path): 17 | self.label_dict_path=label_dict_path 18 | self.label_dict_now = json.load(open(self.label_dict_path,'r',encoding='utf-8')) 19 | self.label_dict=[0 for i in range(len(self.label_dict_now))] 20 | # self.new_dict_element=new_dict_element 21 | 22 | 23 | # def labelling(self,label): 24 | # label_now={"id":self.id_now,"label":label} 25 | # print(label_now) 26 | # # label_dicts=json.load(fr) 27 | # # print(self.label_dicts) 28 | # def update(target,dict_list): 29 | # for dictionary in dict_list: 30 | # if dictionary["id"] == target["id"]: 31 | # dictionary["label"]=target["label"] 32 | # return dict_list 33 | # dict_list.append(target) 34 | # return dict_list 35 | # self.label_dicts=update(label_now,self.label_dicts) 36 | # self.label_dicts.sort(key=lambda x:x['id']) 37 | # print(self.label_dicts) 38 | # json.dump(self.label_dicts,open(self.label_dict_path,'w',encoding='utf-8')) 39 | 40 | def count(self): 41 | unlabelled_number=self.label_dict.count(0) 42 | return unlabelled_number 43 | 44 | def change(self,index): 45 | if self.label_dict[index]==0: 46 | self.label_dict[index]=1 47 | else: 48 | self.label_dict[index]=0 49 | 50 | def show(self,num): 51 | self.st.emit(num) 52 | 53 | def run(self): 54 | unlabelled_number=self.count() 55 | self.st.emit(unlabelled_number) 56 | print(unlabelled_number) 57 | # self.labelling() 58 | # print(len(self.label_dicts)) 59 | 60 | 61 | 62 | class MainWidget(QWidget): 63 | def __init__(self, parent=None): 64 | super(MainWidget, self).__init__(parent) 65 | # 设置窗口标题 66 | self.setWindowTitle("图像标签") 67 | # self.setWindowFlags(Qt.WindowStaysOnTopHint) 68 | # 设置窗口尺寸 69 | self.resize(600, 400) 70 | # # 移动窗口位置 71 | # window.move(200, 200) 72 | self.setMouseTracking(True) 73 | self.image_dir=None 74 | self.image_files=[] 75 | self.id_now=0 76 | self.index_now=0 77 | self.label_dict_path='label.json' 78 | self.label_dicts=None#json.load(open(self.label_dict_path,'r',encoding='utf-8')) 79 | 80 | self.thread1=Thread_1() 81 | 82 | self.image = QLabel(self) 83 | self.image.move(10, 25) 84 | self.image.resize(256,256) 85 | self.image.setScaledContents(True) 86 | 87 | self.label1 = QLabel(self) 88 | self.label1.setText('图像:') 89 | self.label1.move(10, 10) 90 | 91 | self.bt1 = QPushButton(self) 92 | self.bt1.setText('上一张(A)') 93 | self.bt1.move(300, 150) 94 | self.bt1.clicked.connect(lambda: self.switch(-1)) 95 | self.bt1.setShortcut('A') 96 | 97 | self.bt2 = QPushButton(self) 98 | self.bt2.setText('下一张(D)') 99 | self.bt2.move(400, 150) 100 | self.bt2.clicked.connect(lambda: self.switch(1)) 101 | self.bt2.setShortcut('D') 102 | 103 | self.label2 = QLabel(self) 104 | self.label2.setText('请先选择数据集与存储标签的文件:') 105 | self.label2.move(300, 10) 106 | 107 | self.bt3_1 = QPushButton(self) 108 | self.bt3_1.setText('选择数据集文件夹') 109 | self.bt3_1.move(420, 30) 110 | self.bt3_1.clicked.connect(lambda: self.open_dir()) 111 | 112 | self.bt3_2 = QPushButton(self) 113 | self.bt3_2.setText('选择存储标签的文件') 114 | self.bt3_2.move(300, 30) 115 | self.bt3_2.clicked.connect(lambda: self.open_labeldir()) 116 | 117 | self.label3 = QLabel(self) 118 | self.label3.setText('按照图片索引切换:') 119 | self.label3.move(300, 200) 120 | 121 | self.index_imgpath = QComboBox(self) 122 | self.index_imgpath.resize(80,20) 123 | self.index_imgpath.move(410, 200) 124 | self.index_imgpath.currentIndexChanged.connect(self.switch_by_number) 125 | 126 | 127 | self.bt4 = QPushButton(self) 128 | self.bt4.setText('是(J)') 129 | self.bt4.move(300, 100) 130 | self.bt4.clicked.connect(lambda: self.labelling('1')) 131 | self.bt4.setShortcut('J') 132 | 133 | self.bt5 = QPushButton(self) 134 | self.bt5.setText('否(K)') 135 | self.bt5.move(400, 100) 136 | self.bt5.clicked.connect(lambda: self.labelling('0')) 137 | self.bt5.setShortcut('k') 138 | 139 | self.label4 = QLabel(self) 140 | self.label4.setText('当前图像地址:') 141 | self.label4.move(10, 300) 142 | 143 | self.label_imgpath = QTextEdit(self) 144 | self.label_imgpath.resize(200,50) 145 | self.label_imgpath.move(10, 320) 146 | 147 | self.label5 = QLabel(self) 148 | self.label5.setText('结果:') 149 | self.label5.resize(200,50) 150 | self.label5.move(300, 50) 151 | 152 | self.bt6 = QPushButton(self) 153 | self.bt6.setText('跳转至未标注的图片') 154 | self.bt6.resize(200,30) 155 | self.bt6.move(300, 250) 156 | self.bt6.clicked.connect(self.jump_to_unlabelled) 157 | 158 | def open_dir(self): 159 | directory='' 160 | directory = QFileDialog.getExistingDirectory(None, "选择数据集文件夹", "") 161 | # print(directory) 162 | if directory!='': 163 | self.image_dir=directory 164 | files = os.listdir(directory)#[f for f in QFileDialog.getOpenFileNames(None, "选择文件", directory, "All Files (*)")[0]] 165 | self.image_files=files 166 | self.image_files.sort() 167 | self.index_now=len(self.label_dicts)-1 168 | for i in range(len(self.image_files)): 169 | self.index_imgpath.addItem(str(i)) 170 | print(len(files)) 171 | if self.image_files!=None: 172 | self.show_image(self.image_dir+'/'+self.image_files[self.index_now]) 173 | 174 | 175 | def open_labeldir(self): 176 | file,file_type = QFileDialog.getOpenFileName(None, "选择存储标签的文件", "") 177 | # print(directory) 178 | if file!='': 179 | self.label_dict_path=file 180 | f=open(self.label_dict_path,'r',encoding='utf-8') 181 | self.label_dicts=json.load(f) 182 | # self.thread1.start() 183 | # self.thread1.accept(self.label_dict_path) 184 | 185 | def jump_to_unlabelled(self): 186 | ids_now=[j['id'] for j in self.label_dicts] 187 | is_changed=False 188 | for i in range(len(self.image_files)): 189 | id_now=self.search_id(self.image_files[i]) 190 | if id_now not in ids_now: 191 | self.index_now=i 192 | self.show_image(self.image_dir+'/'+self.image_files[self.index_now]) 193 | is_changed=True 194 | break 195 | if is_changed==False: 196 | self.index_now=0 197 | self.show_image(self.image_dir+'/'+self.image_files[self.index_now]) 198 | 199 | def search_id(self,path): 200 | return int(path.split('/')[-1][:-4].split('_')[-1]) 201 | 202 | def show_image(self,path): 203 | print(path) 204 | # image_now=cv2.imread(path) 205 | # image_now=cv2.cvtColor(image_now,cv2.COLOR_BGR2RGB) 206 | # image_now = cv2.resize(image_now,(256,256)) 207 | # showed_image=QImage(path,width=256,height=256,format=QImage.Format_RGB888) 208 | self.image.setPixmap(QPixmap(path)) 209 | self.id_now=self.search_id(path) 210 | self.label_imgpath.setText('第'+str(self.index_now)+'张'+'\n'+self.image_dir+'/'+self.image_files[self.index_now]) 211 | txt=self.check_islabelled() 212 | self.label5.setText(txt) 213 | 214 | 215 | def labelling(self,label): 216 | label_now={"id":self.id_now,"label":label} 217 | print(label_now) 218 | # label_dicts=json.load(fr) 219 | # print(self.label_dicts) 220 | def update(target,dict_list): 221 | for dictionary in dict_list: 222 | if dictionary["id"] == target["id"]: 223 | dictionary["label"]=target["label"] 224 | return dict_list 225 | dict_list.append(target) 226 | return dict_list 227 | self.label_dicts=update(label_now,self.label_dicts) 228 | self.label_dicts.sort(key=lambda x:x['id']) 229 | # print(self.label_dicts) 230 | json.dump(self.label_dicts,open(self.label_dict_path,'w',encoding='utf-8')) 231 | txt=self.check_islabelled() 232 | # print(txt) 233 | self.label5.setText(txt) 234 | # fr.close() 235 | # fw.close() 236 | 237 | def switch(self,direction): 238 | self.index_now=(self.index_now+direction)%len(self.image_files) 239 | print(self.index_now) 240 | self.show_image(self.image_dir+'/'+self.image_files[self.index_now]) 241 | 242 | 243 | def switch_by_number(self): 244 | self.index_now=int(self.index_imgpath.currentText()) 245 | self.show_image(self.image_dir+'/'+self.image_files[self.index_now]) 246 | 247 | def check_islabelled(self): 248 | txt='结果:未标注' 249 | for dictionary in self.label_dicts: 250 | if self.id_now == dictionary['id']: 251 | txt='结果:已标注,标签为:'+dictionary["label"] 252 | return txt 253 | return txt 254 | 255 | def start_thread(self,thread,label_dict_path,new_dict_element): 256 | try: 257 | thread.accept(label_dict_path,new_dict_element) 258 | thread.start() 259 | except Exception as e: 260 | print(e) 261 | 262 | def onChange_prefix(self,text): 263 | self.prefix=text 264 | # print(text) 265 | # print("...", facename) 266 | 267 | def onChange_time(self,is_time): 268 | self.is_time= is_time 269 | 270 | 271 | def onChange_dupforbid(self,is_dupforbid): 272 | self.is_dupforbid= is_dupforbid 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | if __name__ == '__main__': 282 | # 创建一个应用程序对象 283 | app = QApplication(sys.argv) 284 | windows=MainWidget() 285 | windows.show() 286 | # 进入程序的主循环,并通过exit函数确保主循环安全结束 287 | sys.exit(app.exec_()) 288 | --------------------------------------------------------------------------------