├── .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')
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')
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 |
--------------------------------------------------------------------------------