├── README.md ├── AutomaticLabelingTool ├── 4_Labelme_json_file_release.py ├── 1.1_Get_background.py ├── 0_Extract_frames.py ├── 1_Get_background.py ├── 2_Get_binary_maps.py └── 3_Generate_labelme_json.py └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # Automatic-labeling-of-instance-segmentation-Mask-Rcnn-in-static-background-base-on-labelme 2 | 静态背景下实例分割数据集自动标注工具,基于Labelme改进。可以自动生成labelme格式的json文件。(注意:本程序只适用于大量图片基于静态背景)原理是:背景减除后得到高质量的二值图,计算连通域外轮廓坐标,再将信息写入json文件。 3 | -------------------------------------------------------------------------------- /AutomaticLabelingTool/4_Labelme_json_file_release.py: -------------------------------------------------------------------------------- 1 | import os 2 | import natsort 3 | labelme_json = "C:\\Users\\213\AppData\Local\conda\conda\envs\labelme\Scripts\labelme_json_to_dataset.exe" #labelme_json_to_dataset.exe 程序路径 4 | file_path = "E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\front_img\\automatic_label\\" # 处理文件所在路径 5 | dir_info = os.listdir(file_path) 6 | dir_info = natsort.natsorted(dir_info) 7 | """循环处理‘.json’文件""" 8 | os.system('cd ‪C:\\Users\\213\\AppData\\Local\\conda\\conda\\envs\\labelme\\Scripts\\') 9 | for file_name in dir_info: 10 | file_name = os.path.join(file_path + "\\" + file_name) 11 | # 12 | os.system(labelme_json + " " + file_name) -------------------------------------------------------------------------------- /AutomaticLabelingTool/1.1_Get_background.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | ''' 3 | 用两张图片拼接得到背景的程序 实际效果不理想 4 | ''' 5 | import cv2 6 | import numpy as np 7 | from PIL import Image 8 | 9 | img1=np.array(Image.open('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\1.png', 'r').convert('L'))#TODO:打开图片 转成灰度图 10 | img2=np.array(Image.open('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\56.png', 'r').convert('L')) 11 | print(img1.shape) 12 | imgtemp=np.zeros(img1.shape) 13 | img2[:,0:1000]=img1[:,0:1000]#TODO:把img2的左半边换成img1的左半边 14 | cv2.imshow("sa",img2) 15 | cv2.waitKey(0) 16 | cv2.destroyAllWindows() 17 | 18 | cv2.imwrite('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\background6_2.png',img2) 19 | np.save("E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6_2.npy",img2) -------------------------------------------------------------------------------- /AutomaticLabelingTool/0_Extract_frames.py: -------------------------------------------------------------------------------- 1 | ''' 2 | TODO:简单等间隔取帧程序 3 | ''' 4 | import numpy as np 5 | import cv2 6 | counter = 0 7 | count=1 8 | cap = cv2.VideoCapture("E:\\deeplabcut\\test2-jzh-2019-04-22\\videos\\2.mp4") 9 | fgbg = cv2.createBackgroundSubtractorMOG2() 10 | 11 | print(cap.get(5)) 12 | nframes=cap.get(7)//2 #读取的帧数比实际大两倍 13 | print(nframes) 14 | a=nframes//300 #TODO:300:要取的帧数 15 | while(cap.isOpened()): 16 | # Capture frame-by-frame 17 | 18 | ret, frame = cap.read() 19 | 20 | if counter%a==0 : 21 | cv2.imwrite('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\7red\\'+str(count)+'.png',frame) 22 | count+=1 23 | 24 | counter += 1 25 | print(counter) 26 | cv2.waitKey(1) 27 | if not ret: 28 | break 29 | cap.release() 30 | cv2.destroyAllWindows() 31 | -------------------------------------------------------------------------------- /AutomaticLabelingTool/1_Get_background.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from PIL import Image 4 | import cv2 5 | 6 | cap = cv2.VideoCapture("E:\\deeplabcut\\test2-jzh-2019-04-22\\videos\\6.mp4") 7 | back_img = np.zeros((int(cap.get(4)),int(cap.get(3)))) 8 | nframes=int(cap.get(7)//2) 9 | print(nframes) 10 | counter=0 11 | for i in range(nframes): 12 | ret, frame = cap.read() 13 | if ret: 14 | frame=cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 15 | sframe=np.array(frame) 16 | back_img+=sframe 17 | counter+=1 18 | print(counter) 19 | else: 20 | break 21 | back_img/=counter#TODO:平均背景法 22 | np.save("E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6.npy",back_img)#TODO:存背景矩阵 23 | cv2.imwrite("E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\background6.png", back_img) -------------------------------------------------------------------------------- /AutomaticLabelingTool/2_Get_binary_maps.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from PIL import Image 4 | from skimage import data,segmentation,measure,morphology,color 5 | import cv2 6 | 7 | back_img=np.load("E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6.npy")#TODO:读取背景矩阵 8 | img_number=304 9 | #读取所有图片 10 | all_img = [np.array(Image.open('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\'+str(i+1)+'.png', 'r').convert('L')) for i in range(img_number)] 11 | # 帧的宽高 12 | h = all_img[0].shape[0] 13 | v = all_img[0].shape[1] 14 | 15 | for idex,i in enumerate(all_img): 16 | 17 | front_img = np.array(i - back_img) 18 | front_img = np.abs(front_img) 19 | print(i.shape) 20 | 21 | # 前景二值化 设定阈值将前景像素值化为0或1 22 | threshold_level = 100 23 | threshold = np.full((h, v), threshold_level) 24 | front_img = np.array(front_img > threshold, dtype=bool) 25 | dst = morphology.remove_small_objects(front_img, min_size=5000, connectivity=1,in_place=True)#TODO:把小于min_size的连通域去除 26 | front_img = dst*255 27 | front_img = front_img.astype(np.uint8) 28 | kernel = np.ones((9, 9), np.uint8) 29 | front_img = cv2.morphologyEx(front_img, cv2.MORPH_CLOSE, kernel) # TODO:cv2.MORPH_CLOSE闭运算,膨胀腐蚀去毛刺 30 | #保存二值矩阵 31 | np.save('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\front_img\\'+str(idex+1)+'.npy', front_img) 32 | 33 | front_img = np.fmin(front_img, i)# 在原帧上抠图 得到真实的前景 34 | print(front_img.shape) 35 | # 保存抠图图片 36 | print(idex) 37 | Image.fromarray(front_img).convert('RGB').save('E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\front_img\\' + str(idex + 1) + '.jpg') 38 | if idex==1000: 39 | break 40 | 41 | 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /AutomaticLabelingTool/3_Generate_labelme_json.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | ''' 4 | 仿照labelme的json文件写入自己的数据 5 | ''' 6 | import cv2 7 | import json 8 | from base64 import b64encode 9 | from json import dumps 10 | import os 11 | import glob 12 | import numpy as np 13 | from skimage import data,segmentation,measure,morphology,color 14 | 15 | # 参考labelme的json格式重新生成json文件, 16 | # 便可以使用labelme的接口解析数据 17 | class img_to_json(object): 18 | """ 19 | 这个类是用来将图像数据转化成json文件的,方便下一步的处理。主要是为了获取 20 | 图像的字符串信息 21 | """ 22 | def __init__(self, process_img_path, 23 | img_type=".png", 24 | out_file_path="", 25 | out_file_type=".json"): 26 | """ 27 | :param process_img_path: 待处理图片的路径 28 | :param img_type: 待处理图片的类型 29 | :param out_file_path: 输出文件的路径 30 | :param out_file_type: 输出文件的类型 31 | 使用glob从指定路径中获取所有的img_type的图片 32 | """ 33 | self.process_img = [process_img_path] 34 | self.out_file = out_file_path 35 | self.out_file_type = out_file_type 36 | self.img_type = img_type 37 | 38 | def en_decode(self): 39 | """ 40 | 对获取的图像数据进行编码,解码后并存储到指定文件,保存为json文件 41 | :return: null 42 | """ 43 | print('-' * 30) 44 | print("运行 Encode--->Decode\nStart process.....\nPlease wait a moment") 45 | print('-' * 30) 46 | """ 47 | Start process..... Please wait a moment 48 | """ 49 | """filepath, shotname, extension, tempfilename:目标文件所在路径,文件名,文件后缀,文件名+文件后缀""" 50 | def capture_file_info(filename): 51 | (filepath, tempfilename) = os.path.split(filename) 52 | (shotname, extension) = os.path.splitext(tempfilename) 53 | return filepath, shotname, extension, tempfilename 54 | 55 | ENCODING = 'utf-8' # 编码形式为utf-8 56 | 57 | # SCRIPT_NAME, IMAGE_NAME, JSON_NAME = argv # 获得文件名参数 58 | 59 | img = self.process_img # 所有图片的形成的列表信息 60 | # img_number = capture_file_info(img)[1] 61 | # imgs = sorted(img,key=lambda ) 62 | 63 | out_file_path = self.out_file 64 | 65 | # imgtype = self.img_type 66 | 67 | out_file_type = self.out_file_type 68 | print("待处理的图片的数量:",len(img)) 69 | if len(img) == 0: 70 | print("There was nothing under the specified path.") 71 | return 0 72 | for imgname in img: 73 | # midname = imgname[imgname.rindex("\\"):imgname.rindex("." + imgtype)] 74 | midname = capture_file_info(imgname)[1] # midname:图片的名称,不带后缀名 75 | IMAGE_NAME = imgname 76 | # IMAGE_NAME = midname + imgtype 77 | JSON_NAME = midname + out_file_type 78 | # 读取二进制图片,获得原始字节码,注意 'rb' 79 | with open(IMAGE_NAME, 'rb') as jpg_file: 80 | byte_content = jpg_file.read() 81 | # 把原始字节码编码成 base64 字节码 82 | base64_bytes = b64encode(byte_content) 83 | # 将 base64 字节码解码成 utf-8 格式的字符串 84 | base64_string = base64_bytes.decode(ENCODING) 85 | # 用字典的形式保存数据 86 | """raw_data:用来存放加入特性的数据,img_raw_data:用来存放不加入特性的数据,只有图片的字符串数据""" 87 | # raw_data = {} 88 | # raw_data["name"] = IMAGE_NAME 89 | # raw_data["image_base64_string"] = base64_string 90 | img_raw_data = {} 91 | img_raw_data = base64_string 92 | # 将字典变成 json 格式,indent =2:表示缩进为 2 个空格 93 | # json_data = dumps(raw_data) 94 | json_img_data = dumps(img_raw_data) 95 | # 将 json 格式的数据保存到指定的文件中 96 | # with open(out_file_path+JSON_NAME, 'w') as json_file: 97 | # json_file.write(json_img_data) 98 | return base64_string 99 | 100 | def dict_json(imageData,shapes,imagePath,fillColor=None,lineColor=None): 101 | ''' 102 | 103 | :param imageData: str 104 | :param shapes: list 105 | :param imagePath: str 106 | :param fillColor: list 107 | :param lineColor: list 108 | :return: dict 109 | ''' 110 | return {"imageData":imageData,"shapes":shapes,"fillColor":fillColor, 111 | 'imagePath':imagePath,'lineColor':lineColor} 112 | 113 | def dict_shapes(points,label,fill_color=None,line_color=None): 114 | return {'points':points,'fill_color':fill_color,'label':label,'line_color':line_color} 115 | 116 | # 注以下都是虚拟数据,仅为了说明问题 117 | dirnpy='E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\front_img'#TODO:.npy格式二值图矩阵位置 118 | dirpng='E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\'#TODO:.png格式原图位置 119 | npyname = [name for name in os.listdir(dirnpy) if name.endswith('.npy')] 120 | pathdict={} 121 | for i in npyname: 122 | pathdict[os.path.join(dirnpy, i)]=os.path.join(dirpng, i[0:-4]+'.png')#TODO:用字典建立原图与二值图映射关系 123 | # print(pathdict) 124 | counter=0 125 | for npy in pathdict: 126 | 127 | Binary_map=np.load(npy) 128 | contours_map = measure.find_contours(Binary_map,5)#TODO:找到二值连通域的边界坐标,返回一个List[np.array1,np.array2...] 129 | removelist = []#TODO:删除坐标数小于300的轮廓 也就是内轮廓 加入removelist,挨个删除 130 | for i in range(len(contours_map)): 131 | if contours_map[i].shape[0] < 300: 132 | removelist.append(i) 133 | print(removelist) 134 | if len(removelist) != 0: 135 | for j in range(len(removelist) - 1, -1, -1): 136 | print(removelist) 137 | del contours_map[removelist[j]] 138 | 139 | contours_map[0][:,[0,1]] = contours_map[0][:,[1,0]]#TODO:x坐标和y坐标互换 因为lebelme解析时x y坐标是相反的 本程序以图中有两个联通目标为例 140 | contours_map[1][:,[0,1]] = contours_map[1][:,[1,0]] 141 | 142 | trans = img_to_json(process_img_path=pathdict[npy]) 143 | imageData=trans.en_decode() 144 | shapes=[] 145 | # 第一个对象 146 | points=contours_map[0].tolist() # 第一个对象坐标 147 | # fill_color=null 148 | label='mouse1' 149 | # line_color=null 150 | shapes.append(dict_shapes(points,label)) 151 | 152 | # 第二个对象 153 | points=contours_map[1].tolist() # 第二个对象坐标 154 | label='mouse2' 155 | shapes.append(dict_shapes(points,label)) 156 | 157 | fillColor=[255,0,0,128] 158 | 159 | imagePath=pathdict[npy] 160 | 161 | lineColor=[0,255,0,128] 162 | 163 | data=dict_json(imageData,shapes,imagePath,fillColor,lineColor)#TODO:写入字典 164 | 165 | # 写入json文件 166 | json_file = 'E:\\deeplabcut\\test2-jzh-2019-04-22\\labeled-data\\6\\front_img\\automatic_label\\'+npyname[counter][0:-4]+'.json' 167 | counter+=1 168 | json.dump(data,open(json_file,'w')) --------------------------------------------------------------------------------