├── .gitignore ├── LICENSE ├── README.md ├── csv2coco.py ├── csv2labelme.py ├── csv2voc.py ├── labelme2coco.py └── labelme2voc.py /.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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 spytensor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **背景** 2 | 3 | 万事开头难。之前写图像识别的博客教程,也是为了方便那些学了很多理论知识,却对实际项目无从下手的小伙伴,后来转到目标检测来了,师从烨兄、亚光兄,从他们那学了不少检测的知识和操作,今天也终于闲下了,准备写个检测系列的总结。一方面分享知识希望可以一起学习,另一方面让一部分人少走弯路,快速上路(入坑)。 4 | 5 | 此部分代码:[Github](https://github.com/spytensor/prepare_detection_dataset) 6 | 博客地址: [目标检测系列一:如何制作数据集?](http://www.spytensor.com/index.php/archives/48/) 7 | 8 | 9 | **更新** 10 | 11 | - (28/03/2019) 12 | - 新增 `csv2labelme` 13 | 14 | 15 |

1. 内容介绍

16 | 17 | 系列一主要介绍如何在常见的几种数据格式之间进行转换,以及万能中介`csv`格式的使用,这里列出以下几个: 18 | 19 | - csv to coco 20 | - csv to voc 21 | - labelme to coco 22 | - labelme to voc 23 | - csv to json 24 | 25 |

2. 标准格式

26 | 27 | 在使用转换脚本之前,必须要明确的几种格式 28 | 29 |
2.1 csv
30 | 31 | 不要一看是`csv`文件就直接拿来运行,如果不是,可以自行修改代码,或者修改标注文件。 32 | 33 | 转换脚本支持的csv格式应为以下形式: 34 | 35 | - `csv/` 36 | - `labels.csv` 37 | - `images/` 38 | - `image1.jpg` 39 | - `image2.jpg` 40 | - `...` 41 | 42 | `labels.csv` 的形式: 43 | 44 | `/path/to/image,xmin,ymin,xmax,ymax,label` 45 | 46 | 例如: 47 | 48 | ``` 49 | /mfs/dataset/face/0d4c5e4f-fc3c-4d5a-906c-105.jpg,450,154,754,341,face 50 | /mfs/dataset/face/0ddfc5aea-fcdac-421-92dad-144.jpg,143,154,344,341,face 51 | ... 52 | ``` 53 | 注:图片路径请使用绝对路径 54 | 55 |
2.2 voc
56 | 57 | 标准的voc数据格式如下: 58 | 59 | - `VOC2007/` 60 | - `Annotations/` 61 | - `0d4c5e4f-fc3c-4d5a-906c-105.xml` 62 | - `0ddfc5aea-fcdac-421-92dad-144/xml` 63 | - `...` 64 | - `ImageSets/` 65 | - `Main/` 66 | - `train.txt` 67 | - `test.txt` 68 | - `val.txt` 69 | - `trainval.txt` 70 | - `JPEGImages/` 71 | - `0d4c5e4f-fc3c-4d5a-906c-105.jpg` 72 | - `0ddfc5aea-fcdac-421-92dad-144.jpg` 73 | - `...` 74 | 75 |
2.3 coco
76 | 77 | 此处未使用测试集 78 | 79 | - `coco/` 80 | - `annotations/` 81 | - `instances_train2017.json` 82 | - `instances_val2017.json` 83 | - `images/` 84 | - `train2017/` 85 | - `0d4c5e4f-fc3c-4d5a-906c-105.jpg` 86 | - `...` 87 | - `val2017` 88 | - `0ddfc5aea-fcdac-421-92dad-144.jpg` 89 | - `...` 90 | 91 |
2.4 labelme
92 | 93 | 94 | - `labelme/` 95 | - `0d4c5e4f-fc3c-4d5a-906c-105.json` 96 | - `0d4c5e4f-fc3c-4d5a-906c-105.jpg` 97 | - `0ddfc5aea-fcdac-421-92dad-144.json` 98 | - `0ddfc5aea-fcdac-421-92dad-144.jpg` 99 | 100 | Json file 格式: 101 | (imageData那一块太长了,不展示了) 102 | 103 | ```json 104 | { 105 | "version": "3.6.16", 106 | "flags": {}, 107 | "shapes": [ 108 | { 109 | "label": "helmet", 110 | "line_color": null, 111 | "fill_color": null, 112 | "points": [ 113 | [ 114 | 131, 115 | 269 116 | ], 117 | [ 118 | 388, 119 | 457 120 | ] 121 | ], 122 | "shape_type": "rectangle" 123 | } 124 | ], 125 | "lineColor": [ 126 | 0, 127 | 255, 128 | 0, 129 | 128 130 | ], 131 | "fillColor": [ 132 | 255, 133 | 0, 134 | 0, 135 | 128 136 | ], 137 | "imagePath": "004ffe6f-c3e2-3602-84a1-ecd5f437b113.jpg", 138 | "imageData": "" # too long ,so not show here 139 | "imageHeight": 1080, 140 | "imageWidth": 1920 141 | } 142 | ``` 143 | 144 |

3. 如何使用转换脚本

145 | 146 |
3.1 csv2coco
147 | 148 | 首先更改`csv2coco.py`中以下几个配置 149 | 150 | ``` 151 | classname_to_id = {"person": 1} # for your dataset classes 152 | csv_file = "labels.csv" # annatations file path 153 | image_dir = "images/" # original image path 154 | saved_coco_path = "./" # path to save converted coco dataset 155 | ``` 156 | 157 | 然后运行 `python csv2coco.py` 158 | 159 | 会自动创建文件夹并复制图片到相应位置,运行结束后得到如下: 160 | 161 | - `coco/` 162 | - `annotations/` 163 | - `instances_train2017.json` 164 | - `instances_val2017.json` 165 | - `images/` 166 | - `train2017/` 167 | - `0d4c5e4f-fc3c-4d5a-906c-105.jpg` 168 | - `...` 169 | - `val2017` 170 | - `0ddfc5aea-fcdac-421-92dad-144.jpg` 171 | - `...` 172 | 173 |
3.2 csv2voc
174 | 175 | 首先更改`csv2voc.py`中以下几个配置 176 | 177 | ``` 178 | csv_file = "labels.csv" 179 | saved_path = ".VOC2007/" # path to save converted voc dataset 180 | image_save_path = "./JPEGImages/" # converted voc images path 181 | image_raw_parh = "images/" # original image path 182 | ``` 183 | 184 | 然后运行 `python csv2voc.py` 185 | 186 | 同样会自动创建文件夹并复制图片到相应位置,运行结束后得到如下: 187 | 188 | 189 | - `VOC2007/` 190 | - `Annotations/` 191 | - `0d4c5e4f-fc3c-4d5a-906c-105.xml` 192 | - `0ddfc5aea-fcdac-421-92dad-144/xml` 193 | - `...` 194 | - `ImageSets/` 195 | - `Main/` 196 | - `train.txt` 197 | - `test.txt` 198 | - `val.txt` 199 | - `trainval.txt` 200 | - `JPEGImages/` 201 | - `0d4c5e4f-fc3c-4d5a-906c-105.jpg` 202 | - `0ddfc5aea-fcdac-421-92dad-144.jpg` 203 | - `...` 204 | 205 |
3.3 labelme2coco
206 | 207 | 首先更改`labelme2coco.py`中以下几个配置 208 | 209 | ``` 210 | classname_to_id = {"person": 1} # for your dataset classes 211 | labelme_path = "labelme/" # path for labelme dataset 212 | saved_coco_path = "./" # path for saved coco dataset 213 | ``` 214 | 然后运行 `python labelme2coco.py`,生成文件形式同`csv2coco` 215 | 216 |
3.4 labelme2voc
217 | 218 | 首先更改`labelme2voc.py`中以下几个配置 219 | 220 | ``` 221 | labelme_path = "labelme/" # path for labelme dataset 222 | saved_coco_path = "./" # path for saved coco dataset 223 | ``` 224 | 然后运行 `python labelme2voc.py`,生成文件形式同`csv2voc` 225 | 226 |
3.5 csv2labelme
227 | 228 | 首先更改`csv2labelme.py`中以下几个配置 229 | 230 | ``` 231 | image_path = "./images/" # path for images 232 | csv_file = "./" # path for csv annotations 233 | ``` 234 | 然后运行 `python csv2labelme.py`,生成的`json`文件会保存在`image_path`下,切换路径过去,直接`labelme`便 235 | 可以查看标签. 236 | 237 | 238 |

4. 万能中介csv

239 | 240 | 从上面的转换格式中可以看出,并没有给出如何转到csv的,一是因为太过于简单,而是主流检测框架很少支持这种格式的数据输入。以下给出如何将标注信息写入`csv` 241 | 242 | ```python 243 | info = [[filename0,"xmin ymin xmax ymax label0"], 244 | filename1,"xmin ymin xmax ymax label1"] 245 | csv_labels = open("csv_labels.csv","w") 246 | for filename,bboxes in info: 247 | bbox = bboxes.split(" ") 248 | label = bbox[-1] 249 | csv_labels.write(filename+","+bbox[0]+","+bbox[1]+","+bbox[2]+","+bbox[3]+","+label+"\n") 250 | csv_labels.close() 251 | ``` 252 | 253 | 是不是非常简单。。。如果你不知道如何从原始的标签文件中读取得到标注信息,那没办法了,学学编程吧,23333 254 | 255 |

5. 下一篇

256 | 如何做数据扩充 257 | -------------------------------------------------------------------------------- /csv2coco.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | @time: 2019/01/11 11:28 4 | spytensor 5 | ''' 6 | 7 | import os 8 | import json 9 | import numpy as np 10 | import pandas as pd 11 | import glob 12 | import cv2 13 | import os 14 | import shutil 15 | from IPython import embed 16 | from sklearn.model_selection import train_test_split 17 | np.random.seed(41) 18 | 19 | #0为背景 20 | classname_to_id = {"person": 1} 21 | 22 | class Csv2CoCo: 23 | 24 | def __init__(self,image_dir,total_annos): 25 | self.images = [] 26 | self.annotations = [] 27 | self.categories = [] 28 | self.img_id = 0 29 | self.ann_id = 0 30 | self.image_dir = image_dir 31 | self.total_annos = total_annos 32 | 33 | def save_coco_json(self, instance, save_path): 34 | json.dump(instance, open(save_path, 'w'), ensure_ascii=False, indent=2) # indent=2 更加美观显示 35 | 36 | # 由txt文件构建COCO 37 | def to_coco(self, keys): 38 | self._init_categories() 39 | for key in keys: 40 | self.images.append(self._image(key)) 41 | shapes = self.total_annos[key] 42 | for shape in shapes: 43 | bboxi = [] 44 | for cor in shape[:-1]: 45 | bboxi.append(int(cor)) 46 | label = shape[-1] 47 | annotation = self._annotation(bboxi,label) 48 | self.annotations.append(annotation) 49 | self.ann_id += 1 50 | self.img_id += 1 51 | instance = {} 52 | instance['info'] = 'spytensor created' 53 | instance['license'] = ['license'] 54 | instance['images'] = self.images 55 | instance['annotations'] = self.annotations 56 | instance['categories'] = self.categories 57 | return instance 58 | 59 | # 构建类别 60 | def _init_categories(self): 61 | for k, v in classname_to_id.items(): 62 | category = {} 63 | category['id'] = v 64 | category['name'] = k 65 | self.categories.append(category) 66 | 67 | # 构建COCO的image字段 68 | def _image(self, path): 69 | image = {} 70 | print(path) 71 | img = cv2.imread(self.image_dir + path) 72 | image['height'] = img.shape[0] 73 | image['width'] = img.shape[1] 74 | image['id'] = self.img_id 75 | image['file_name'] = path 76 | return image 77 | 78 | # 构建COCO的annotation字段 79 | def _annotation(self, shape,label): 80 | # label = shape[-1] 81 | points = shape[:4] 82 | annotation = {} 83 | annotation['id'] = self.ann_id 84 | annotation['image_id'] = self.img_id 85 | annotation['category_id'] = int(classname_to_id[label]) 86 | annotation['segmentation'] = self._get_seg(points) 87 | annotation['bbox'] = self._get_box(points) 88 | annotation['iscrowd'] = 0 89 | annotation['area'] = self._get_area(points) 90 | return annotation 91 | 92 | # COCO的格式: [x1,y1,w,h] 对应COCO的bbox格式 93 | def _get_box(self, points): 94 | min_x = points[0] 95 | min_y = points[1] 96 | max_x = points[2] 97 | max_y = points[3] 98 | return [min_x, min_y, max_x - min_x, max_y - min_y] 99 | # 计算面积 100 | def _get_area(self, points): 101 | min_x = points[0] 102 | min_y = points[1] 103 | max_x = points[2] 104 | max_y = points[3] 105 | return (max_x - min_x+1) * (max_y - min_y+1) 106 | # segmentation 107 | def _get_seg(self, points): 108 | min_x = points[0] 109 | min_y = points[1] 110 | max_x = points[2] 111 | max_y = points[3] 112 | h = max_y - min_y 113 | w = max_x - min_x 114 | a = [] 115 | a.append([min_x,min_y, min_x,min_y+0.5*h, min_x,max_y, min_x+0.5*w,max_y, max_x,max_y, max_x,max_y-0.5*h, max_x,min_y, max_x-0.5*w,min_y]) 116 | return a 117 | 118 | 119 | if __name__ == '__main__': 120 | csv_file = "train.csv" 121 | image_dir = "images/" 122 | saved_coco_path = "./" 123 | # 整合csv格式标注文件 124 | total_csv_annotations = {} 125 | annotations = pd.read_csv(csv_file,header=None).values 126 | for annotation in annotations: 127 | key = annotation[0].split(os.sep)[-1] 128 | value = np.array([annotation[1:]]) 129 | if key in total_csv_annotations.keys(): 130 | total_csv_annotations[key] = np.concatenate((total_csv_annotations[key],value),axis=0) 131 | else: 132 | total_csv_annotations[key] = value 133 | # 按照键值划分数据 134 | total_keys = list(total_csv_annotations.keys()) 135 | train_keys, val_keys = train_test_split(total_keys, test_size=0.2) 136 | print("train_n:", len(train_keys), 'val_n:', len(val_keys)) 137 | # 创建必须的文件夹 138 | if not os.path.exists('%scoco/annotations/'%saved_coco_path): 139 | os.makedirs('%scoco/annotations/'%saved_coco_path) 140 | if not os.path.exists('%scoco/images/train2017/'%saved_coco_path): 141 | os.makedirs('%scoco/images/train2017/'%saved_coco_path) 142 | if not os.path.exists('%scoco/images/val2017/'%saved_coco_path): 143 | os.makedirs('%scoco/images/val2017/'%saved_coco_path) 144 | # 把训练集转化为COCO的json格式 145 | l2c_train = Csv2CoCo(image_dir=image_dir,total_annos=total_csv_annotations) 146 | train_instance = l2c_train.to_coco(train_keys) 147 | l2c_train.save_coco_json(train_instance, '%scoco/annotations/instances_train2017.json'%saved_coco_path) 148 | for file in train_keys: 149 | shutil.copy(image_dir+file,"%scoco/images/train2017/"%saved_coco_path) 150 | for file in val_keys: 151 | shutil.copy(image_dir+file,"%scoco/images/val2017/"%saved_coco_path) 152 | # 把验证集转化为COCO的json格式 153 | l2c_val = Csv2CoCo(image_dir=image_dir,total_annos=total_csv_annotations) 154 | val_instance = l2c_val.to_coco(val_keys) 155 | l2c_val.save_coco_json(val_instance, '%scoco/annotations/instances_val2017.json'%saved_coco_path) 156 | 157 | -------------------------------------------------------------------------------- /csv2labelme.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | import json 4 | import pandas as pd 5 | import numpy as np 6 | from glob import glob 7 | from tqdm import tqdm 8 | from IPython import embed 9 | import base64 10 | from labelme import utils 11 | image_path = "./images/" 12 | csv_file = "./train_labels.csv" 13 | annotations = pd.read_csv(csv_file,header=None).values 14 | total_csv_annotations = {} 15 | for annotation in annotations: 16 | key = annotation[0].split(os.sep)[-1] 17 | value = np.array([annotation[1:]]) 18 | if key in total_csv_annotations.keys(): 19 | total_csv_annotations[key] = np.concatenate((total_csv_annotations[key],value),axis=0) 20 | else: 21 | total_csv_annotations[key] = value 22 | for key,value in total_csv_annotations.items(): 23 | height,width = cv2.imread(image_path+key).shape[:2] 24 | labelme_format = { 25 | "version":"3.6.16", 26 | "flags":{}, 27 | "lineColor":[0,255,0,128], 28 | "fillColor":[255,0,0,128], 29 | "imagePath":key, 30 | "imageHeight":height, 31 | "imageWidth":width 32 | } 33 | with open(image_path+key,"rb") as f: 34 | imageData = f.read() 35 | imageData = base64.b64encode(imageData).decode('utf-8') 36 | #img = utils.img_b64_to_arr(imageData) 37 | labelme_format["imageData"] = imageData 38 | shapes = [] 39 | for shape in value: 40 | label = shape[-1] 41 | s = {"label":label,"line_color":None,"fill_color":None,"shape_type":"rectangle"} 42 | points = [ 43 | [shape[0],shape[1]], 44 | [shape[2],shape[3]] 45 | ] 46 | s["points"] = points 47 | shapes.append(s) 48 | labelme_format["shapes"] = shapes 49 | json.dump(labelme_format,open("%s/%s/"%(image_path,key.replace(".jpg",".json")),"w"),ensure_ascii=False, indent=2) -------------------------------------------------------------------------------- /csv2voc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import codecs 4 | import pandas as pd 5 | import json 6 | from glob import glob 7 | import cv2 8 | import shutil 9 | from sklearn.model_selection import train_test_split 10 | from IPython import embed 11 | #1.标签路径 12 | csv_file = "../csv/train_labels.csv" 13 | saved_path = "./VOCdevkit/VOC2007/" #保存路径 14 | image_save_path = "./JPEGImages/" 15 | image_raw_parh = "../csv/images/" 16 | #2.创建要求文件夹 17 | if not os.path.exists(saved_path + "Annotations"): 18 | os.makedirs(saved_path + "Annotations") 19 | if not os.path.exists(saved_path + "JPEGImages/"): 20 | os.makedirs(saved_path + "JPEGImages/") 21 | if not os.path.exists(saved_path + "ImageSets/Main/"): 22 | os.makedirs(saved_path + "ImageSets/Main/") 23 | 24 | #3.获取待处理文件 25 | total_csv_annotations = {} 26 | annotations = pd.read_csv(csv_file,header=None).values 27 | for annotation in annotations: 28 | key = annotation[0].split(os.sep)[-1] 29 | value = np.array([annotation[1:]]) 30 | if key in total_csv_annotations.keys(): 31 | total_csv_annotations[key] = np.concatenate((total_csv_annotations[key],value),axis=0) 32 | else: 33 | total_csv_annotations[key] = value 34 | 35 | #4.读取标注信息并写入 xml 36 | for filename,label in total_csv_annotations.items(): 37 | #embed() 38 | height, width, channels = cv2.imread(image_raw_parh + filename).shape 39 | #embed() 40 | with codecs.open(saved_path + "Annotations/"+filename.replace(".jpg",".xml"),"w","utf-8") as xml: 41 | xml.write('\n') 42 | xml.write('\t' + 'UAV_data' + '\n') 43 | xml.write('\t' + filename + '\n') 44 | xml.write('\t\n') 45 | xml.write('\t\tThe UAV autolanding\n') 46 | xml.write('\t\tUAV AutoLanding\n') 47 | xml.write('\t\tflickr\n') 48 | xml.write('\t\tNULL\n') 49 | xml.write('\t\n') 50 | xml.write('\t\n') 51 | xml.write('\t\tNULL\n') 52 | xml.write('\t\tChaojieZhu\n') 53 | xml.write('\t\n') 54 | xml.write('\t\n') 55 | xml.write('\t\t'+ str(width) + '\n') 56 | xml.write('\t\t'+ str(height) + '\n') 57 | xml.write('\t\t' + str(channels) + '\n') 58 | xml.write('\t\n') 59 | xml.write('\t\t0\n') 60 | if isinstance(label,float): 61 | ## 空白 62 | xml.write('') 63 | continue 64 | for label_detail in label: 65 | labels = label_detail 66 | #embed() 67 | xmin = int(labels[0]) 68 | ymin = int(labels[1]) 69 | xmax = int(labels[2]) 70 | ymax = int(labels[3]) 71 | label_ = labels[-1] 72 | if xmax <= xmin: 73 | pass 74 | elif ymax <= ymin: 75 | pass 76 | else: 77 | xml.write('\t\n') 78 | xml.write('\t\t'+label_+'\n') 79 | xml.write('\t\tUnspecified\n') 80 | xml.write('\t\t1\n') 81 | xml.write('\t\t0\n') 82 | xml.write('\t\t\n') 83 | xml.write('\t\t\t' + str(xmin) + '\n') 84 | xml.write('\t\t\t' + str(ymin) + '\n') 85 | xml.write('\t\t\t' + str(xmax) + '\n') 86 | xml.write('\t\t\t' + str(ymax) + '\n') 87 | xml.write('\t\t\n') 88 | xml.write('\t\n') 89 | print(filename,xmin,ymin,xmax,ymax,labels) 90 | xml.write('') 91 | 92 | 93 | #6.split files for txt 94 | txtsavepath = saved_path + "ImageSets/Main/" 95 | ftrainval = open(txtsavepath+'/trainval.txt', 'w') 96 | ftest = open(txtsavepath+'/test.txt', 'w') 97 | ftrain = open(txtsavepath+'/train.txt', 'w') 98 | fval = open(txtsavepath+'/val.txt', 'w') 99 | total_files = glob(saved_path+"./Annotations/*.xml") 100 | total_files = [i.split("/")[-1].split(".xml")[0] for i in total_files] 101 | #test_filepath = "" 102 | for file in total_files: 103 | ftrainval.write(file + "\n") 104 | 105 | # move images to voc JPEGImages folder 106 | for image in glob(image_raw_parh+"/*.jpg"): 107 | shutil.copy(image,saved_path+image_save_path) 108 | 109 | train_files,val_files = train_test_split(total_files,test_size=0.15,random_state=42) 110 | 111 | for file in train_files: 112 | ftrain.write(file + "\n") 113 | #val 114 | for file in val_files: 115 | fval.write(file + "\n") 116 | 117 | ftrainval.close() 118 | ftrain.close() 119 | fval.close() 120 | #ftest.close() -------------------------------------------------------------------------------- /labelme2coco.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import numpy as np 4 | import glob 5 | import shutil 6 | from sklearn.model_selection import train_test_split 7 | np.random.seed(41) 8 | 9 | #0为背景 10 | classname_to_id = {"person": 1} 11 | 12 | class Lableme2CoCo: 13 | 14 | def __init__(self): 15 | self.images = [] 16 | self.annotations = [] 17 | self.categories = [] 18 | self.img_id = 0 19 | self.ann_id = 0 20 | 21 | def save_coco_json(self, instance, save_path): 22 | json.dump(instance, open(save_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=1) # indent=2 更加美观显示 23 | 24 | # 由json文件构建COCO 25 | def to_coco(self, json_path_list): 26 | self._init_categories() 27 | for json_path in json_path_list: 28 | obj = self.read_jsonfile(json_path) 29 | self.images.append(self._image(obj, json_path)) 30 | shapes = obj['shapes'] 31 | for shape in shapes: 32 | annotation = self._annotation(shape) 33 | self.annotations.append(annotation) 34 | self.ann_id += 1 35 | self.img_id += 1 36 | instance = {} 37 | instance['info'] = 'spytensor created' 38 | instance['license'] = ['license'] 39 | instance['images'] = self.images 40 | instance['annotations'] = self.annotations 41 | instance['categories'] = self.categories 42 | return instance 43 | 44 | # 构建类别 45 | def _init_categories(self): 46 | for k, v in classname_to_id.items(): 47 | category = {} 48 | category['id'] = v 49 | category['name'] = k 50 | self.categories.append(category) 51 | 52 | # 构建COCO的image字段 53 | def _image(self, obj, path): 54 | image = {} 55 | from labelme import utils 56 | img_x = utils.img_b64_to_arr(obj['imageData']) 57 | h, w = img_x.shape[:-1] 58 | image['height'] = h 59 | image['width'] = w 60 | image['id'] = self.img_id 61 | image['file_name'] = os.path.basename(path).replace(".json", ".jpg") 62 | return image 63 | 64 | # 构建COCO的annotation字段 65 | def _annotation(self, shape): 66 | label = shape['label'] 67 | points = shape['points'] 68 | annotation = {} 69 | annotation['id'] = self.ann_id 70 | annotation['image_id'] = self.img_id 71 | annotation['category_id'] = int(classname_to_id[label]) 72 | annotation['segmentation'] = [np.asarray(points).flatten().tolist()] 73 | annotation['bbox'] = self._get_box(points) 74 | annotation['iscrowd'] = 0 75 | annotation['area'] = 1.0 76 | return annotation 77 | 78 | # 读取json文件,返回一个json对象 79 | def read_jsonfile(self, path): 80 | with open(path, "r", encoding='utf-8') as f: 81 | return json.load(f) 82 | 83 | # COCO的格式: [x1,y1,w,h] 对应COCO的bbox格式 84 | def _get_box(self, points): 85 | min_x = min_y = np.inf 86 | max_x = max_y = 0 87 | for x, y in points: 88 | min_x = min(min_x, x) 89 | min_y = min(min_y, y) 90 | max_x = max(max_x, x) 91 | max_y = max(max_y, y) 92 | return [min_x, min_y, max_x - min_x, max_y - min_y] 93 | 94 | 95 | if __name__ == '__main__': 96 | labelme_path = "labelme/" 97 | saved_coco_path = "./" 98 | # 创建文件 99 | if not os.path.exists("%scoco/annotations/"%saved_coco_path): 100 | os.makedirs("%scoco/annotations/"%saved_coco_path) 101 | if not os.path.exists("%scoco/images/train2017/"%saved_coco_path): 102 | os.makedirs("%scoco/images/train2017"%saved_coco_path) 103 | if not os.path.exists("%scoco/images/val2017/"%saved_coco_path): 104 | os.makedirs("%scoco/images/val2017"%saved_coco_path) 105 | # 获取images目录下所有的joson文件列表 106 | json_list_path = glob.glob(labelme_path + "/*.json") 107 | # 数据划分,这里没有区分val2017和tran2017目录,所有图片都放在images目录下 108 | train_path, val_path = train_test_split(json_list_path, test_size=0.12) 109 | print("train_n:", len(train_path), 'val_n:', len(val_path)) 110 | 111 | # 把训练集转化为COCO的json格式 112 | l2c_train = Lableme2CoCo() 113 | train_instance = l2c_train.to_coco(train_path) 114 | l2c_train.save_coco_json(train_instance, '%scoco/annotations/instances_train2017.json'%saved_coco_path) 115 | for file in train_path: 116 | shutil.copy(file.replace("json","jpg"),"%scoco/images/train2017/"%saved_coco_path) 117 | for file in val_path: 118 | shutil.copy(file.replace("json","jpg"),"%scoco/images/val2017/"%saved_coco_path) 119 | 120 | # 把验证集转化为COCO的json格式 121 | l2c_val = Lableme2CoCo() 122 | val_instance = l2c_val.to_coco(val_path) 123 | l2c_val.save_coco_json(val_instance, '%scoco/annotations/instances_val2017.json'%saved_coco_path) 124 | -------------------------------------------------------------------------------- /labelme2voc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import codecs 4 | import json 5 | from glob import glob 6 | import cv2 7 | import shutil 8 | from sklearn.model_selection import train_test_split 9 | #1.标签路径 10 | labelme_path = "./labelme/" #原始labelme标注数据路径 11 | saved_path = "./VOCdevkit/VOC2007/" #保存路径 12 | 13 | #2.创建要求文件夹 14 | if not os.path.exists(saved_path + "Annotations"): 15 | os.makedirs(saved_path + "Annotations") 16 | if not os.path.exists(saved_path + "JPEGImages/"): 17 | os.makedirs(saved_path + "JPEGImages/") 18 | if not os.path.exists(saved_path + "ImageSets/Main/"): 19 | os.makedirs(saved_path + "ImageSets/Main/") 20 | 21 | #3.获取待处理文件 22 | files = glob(labelme_path + "*.json") 23 | files = [i.split("/")[-1].split(".json")[0] for i in files] 24 | 25 | #4.读取标注信息并写入 xml 26 | for json_file_ in files: 27 | json_filename = labelme_path + json_file_ + ".json" 28 | json_file = json.load(open(json_filename,"r",encoding="utf-8")) 29 | height, width, channels = cv2.imread(labelme_path + json_file_ +".jpg").shape 30 | with codecs.open(saved_path + "Annotations/"+json_file_ + ".xml","w","utf-8") as xml: 31 | xml.write('\n') 32 | xml.write('\t' + 'UAV_data' + '\n') 33 | xml.write('\t' + json_file_ + ".jpg" + '\n') 34 | xml.write('\t\n') 35 | xml.write('\t\tThe UAV autolanding\n') 36 | xml.write('\t\tUAV AutoLanding\n') 37 | xml.write('\t\tflickr\n') 38 | xml.write('\t\tNULL\n') 39 | xml.write('\t\n') 40 | xml.write('\t\n') 41 | xml.write('\t\tNULL\n') 42 | xml.write('\t\tChaojieZhu\n') 43 | xml.write('\t\n') 44 | xml.write('\t\n') 45 | xml.write('\t\t'+ str(width) + '\n') 46 | xml.write('\t\t'+ str(height) + '\n') 47 | xml.write('\t\t' + str(channels) + '\n') 48 | xml.write('\t\n') 49 | xml.write('\t\t0\n') 50 | for multi in json_file["shapes"]: 51 | points = np.array(multi["points"]) 52 | xmin = min(points[:,0]) 53 | xmax = max(points[:,0]) 54 | ymin = min(points[:,1]) 55 | ymax = max(points[:,1]) 56 | label = multi["label"] 57 | if xmax <= xmin: 58 | pass 59 | elif ymax <= ymin: 60 | pass 61 | else: 62 | xml.write('\t\n') 63 | xml.write('\t\t'+label+'\n') 64 | xml.write('\t\tUnspecified\n') 65 | xml.write('\t\t1\n') 66 | xml.write('\t\t0\n') 67 | xml.write('\t\t\n') 68 | xml.write('\t\t\t' + str(xmin) + '\n') 69 | xml.write('\t\t\t' + str(ymin) + '\n') 70 | xml.write('\t\t\t' + str(xmax) + '\n') 71 | xml.write('\t\t\t' + str(ymax) + '\n') 72 | xml.write('\t\t\n') 73 | xml.write('\t\n') 74 | print(json_filename,xmin,ymin,xmax,ymax,label) 75 | xml.write('') 76 | 77 | #5.复制图片到 VOC2007/JPEGImages/下 78 | image_files = glob(labelme_path + "*.jpg") 79 | print("copy image files to VOC007/JPEGImages/") 80 | for image in image_files: 81 | shutil.copy(image,saved_path +"JPEGImages/") 82 | 83 | #6.split files for txt 84 | txtsavepath = saved_path + "ImageSets/Main/" 85 | ftrainval = open(txtsavepath+'/trainval.txt', 'w') 86 | ftest = open(txtsavepath+'/test.txt', 'w') 87 | ftrain = open(txtsavepath+'/train.txt', 'w') 88 | fval = open(txtsavepath+'/val.txt', 'w') 89 | total_files = glob("./VOC2007/Annotations/*.xml") 90 | total_files = [i.split("/")[-1].split(".xml")[0] for i in total_files] 91 | #test_filepath = "" 92 | for file in total_files: 93 | ftrainval.write(file + "\n") 94 | #test 95 | #for file in os.listdir(test_filepath): 96 | # ftest.write(file.split(".jpg")[0] + "\n") 97 | #split 98 | train_files,val_files = train_test_split(total_files,test_size=0.15,random_state=42) 99 | #train 100 | for file in train_files: 101 | ftrain.write(file + "\n") 102 | #val 103 | for file in val_files: 104 | fval.write(file + "\n") 105 | 106 | ftrainval.close() 107 | ftrain.close() 108 | fval.close() 109 | #ftest.close() 110 | --------------------------------------------------------------------------------