Skip to content

Commit 703a34c

Browse files
committed
first version
1 parent 5cc4397 commit 703a34c

File tree

5 files changed

+736
-2
lines changed

5 files changed

+736
-2
lines changed

README.md

Lines changed: 235 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,235 @@
1-
# prepare_detection_dataset
2-
convert dataset to coco/voc format
1+
**背景**
2+
3+
万事开头难。之前写图像识别的博客教程,也是为了方便那些学了很多理论知识,却对实际项目无从下手的小伙伴,后来转到目标检测来了,师从烨兄、亚光兄,从他们那学了不少检测的知识和操作,今天也终于闲下了,准备写个检测系列的总结。一方面分享知识希望可以一起学习,另一方面让一部分人少走弯路,快速上路(入坑)。
4+
5+
此部分代码:[Github](https://github.com/spytensor/prepare_detection_dataset)
6+
7+
<h4 id="1">1. 内容介绍</h4>
8+
9+
系列一主要介绍如何在常见的几种数据格式之间进行转换,以及万能中介`csv`格式的使用,这里列出以下几个:
10+
11+
- csv to coco
12+
- csv to voc
13+
- labelme to coco
14+
- labelme to voc
15+
16+
<h4 id="2">2. 标准格式</h4>
17+
18+
在使用转换脚本之前,必须要明确的几种格式
19+
20+
<h5 id="2.1">2.1 csv</h5>
21+
22+
不要一看是`csv`文件就直接拿来运行,如果不是,可以自行修改代码,或者修改标注文件。
23+
24+
转换脚本支持的csv格式应为以下形式:
25+
26+
- `csv/`
27+
- `labels.csv`
28+
- `images/`
29+
- `image1.jpg`
30+
- `image2.jpg`
31+
- `...`
32+
33+
`labels.csv` 的形式:
34+
35+
`/path/to/image,xmin,ymin,xmax,ymax,label`
36+
37+
例如:
38+
39+
```
40+
/mfs/dataset/face/0d4c5e4f-fc3c-4d5a-906c-105.jpg,450,154,754,341,face
41+
/mfs/dataset/face/0ddfc5aea-fcdac-421-92dad-144.jpg,143,154,344,341,face
42+
...
43+
```
44+
注:图片路径请使用绝对路径
45+
46+
<h5 id="2.2">2.2 voc</h5>
47+
48+
标准的voc数据格式如下:
49+
50+
- `VOC2007/`
51+
- `Annotations/`
52+
- `0d4c5e4f-fc3c-4d5a-906c-105.xml`
53+
- `0ddfc5aea-fcdac-421-92dad-144/xml`
54+
- `...`
55+
- `ImageSets/`
56+
- `Main/`
57+
- `train.txt`
58+
- `test.txt`
59+
- `val.txt`
60+
- `trainval.txt`
61+
- `JPEGImages/`
62+
- `0d4c5e4f-fc3c-4d5a-906c-105.jpg`
63+
- `0ddfc5aea-fcdac-421-92dad-144.jpg`
64+
- `...`
65+
66+
<h5 id="2.3">2.3 coco</h5>
67+
68+
此处未使用测试集
69+
70+
- `coco/`
71+
- `annotations/`
72+
- `instances_train2017.json`
73+
- `instances_val2017.json`
74+
- `images/`
75+
- `train2017/`
76+
- `0d4c5e4f-fc3c-4d5a-906c-105.jpg`
77+
- `...`
78+
- `val2017`
79+
- `0ddfc5aea-fcdac-421-92dad-144.jpg`
80+
- `...`
81+
82+
<h5 id="2.4">2.4 labelme</h5>
83+
84+
85+
- `labelme/`
86+
- `0d4c5e4f-fc3c-4d5a-906c-105.json`
87+
- `0d4c5e4f-fc3c-4d5a-906c-105.jpg`
88+
- `0ddfc5aea-fcdac-421-92dad-144.json`
89+
- `0ddfc5aea-fcdac-421-92dad-144.jpg`
90+
91+
Json file 格式:
92+
(imageData那一块太长了,不展示了)
93+
94+
```json
95+
{
96+
"version": "3.6.16",
97+
"flags": {},
98+
"shapes": [
99+
{
100+
"label": "helmet",
101+
"line_color": null,
102+
"fill_color": null,
103+
"points": [
104+
[
105+
131,
106+
269
107+
],
108+
[
109+
388,
110+
457
111+
]
112+
],
113+
"shape_type": "rectangle"
114+
}
115+
],
116+
"lineColor": [
117+
0,
118+
255,
119+
0,
120+
128
121+
],
122+
"fillColor": [
123+
255,
124+
0,
125+
0,
126+
128
127+
],
128+
"imagePath": "004ffe6f-c3e2-3602-84a1-ecd5f437b113.jpg",
129+
"imageData": "" # too long ,so not show here
130+
"imageHeight": 1080,
131+
"imageWidth": 1920
132+
}
133+
```
134+
135+
<h4 id="3">3. 如何使用转换脚本</h4>
136+
137+
<h5 id="3.1">3.1 csv2coco</h5>
138+
139+
首先更改`csv2coco.py`中以下几个配置
140+
141+
```
142+
classname_to_id = {"person": 1} # for your dataset classes
143+
csv_file = "labels.csv" # annatations file path
144+
image_dir = "images/" # original image path
145+
saved_coco_path = "./" # path to save converted coco dataset
146+
```
147+
148+
然后运行 `python csv2coco.py`
149+
150+
会自动创建文件夹并复制图片到相应位置,运行结束后得到如下:
151+
152+
- `coco/`
153+
- `annotations/`
154+
- `instances_train2017.json`
155+
- `instances_val2017.json`
156+
- `images/`
157+
- `train2017/`
158+
- `0d4c5e4f-fc3c-4d5a-906c-105.jpg`
159+
- `...`
160+
- `val2017`
161+
- `0ddfc5aea-fcdac-421-92dad-144.jpg`
162+
- `...`
163+
164+
<h5 id="3.2">3.2 csv2voc</h5>
165+
166+
首先更改`csv2voc.py`中以下几个配置
167+
168+
```
169+
csv_file = "labels.csv"
170+
saved_path = ".VOC2007/" # path to save converted voc dataset
171+
image_save_path = "./JPEGImages/" # converted voc images path
172+
image_raw_parh = "images/" # original image path
173+
```
174+
175+
然后运行 `python csv2voc.py`
176+
177+
同样会自动创建文件夹并复制图片到相应位置,运行结束后得到如下:
178+
179+
180+
- `VOC2007/`
181+
- `Annotations/`
182+
- `0d4c5e4f-fc3c-4d5a-906c-105.xml`
183+
- `0ddfc5aea-fcdac-421-92dad-144/xml`
184+
- `...`
185+
- `ImageSets/`
186+
- `Main/`
187+
- `train.txt`
188+
- `test.txt`
189+
- `val.txt`
190+
- `trainval.txt`
191+
- `JPEGImages/`
192+
- `0d4c5e4f-fc3c-4d5a-906c-105.jpg`
193+
- `0ddfc5aea-fcdac-421-92dad-144.jpg`
194+
- `...`
195+
196+
<h5 id="3.3">3.3 labelme2coco</h5>
197+
198+
首先更改`labelme2coco.py`中以下几个配置
199+
200+
```
201+
classname_to_id = {"person": 1} # for your dataset classes
202+
labelme_path = "labelme/" # path for labelme dataset
203+
saved_coco_path = "./" # path for saved coco dataset
204+
```
205+
然后运行 `python labelme2coco.py`,生成文件形式同`csv2coco`
206+
207+
<h5 id="3.4">3.4 labelme2voc</h5>
208+
209+
首先更改`labelme2voc.py`中以下几个配置
210+
211+
```
212+
labelme_path = "labelme/" # path for labelme dataset
213+
saved_coco_path = "./" # path for saved coco dataset
214+
```
215+
然后运行 `python labelme2voc.py`,生成文件形式同`csv2voc`
216+
217+
<h4 id="4">4. 万能中介csv</h4>
218+
219+
从上面的转换格式中可以看出,并没有给出如何转到csv的,一是因为太过于简单,而是主流检测框架很少支持这种格式的数据输入。以下给出如何将标注信息写入`csv`
220+
221+
```python
222+
info = [[filename0,"xmin ymin xmax ymax label0"],
223+
filename1,"xmin ymin xmax ymax label1"]
224+
csv_labels = open("csv_labels.csv","w")
225+
for filename,bboxes in info:
226+
bbox = bboxes.split(" ")
227+
label = bbox[-1]
228+
csv_labels.write(filename+","+bbox[0]+","+bbox[1]+","+bbox[2]+","+bbox[3]+","+label+"\n")
229+
csv_labels.close()
230+
```
231+
232+
是不是非常简单。。。如果你不知道如何从原始的标签文件中读取得到标注信息,那没办法了,学学编程吧,23333
233+
234+
<h4 id="5">5. 下一篇</h4>
235+
如何做数据扩充

csv2coco.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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'] = 1.0
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+
# segmentation
100+
def _get_seg(self, points):
101+
min_x = points[0]
102+
min_y = points[1]
103+
max_x = points[2]
104+
max_y = points[3]
105+
h = max_y - min_y
106+
w = max_x - min_x
107+
a = []
108+
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])
109+
return a
110+
111+
112+
if __name__ == '__main__':
113+
csv_file = "train.csv"
114+
image_dir = "images/"
115+
saved_coco_path = "./"
116+
# 整合csv格式标注文件
117+
total_csv_annotations = {}
118+
annotations = pd.read_csv(csv_file,header=None).values
119+
for annotation in annotations:
120+
key = annotation[0].split(os.sep)[-1]
121+
value = np.array([annotation[1:]])
122+
if key in total_csv_annotations.keys():
123+
total_csv_annotations[key] = np.concatenate((total_csv_annotations[key],value),axis=0)
124+
else:
125+
total_csv_annotations[key] = value
126+
# 按照键值划分数据
127+
total_keys = list(total_csv_annotations.keys())
128+
train_keys, val_keys = train_test_split(total_keys, test_size=0.2)
129+
print("train_n:", len(train_keys), 'val_n:', len(val_keys))
130+
# 创建必须的文件夹
131+
if not os.path.exists('%scoco/annotations/'%saved_coco_path):
132+
os.makedirs('%scoco/annotations/'%saved_coco_path)
133+
if not os.path.exists('%scoco/images/train2017/'%saved_coco_path):
134+
os.makedirs('%scoco/images/train2017/'%saved_coco_path)
135+
if not os.path.exists('%scoco/images/val2017/'%saved_coco_path):
136+
os.makedirs('%scoco/images/val2017/'%saved_coco_path)
137+
# 把训练集转化为COCO的json格式
138+
l2c_train = Csv2CoCo(image_dir=image_dir,total_annos=total_csv_annotations)
139+
train_instance = l2c_train.to_coco(train_keys)
140+
l2c_train.save_coco_json(train_instance, '%scoco/annotations/instances_train2017.json'%saved_coco_path)
141+
for file in train_keys:
142+
shutil.copy(image_dir+file,"%scoco/images/train2017/"%saved_coco_path)
143+
for file in val_keys:
144+
shutil.copy(image_dir+file,"%scoco/images/val2017/"%saved_coco_path)
145+
# 把验证集转化为COCO的json格式
146+
l2c_val = Csv2CoCo(image_dir=image_dir,total_annos=total_csv_annotations)
147+
val_instance = l2c_val.to_coco(val_keys)
148+
l2c_val.save_coco_json(val_instance, '%scoco/annotations/instances_val2017.json'%saved_coco_path)
149+

0 commit comments

Comments
 (0)