stereoid commited on
Commit
52474be
·
verified ·
1 Parent(s): e5a55f8

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. approach/ovod/d-cube/.assets/d-cube_logo.png +0 -0
  2. approach/ovod/d-cube/d_cube/__init__.py +1 -0
  3. approach/ovod/d-cube/d_cube/d3.py +775 -0
  4. approach/ovod/d-cube/d_cube/data_util.py +269 -0
  5. approach/ovod/d-cube/d_cube/vis_util.py +199 -0
  6. approach/ovod/d-cube/eval_sota/README.md +27 -0
  7. approach/ovod/d-cube/eval_sota/groundingdino.py +304 -0
  8. approach/ovod/d-cube/eval_sota/owl_vit.py +192 -0
  9. approach/ovod/d-cube/eval_sota/sphinx.py +184 -0
  10. approach/ovod/d-cube/scripts/eval_and_analysis_json.py +190 -0
  11. approach/ovod/d-cube/scripts/eval_json_example.py +16 -0
  12. approach/ovod/d-cube/scripts/get_d3_stat.py +98 -0
  13. approach/ovod/detectron2/.circleci/config.yml +270 -0
  14. approach/ovod/detectron2/.circleci/import-tests.sh +16 -0
  15. approach/ovod/detectron2/.github/CODE_OF_CONDUCT.md +5 -0
  16. approach/ovod/detectron2/.github/CONTRIBUTING.md +68 -0
  17. approach/ovod/detectron2/.github/Detectron2-Logo-Horz.svg +1 -0
  18. approach/ovod/detectron2/.github/ISSUE_TEMPLATE.md +5 -0
  19. approach/ovod/detectron2/.github/pull_request_template.md +10 -0
  20. approach/ovod/detectron2/configs/Base-RCNN-C4.yaml +18 -0
  21. approach/ovod/detectron2/configs/Base-RCNN-DilatedC5.yaml +31 -0
  22. approach/ovod/detectron2/configs/Base-RCNN-FPN.yaml +42 -0
  23. approach/ovod/detectron2/configs/Base-RetinaNet.yaml +25 -0
  24. approach/ovod/detectron2/demo/README.md +8 -0
  25. approach/ovod/detectron2/demo/demo.py +188 -0
  26. approach/ovod/detectron2/demo/predictor.py +220 -0
  27. approach/ovod/detectron2/detectron2/__init__.py +10 -0
  28. approach/ovod/detectron2/dev/README.md +7 -0
  29. approach/ovod/detectron2/dev/linter.sh +42 -0
  30. approach/ovod/detectron2/dev/parse_results.sh +45 -0
  31. approach/ovod/detectron2/dev/run_inference_tests.sh +44 -0
  32. approach/ovod/detectron2/dev/run_instant_tests.sh +27 -0
  33. approach/ovod/detectron2/docs/.gitignore +1 -0
  34. approach/ovod/detectron2/docs/Makefile +19 -0
  35. approach/ovod/detectron2/docs/README.md +15 -0
  36. approach/ovod/detectron2/docs/conf.py +391 -0
  37. approach/ovod/detectron2/docs/index.rst +14 -0
  38. approach/ovod/detectron2/docs/notes/benchmarks.md +196 -0
  39. approach/ovod/detectron2/docs/notes/changelog.md +48 -0
  40. approach/ovod/detectron2/docs/notes/compatibility.md +84 -0
  41. approach/ovod/detectron2/docs/notes/contributing.md +68 -0
  42. approach/ovod/detectron2/docs/notes/index.rst +10 -0
  43. approach/ovod/detectron2/docs/requirements.txt +24 -0
  44. approach/ovod/detectron2/docs/tutorials/README.md +4 -0
  45. approach/ovod/detectron2/docs/tutorials/builtin_datasets.md +140 -0
  46. approach/ovod/detectron2/docs/tutorials/configs.md +62 -0
  47. approach/ovod/detectron2/docs/tutorials/data_loading.md +95 -0
  48. approach/ovod/detectron2/docs/tutorials/datasets.md +290 -0
  49. approach/ovod/detectron2/docs/tutorials/deployment.md +137 -0
  50. approach/ovod/detectron2/docs/tutorials/extend.md +141 -0
approach/ovod/d-cube/.assets/d-cube_logo.png ADDED
approach/ovod/d-cube/d_cube/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .d3 import D3
approach/ovod/d-cube/d_cube/d3.py ADDED
@@ -0,0 +1,775 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie and Zhao Zhang"
3
+ __maintainer__ = "Chi Xie"
4
+ # this is the core of the d-cube toolkit
5
+ import os
6
+ import os.path as osp
7
+ import json
8
+ from collections import defaultdict
9
+
10
+ import numpy as np
11
+ from pycocotools import mask
12
+ import cv2
13
+ import matplotlib.pyplot as plt
14
+
15
+
16
+ from .data_util import *
17
+
18
+
19
+ class D3:
20
+ def __init__(self, img_root, anno_root):
21
+ self.image_dir = img_root
22
+ self.anno_dir = anno_root
23
+ self.load_data()
24
+
25
+ def load_data(self):
26
+ file_names = ["sentences.pkl", "annotations.pkl", "images.pkl", "groups.pkl"]
27
+ self.data = {
28
+ name.split(".")[0]: load_pkl(osp.join(self.anno_dir, name))
29
+ for name in file_names
30
+ }
31
+
32
+ def get_sent_ids(self, anno_ids=[], img_ids=[], group_ids=[], sent_ids=[]):
33
+ """get sentence ids for D-cube.
34
+
35
+ Args:
36
+ anno_ids (list, optional): annotation ids to get sentence ids. Defaults to [].
37
+ img_ids (list, optional): image ids to get sentence ids. Defaults to [].
38
+ group_ids (list, optional): group ids to get sentence ids. Defaults to [].
39
+ sent_ids (list, optional): additional sentence ids you want to include. Defaults to [].
40
+
41
+ Raises:
42
+ Exception: anno_ids, img_ids and group_ids cannot be used together.
43
+
44
+ Returns:
45
+ list: sentence ids.
46
+ """
47
+ img_ids = img_ids if isinstance(img_ids, list) else [img_ids]
48
+ anno_ids = anno_ids if isinstance(anno_ids, list) else [anno_ids]
49
+ group_ids = group_ids if isinstance(group_ids, list) else [group_ids]
50
+ sent_ids = sent_ids if isinstance(sent_ids, list) else [sent_ids]
51
+
52
+ if not any([img_ids, anno_ids, group_ids, sent_ids]):
53
+ return list(self.data["sentences"].keys())
54
+
55
+ if (
56
+ (anno_ids and img_ids)
57
+ or (anno_ids and group_ids)
58
+ or (img_ids and group_ids)
59
+ ):
60
+ raise Exception("anno_ids, img_ids, group_ids can only be used alone")
61
+
62
+ out_ids_set = set()
63
+ if img_ids:
64
+ for img_id in img_ids:
65
+ imganno_ids = self.data["images"][img_id]["anno_id"]
66
+ for ianno_id in imganno_ids:
67
+ out_ids_set |= set(self.data["annotations"][ianno_id]["sent_id"])
68
+
69
+ if group_ids:
70
+ for group_id in group_ids:
71
+ out_ids_set |= set(self.data["groups"][group_id]["inner_sent_id"])
72
+
73
+ if anno_ids:
74
+ for ianno_id in anno_ids:
75
+ out_ids_set |= set(self.data["annotations"][ianno_id]["sent_id"])
76
+
77
+ if sent_ids:
78
+ out_ids_set &= set(sent_ids)
79
+
80
+ return list(out_ids_set)
81
+
82
+ def get_anno_ids(self, anno_ids=[], img_ids=[], group_ids=[], sent_ids=[]):
83
+ """get annotation ids for D-cube.
84
+
85
+ Args:
86
+ anno_ids (list, optional): additional annotation ids you want to include. Defaults to [].
87
+ img_ids (list, optional): image ids to get annotation ids. Defaults to [].
88
+ group_ids (list, optional): group ids to get annotation ids. Defaults to [].
89
+ sent_ids (list, optional): sentence ids to get annotation ids. Defaults to [].
90
+
91
+ Raises:
92
+ Exception: img_ids and group_ids cannot be used together.
93
+
94
+ Returns:
95
+ list: annotation ids.
96
+ """
97
+ img_ids = img_ids if isinstance(img_ids, list) else [img_ids]
98
+ anno_ids = anno_ids if isinstance(anno_ids, list) else [anno_ids]
99
+ group_ids = group_ids if isinstance(group_ids, list) else [group_ids]
100
+ sent_ids = sent_ids if isinstance(sent_ids, list) else [sent_ids]
101
+
102
+ if not any([img_ids, anno_ids, group_ids, sent_ids]):
103
+ return list(self.data["annotations"].keys())
104
+
105
+ if img_ids and group_ids:
106
+ raise Exception("img_ids, group_ids can only be used alone")
107
+
108
+ out_ids_set = set()
109
+ if img_ids:
110
+ for img_id in img_ids:
111
+ out_ids_set |= set(self.data["images"][img_id]["anno_id"])
112
+
113
+ if group_ids:
114
+ for group_id in group_ids:
115
+ for groupimg_id in self.data["groups"][group_id]["img_id"]:
116
+ out_ids_set |= set(self.data["images"][groupimg_id]["anno_id"])
117
+
118
+ if sent_ids and img_ids:
119
+ for sent_id in sent_ids:
120
+ out_ids_set &= set(self.data["sentences"][sent_id]["anno_id"])
121
+ else:
122
+ for sent_id in sent_ids:
123
+ out_ids_set |= set(self.data["sentences"][sent_id]["anno_id"])
124
+
125
+ if anno_ids:
126
+ out_ids_set &= set(anno_ids)
127
+
128
+ return list(out_ids_set)
129
+
130
+ def get_img_ids(self, anno_ids=[], img_ids=[], group_ids=[], sent_ids=[]):
131
+ """get image ids for D-cube.
132
+
133
+ Args:
134
+ anno_ids (list, optional): annotation ids to get image ids. Defaults to [].
135
+ img_ids (list, optional): additional image ids you want to include. Defaults to [].
136
+ group_ids (list, optional): group ids to get image ids. Defaults to [].
137
+ sent_ids (list, optional): sentence ids to get image ids. Defaults to [].
138
+
139
+ Raises:
140
+ Exception: anno_ids and img_ids cannot be used together.
141
+ Exception: anno_ids and group_ids cannot be used together.
142
+
143
+ Returns:
144
+ list: image ids.
145
+ """
146
+ img_ids = img_ids if isinstance(img_ids, list) else [img_ids]
147
+ anno_ids = anno_ids if isinstance(anno_ids, list) else [anno_ids]
148
+ group_ids = group_ids if isinstance(group_ids, list) else [group_ids]
149
+ sent_ids = sent_ids if isinstance(sent_ids, list) else [sent_ids]
150
+
151
+ if not any([img_ids, anno_ids, group_ids, sent_ids]):
152
+ return list(self.data["images"].keys())
153
+
154
+ if anno_ids and img_ids:
155
+ raise Exception("anno_ids and img_ids can only be used alone")
156
+ if anno_ids and group_ids:
157
+ raise Exception("anno_ids and group_ids can only be used alone")
158
+
159
+ out_ids_set = set()
160
+ if anno_ids:
161
+ for ianno_id in anno_ids:
162
+ out_ids_set.add(self.data["annotations"][ianno_id]["img_id"])
163
+
164
+ if group_ids:
165
+ for group_id in group_ids:
166
+ out_ids_set |= set(self.data["groups"][group_id]["img_id"])
167
+
168
+ if sent_ids:
169
+ for sent_id in sent_ids:
170
+ for sentanno_id in self.data["sentences"][sent_id]["anno_id"]:
171
+ out_ids_set.add(self.data["annotations"][sentanno_id]["image_id"])
172
+
173
+ if img_ids:
174
+ out_ids_set &= set(img_ids)
175
+
176
+ return list(out_ids_set)
177
+
178
+ def get_group_ids(self, anno_ids=[], img_ids=[], group_ids=[], sent_ids=[]):
179
+ """get group ids for D-cube.
180
+
181
+ Args:
182
+ anno_ids (list, optional): annotation ids to get group ids. Defaults to [].
183
+ img_ids (list, optional): image ids to get group ids. Defaults to [].
184
+ group_ids (list, optional): additional group_ids you want to include. Defaults to [].
185
+ sent_ids (list, optional): sentence ids to get group ids. Defaults to [].
186
+
187
+ Raises:
188
+ Exception: anno_ids, img_ids and sent_ids cannot be used together.
189
+
190
+ Returns:
191
+ list: group ids.
192
+ """
193
+ img_ids = img_ids if isinstance(img_ids, list) else [img_ids]
194
+ anno_ids = anno_ids if isinstance(anno_ids, list) else [anno_ids]
195
+ group_ids = group_ids if isinstance(group_ids, list) else [group_ids]
196
+ sent_ids = sent_ids if isinstance(sent_ids, list) else [sent_ids]
197
+
198
+ if not any([img_ids, anno_ids, group_ids, sent_ids]):
199
+ return list(self.data["groups"].keys())
200
+
201
+ if anno_ids and img_ids:
202
+ raise Exception("anno_ids and img_ids can only be used alone")
203
+ if anno_ids and sent_ids:
204
+ raise Exception("anno_ids and sent_ids can only be used alone")
205
+ if img_ids and sent_ids:
206
+ raise Exception("img_ids and sent_ids can only be used alone")
207
+
208
+ out_ids_set = set()
209
+ if img_ids:
210
+ for img_id in img_ids:
211
+ out_ids_set.add(self.data["images"][img_id]["group_id"])
212
+
213
+ if anno_ids:
214
+ for anno_id in anno_ids:
215
+ out_ids_set.add(self.data["annotations"][anno_id]["group_id"])
216
+
217
+ if sent_ids:
218
+ for sent_id in sent_ids:
219
+ out_ids_set |= set(self.data["sentences"][sent_id]["group_id"])
220
+
221
+ if group_ids:
222
+ out_ids_set &= set(group_ids)
223
+
224
+ return list(out_ids_set)
225
+
226
+ def load_sents(self, sent_ids=None):
227
+ """load sentence info.
228
+
229
+ Args:
230
+ sent_ids (list, int, optional): sentence ids. Defaults to None.
231
+
232
+ Returns:
233
+ list: a list of sentence info.
234
+ """
235
+ if sent_ids is not None and not isinstance(sent_ids, list):
236
+ sent_ids = [sent_ids]
237
+ if isinstance(sent_ids, list):
238
+ return [self.data["sentences"][sent_id] for sent_id in sent_ids]
239
+ else:
240
+ return list(self.data["sentences"].values())
241
+
242
+ def load_annos(self, anno_ids=None):
243
+ """load annotation info.
244
+
245
+ Args:
246
+ anno_ids (list, int, optional): annotation ids. Defaults to None.
247
+
248
+ Returns:
249
+ list: a list of annotation info.
250
+ """
251
+ if anno_ids is not None and not isinstance(anno_ids, list):
252
+ anno_ids = [anno_ids]
253
+ if isinstance(anno_ids, list):
254
+ return [self.data["annotations"][anno_id] for anno_id in anno_ids]
255
+ else:
256
+ return list(self.data["annotations"].values())
257
+
258
+ def load_imgs(self, img_ids=None):
259
+ """load image info.
260
+
261
+ Args:
262
+ img_ids (list, int, optional): image ids. Defaults to None.
263
+
264
+ Returns:
265
+ list: a list of image info.
266
+ """
267
+ if img_ids is not None and not isinstance(img_ids, list):
268
+ img_ids = [img_ids]
269
+ if isinstance(img_ids, list):
270
+ return [self.data["images"][img_ids] for img_ids in img_ids]
271
+ else:
272
+ return list(self.data["images"].values())
273
+
274
+ def load_groups(self, group_ids=None):
275
+ """load group info.
276
+
277
+ Args:
278
+ group_ids (list, int, optional): group ids. Defaults to None.
279
+
280
+ Returns:
281
+ list: a list of group info.
282
+ """
283
+ if group_ids is not None and not isinstance(group_ids, list):
284
+ group_ids = [group_ids]
285
+ if isinstance(group_ids, list):
286
+ return [self.data["groups"][group_ids] for group_ids in group_ids]
287
+ else:
288
+ return list(self.data["groups"].values())
289
+
290
+ def get_mask(self, anno):
291
+ rle = anno[0]["segmentation"]
292
+ m = mask.decode(rle)
293
+ m = np.sum(
294
+ m, axis=2
295
+ ) # sometimes there are multiple binary map (corresponding to multiple segs)
296
+ m = m.astype(np.uint8) # convert to np.uint8
297
+ # compute area
298
+ area = sum(mask.area(rle)) # should be close to ann['area']
299
+ return {"mask": m, "area": area}
300
+
301
+ def show_mask(self, anno):
302
+ M = self.get_mask(anno)
303
+ msk = M["mask"]
304
+ ax = plt.gca()
305
+ ax.imshow(msk)
306
+
307
+ def show_image_seg(
308
+ self,
309
+ img_ids=[],
310
+ save_dir=None,
311
+ show_sent=False,
312
+ on_image=False,
313
+ checkerboard_bg=False,
314
+ is_instance=True,
315
+ ):
316
+ if is_instance and checkerboard_bg:
317
+ raise ValueError(
318
+ "Cannot apply both is_instance and checkboard_bg at the same time."
319
+ )
320
+ img_infos = self.load_imgs(img_ids=img_ids)
321
+ for img_idx, img_info in enumerate(img_infos):
322
+ img = cv2.imread(osp.join(self.image_dir, img_info["file_name"]))
323
+ anno_infos = self.load_annos(img_info["anno_id"])
324
+
325
+ bm_canvas = defaultdict(list)
326
+ merge_canvas = defaultdict(list)
327
+ for anno_info in anno_infos:
328
+ for sent_id in anno_info["sent_id"]:
329
+ bm_canvas[sent_id].append(anno_info["segmentation"])
330
+
331
+ for sent_id, bm_list in bm_canvas.items():
332
+ merge_canvas[sent_id] = merge_rle(
333
+ bm_list, is_instance=is_instance, on_image=on_image
334
+ )
335
+
336
+ cv2.imwrite(osp.join(save_dir, f"{img_info['id']}.png"), img)
337
+ for sent_id, merge_mask in merge_canvas.items():
338
+ if checkerboard_bg:
339
+ merge_mask = add_checkerboard_bg(img, merge_mask)
340
+ elif on_image:
341
+ merge_mask = visualize_mask_on_image(img, merge_mask, add_edge=True)
342
+ if show_sent:
343
+ sent_en = self.load_sents(sent_ids=sent_id)[0]["raw_sent"]
344
+ merge_mask = paste_text(merge_mask, sent_en)
345
+ cv2.imwrite(
346
+ osp.join(save_dir, f"{img_info['id']}_{sent_id}.png"), merge_mask
347
+ )
348
+
349
+ return merge_canvas
350
+
351
+ def show_group_seg(
352
+ self,
353
+ group_ids,
354
+ save_root,
355
+ show_sent=True,
356
+ is_instance=True,
357
+ on_image=False,
358
+ checkerboard_bg=False,
359
+ ):
360
+ group_infos = self.load_groups(group_ids=group_ids)
361
+ for group_info in group_infos:
362
+ save_dir = osp.join(save_root, group_info["group_name"])
363
+ os.makedirs(save_dir, exist_ok=True)
364
+ self.show_image_seg(
365
+ img_ids=group_info["img_id"],
366
+ save_dir=save_dir,
367
+ show_sent=show_sent,
368
+ is_instance=is_instance,
369
+ on_image=on_image,
370
+ checkerboard_bg=checkerboard_bg,
371
+ )
372
+
373
+ def show_image_seg_bbox(
374
+ self,
375
+ img_ids=[],
376
+ save_dir=None,
377
+ show_sent=False,
378
+ on_image=False,
379
+ checkerboard_bg=False,
380
+ is_instance=True,
381
+ ):
382
+ if is_instance and checkerboard_bg:
383
+ raise ValueError(
384
+ "Cannot apply both is_instance and checkboard_bg at the same time."
385
+ )
386
+ img_infos = self.load_imgs(img_ids=img_ids)
387
+ for img_idx, img_info in enumerate(img_infos):
388
+ img = cv2.imread(osp.join(self.image_dir, img_info["file_name"]))
389
+ anno_infos = self.load_annos(img_info["anno_id"])
390
+
391
+ bm_canvas = defaultdict(list)
392
+ merge_canvas = defaultdict(list)
393
+ sent_boxes = defaultdict(list)
394
+ for anno_info in anno_infos:
395
+ for sent_id in anno_info["sent_id"]:
396
+ bm_canvas[sent_id].append(anno_info["segmentation"])
397
+ sent_boxes[sent_id].append(anno_info["bbox"][0].tolist())
398
+
399
+ for sent_id, bm_list in bm_canvas.items():
400
+ merge_canvas[sent_id] = merge_rle(
401
+ bm_list, is_instance=is_instance, on_image=on_image
402
+ )
403
+
404
+ cv2.imwrite(osp.join(save_dir, f"{img_info['id']}.png"), img)
405
+ for sent_id, merge_mask in merge_canvas.items():
406
+ # vis mask
407
+ if checkerboard_bg:
408
+ merge_mask = add_checkerboard_bg(img, merge_mask)
409
+ elif on_image:
410
+ merge_mask = visualize_mask_on_image(img, merge_mask, add_edge=True)
411
+ # vis box
412
+ bboxes = sent_boxes[sent_id]
413
+ merge_mask = visualize_bbox_on_image(merge_mask, bboxes)
414
+ # vis sent
415
+ if show_sent:
416
+ sent_en = self.load_sents(sent_ids=sent_id)[0]["raw_sent"]
417
+ merge_mask = paste_text(merge_mask, sent_en)
418
+ cv2.imwrite(
419
+ osp.join(save_dir, f"{img_info['id']}_{sent_id}.png"), merge_mask
420
+ )
421
+
422
+ return merge_canvas
423
+
424
+ def show_group_seg_bbox(
425
+ self,
426
+ group_ids,
427
+ save_root,
428
+ show_sent=True,
429
+ is_instance=True,
430
+ on_image=False,
431
+ checkerboard_bg=False,
432
+ ):
433
+ group_infos = self.load_groups(group_ids=group_ids)
434
+ for group_info in group_infos:
435
+ save_dir = osp.join(save_root, group_info["group_name"])
436
+ os.makedirs(save_dir, exist_ok=True)
437
+ self.show_image_seg_bbox(
438
+ img_ids=group_info["img_id"],
439
+ save_dir=save_dir,
440
+ show_sent=show_sent,
441
+ is_instance=is_instance,
442
+ on_image=on_image,
443
+ checkerboard_bg=checkerboard_bg,
444
+ )
445
+
446
+ def show_image_bbox(self, img_ids=[], save_dir=None, show_sent=False):
447
+ img_infos = self.load_imgs(img_ids=img_ids)
448
+ for img_idx, img_info in enumerate(img_infos):
449
+ img = cv2.imread(osp.join(self.image_dir, img_info["file_name"]))
450
+ anno_infos = self.load_annos(img_info["anno_id"])
451
+
452
+ sent_boxes = defaultdict(list)
453
+ for anno_info in anno_infos:
454
+ for sent_id in anno_info["sent_id"]:
455
+ sent_boxes[sent_id].append(anno_info["bbox"][0].tolist())
456
+
457
+ cv2.imwrite(osp.join(save_dir, f"{img_info['id']}.png"), img)
458
+ for sent_id, bboxes in sent_boxes.items():
459
+ merge_img = visualize_bbox_on_image(img, bboxes)
460
+ if show_sent:
461
+ sent_en = self.load_sents(sent_ids=sent_id)[0]["raw_sent"]
462
+ merge_img = paste_text(merge_img, sent_en)
463
+ cv2.imwrite(
464
+ osp.join(save_dir, f"{img_info['id']}_{sent_id}.png"), merge_img
465
+ )
466
+
467
+ def show_group_bbox(self, group_ids, save_root, show_sent=True):
468
+ group_infos = self.load_groups(group_ids=group_ids)
469
+ for group_info in group_infos:
470
+ save_dir = osp.join(save_root, group_info["group_name"])
471
+ os.makedirs(save_dir, exist_ok=True)
472
+ self.show_image_bbox(
473
+ img_ids=group_info["img_id"], save_dir=save_dir, show_sent=show_sent
474
+ )
475
+
476
+ def stat_description(self, with_rev=False, inter_group=False):
477
+ """calculate and print dataset statistics.
478
+
479
+ Args:
480
+ with_rev (bool, optional): consider absence descriptions or not. Defaults to False.
481
+ inter_group (bool, optional): calculate under intra- or inter-group settings. Defaults to False.
482
+ """
483
+ stat_dict = {}
484
+ # Number of sents
485
+ sent_ids = list(self.data["sentences"].keys())
486
+ if not with_rev:
487
+ sent_ids = [sent_id for sent_id in sent_ids if not self.is_revsent(sent_id)]
488
+ stat_dict["nsent"] = len(sent_ids)
489
+ # Number of annos / instance # TODO: rm rev
490
+ stat_dict["nanno"] = len(self.data["annotations"].keys())
491
+ # Number of images
492
+ stat_dict["nimg"] = len(self.data["images"].keys())
493
+ # Number of groups
494
+ stat_dict["ngroup"] = len(self.data["groups"].keys())
495
+
496
+ # Number of img-sent pair
497
+ num_img_sent = 0
498
+ for img_id in self.data["images"].keys():
499
+ anno_ids = self.get_anno_ids(img_ids=img_id)
500
+ anno_infos = self.load_annos(anno_ids=anno_ids)
501
+ cur_sent_set = set()
502
+ group_sent_ids = set(
503
+ self.load_groups(self.get_group_ids(img_ids=img_id))[0]["inner_sent_id"]
504
+ )
505
+ for anno_info in anno_infos:
506
+ cur_sent_set |= set(
507
+ [i for i in anno_info["sent_id"] if i in group_sent_ids]
508
+ )
509
+ if not with_rev:
510
+ cur_sent_set = [
511
+ sent_id for sent_id in cur_sent_set if not self.is_revsent(sent_id)
512
+ ]
513
+ num_img_sent += len(cur_sent_set)
514
+ stat_dict["num_img_sent"] = num_img_sent
515
+
516
+ # Number of absence img-sent pair
517
+ num_anti_img_sent = 0
518
+ for img_id in self.data["images"].keys():
519
+ anno_ids = self.get_anno_ids(img_ids=img_id)
520
+ anno_infos = self.load_annos(anno_ids=anno_ids)
521
+ cur_sent_set = set()
522
+ group_sent_ids = set(
523
+ self.load_groups(self.get_group_ids(img_ids=img_id))[0]["inner_sent_id"]
524
+ )
525
+ for anno_info in anno_infos:
526
+ cur_sent_set |= set(
527
+ [i for i in anno_info["sent_id"] if i in group_sent_ids]
528
+ )
529
+ assert group_sent_ids.issuperset(
530
+ cur_sent_set
531
+ ), f"{group_sent_ids}, {cur_sent_set}"
532
+ cur_anti_sent_set = group_sent_ids - cur_sent_set
533
+ if not with_rev:
534
+ cur_anti_sent_set = [
535
+ sent_id
536
+ for sent_id in cur_anti_sent_set
537
+ if not self.is_revsent(sent_id)
538
+ ]
539
+ num_anti_img_sent += len(cur_anti_sent_set)
540
+ stat_dict["num_anti_img_sent"] = num_anti_img_sent
541
+
542
+ # Number of anno-sent pair
543
+ num_anno_sent = 0
544
+ anno_infos = self.load_annos()
545
+ for anno_info in anno_infos:
546
+ if inter_group:
547
+ anno_sent_ids = [i for i in anno_info["sent_id"]]
548
+ else:
549
+ group_sent_ids = set(
550
+ self.load_groups(anno_info["group_id"])[0]["inner_sent_id"]
551
+ )
552
+ anno_sent_ids = [i for i in anno_info["sent_id"] if i in group_sent_ids]
553
+ if not with_rev:
554
+ anno_sent_ids = [
555
+ sent_id for sent_id in anno_sent_ids if not self.is_revsent(sent_id)
556
+ ]
557
+ num_anno_sent += len(anno_sent_ids)
558
+
559
+ stat_dict["num_anno_sent"] = num_anno_sent
560
+
561
+ # Number of anti anno-sent pair
562
+ num_anti_anno_sent = 0
563
+ anno_infos = self.load_annos()
564
+ for anno_info in anno_infos:
565
+ if inter_group:
566
+ all_sent_ids = set(self.get_sent_ids())
567
+ anno_sent_ids = anno_info["sent_id"]
568
+
569
+ anti_sent_ids = [
570
+ sent_id for sent_id in all_sent_ids if sent_id not in anno_sent_ids
571
+ ]
572
+ else:
573
+ group_sent_ids = set(
574
+ self.load_groups(anno_info["group_id"])[0]["inner_sent_id"]
575
+ )
576
+ anno_sent_ids = [i for i in anno_info["sent_id"] if i in group_sent_ids]
577
+
578
+ anti_sent_ids = [
579
+ sent_id
580
+ for sent_id in group_sent_ids
581
+ if sent_id not in anno_sent_ids
582
+ ]
583
+
584
+ if not with_rev:
585
+ anti_sent_ids = [
586
+ sent_id for sent_id in anti_sent_ids if not self.is_revsent(sent_id)
587
+ ]
588
+ num_anti_anno_sent += len(anti_sent_ids)
589
+
590
+ stat_dict["num_anti_anno_sent"] = num_anti_anno_sent
591
+
592
+ # Len of sentence
593
+ totle_len = 0
594
+ for sent_info in self.load_sents(sent_ids):
595
+ totle_len += len(sent_info["raw_sent"].split())
596
+
597
+ stat_dict["avg_sent_len"] = totle_len / stat_dict["nsent"]
598
+
599
+ print(stat_dict)
600
+
601
+ def is_revsent(self, sent_id):
602
+ sent_info = self.load_sents(sent_ids=sent_id)
603
+ return sent_info[0]["is_negative"]
604
+
605
+ def data2coca(self, out_root, with_rev=False):
606
+ group_infos = self.load_groups()
607
+ for group_info in group_infos:
608
+ sent_ids = group_info["inner_sent_id"]
609
+ if not with_rev:
610
+ sent_ids = [
611
+ sent_id for sent_id in sent_ids if not self.is_revsent(sent_id)
612
+ ]
613
+ sent_infos = self.load_sents(sent_ids)
614
+ for sent_info in sent_infos:
615
+ sent = sent_info["raw_sent"]
616
+ img_infos = self.load_imgs(group_info["img_id"])
617
+ for img_info in img_infos:
618
+ src_img_path = osp.join(self.image_dir, img_info["file_name"])
619
+ raw_name = img_info["file_name"].split("/")[-1]
620
+ out_img_dir = osp.join(out_root, "images", sent)
621
+ os.makedirs(out_img_dir, exist_ok=True)
622
+ out_img_path = osp.join(out_img_dir, raw_name)
623
+ copy_file(src_img_path, out_img_path)
624
+
625
+ out_mask_dir = osp.join(out_root, "masks", sent)
626
+ os.makedirs(out_mask_dir, exist_ok=True)
627
+ out_mask_path = osp.join(
628
+ out_mask_dir, raw_name.replace(".jpg", ".png")
629
+ )
630
+
631
+ cur_anno_ids = self.get_anno_ids(
632
+ img_ids=img_info["id"], sent_ids=sent_info["id"]
633
+ )
634
+ anno_infos = self.load_annos(cur_anno_ids)
635
+ rle_list = [anno_info["segmentation"] for anno_info in anno_infos]
636
+ bmask = merge2bin(rle_list, img_info["height"], img_info["width"])
637
+ cv2.imwrite(out_mask_path, bmask)
638
+
639
+ def convert2coco(self, out_root, anti_mode=False, is_group_separated=True):
640
+ """
641
+ Convert the annotation format of D^3 dataset to COCO.
642
+ 1. The sent_id can be viewed as category_id in COCO.
643
+ 2. If `is_group_separated` is True, `outer_sent_id` does not need to be considered.
644
+ 3. if `with_rev` is False, sents that meet `is_revsent` will be ignore.
645
+ """
646
+ os.makedirs(out_root, exist_ok=True)
647
+ coco_dict = {
648
+ "images": [],
649
+ "categories": [],
650
+ "annotations": [],
651
+ }
652
+
653
+ sent_ids = self.get_sent_ids()
654
+ if anti_mode == 1:
655
+ sent_ids = [sent_id for sent_id in sent_ids if not self.is_revsent(sent_id)]
656
+ elif anti_mode == 2:
657
+ sent_ids = [sent_id for sent_id in sent_ids if self.is_revsent(sent_id)]
658
+ elif anti_mode == 0:
659
+ pass
660
+ else:
661
+ raise Exception("Unimplemented anti_mode.")
662
+
663
+ sent_infos = self.load_sents(sent_ids)
664
+ for isent_info in sent_infos:
665
+ coco_dict["categories"].append(
666
+ {
667
+ "id": isent_info["id"],
668
+ "name": isent_info["raw_sent"],
669
+ }
670
+ )
671
+
672
+ item_id = 0
673
+ img_infos = self.load_imgs()
674
+ for iimg_info in img_infos:
675
+ coco_dict["images"].append(
676
+ {
677
+ "id": iimg_info["id"],
678
+ "file_name": iimg_info["file_name"],
679
+ "height": iimg_info["height"],
680
+ "width": iimg_info["width"],
681
+ }
682
+ )
683
+
684
+ anno_ids = self.get_anno_ids(img_ids=iimg_info["id"])
685
+ anno_infos = self.load_annos(anno_ids)
686
+
687
+ for ianno_info in anno_infos:
688
+ if is_group_separated:
689
+ inner_group_sent_ids = [
690
+ isent_id
691
+ for isent_id in ianno_info["sent_id"]
692
+ if isent_id
693
+ in self.load_groups(ianno_info["group_id"])[0]["inner_sent_id"]
694
+ ]
695
+ cur_sent_ids = inner_group_sent_ids
696
+ else:
697
+ cur_sent_ids = ianno_info["sent_id"]
698
+
699
+ for isent_id in cur_sent_ids:
700
+ if isent_id not in sent_ids:
701
+ continue
702
+
703
+ seg = ianno_info["segmentation"][0].copy()
704
+ if isinstance(seg, dict): # RLE
705
+ counts = seg["counts"]
706
+ if not isinstance(counts, str):
707
+ # make it json-serializable
708
+ seg["counts"] = counts.decode("ascii")
709
+
710
+ coco_dict["annotations"].append(
711
+ {
712
+ "id": item_id,
713
+ "image_id": iimg_info["id"],
714
+ "category_id": isent_id,
715
+ "segmentation": seg,
716
+ "area": int(ianno_info["area"][0]),
717
+ "bbox": [
718
+ int(cord) for cord in ianno_info["bbox"][0].tolist()
719
+ ],
720
+ "iscrowd": 0, # TODO: ianno_info["iscrowd"]
721
+ }
722
+ )
723
+ item_id += 1
724
+
725
+ with open(osp.join(out_root, "coco_annotations.json"), "w") as f:
726
+ json.dump(coco_dict, f, indent=4)
727
+
728
+ def sent_analyse(self, save_dir, with_rev=False):
729
+ """analyze word info in D-cube and generate word length histograms, word clouds, etc.
730
+
731
+ Args:
732
+ save_dir (str): path to save the visualized results.
733
+ with_rev (bool, optional): consider absence descriptions or not. Defaults to False.
734
+ """
735
+ sent_ids = self.get_sent_ids()
736
+ if not with_rev:
737
+ sent_ids = [sent_id for sent_id in sent_ids if not self.is_revsent(sent_id)]
738
+
739
+ sent_lens, sent_raws = [], []
740
+ sent_infos = self.load_sents(sent_ids)
741
+ for isent_info in sent_infos:
742
+ sent_raws.append(isent_info["raw_sent"])
743
+ sent_lens.append(len(isent_info["raw_sent"].split()))
744
+
745
+ os.makedirs(save_dir, exist_ok=True)
746
+ # plot_hist(
747
+ # sent_lens,
748
+ # bins=max(sent_lens) - min(sent_lens) + 1,
749
+ # save_path=osp.join(save_dir, "words_hist.pdf"),
750
+ # x="Lengths of descriptions",
751
+ # )
752
+ # generate_wordclouds(sent_raws, osp.join(save_dir, "word_clouds"))
753
+
754
+ def group_analysis(self, save_dir, with_rev=False):
755
+ group_infos = self.load_groups()
756
+ scene_tree = defaultdict(dict)
757
+
758
+ for group_info in group_infos:
759
+ scene_tree[group_info["scene"]][group_info["group_name"]] = {"nimg": 0.1}
760
+
761
+ # vis_group_tree(scene_tree, osp.join(save_dir, 'scene_tree.png')) # the visualized result is ugly
762
+
763
+ def bbox_num_analyze(self):
764
+ n_cat = len(self.data["sentences"].keys())
765
+ all_img_ids = self.data["images"].keys()
766
+ n_img = len(all_img_ids)
767
+ cat_obj_count = np.zeros((n_cat, n_img), dtype=int)
768
+ for img_id in all_img_ids:
769
+ # img_cat_ids = self.get_sent_ids(img_ids=img_id)
770
+ anno_ids = self.get_anno_ids(img_ids=img_id)
771
+ anno_infos = self.load_annos(anno_ids=anno_ids)
772
+ for anno in anno_infos:
773
+ for sid in anno["sent_id"]:
774
+ cat_obj_count[sid - 1, img_id] += 1
775
+ return cat_obj_count
approach/ovod/d-cube/d_cube/data_util.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie and Zhao Zhang"
3
+ __maintainer__ = "Chi Xie"
4
+ # data utility functions are defined in the script
5
+ import json
6
+ import pickle
7
+ import shutil
8
+
9
+ # from io import StringIO
10
+ # import string
11
+
12
+ import numpy as np
13
+ import cv2
14
+ from pycocotools import mask as cocomask
15
+
16
+ VOC_COLORMAP = [
17
+ [128, 0, 0],
18
+ [0, 128, 0],
19
+ [128, 128, 0],
20
+ [0, 0, 128],
21
+ [128, 0, 128],
22
+ [0, 128, 128],
23
+ [128, 128, 128],
24
+ [64, 0, 0],
25
+ [192, 0, 0],
26
+ [64, 128, 0],
27
+ [192, 128, 0],
28
+ [64, 0, 128],
29
+ [192, 0, 128],
30
+ [64, 128, 128],
31
+ [192, 128, 128],
32
+ [0, 64, 0],
33
+ [128, 64, 0],
34
+ [0, 192, 0],
35
+ [128, 192, 0],
36
+ [0, 64, 128],
37
+ ]
38
+
39
+
40
+ def visualize_bbox_on_image(img, bbox_list, save_path=None, thickness=3):
41
+ img_copy = img.copy()
42
+ for i, bbox in enumerate(bbox_list):
43
+ color = tuple(VOC_COLORMAP[i % len(VOC_COLORMAP)])
44
+ x, y, w, h = bbox
45
+ img_copy = cv2.rectangle(
46
+ img_copy, (int(x), int(y)), (int((x + w)), int(y + h)), color, thickness
47
+ )
48
+ if save_path:
49
+ cv2.imwrite(save_path, img_copy)
50
+ return img_copy
51
+
52
+
53
+ def rle2bmask(rle):
54
+ bm = cocomask.decode(rle)
55
+ if len(bm.shape) == 3:
56
+ bm = np.sum(
57
+ bm, axis=2
58
+ ) # sometimes there are multiple binary map (corresponding to multiple segs)
59
+ bm = bm.astype(np.uint8) # convert to np.uint8
60
+ return bm
61
+
62
+
63
+ def merge_rle(rle_list, is_instance=True, on_image=False):
64
+ if is_instance:
65
+ cm_list = []
66
+ for rle_idx, rle in enumerate(rle_list):
67
+ color = VOC_COLORMAP[rle_idx]
68
+ bm = rle2bmask(rle)
69
+ cm = cv2.cvtColor(bm, cv2.COLOR_GRAY2BGR)
70
+ cm_list.append(cm * color)
71
+ merge_map = np.sum(cm_list, axis=0, dtype=np.uint8)
72
+ else:
73
+ bm_list = [rle2bmask(rle) for rle in rle_list]
74
+ merge_map = np.sum(bm_list, axis=0, dtype=np.uint8)
75
+ merge_map[merge_map >= 1] = 1
76
+ if not on_image:
77
+ color = VOC_COLORMAP[0]
78
+ merge_map = cv2.cvtColor(merge_map, cv2.COLOR_GRAY2BGR)
79
+ merge_map *= np.array(color, dtype=np.uint8)
80
+
81
+ merge_map[merge_map > 255] = 255
82
+
83
+ if not on_image:
84
+ tmp_sum_map = np.sum(merge_map, axis=-1)
85
+ merge_map[tmp_sum_map == 0] = 220
86
+ return merge_map
87
+
88
+
89
+ def merge2bin(rle_list, img_h, img_w):
90
+ if rle_list:
91
+ bm_list = [rle2bmask(rle) for rle in rle_list]
92
+ merge_map = np.sum(bm_list, axis=0, dtype=np.uint8)
93
+ merge_map[merge_map >= 1] = 255
94
+ merge_map = np.expand_dims(merge_map, axis=-1)
95
+ return merge_map
96
+ else:
97
+ return np.zeros([img_h, img_w, 1], dtype=np.uint8)
98
+
99
+
100
+ def paste_text(img, text):
101
+ fontFace = cv2.FONT_HERSHEY_COMPLEX_SMALL
102
+ overlay = img.copy()
103
+ # fontFace = cv2.FONT_HERSHEY_TRIPLEX
104
+ fontScale = 1
105
+ thickness = 1
106
+ backgroud_alpha = 0.8
107
+
108
+ retval, baseLine = cv2.getTextSize(
109
+ text, fontFace=fontFace, fontScale=fontScale, thickness=thickness
110
+ )
111
+ topleft = (0, 0)
112
+ # bottomright = (topleft[0] + retval[0], topleft[1] + retval[1]+10)
113
+ bottomright = (img.shape[1], topleft[1] + retval[1] + 10)
114
+
115
+ cv2.rectangle(overlay, topleft, bottomright, thickness=-1, color=(250, 250, 250))
116
+ img = cv2.addWeighted(overlay, backgroud_alpha, img, 1 - backgroud_alpha, 0)
117
+
118
+ cv2.putText(
119
+ img,
120
+ text,
121
+ (0, baseLine + 10),
122
+ fontScale=fontScale,
123
+ fontFace=fontFace,
124
+ thickness=thickness,
125
+ color=(10, 10, 10),
126
+ )
127
+ return img
128
+
129
+
130
+ def load_json(json_path, to_int=False):
131
+ clean_res_dic = {}
132
+ with open(json_path, "r", encoding="utf-8") as f_in:
133
+ res_dic = json.load(f_in)
134
+
135
+ for ikey, iv in res_dic.items():
136
+ ikey = int(ikey.strip()) if to_int else ikey.strip()
137
+ clean_res_dic[ikey] = iv
138
+
139
+ return clean_res_dic
140
+
141
+
142
+ def path_map(src_path, obj_path):
143
+ def inner_map(full_path):
144
+ return full_path.replace(src_path, obj_path)
145
+
146
+
147
+ def save_pkl(src, obj_path):
148
+ with open(obj_path, "wb") as f_out:
149
+ pickle.dump(src, f_out)
150
+
151
+
152
+ def load_pkl(src_path):
153
+ with open(src_path, "rb") as f_in:
154
+ in_pkl = pickle.load(f_in)
155
+ return in_pkl
156
+
157
+
158
+ def copy_file(src_path, obj_path):
159
+ shutil.copy(src_path, obj_path)
160
+
161
+
162
+ def sentence_analysis():
163
+ return 0
164
+
165
+
166
+ def add_checkerboard_bg(image, mask, save_path=None):
167
+ # Create a new image with the same size as the original image
168
+ new_image = np.zeros_like(image)
169
+
170
+ # Define the size of the checkerboard pattern
171
+ checkerboard_size = 24
172
+
173
+ # Loop over each pixel in the mask
174
+ for x in range(mask.shape[1]):
175
+ for y in range(mask.shape[0]):
176
+ # If the pixel is transparent, draw a checkerboard pattern
177
+ if mask[y, x] == 0:
178
+ if (x // checkerboard_size) % 2 == (y // checkerboard_size) % 2:
179
+ new_image[y, x] = (255, 255, 255)
180
+ else:
181
+ new_image[y, x] = (128, 128, 128)
182
+ # Otherwise, copy the corresponding pixel from the original image
183
+ else:
184
+ new_image[y, x] = image[y, x]
185
+
186
+ # Save the new image with the checkerboard background
187
+ if save_path:
188
+ cv2.imwrite(save_path, new_image)
189
+ return new_image
190
+
191
+
192
+ def visualize_mask_on_image(
193
+ img, mask, save_path=None, add_edge=False, dark_background=False
194
+ ):
195
+ # Convert the mask to a binary mask if it's not already
196
+ if mask.max() > 1:
197
+ mask = mask.astype(np.uint8) // 255
198
+
199
+ # Convert the mask to a 3-channel mask if it's not already
200
+ if len(mask.shape) == 2:
201
+ mask = np.expand_dims(mask, axis=2)
202
+ mask = np.tile(mask, (1, 1, 3))
203
+
204
+ # Create a color map for the mask
205
+ cmap = np.array([255, 117, 44], dtype=np.uint8)
206
+ mask_colors = mask * cmap
207
+
208
+ # Add an opaque white edge to the mask if desired
209
+ if add_edge:
210
+ if len(mask.shape) == 2:
211
+ mask = np.expand_dims(mask, axis=2)
212
+ mask = np.tile(mask, (1, 1, 3))
213
+
214
+ kernel = np.ones((5, 5), dtype=np.uint8)
215
+ mask_edge = cv2.erode(mask, kernel, iterations=1)
216
+ mask_edge = mask - mask_edge
217
+
218
+ # mask_edge = np.tile(mask_edge[:, :, np.newaxis], [1, 1, 3])
219
+ mask_colors[mask_edge > 0] = 255
220
+
221
+ # Overlay the mask on the masked image
222
+ if dark_background:
223
+ masked_img = cv2.addWeighted(img, 0.4, mask_colors, 0.6, 0)
224
+ else:
225
+ masked_img = img.copy()
226
+ masked_img[mask > 0] = cv2.addWeighted(img, 0.4, mask_colors, 0.6, 0)[mask > 0]
227
+
228
+ # Save the result to the specified path if provided
229
+ if save_path is not None:
230
+ cv2.imwrite(save_path, masked_img)
231
+
232
+ return masked_img
233
+
234
+
235
+ # def visualize_mask_on_image(img, mask, save_path=None, add_edge=False):
236
+ # # Convert the mask to a binary mask if it's not already
237
+ # if mask.max() > 1:
238
+ # mask = mask.astype(np.uint8) // 255
239
+
240
+ # # Convert the mask to a 3-channel mask if it's not already
241
+ # if len(mask.shape) == 2:
242
+ # mask = np.expand_dims(mask, axis=2)
243
+ # mask = np.tile(mask, (1, 1, 3))
244
+
245
+ # # Create a color map for the mask
246
+ # cmap = np.array([255, 117, 44], dtype=np.uint8)
247
+ # mask_colors = mask * cmap
248
+
249
+ # # Add an opaque white edge to the mask if desired
250
+ # if add_edge:
251
+ # if len(mask.shape) == 2:
252
+ # mask = np.expand_dims(mask, axis=2)
253
+ # mask = np.tile(mask, (1, 1, 3))
254
+
255
+ # kernel = np.ones((5, 5), dtype=np.uint8)
256
+ # mask_edge = cv2.erode(mask, kernel, iterations=1)
257
+ # mask_edge = mask - mask_edge
258
+
259
+ # # mask_edge = np.tile(mask_edge[:, :, np.newaxis], [1, 1, 3])
260
+ # mask_colors[mask_edge > 0] = 255
261
+
262
+ # # Overlay the mask on the masked image
263
+ # masked_img = cv2.addWeighted(img, 0.5, mask_colors, 0.5, 0)
264
+
265
+ # # Save the result to the specified path if provided
266
+ # if save_path is not None:
267
+ # cv2.imwrite(save_path, masked_img)
268
+
269
+ # return masked_img
approach/ovod/d-cube/d_cube/vis_util.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie and Zhao Zhang"
3
+ __maintainer__ = "Chi Xie"
4
+ import os
5
+ from collections import Counter
6
+
7
+ import spacy
8
+ import matplotlib.pyplot as plt
9
+ import seaborn as sns
10
+ from wordcloud import WordCloud
11
+
12
+ # from pycirclize import Circos
13
+ # from Bio.Phylo.BaseTree import Tree
14
+ # from Bio import Phylo
15
+ # from newick import Node
16
+
17
+
18
+ def plot_hist(data, bins=10, is_norm=False, save_path=None, x=None):
19
+ sns.set_theme(style="whitegrid", font_scale=2.0)
20
+ ax = sns.histplot(data, bins=bins, common_norm=is_norm, kde=False)
21
+ ax.set_xlabel(x)
22
+ plt.tight_layout()
23
+ plt.savefig(save_path)
24
+ plt.close()
25
+
26
+
27
+ def plot_bars(names, nums, is_sort, save_path=None):
28
+ sns.set(style="whitegrid")
29
+
30
+ if is_sort:
31
+ zipped = zip(nums, names)
32
+ sort_zipped = sorted(zipped, key=lambda x: (x[0], x[1]))
33
+ result = zip(*sort_zipped)
34
+ nums, names = [list(x) for x in result]
35
+
36
+ fontx = {"family": "Times New Roman", "size": 10}
37
+ fig, ax = plt.subplots()
38
+ fig = plt.figure(figsize=(16, 4))
39
+ # sns.set_palette("PuBuGn_d")
40
+ sns.barplot(names, nums, palette=sns.cubehelix_palette(80, start=0.5, rot=-0.75))
41
+ fig.autofmt_xdate(rotation=90)
42
+ plt.tick_params(axis="x", labelsize=10)
43
+ labels = ax.get_xticklabels() + ax.get_yticklabels()
44
+ [label.set_fontname("Times New Roman") for label in labels]
45
+ plt.tight_layout()
46
+ plt.savefig(save_path)
47
+
48
+
49
+ def generate_wordclouds(sentences, save_dir):
50
+ """Generates word clouds for different parts of speech in a list of sentences.
51
+
52
+ Args:
53
+ sentences: A list of sentences.
54
+ save_dir: The directory to save the word cloud images.
55
+ """
56
+
57
+ os.makedirs(save_dir, exist_ok=True)
58
+ # Load the spacy model
59
+ nlp = spacy.load("en_core_web_sm")
60
+
61
+ # Define the parts of speech to include in the word clouds
62
+ pos_to_include = ["NOUN", "VERB", "ADJ", "ADV"]
63
+
64
+ # Process each sentence and collect the relevant words for each part of speech
65
+ words_by_pos = {pos: [] for pos in pos_to_include}
66
+ for sent in sentences:
67
+ doc = nlp(sent)
68
+ for token in doc:
69
+ if token.pos_ in pos_to_include:
70
+ words_by_pos[token.pos_].append(token.lemma_.lower())
71
+
72
+ # Generate a word cloud for each part of speech
73
+ for pos, words in words_by_pos.items():
74
+ if len(words) == 0:
75
+ continue # skip parts of speech with no words
76
+
77
+ # Count the frequency of each word
78
+ word_counts = Counter(words)
79
+
80
+ # Generate the word cloud
81
+ wordcloud = WordCloud(
82
+ width=800,
83
+ height=800,
84
+ background_color="white",
85
+ max_words=200,
86
+ colormap="Set2",
87
+ max_font_size=150,
88
+ ).generate_from_frequencies(word_counts)
89
+
90
+ # Save the word cloud image
91
+ filename = f"{pos.lower()}_wordcloud.png"
92
+ filepath = os.path.join(save_dir, filename)
93
+ wordcloud.to_file(filepath)
94
+
95
+
96
+ # def vis_group_tree(data_dict, save_path):
97
+
98
+ # # Create 3 randomized trees
99
+ # tree_size_list = [60, 40, 50]
100
+ # trees = [Tree.randomized(string.ascii_uppercase, branch_stdev=0.5) for size in tree_size_list]
101
+
102
+ # # Initialize circos sector with 3 randomized tree size
103
+ # sectors = {name: size for name, size in zip(list("ABC"), tree_size_list)}
104
+ # circos = Circos(sectors, space=5)
105
+
106
+ # colors = ["tomato", "skyblue", "limegreen"]
107
+ # cmaps = ["bwr", "viridis", "Spectral"]
108
+ # for idx, sector in enumerate(circos.sectors):
109
+ # sector.text(sector.name, r=120, size=12)
110
+ # # Plot randomized tree
111
+ # tree = trees[idx]
112
+ # tree_track = sector.add_track((30, 70))
113
+ # tree_track.axis(fc=colors[idx], alpha=0.2)
114
+ # tree_track.tree(tree, leaf_label_size=3, leaf_label_margin=21)
115
+ # # Plot randomized bar
116
+ # bar_track = sector.add_track((70, 90))
117
+ # x = np.arange(0, int(sector.size)) + 0.5
118
+ # height = np.random.randint(1, 10, int(sector.size))
119
+ # bar_track.bar(x, height, facecolor=colors[idx], ec="grey", lw=0.5, hatch="//")
120
+
121
+ # circos.savefig(save_path, dpi=600)
122
+
123
+ # def clean_newick_key(in_str):
124
+ # bad_chars = [':', ';', ',', '(', ')']
125
+ # for bad_char in bad_chars:
126
+ # in_str = in_str.replace(bad_char, ' ')
127
+ # return in_str
128
+
129
+ # def build_tree_from_dict(data):
130
+ # root = Node() # create the root node
131
+ # for key, value in data.items():
132
+ # node = Node(name=clean_newick_key(key)) # name doesn't need to be cleaned
133
+ # if value is not None:
134
+ # child_node = build_tree_from_dict(value)
135
+ # node.add_descendant(child_node)
136
+ # root.add_descendant(node)
137
+
138
+ # return root
139
+
140
+
141
+ def replace_chars_in_dict_keys(d):
142
+ """
143
+ Replaces the characters ':', ';', ',', '(', and ')' in the keys of a nested dictionary with '_'.
144
+ """
145
+ new_dict = {}
146
+ for k, v in d.items():
147
+ if isinstance(v, dict):
148
+ v = replace_chars_in_dict_keys(v)
149
+ new_key = k.translate(str.maketrans(":;,()", "_____"))
150
+ new_dict[new_key] = v
151
+ return new_dict
152
+
153
+
154
+ def build_newick_tree(tree_dict):
155
+ newick_tree = ""
156
+ if isinstance(tree_dict, dict):
157
+ for key, value in tree_dict.items():
158
+ if isinstance(value, dict):
159
+ subtree = build_newick_tree(value)
160
+ if subtree:
161
+ newick_tree += "(" + subtree + ")" + key + ","
162
+ else:
163
+ newick_tree += key + ","
164
+ else:
165
+ newick_tree += key + ":" + str(value) + ","
166
+ newick_tree = newick_tree.rstrip(",") + ")"
167
+ return newick_tree
168
+ else:
169
+ return None
170
+
171
+
172
+ # def vis_group_tree(data_dict, save_path):
173
+ # data_dic = replace_chars_in_dict_keys(data_dict)
174
+ # super_group_names = data_dict.keys()
175
+
176
+ # # Create 3 randomized trees
177
+ # tree_size_list = [60, 40, 50]
178
+ # trees = [Phylo.read(StringIO(build_newick_tree(data_dict[super_group_name])), "newick") for super_group_name in super_group_names]
179
+
180
+ # # Initialize circos sector with 3 randomized tree size
181
+ # sectors = {name: size for name, size in zip(list("ABC"), tree_size_list)}
182
+ # circos = Circos(sectors, space=5)
183
+
184
+ # colors = ["tomato", "skyblue", "limegreen"]
185
+ # cmaps = ["bwr", "viridis", "Spectral"]
186
+ # for idx, sector in enumerate(circos.sectors):
187
+ # sector.text(sector.name, r=120, size=12)
188
+ # # Plot randomized tree
189
+ # tree = trees[idx]
190
+ # tree_track = sector.add_track((30, 70))
191
+ # tree_track.axis(fc=colors[idx], alpha=0.2)
192
+ # tree_track.tree(tree, leaf_label_size=3, leaf_label_margin=21)
193
+ # # Plot randomized bar
194
+ # bar_track = sector.add_track((70, 90))
195
+ # x = np.arange(0, int(sector.size)) + 0.5
196
+ # height = np.random.randint(1, 10, int(sector.size))
197
+ # bar_track.bar(x, height, facecolor=colors[idx], ec="grey", lw=0.5, hatch="//")
198
+
199
+ # circos.savefig(save_path, dpi=600)
approach/ovod/d-cube/eval_sota/README.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Evaluting SOTA Methods on $D^3$
2
+
3
+ ## Leaderboard
4
+
5
+ In this directory, we keep the scripts or github links (official or custom) to evaluate SOTA methods (REC/OVD/DOD/MLLM) on $D^3$:
6
+
7
+ | Name | Paper | Original Tasks | Training Data | Evaluation Code | Intra-FULL/PRES/ABS/Inter-FULL/PRES/ABS | Source | Note |
8
+ |:-----|:-----:|:----:|:-----:|:-----:|:-----:|:-----:|:-----:|
9
+ | OFA-large | [OFA: Unifying Architectures, Tasks, and Modalities Through a Simple Sequence-to-Sequence Learning Framework (ICML 2022)](https://arxiv.org/abs/2202.03052) | REC | - | - | 4.2/4.1/4.6/0.1/0.1/0.1 | [DOD paper](https://arxiv.org/abs/2307.12813) | - |
10
+ | CORA-R50 | [CORA: Adapting CLIP for Open-Vocabulary Detection with Region Prompting and Anchor Pre-Matching (CVPR 2023)](https://openaccess.thecvf.com/content/CVPR2023/papers/Wu_CORA_Adapting_CLIP_for_Open-Vocabulary_Detection_With_Region_Prompting_and_CVPR_2023_paper.pdf) | OVD | - | - | 6.2/6.7/5.0/2.0/2.2/1.3 | [DOD paper](https://arxiv.org/abs/2307.12813) | - |
11
+ | OWL-ViT-large | [Simple Open-Vocabulary Object Detection with Vision Transformers (ECCV 2022)](https://www.ecva.net/papers/eccv_2022/papers_ECCV/papers/136700714.pdf) | OVD | - | [DOD official](./owl_vit.py) | 9.6/10.7/6.4/2.5/2.9/2.1 | [DOD paper](https://arxiv.org/abs/2307.12813) | Post-processing hyper-parameters may affect the performance and the result may not exactly match the paper |
12
+ | SPHINX-7B | [SPHINX: The Joint Mixing of Weights, Tasks, and Visual Embeddings for Multi-modal Large Language Models (arxiv 2023)](https://arxiv.org/abs/2311.07575) | **MLLM** capable of REC | - | [DOD official](./sphinx.py) | 10.6/11.4/7.9/-/-/- | DOD authors | A lot of contribution from [Jie Li](https://github.com/theFool32) |
13
+ | GLIP-T | [Grounded Language-Image Pre-training (CVPR 2022)](https://arxiv.org/abs/2112.03857) | OVD & PG | - | - | 19.1/18.3/21.5/-/-/- | GEN paper | - |
14
+ | UNINEXT-huge | [Universal Instance Perception as Object Discovery and Retrieval (CVPR 2023)](https://arxiv.org/abs/2303.06674v2) | OVD & REC | - | [DOD official](https://github.com/Charles-Xie/UNINEXT_D3) | 20.0/20.6/18.1/3.3/3.9/1.6 | [DOD paper](https://arxiv.org/abs/2307.12813) | - |
15
+ | Grounding-DINO-base | [Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection (arxiv 2023)](https://arxiv.org/abs/2303.05499) | OVD & REC | - | [DOD official](./groundingdino.py) | 20.7/20.1/22.5/2.7/2.4/3.5 | [DOD paper](https://arxiv.org/abs/2307.12813) | Post-processing hyper-parameters may affect the performance and the result may not exactly match the paper |
16
+ | OFA-DOD-base | [Described Object Detection: Liberating Object Detection with Flexible Expressions (NeurIPS 2023)](https://arxiv.org/abs/2307.12813) | DOD | - | - | 21.6/23.7/15.4/5.7/6.9/2.3 | [DOD paper](https://arxiv.org/abs/2307.12813) | - |
17
+ | FIBER-B | [Coarse-to-Fine Vision-Language Pre-training with Fusion in the Backbone (NeurIPS 2022)](https://arxiv.org/abs/2206.07643) | OVD & REC | - | - | 22.7/21.5/26.0/-/-/- | GEN paper | - |
18
+ | MM-Grounding-DINO | [An Open and Comprehensive Pipeline for Unified Object Grounding and Detection (arxiv 2024)](https://arxiv.org/abs/2401.02361) | DOD & OVD & REC | O365, GoldG, GRIT, V3Det | [MM-GDINO official](https://github.com/open-mmlab/mmdetection/tree/main/configs/mm_grounding_dino#zero-shot-description-detection-datasetdod) | 22.9/21.9/26.0/-/-/- | MM-GDINO paper | - |
19
+ | GEN (FIBER-B) | [Generating Enhanced Negatives for Training Language-Based Object Detectors (arxiv 2024](https://arxiv.org/abs/2401.00094) | DOD | - | - | 26.0/25.2/28.1/-/-/- | GEN paper | Enhancement based on FIBER-B |
20
+ | APE-large (D) | [Aligning and Prompting Everything All at Once for Universal Visual Perception (arxiv 2023)](https://arxiv.org/abs/2312.02153) | DOD & OVD & REC | COCO, LVIS, O365, OpenImages, Visual Genome, RefCOCO/+/g, SA-1B, GQA, PhraseCut, Flickr30k | [APE official](https://github.com/shenyunhang/APE) | 37.5/38.8/33.9/21.0/22.0/17.9 | APE paper | Extra training data helps for this amazing performance |
21
+
22
+
23
+ Some extra notes:
24
+ - Each method is currently recorded by *the variant with the highest performance* in this table, if there are multiple variants available, so it's only a leaderboard, not meant for fair comparison.
25
+ - Methods like GLIP, FIBER, etc. are actually not evaluated on OVD benchmarks. For zero-shot eval on DOD, We currently do not distinguish between methods for OVD benchmarks and methods for ZS-OD, as long as it is verified with open-set detection capability.
26
+
27
+ For other variants (e.g. for a fair comparison regarding data, backbone, etc.), please refer to the papers.
approach/ovod/d-cube/eval_sota/groundingdino.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie"
3
+ __maintainer__ = "Chi Xie"
4
+
5
+ # An example for how to run this script:
6
+ # CUDA_VISIBLE_DEVICES=0
7
+ # python groundingdino.py \
8
+ # -c ./groundingdino/config/GroundingDINO_SwinB.cfg.py \
9
+ # -p ./ckpt/groundingdino_swinb_cogcoor.pth \
10
+ # -o "outputs/gdino_d3" \
11
+ # --box_threshold 0.05 \
12
+ # --text_threshold 0.05 \
13
+ # --img-top1
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+
19
+ import numpy as np
20
+ import torch
21
+ from PIL import Image, ImageDraw, ImageFont
22
+ from pycocotools.coco import COCO
23
+ from pycocotools.cocoeval import COCOeval
24
+ from tqdm import tqdm
25
+
26
+ import groundingdino.datasets.transforms as T
27
+ from groundingdino.models import build_model
28
+ from groundingdino.util.slconfig import SLConfig
29
+ from groundingdino.util.utils import clean_state_dict, get_phrases_from_posmap
30
+ from d_cube import D3
31
+
32
+
33
+ def plot_boxes_to_image(image_pil, tgt):
34
+ H, W = tgt["size"]
35
+ boxes = tgt["boxes"]
36
+ labels = tgt["labels"]
37
+ assert len(boxes) == len(labels), "boxes and labels must have same length"
38
+
39
+ draw = ImageDraw.Draw(image_pil)
40
+ mask = Image.new("L", image_pil.size, 0)
41
+ mask_draw = ImageDraw.Draw(mask)
42
+
43
+ # draw boxes and masks
44
+ for box, label in zip(boxes, labels):
45
+ # from 0..1 to 0..W, 0..H
46
+ box = box * torch.Tensor([W, H, W, H])
47
+ # from xywh to xyxy
48
+ box[:2] -= box[2:] / 2
49
+ box[2:] += box[:2]
50
+ # random color
51
+ color = tuple(np.random.randint(0, 255, size=3).tolist())
52
+ # draw
53
+ x0, y0, x1, y1 = box
54
+ x0, y0, x1, y1 = int(x0), int(y0), int(x1), int(y1)
55
+
56
+ draw.rectangle([x0, y0, x1, y1], outline=color, width=6)
57
+ # draw.text((x0, y0), str(label), fill=color)
58
+
59
+ font = ImageFont.load_default()
60
+ if hasattr(font, "getbbox"):
61
+ bbox = draw.textbbox((x0, y0), str(label), font)
62
+ else:
63
+ w, h = draw.textsize(str(label), font)
64
+ bbox = (x0, y0, w + x0, y0 + h)
65
+ # bbox = draw.textbbox((x0, y0), str(label))
66
+ draw.rectangle(bbox, fill=color)
67
+ draw.text((x0, y0), str(label), fill="white")
68
+
69
+ mask_draw.rectangle([x0, y0, x1, y1], fill=255, width=6)
70
+ return image_pil, mask
71
+
72
+
73
+ def load_image(image_path):
74
+ # load image
75
+ image_pil = Image.open(image_path).convert("RGB") # load image
76
+
77
+ transform = T.Compose(
78
+ [
79
+ T.RandomResize([800], max_size=1333),
80
+ T.ToTensor(),
81
+ T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
82
+ ]
83
+ )
84
+ image, _ = transform(image_pil, None) # 3, h, w
85
+ return image_pil, image
86
+
87
+
88
+ def load_model(model_config_path, model_checkpoint_path, cpu_only=False):
89
+ args = SLConfig.fromfile(model_config_path)
90
+ args.device = "cuda" if not cpu_only else "cpu"
91
+ model = build_model(args)
92
+ checkpoint = torch.load(model_checkpoint_path, map_location="cpu")
93
+ load_res = model.load_state_dict(clean_state_dict(checkpoint["model"]), strict=False)
94
+ print(load_res)
95
+ _ = model.eval()
96
+ return model
97
+
98
+
99
+ def get_grounding_output(model, image, caption, box_threshold, text_threshold, with_logits=True, cpu_only=False):
100
+ caption = caption.lower()
101
+ caption = caption.strip()
102
+ if not caption.endswith("."):
103
+ caption = caption + "."
104
+ device = "cuda" if not cpu_only else "cpu"
105
+ model = model.to(device)
106
+ image = image.to(device)
107
+ with torch.no_grad():
108
+ outputs = model(image[None], captions=[caption])
109
+ logits = outputs["pred_logits"].cpu().sigmoid()[0] # (nq, 256)
110
+ boxes = outputs["pred_boxes"].cpu()[0] # (nq, 4)
111
+ logits.shape[0]
112
+
113
+ # filter output
114
+ logits_filt = logits.clone()
115
+ boxes_filt = boxes.clone()
116
+ filt_mask = logits_filt.max(dim=1)[0] > box_threshold
117
+ logits_filt = logits_filt[filt_mask] # num_filt, 256
118
+ boxes_filt = boxes_filt[filt_mask] # num_filt, 4
119
+ logits_filt.shape[0]
120
+
121
+ # get phrase
122
+ tokenlizer = model.tokenizer
123
+ tokenized = tokenlizer(caption)
124
+ # build pred
125
+ pred_phrases = []
126
+ logits_list = []
127
+ for logit, box in zip(logits_filt, boxes_filt):
128
+ pred_phrase = get_phrases_from_posmap(logit > text_threshold, tokenized, tokenlizer)
129
+ logits_list.append(logit.max().item())
130
+ if with_logits:
131
+ pred_phrases.append(pred_phrase + f"({str(logit.max().item())[:4]})")
132
+ else:
133
+ pred_phrases.append(pred_phrase)
134
+
135
+ return boxes_filt, pred_phrases, logits_list
136
+
137
+
138
+ def get_dataset_iter(coco):
139
+ img_ids = coco.get_img_ids()
140
+ for img_id in img_ids:
141
+ img_info = coco.load_imgs(img_id)[0]
142
+ file_name = img_info["file_name"]
143
+ img_path = os.path.join(IMG_ROOT, file_name)
144
+ yield img_id, img_path
145
+
146
+
147
+ def eval_on_d3(pred_path, mode="pn"):
148
+ assert mode in ("pn", "p", "n")
149
+ if mode == "pn":
150
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_full_annotations.json")
151
+ elif mode == "p":
152
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_pres_annotations.json")
153
+ else:
154
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_abs_annotations.json")
155
+ coco = COCO(gt_path)
156
+ d3_res = coco.loadRes(pred_path)
157
+ cocoEval = COCOeval(coco, d3_res, "bbox")
158
+ cocoEval.evaluate()
159
+ cocoEval.accumulate()
160
+ cocoEval.summarize()
161
+
162
+ # comment the following if u only need intra/inter map for full/pres/abs
163
+ # ===================== uncomment this if u need detailed analysis =====================
164
+ # aps = cocoEval.eval["precision"][:, :, :, 0, -1]
165
+ # category_ids = coco.getCatIds()
166
+ # category_names = [cat["name"] for cat in coco.loadCats(category_ids)]
167
+
168
+ # aps_lens = defaultdict(list)
169
+ # counter_lens = defaultdict(int)
170
+ # for i in range(len(category_names)):
171
+ # ap = aps[:, :, i]
172
+ # ap_value = ap[ap > -1].mean()
173
+ # if not np.isnan(ap_value):
174
+ # len_ref = len(category_names[i].split(" "))
175
+ # aps_lens[len_ref].append(ap_value)
176
+ # counter_lens[len_ref] += 1
177
+
178
+ # ap_sum_short = sum([sum(aps_lens[i]) for i in range(0, 4)])
179
+ # ap_sum_mid = sum([sum(aps_lens[i]) for i in range(4, 7)])
180
+ # ap_sum_long = sum([sum(aps_lens[i]) for i in range(7, 10)])
181
+ # ap_sum_very_long = sum(
182
+ # [sum(aps_lens[i]) for i in range(10, max(counter_lens.keys()) + 1)]
183
+ # )
184
+ # c_sum_short = sum([counter_lens[i] for i in range(1, 4)])
185
+ # c_sum_mid = sum([counter_lens[i] for i in range(4, 7)])
186
+ # c_sum_long = sum([counter_lens[i] for i in range(7, 10)])
187
+ # c_sum_very_long = sum(
188
+ # [counter_lens[i] for i in range(10, max(counter_lens.keys()) + 1)]
189
+ # )
190
+ # map_short = ap_sum_short / c_sum_short
191
+ # map_mid = ap_sum_mid / c_sum_mid
192
+ # map_long = ap_sum_long / c_sum_long
193
+ # map_very_long = ap_sum_very_long / c_sum_very_long
194
+ # print(
195
+ # f"mAP over reference length: short - {map_short:.4f}, mid - {map_mid:.4f}, long - {map_long:.4f}, very long - {map_very_long:.4f}"
196
+ # )
197
+ # ===================== uncomment this if u need detailed analysis =====================
198
+
199
+
200
+ def inference_on_d3(data_iter, model, args, box_threshold, text_threshold):
201
+ pred = []
202
+ for idx, (img_id, image_path) in enumerate(tqdm(data_iter)):
203
+ # load image
204
+ image_pil, image = load_image(image_path)
205
+ size = image_pil.size
206
+ W, H = size
207
+
208
+ group_ids = d3.get_group_ids(img_ids=[img_id])
209
+ sent_ids = d3.get_sent_ids(group_ids=group_ids)
210
+ sent_list = d3.load_sents(sent_ids=sent_ids)
211
+ text_list = [sent['raw_sent'] for sent in sent_list]
212
+
213
+ for sent_id, text_prompt in zip(sent_ids, text_list):
214
+ # run model
215
+ boxes_filt, pred_phrases, logit_list = get_grounding_output(
216
+ model, image, text_prompt, box_threshold, text_threshold, cpu_only=args.cpu_only, with_logits=False,
217
+ )
218
+ if args.vis:
219
+ pred_dict = {
220
+ "boxes": boxes_filt, # [x_center, y_center, w, h]
221
+ "size": [size[1], size[0]],
222
+ "labels": [f"{phrase}({str(logit)[:4]})" for phrase, logit in zip(pred_phrases, logit_list)],
223
+ }
224
+ image_with_box = plot_boxes_to_image(image_pil.copy(), pred_dict)[0]
225
+ image_with_box.save(os.path.join(output_dir, f"{img_id}_{text_prompt}.jpg"))
226
+ if not logit_list:
227
+ continue
228
+ if args.img_top1:
229
+ max_score_idx = logit_list.index(max(logit_list))
230
+ bboxes, phrases, logits = [boxes_filt[max_score_idx]], [pred_phrases[max_score_idx]], [logit_list[max_score_idx]]
231
+ else:
232
+ bboxes, phrases, logits = boxes_filt, pred_phrases, logit_list
233
+ for box, phrase, logit in zip(bboxes, phrases, logits):
234
+ if len(phrase) > args.overlap_percent * len(text_prompt) or phrase == text_prompt:
235
+ x1, y1, w, h = box.tolist()
236
+ x0, y0 = x1 - w / 2, y1 - h / 2
237
+ pred_item = {
238
+ "image_id": img_id,
239
+ "category_id": sent_id,
240
+ "bbox": [x0 * W, y0 * H, w * W, h * H],
241
+ "score": float(logit),
242
+ }
243
+ pred.append(pred_item)
244
+
245
+ return pred
246
+
247
+
248
+ if __name__ == "__main__":
249
+ IMG_ROOT = None # set here
250
+ JSON_ANNO_PATH = None # set here
251
+ PKL_ANNO_PATH = None # set here
252
+ assert IMG_ROOT is not None, "Please set IMG_ROOT in the script first"
253
+ assert JSON_ANNO_PATH is not None, "Please set JSON_ANNO_PATH in the script first"
254
+ assert PKL_ANNO_PATH is not None, "Please set PKL_ANNO_PATH in the script first"
255
+
256
+ d3 = D3(IMG_ROOT, PKL_ANNO_PATH)
257
+
258
+ parser = argparse.ArgumentParser("Grounding DINO evaluation on D-cube (https://arxiv.org/abs/2307.12813)", add_help=True)
259
+ parser.add_argument("--config_file", "-c", type=str, required=True, help="path to config file")
260
+ parser.add_argument(
261
+ "--checkpoint_path", "-p", type=str, required=True, help="path to checkpoint file"
262
+ )
263
+ # parser.add_argument("--image_path", "-i", type=str, required=True, help="path to image file")
264
+ # parser.add_argument("--text_prompt", "-t", type=str, required=True, help="text prompt")
265
+ parser.add_argument(
266
+ "--output_dir", "-o", type=str, default="outputs", required=True, help="output directory"
267
+ )
268
+ parser.add_argument("--vis", action="store_true", help="visualization on D3")
269
+
270
+ parser.add_argument("--box_threshold", type=float, default=0.3, help="box threshold")
271
+ parser.add_argument("--text_threshold", type=float, default=0.25, help="text threshold")
272
+
273
+ parser.add_argument("--cpu-only", action="store_true", help="running on cpu only!, default=False")
274
+ parser.add_argument("--img-top1", action="store_true", help="select only the box with top max score")
275
+ # parser.add_argument("--overlap-percent", type=float, default=1.0, help="overlapping percentage between input prompt and output label")
276
+ # this overlapping percentage denotes an additional post-processing technique we designed. if you turn this on, you may get higher performance by tuning this parameter.
277
+ args = parser.parse_args()
278
+ args.overlap_percent = 1 # by default, we do not use this technique.
279
+ print(args)
280
+
281
+ # cfg
282
+ config_file = args.config_file # change the path of the model config file
283
+ checkpoint_path = args.checkpoint_path # change the path of the model
284
+ # image_path = args.image_path
285
+ # text_prompt = args.text_prompt
286
+ output_dir = args.output_dir
287
+ box_threshold = args.box_threshold
288
+ text_threshold = args.text_threshold
289
+
290
+ # make dir
291
+ os.makedirs(output_dir, exist_ok=True)
292
+ # load model
293
+ model = load_model(config_file, checkpoint_path, cpu_only=args.cpu_only)
294
+
295
+ data_iter = get_dataset_iter(d3)
296
+
297
+ pred = inference_on_d3(data_iter, model, args, box_threshold=box_threshold, text_threshold=text_threshold)
298
+
299
+ pred_path = os.path.join(output_dir, f"prediction.json")
300
+ with open(pred_path, "w") as f_:
301
+ json.dump(pred, f_)
302
+ eval_on_d3(pred_path, mode='pn')
303
+ eval_on_d3(pred_path, mode='p')
304
+ eval_on_d3(pred_path, mode='n')
approach/ovod/d-cube/eval_sota/owl_vit.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from collections import defaultdict
4
+
5
+ from tqdm import tqdm
6
+ from PIL import Image
7
+ import numpy as np
8
+ from pycocotools.coco import COCO
9
+ from pycocotools.cocoeval import COCOeval
10
+ import torch
11
+ from transformers import OwlViTProcessor, OwlViTForObjectDetection
12
+
13
+ from d_cube import D3
14
+
15
+
16
+ def write_json(json_path, json_data):
17
+ with open(json_path, "w") as f_:
18
+ json.dump(json_data, f_)
19
+
20
+
21
+ def read_json(json_path):
22
+ with open(json_path, "r") as f_:
23
+ json_data = json.load(f_)
24
+ return json_data
25
+
26
+
27
+ def load_image_general(image_path):
28
+ image_pil = Image.open(image_path)
29
+ return image_pil
30
+
31
+
32
+ def get_prediction(model, image, captions, cpu_only=False):
33
+ for i in range(len(captions)):
34
+ captions[i] = captions[i].lower()
35
+ captions[i] = captions[i].strip()
36
+ if not captions[i].endswith("."):
37
+ captions[i] = captions[i] + "."
38
+ device = "cuda" if not cpu_only else "cpu"
39
+ model = model.to(device)
40
+ with torch.no_grad():
41
+ inputs = processor(text=[captions], images=image, return_tensors="pt").to(
42
+ device
43
+ )
44
+ outputs = model(**inputs)
45
+ target_size = torch.Tensor([image.size[::-1]]).to(device)
46
+ results = processor.post_process_object_detection(
47
+ outputs=outputs, target_sizes=target_size, threshold=0.1
48
+ # the post precessing threshold will affect the performance obviously
49
+ # you may tune it to get better performance, e.g., 0.05
50
+ )
51
+ boxes, scores, labels = (
52
+ results[0]["boxes"],
53
+ results[0]["scores"],
54
+ results[0]["labels"],
55
+ )
56
+ return boxes, scores, labels
57
+
58
+
59
+ def get_dataset_iter(coco):
60
+ img_ids = coco.get_img_ids()
61
+ for img_id in img_ids:
62
+ img_info = coco.load_imgs(img_id)[0]
63
+ file_name = img_info["file_name"]
64
+ img_path = os.path.join(IMG_ROOT, file_name)
65
+ yield img_id, img_path
66
+
67
+
68
+ def eval_on_d3(pred_path, mode="pn"):
69
+ assert mode in ("pn", "p", "n")
70
+ if mode == "pn":
71
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_full_annotations.json")
72
+ elif mode == "p":
73
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_pres_annotations.json")
74
+ else:
75
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_abs_annotations.json")
76
+ coco = COCO(gt_path)
77
+ d3_res = coco.loadRes(pred_path)
78
+ cocoEval = COCOeval(coco, d3_res, "bbox")
79
+ cocoEval.evaluate()
80
+ cocoEval.accumulate()
81
+ cocoEval.summarize()
82
+
83
+ # comment the following if u only need intra/inter map for full/pres/abs
84
+ # ===================== uncomment this if u need detailed analysis =====================
85
+ # aps = cocoEval.eval["precision"][:, :, :, 0, -1]
86
+ # category_ids = coco.getCatIds()
87
+ # category_names = [cat["name"] for cat in coco.loadCats(category_ids)]
88
+
89
+ # aps_lens = defaultdict(list)
90
+ # counter_lens = defaultdict(int)
91
+ # for i in range(len(category_names)):
92
+ # ap = aps[:, :, i]
93
+ # ap_value = ap[ap > -1].mean()
94
+ # if not np.isnan(ap_value):
95
+ # len_ref = len(category_names[i].split(" "))
96
+ # aps_lens[len_ref].append(ap_value)
97
+ # counter_lens[len_ref] += 1
98
+
99
+ # ap_sum_short = sum([sum(aps_lens[i]) for i in range(0, 4)])
100
+ # ap_sum_mid = sum([sum(aps_lens[i]) for i in range(4, 7)])
101
+ # ap_sum_long = sum([sum(aps_lens[i]) for i in range(7, 10)])
102
+ # ap_sum_very_long = sum(
103
+ # [sum(aps_lens[i]) for i in range(10, max(counter_lens.keys()) + 1)]
104
+ # )
105
+ # c_sum_short = sum([counter_lens[i] for i in range(1, 4)])
106
+ # c_sum_mid = sum([counter_lens[i] for i in range(4, 7)])
107
+ # c_sum_long = sum([counter_lens[i] for i in range(7, 10)])
108
+ # c_sum_very_long = sum(
109
+ # [counter_lens[i] for i in range(10, max(counter_lens.keys()) + 1)]
110
+ # )
111
+ # map_short = ap_sum_short / c_sum_short
112
+ # map_mid = ap_sum_mid / c_sum_mid
113
+ # map_long = ap_sum_long / c_sum_long
114
+ # map_very_long = ap_sum_very_long / c_sum_very_long
115
+ # print(
116
+ # f"mAP over reference length: short - {map_short:.4f}, mid - {map_mid:.4f}, long - {map_long:.4f}, very long - {map_very_long:.4f}"
117
+ # )
118
+ # ===================== uncomment this if u need detailed analysis =====================
119
+
120
+
121
+ def inference_on_d3(data_iter, model):
122
+ pred = []
123
+ error = []
124
+ for img_id, image_path in tqdm(data_iter):
125
+ image = load_image_general(image_path)
126
+
127
+ # ==================================== intra-group setting ====================================
128
+ # each image is evaluated with the categories in its group (usually 4)
129
+ group_ids = d3.get_group_ids(img_ids=[img_id])
130
+ sent_ids = d3.get_sent_ids(group_ids=group_ids)
131
+ # ==================================== intra-group setting ====================================
132
+ # ==================================== inter-group setting ====================================
133
+ # each image is evaluated with all categories in the dataset (422 for the first version of the dataset)
134
+ # sent_ids = d3.get_sent_ids()
135
+ # ==================================== inter-group setting ====================================
136
+ sent_list = d3.load_sents(sent_ids=sent_ids)
137
+ text_list = [sent["raw_sent"] for sent in sent_list]
138
+
139
+ try:
140
+ boxes, scores, labels = get_prediction(model, image, text_list, cpu_only=False)
141
+ for box, score, label in zip(boxes, scores, labels):
142
+ pred_item = {
143
+ "image_id": img_id,
144
+ "category_id": sent_ids[label],
145
+ "bbox": convert_to_xywh(box.tolist()), # use xywh
146
+ "score": float(score),
147
+ }
148
+ pred.append(pred_item) # the output to be saved to JSON.
149
+ except:
150
+ print("error!!!")
151
+ return pred, error
152
+
153
+
154
+ def convert_to_xywh(bbox_xyxy):
155
+ """
156
+ Convert top-left and bottom-right corner coordinates to [x, y, width, height] format.
157
+ """
158
+ x1, y1, x2, y2 = bbox_xyxy
159
+ width = x2 - x1
160
+ height = y2 - y1
161
+ return [x1, y1, width, height]
162
+
163
+
164
+ if __name__ == "__main__":
165
+ IMG_ROOT = None # set here
166
+ JSON_ANNO_PATH = None # set here
167
+ PKL_ANNO_PATH = None # set here
168
+ assert IMG_ROOT is not None, "Please set IMG_ROOT in the script first"
169
+ assert JSON_ANNO_PATH is not None, "Please set JSON_ANNO_PATH in the script first"
170
+ assert PKL_ANNO_PATH is not None, "Please set PKL_ANNO_PATH in the script first"
171
+
172
+ d3 = D3(IMG_ROOT, PKL_ANNO_PATH)
173
+
174
+ output_dir = "ovd/owlvit/"
175
+ os.makedirs(output_dir, exist_ok=True)
176
+
177
+ # model prediction
178
+ processor = OwlViTProcessor.from_pretrained("owl-vit")
179
+ model = OwlViTForObjectDetection.from_pretrained("owl-vit")
180
+ data_iter = get_dataset_iter(d3)
181
+ pred, error = inference_on_d3(data_iter, model)
182
+
183
+ pred_path = os.path.join(output_dir, f"prediction.json")
184
+ pred_path_error = os.path.join(output_dir, "error.json")
185
+ write_json(pred_path, pred)
186
+ write_json(pred_path_error, error)
187
+ # see https://github.com/shikras/d-cube/blob/main/doc.md#output-format for the output format
188
+ # the output format is identical to COCO.
189
+
190
+ eval_on_d3(pred_path, mode="pn") # the FULL setting
191
+ eval_on_d3(pred_path, mode="p") # the PRES setting
192
+ eval_on_d3(pred_path, mode="n") # the ABS setting
approach/ovod/d-cube/eval_sota/sphinx.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie and Jie Li"
3
+ __maintainer__ = "Chi Xie"
4
+
5
+ import json
6
+ import os
7
+ from collections import defaultdict
8
+ import re
9
+
10
+ from PIL import Image
11
+ from pycocotools.coco import COCO
12
+ from pycocotools.cocoeval import COCOeval
13
+
14
+ from d_cube import D3
15
+
16
+
17
+ def write_json(json_path, json_data):
18
+ with open(json_path, "w") as f_:
19
+ json.dump(json_data, f_)
20
+
21
+
22
+ def read_json(json_path):
23
+ with open(json_path, "r") as f_:
24
+ json_data = json.load(f_)
25
+ return json_data
26
+
27
+
28
+ def load_image_general(image_path):
29
+ image_pil = Image.open(image_path)
30
+ return image_pil
31
+
32
+
33
+ def extract_boxes(input_string):
34
+ # if input_string.startswith("None"):
35
+ # return []
36
+ # Define the pattern using regular expression
37
+ pattern = r'\[([\d.,; ]+)\]'
38
+
39
+ # Search for the pattern in the input string
40
+ match = re.search(pattern, input_string)
41
+
42
+ # If a match is found, extract and return the boxes as a list
43
+ if match:
44
+ boxes_str = match.group(1)
45
+ boxes_list = [list(map(float, box.split(','))) for box in boxes_str.split(';')]
46
+ return boxes_list
47
+ else:
48
+ return []
49
+
50
+
51
+ def get_prediction(mllm_res, image, captions, cpu_only=False):
52
+ boxes, scores, labels = [], [], []
53
+ width, height = image.size
54
+ for idx, res_item in enumerate(mllm_res):
55
+ boxes_list = extract_boxes(res_item["answer"])
56
+ for bbox in boxes_list:
57
+ bbox_rescaled = get_true_bbox(image.size, bbox)
58
+ boxes.append(bbox_rescaled)
59
+ scores.append(1.0)
60
+ labels.append(idx)
61
+ return boxes, scores, labels
62
+
63
+
64
+ def get_dataset_iter(coco):
65
+ img_ids = coco.get_img_ids()
66
+ for img_id in img_ids:
67
+ img_info = coco.load_imgs(img_id)[0]
68
+ file_name = img_info["file_name"]
69
+ img_path = os.path.join(IMG_ROOT, file_name)
70
+ yield img_id, file_name, img_path
71
+
72
+
73
+ def eval_on_d3(pred_path, mode="pn"):
74
+ assert mode in ("pn", "p", "n")
75
+ if mode == "pn":
76
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_full_annotations.json")
77
+ elif mode == "p":
78
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_pres_annotations.json")
79
+ else:
80
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_abs_annotations.json")
81
+ coco = COCO(gt_path)
82
+ d3_res = coco.loadRes(pred_path)
83
+ cocoEval = COCOeval(coco, d3_res, "bbox")
84
+ cocoEval.evaluate()
85
+ cocoEval.accumulate()
86
+ cocoEval.summarize()
87
+
88
+
89
+ def group_sphinx_res_by_img(inference_res):
90
+ inference_res_by_img = defaultdict(list)
91
+ for res_item in inference_res:
92
+ img_path = "/".join(res_item["image_path"].split("/")[-2:])
93
+ inference_res_by_img[img_path].append(res_item)
94
+ inference_res_by_img = dict(inference_res_by_img)
95
+ return inference_res_by_img
96
+
97
+
98
+ def get_true_bbox(img_size, bbox):
99
+ width, height = img_size
100
+ max_edge = max(height, width)
101
+ bbox = [v * max_edge for v in bbox]
102
+ diff = abs(width - height) // 2
103
+ if height < width:
104
+ bbox[1] -= diff
105
+ bbox[3] -= diff
106
+ else:
107
+ bbox[0] -= diff
108
+ bbox[2] -= diff
109
+ return bbox
110
+
111
+
112
+ def inference_on_d3(data_iter, inference_res):
113
+ pred = []
114
+ inf_res_by_img = group_sphinx_res_by_img(inference_res)
115
+ for idx, (img_id, img_name, img_path) in enumerate(data_iter):
116
+ image = load_image_general(img_path)
117
+
118
+ # ==================================== intra-group setting ====================================
119
+ # each image is evaluated with the categories in its group (usually 4)
120
+ group_ids = d3.get_group_ids(img_ids=[img_id])
121
+ sent_ids = d3.get_sent_ids(group_ids=group_ids)
122
+ # ==================================== intra-group setting ====================================
123
+ # ==================================== inter-group setting ====================================
124
+ # each image is evaluated with all categories in the dataset (422 for the first version of the dataset)
125
+ # sent_ids = d3.get_sent_ids()
126
+ # ==================================== inter-group setting ====================================
127
+ sent_list = d3.load_sents(sent_ids=sent_ids)
128
+ text_list = [sent["raw_sent"] for sent in sent_list]
129
+
130
+ boxes, scores, labels = get_prediction(inf_res_by_img[img_name], image, text_list, cpu_only=False)
131
+ for box, score, label in zip(boxes, scores, labels):
132
+ pred_item = {
133
+ "image_id": img_id,
134
+ "category_id": sent_ids[label],
135
+ "bbox": convert_to_xywh(box), # use xywh
136
+ "score": float(score),
137
+ }
138
+ pred.append(pred_item) # the output to be saved to JSON.
139
+ return pred
140
+
141
+
142
+ def convert_to_xywh(bbox_xyxy):
143
+ """
144
+ Convert top-left and bottom-right corner coordinates to [x, y, width, height] format.
145
+ """
146
+ x1, y1, x2, y2 = bbox_xyxy
147
+ width = x2 - x1
148
+ height = y2 - y1
149
+ return [x1, y1, width, height]
150
+
151
+
152
+ if __name__ == "__main__":
153
+ IMG_ROOT = None # set here
154
+ JSON_ANNO_PATH = None # set here
155
+ PKL_ANNO_PATH = None # set here
156
+ # ============================== SPHINX inference result file ===============
157
+ SPHINX_INFERENCE_RES_PATH = None
158
+ # You can download the SPHINX d3 inference result example from:
159
+ # https://github.com/shikras/d-cube/files/14276682/sphinx_d3_result.json
160
+ # For the inference process, please refer to SPHINX official repo (https://github.com/Alpha-VLLM/LLaMA2-Accessory)
161
+ # the prompts we used are available in this JSON file
162
+ # Thanks for the contribution from Jie Li (https://github.com/theFool32)
163
+ # ============================== SPHINX inference result file ===============
164
+ assert IMG_ROOT is not None, "Please set IMG_ROOT in the script first"
165
+ assert JSON_ANNO_PATH is not None, "Please set JSON_ANNO_PATH in the script first"
166
+ assert PKL_ANNO_PATH is not None, "Please set PKL_ANNO_PATH in the script first"
167
+
168
+ d3 = D3(IMG_ROOT, PKL_ANNO_PATH)
169
+
170
+ output_dir = "mllm/sphinx/" # or whatever you prefer
171
+ inference_res = read_json(SPHINX_INFERENCE_RES_PATH)
172
+
173
+ # model prediction
174
+ data_iter = get_dataset_iter(d3)
175
+ pred = inference_on_d3(data_iter, inference_res)
176
+
177
+ pred_path = os.path.join(output_dir, f"prediction.json")
178
+ write_json(pred_path, pred)
179
+ # see https://github.com/shikras/d-cube/blob/main/doc.md#output-format for the output format
180
+ # the output format is identical to COCO.
181
+
182
+ eval_on_d3(pred_path, mode="pn") # the FULL setting
183
+ eval_on_d3(pred_path, mode="p") # the PRES setting
184
+ eval_on_d3(pred_path, mode="n") # the ABS setting
approach/ovod/d-cube/scripts/eval_and_analysis_json.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie and Zhao Zhang"
3
+ __maintainer__ = "Chi Xie"
4
+ # this script takes the result json in, and print evaluation and analysis result on D-cube (FULL/PRES/ABS, etc.)
5
+ import os
6
+ import json
7
+ import argparse
8
+ from collections import defaultdict
9
+
10
+ import numpy as np
11
+ from pycocotools.coco import COCO
12
+ from pycocotools.cocoeval import COCOeval
13
+
14
+ from d_cube import D3
15
+
16
+ def eval_on_d3(pred_path, mode="pn", nbox_partition=None, lref_partition=False):
17
+ assert mode in ("pn", "p", "n")
18
+ if mode == "pn":
19
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_full_annotations.json")
20
+ elif mode == "p":
21
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_pres_annotations.json")
22
+ else:
23
+ gt_path = os.path.join(JSON_ANNO_PATH, "d3_abs_annotations.json")
24
+
25
+ if nbox_partition:
26
+ gt_path, pred_path = nbox_partition_json(gt_path, pred_path, nbox_partition)
27
+
28
+ # Eval results
29
+ coco = COCO(gt_path)
30
+ d3_res = coco.loadRes(pred_path)
31
+ cocoEval = COCOeval(coco, d3_res, "bbox")
32
+ cocoEval.evaluate()
33
+ cocoEval.accumulate()
34
+ cocoEval.summarize()
35
+
36
+ aps = cocoEval.eval["precision"][:, :, :, 0, -1]
37
+ category_ids = coco.getCatIds()
38
+ category_names = [cat["name"] for cat in coco.loadCats(category_ids)]
39
+
40
+ if lref_partition:
41
+ aps_lens = defaultdict(list)
42
+ counter_lens = defaultdict(int)
43
+ for i in range(len(category_names)):
44
+ ap = aps[:, :, i]
45
+ ap_value = ap[ap > -1].mean()
46
+ if not np.isnan(ap_value):
47
+ len_ref = len(category_names[i].split(" "))
48
+ aps_lens[len_ref].append(ap_value)
49
+ counter_lens[len_ref] += 1
50
+
51
+ ap_sum_short = sum([sum(aps_lens[i]) for i in range(0, 4)])
52
+ ap_sum_mid = sum([sum(aps_lens[i]) for i in range(4, 7)])
53
+ ap_sum_long = sum([sum(aps_lens[i]) for i in range(7, 10)])
54
+ ap_sum_very_long = sum(
55
+ [sum(aps_lens[i]) for i in range(10, max(counter_lens.keys()) + 1)]
56
+ )
57
+ c_sum_short = sum([counter_lens[i] for i in range(1, 4)])
58
+ c_sum_mid = sum([counter_lens[i] for i in range(4, 7)])
59
+ c_sum_long = sum([counter_lens[i] for i in range(7, 10)])
60
+ c_sum_very_long = sum(
61
+ [counter_lens[i] for i in range(10, max(counter_lens.keys()) + 1)]
62
+ )
63
+ map_short = ap_sum_short / c_sum_short
64
+ map_mid = ap_sum_mid / c_sum_mid
65
+ map_long = ap_sum_long / c_sum_long
66
+ map_very_long = ap_sum_very_long / c_sum_very_long
67
+ print(
68
+ f"mAP over reference length: short - {map_short:.4f}, mid - {map_mid:.4f}, long - {map_long:.4f}, very long - {map_very_long:.4f}"
69
+ )
70
+
71
+
72
+ def nbox_partition_json(gt_path, pred_path, nbox_partition):
73
+ with open(gt_path, "r") as f_gt:
74
+ gts = json.load(f_gt)
75
+ with open(pred_path, "r") as f_pred:
76
+ preds = json.load(f_pred)
77
+
78
+ cat_obj_count = d3.bbox_num_analyze()
79
+ annos = gts["annotations"]
80
+ new_annos = []
81
+ for ann in annos:
82
+ img_id = ann["image_id"]
83
+ category_id = ann["category_id"]
84
+ if nbox_partition == "one" and cat_obj_count[category_id - 1, img_id] == 1:
85
+ new_annos.append(ann)
86
+ if nbox_partition == "multi" and cat_obj_count[category_id - 1, img_id] > 1:
87
+ new_annos.append(ann)
88
+ if nbox_partition == "two" and cat_obj_count[category_id - 1, img_id] == 2:
89
+ new_annos.append(ann)
90
+ if nbox_partition == "three" and cat_obj_count[category_id - 1, img_id] == 3:
91
+ new_annos.append(ann)
92
+ if nbox_partition == "four" and cat_obj_count[category_id - 1, img_id] == 4:
93
+ new_annos.append(ann)
94
+ if nbox_partition == "four_more" and cat_obj_count[category_id - 1, img_id] > 4:
95
+ new_annos.append(ann)
96
+ gts["annotations"] = new_annos
97
+ new_gts = gts
98
+ new_preds = []
99
+ for prd in preds:
100
+ img_id = prd["image_id"]
101
+ category_id = prd["category_id"]
102
+ if nbox_partition == "no" and cat_obj_count[category_id - 1, img_id] == 0:
103
+ new_preds.append(prd)
104
+ if nbox_partition == "one" and cat_obj_count[category_id - 1, img_id] == 1:
105
+ new_preds.append(prd)
106
+ if nbox_partition == "multi" and cat_obj_count[category_id - 1, img_id] > 1:
107
+ new_preds.append(prd)
108
+ if nbox_partition == "two" and cat_obj_count[category_id - 1, img_id] == 2:
109
+ new_preds.append(prd)
110
+ if nbox_partition == "three" and cat_obj_count[category_id - 1, img_id] == 3:
111
+ new_preds.append(prd)
112
+ if nbox_partition == "four" and cat_obj_count[category_id - 1, img_id] == 4:
113
+ new_preds.append(prd)
114
+ if nbox_partition == "four_more" and cat_obj_count[category_id - 1, img_id] > 4:
115
+ new_preds.append(prd)
116
+
117
+ new_gt_path = gt_path.replace(".json", f".{nbox_partition}-instance.json")
118
+ new_pred_path = pred_path.replace(".json", f".{nbox_partition}-instance.json")
119
+ with open(new_gt_path, "w") as fo_gt:
120
+ json.dump(new_gts, fo_gt)
121
+ with open(new_pred_path, "w") as fo_pred:
122
+ json.dump(new_preds, fo_pred)
123
+ return new_gt_path, new_pred_path
124
+
125
+
126
+ def convert_to_xywh(x1, y1, x2, y2):
127
+ """
128
+ Convert top-left and bottom-right corner coordinates to [x,y,width,height] format.
129
+ """
130
+ width = x2 - x1
131
+ height = y2 - y1
132
+ return x1, y1, width, height
133
+
134
+
135
+ def transform_json_boxes(pred_path):
136
+ with open(pred_path, "r") as f_:
137
+ res = json.load(f_)
138
+ for item in res:
139
+ item["bbox"] = convert_to_xywh(*item["bbox"])
140
+ res_path = pred_path.replace(".json", ".xywh.json")
141
+ with open(res_path, "w") as f_w:
142
+ json.dump(res, f_w)
143
+ return res_path
144
+
145
+
146
+ if __name__ == "__main__":
147
+ D3_DATASET_ROOT = os.environ.get("D3_DATASET_ROOT")
148
+ if not D3_DATASET_ROOT:
149
+ raise RuntimeError("Set D3_DATASET_ROOT to the extracted D3 dataset directory.")
150
+ IMG_ROOT = D3_DATASET_ROOT
151
+ JSON_ANNO_PATH = os.path.join(D3_DATASET_ROOT, "d3_json")
152
+ PKL_ANNO_PATH = os.path.join(D3_DATASET_ROOT, "d3_pkl")
153
+ d3 = D3(IMG_ROOT, PKL_ANNO_PATH)
154
+
155
+ parser = argparse.ArgumentParser(
156
+ "An example script for D-cube evaluation with prediction file (JSON)",
157
+ add_help=True,
158
+ )
159
+ parser.add_argument("pred_path", type=str, help="path to the prediction JSON file")
160
+ parser.add_argument(
161
+ "--partition-by-nbox",
162
+ action="store_true",
163
+ help="divide the images by num of boxes for each ref",
164
+ )
165
+ parser.add_argument(
166
+ "--partition-by-lens",
167
+ action="store_true",
168
+ help="divide the references by their lengths",
169
+ )
170
+ parser.add_argument(
171
+ "--xyxy2xywh",
172
+ action="store_true",
173
+ help="transform box coords from xyxy to xywh",
174
+ )
175
+ args = parser.parse_args()
176
+ if args.xyxy2xywh:
177
+ pred_path = transform_json_boxes(args.pred_path)
178
+ else:
179
+ pred_path = args.pred_path
180
+ pred_path = args.pred_path
181
+ if args.partition_by_nbox:
182
+ # partiton: no-instance, one-instance, multi-instance
183
+ for mode in ("pn", "p", "n"):
184
+ # for ptt in ('no', 'one', 'multi'):
185
+ for ptt in ("no", "one", "two", "three", "four", "four_more"):
186
+ eval_on_d3(pred_path, mode=mode, nbox_partition=ptt)
187
+ else:
188
+ eval_on_d3(pred_path, mode="pn", lref_partition=args.partition_by_lens)
189
+ eval_on_d3(pred_path, mode="p", lref_partition=args.partition_by_lens)
190
+ eval_on_d3(pred_path, mode="n", lref_partition=args.partition_by_lens)
approach/ovod/d-cube/scripts/eval_json_example.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __author__ = "Chi Xie and Zhao Zhang"
3
+ __maintainer__ = "Chi Xie"
4
+ # this script takes the result json in, and print evaluation and analysis result on D-cube (FULL/PRES/ABS, etc.)
5
+ from pycocotools.coco import COCO
6
+ from pycocotools.cocoeval import COCOeval
7
+
8
+ # Eval results with COCOAPI
9
+ gt_path = "./d3_full_annotations.json" # FULL, PRES or ABS
10
+ pred_path = None # set your prediction JSON path
11
+ coco = COCO(gt_path)
12
+ d3_res = coco.loadRes(pred_path)
13
+ cocoEval = COCOeval(coco, d3_res, "bbox")
14
+ cocoEval.evaluate()
15
+ cocoEval.accumulate()
16
+ cocoEval.summarize()
approach/ovod/d-cube/scripts/get_d3_stat.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from d_cube.vis_util import plot_hist
4
+ from d_cube import D3
5
+
6
+
7
+ def vis_num_instance(cat_obj_count):
8
+ # Assuming `cat_obj_count` is your numpy array of shape [n_cat, n_img]
9
+
10
+ # Calculate the total number of instances in each image
11
+ total_instances_per_image = np.sum(cat_obj_count, axis=0)
12
+
13
+ # # Plot the histogram
14
+ # plt.hist(total_instances_per_image, bins=20)
15
+ # plt.xlabel('Number of Instances')
16
+ # plt.ylabel('Frequency')
17
+ # plt.title('Distribution of Number of Instances on a Image')
18
+
19
+ # # Save the figure
20
+ # plt.savefig('vis_fig/instance_distribution.png', bbox_inches='tight')
21
+ # plt.close()
22
+ plot_hist(
23
+ total_instances_per_image,
24
+ bins=max(total_instances_per_image) - min(total_instances_per_image) + 1,
25
+ save_path="vis_fig/instance_dist_hist.pdf",
26
+ )
27
+
28
+
29
+ def vis_num_category(cat_obj_count):
30
+ # Assuming `cat_obj_count` is your numpy array of shape [n_cat, n_img]
31
+
32
+ # Calculate the number of categories in each image
33
+ num_categories_per_image = np.sum(cat_obj_count > 0, axis=0)
34
+
35
+ # # Plot the histogram
36
+ # plt.hist(num_categories_per_image, bins=20)
37
+ # plt.xlabel('Number of Categories')
38
+ # plt.ylabel('Frequency')
39
+ # plt.title('Distribution of Number of Categories on a Image')
40
+
41
+ # # Save the figure
42
+ # plt.savefig('vis_fig/category_distribution.png', bbox_inches='tight')
43
+ # plt.close()
44
+ plot_hist(
45
+ num_categories_per_image,
46
+ bins=max(num_categories_per_image) - min(num_categories_per_image) + 1,
47
+ save_path="vis_fig/category_dist_hist.pdf",
48
+ )
49
+
50
+
51
+ def vis_num_img_per_cat(cat_obj_count):
52
+ num_img_per_cat = np.sum(cat_obj_count > 0, axis=1)
53
+ plot_hist(
54
+ num_img_per_cat,
55
+ bins=20,
56
+ save_path="vis_fig/nimg_pcat_hist.pdf",
57
+ x="Num. of images",
58
+ )
59
+
60
+
61
+ def vis_num_box_per_cat(cat_obj_count):
62
+ num_box_per_cat = np.sum(cat_obj_count, axis=1)
63
+ plot_hist(
64
+ num_box_per_cat,
65
+ bins=20,
66
+ save_path="vis_fig/nbox_pcat_hist.pdf",
67
+ x="Num. of instances",
68
+ )
69
+
70
+
71
+ def vis_num_box_per_cat_per_img(cat_obj_count):
72
+ img_obj_count = cat_obj_count.reshape(-1)
73
+ plot_hist(
74
+ img_obj_count[img_obj_count > 0],
75
+ bins=max(img_obj_count) - min(img_obj_count) + 1,
76
+ save_path="vis_fig/nbox_pcat_pimg_hist.pdf",
77
+ x="Num. of instances on a image",
78
+ )
79
+
80
+
81
+ if __name__ == "__main__":
82
+ IMG_ROOT = None # set here
83
+ PKL_ANNO_PATH = None # set here
84
+ assert IMG_ROOT is not None, "Please set IMG_ROOT in the script first"
85
+ assert PKL_ANNO_PATH is not None, "Please set PKL_ANNO_PATH in the script first"
86
+ d3 = D3(IMG_ROOT, PKL_ANNO_PATH)
87
+
88
+ cat_obj_count = d3.bbox_num_analyze()
89
+ vis_num_instance(cat_obj_count)
90
+ vis_num_category(cat_obj_count)
91
+ vis_num_img_per_cat(cat_obj_count)
92
+ vis_num_box_per_cat(cat_obj_count)
93
+ vis_num_box_per_cat_per_img(cat_obj_count)
94
+
95
+ d3.stat_description(with_rev=False)
96
+ d3.stat_description(with_rev=True)
97
+ d3.stat_description(with_rev=False, inter_group=True)
98
+ d3.stat_description(with_rev=True, inter_group=True)
approach/ovod/detectron2/.circleci/config.yml ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: 2.1
2
+
3
+ # -------------------------------------------------------------------------------------
4
+ # Environments to run the jobs in
5
+ # -------------------------------------------------------------------------------------
6
+ cpu: &cpu
7
+ machine:
8
+ image: ubuntu-2004:202107-02
9
+ resource_class: medium
10
+
11
+ gpu: &gpu
12
+ machine:
13
+ # NOTE: use a cuda version that's supported by all our pytorch versions
14
+ image: ubuntu-1604-cuda-11.1:202012-01
15
+ resource_class: gpu.nvidia.small
16
+
17
+ windows-cpu: &windows_cpu
18
+ machine:
19
+ resource_class: windows.medium
20
+ image: windows-server-2019-vs2019:stable
21
+ shell: powershell.exe
22
+
23
+ # windows-gpu: &windows_gpu
24
+ # machine:
25
+ # resource_class: windows.gpu.nvidia.medium
26
+ # image: windows-server-2019-nvidia:stable
27
+
28
+ version_parameters: &version_parameters
29
+ parameters:
30
+ pytorch_version:
31
+ type: string
32
+ torchvision_version:
33
+ type: string
34
+ pytorch_index:
35
+ type: string
36
+ # use test wheels index to have access to RC wheels
37
+ # https://download.pytorch.org/whl/test/torch_test.html
38
+ default: "https://download.pytorch.org/whl/torch_stable.html"
39
+ python_version: # NOTE: only affect linux
40
+ type: string
41
+ default: '3.7.9'
42
+
43
+ environment:
44
+ PYTORCH_VERSION: << parameters.pytorch_version >>
45
+ TORCHVISION_VERSION: << parameters.torchvision_version >>
46
+ PYTORCH_INDEX: << parameters.pytorch_index >>
47
+ PYTHON_VERSION: << parameters.python_version>>
48
+ # point datasets to ~/.torch so it's cached in CI
49
+ DETECTRON2_DATASETS: ~/.torch/datasets
50
+
51
+ # -------------------------------------------------------------------------------------
52
+ # Re-usable commands
53
+ # -------------------------------------------------------------------------------------
54
+ # install_nvidia_driver: &install_nvidia_driver
55
+ # - run:
56
+ # name: Install nvidia driver
57
+ # working_directory: ~/
58
+ # command: |
59
+ # wget -q 'https://s3.amazonaws.com/ossci-linux/nvidia_driver/NVIDIA-Linux-x86_64-430.40.run'
60
+ # sudo /bin/bash ./NVIDIA-Linux-x86_64-430.40.run -s --no-drm
61
+ # nvidia-smi
62
+
63
+ add_ssh_keys: &add_ssh_keys
64
+ # https://circleci.com/docs/2.0/add-ssh-key/
65
+ - add_ssh_keys:
66
+ fingerprints:
67
+ - "e4:13:f2:22:d4:49:e8:e4:57:5a:ac:20:2f:3f:1f:ca"
68
+
69
+ install_python: &install_python
70
+ - run:
71
+ name: Install Python
72
+ working_directory: ~/
73
+ command: |
74
+ # upgrade pyenv
75
+ cd /opt/circleci/.pyenv/plugins/python-build/../.. && git pull && cd -
76
+ pyenv install -s $PYTHON_VERSION
77
+ pyenv global $PYTHON_VERSION
78
+ python --version
79
+ which python
80
+ pip install --upgrade pip
81
+
82
+ setup_venv: &setup_venv
83
+ - run:
84
+ name: Setup Virtual Env
85
+ working_directory: ~/
86
+ command: |
87
+ python -m venv ~/venv
88
+ echo ". ~/venv/bin/activate" >> $BASH_ENV
89
+ . ~/venv/bin/activate
90
+ python --version
91
+ which python
92
+ which pip
93
+ pip install --upgrade pip
94
+
95
+ setup_venv_win: &setup_venv_win
96
+ - run:
97
+ name: Setup Virtual Env for Windows
98
+ command: |
99
+ pip install virtualenv
100
+ python -m virtualenv env
101
+ .\env\Scripts\activate
102
+ python --version
103
+ which python
104
+ which pip
105
+
106
+ install_linux_dep: &install_linux_dep
107
+ - run:
108
+ name: Install Dependencies
109
+ command: |
110
+ # disable crash coredump, so unittests fail fast
111
+ sudo systemctl stop apport.service
112
+ # install from github to get latest; install iopath first since fvcore depends on it
113
+ pip install --progress-bar off -U 'git+https://github.com/facebookresearch/iopath'
114
+ pip install --progress-bar off -U 'git+https://github.com/facebookresearch/fvcore'
115
+ # Don't use pytest-xdist: cuda tests are unstable under multi-process workers.
116
+ pip install --progress-bar off ninja opencv-python-headless pytest tensorboard pycocotools onnx
117
+ pip install --progress-bar off torch==$PYTORCH_VERSION -f $PYTORCH_INDEX
118
+ if [[ "$TORCHVISION_VERSION" == "master" ]]; then
119
+ pip install git+https://github.com/pytorch/vision.git
120
+ else
121
+ pip install --progress-bar off torchvision==$TORCHVISION_VERSION -f $PYTORCH_INDEX
122
+ fi
123
+
124
+ python -c 'import torch; print("CUDA:", torch.cuda.is_available())'
125
+ gcc --version
126
+
127
+ install_detectron2: &install_detectron2
128
+ - run:
129
+ name: Install Detectron2
130
+ command: |
131
+ # Remove first, in case it's in the CI cache
132
+ pip uninstall -y detectron2
133
+
134
+ pip install --progress-bar off -e .[all]
135
+ python -m detectron2.utils.collect_env
136
+ ./datasets/prepare_for_tests.sh
137
+
138
+ run_unittests: &run_unittests
139
+ - run:
140
+ name: Run Unit Tests
141
+ command: |
142
+ pytest -sv --durations=15 tests # parallel causes some random failures
143
+
144
+ uninstall_tests: &uninstall_tests
145
+ - run:
146
+ name: Run Tests After Uninstalling
147
+ command: |
148
+ pip uninstall -y detectron2
149
+ # Remove built binaries
150
+ rm -rf build/ detectron2/*.so
151
+ # Tests that code is importable without installation
152
+ PYTHONPATH=. ./.circleci/import-tests.sh
153
+
154
+
155
+ # -------------------------------------------------------------------------------------
156
+ # Jobs to run
157
+ # -------------------------------------------------------------------------------------
158
+ jobs:
159
+ linux_cpu_tests:
160
+ <<: *cpu
161
+ <<: *version_parameters
162
+
163
+ working_directory: ~/detectron2
164
+
165
+ steps:
166
+ - checkout
167
+
168
+ # Cache the venv directory that contains python, dependencies, and checkpoints
169
+ # Refresh the key when dependencies should be updated (e.g. when pytorch releases)
170
+ - restore_cache:
171
+ keys:
172
+ - cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827
173
+
174
+ - <<: *install_python
175
+ - <<: *install_linux_dep
176
+ - <<: *install_detectron2
177
+ - <<: *run_unittests
178
+ - <<: *uninstall_tests
179
+
180
+ - save_cache:
181
+ paths:
182
+ - /opt/circleci/.pyenv
183
+ - ~/.torch
184
+ key: cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827
185
+
186
+
187
+ linux_gpu_tests:
188
+ <<: *gpu
189
+ <<: *version_parameters
190
+
191
+ working_directory: ~/detectron2
192
+
193
+ steps:
194
+ - checkout
195
+
196
+ - restore_cache:
197
+ keys:
198
+ - cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827
199
+
200
+ - <<: *install_python
201
+ - <<: *install_linux_dep
202
+ - <<: *install_detectron2
203
+ - <<: *run_unittests
204
+ - <<: *uninstall_tests
205
+
206
+ - save_cache:
207
+ paths:
208
+ - /opt/circleci/.pyenv
209
+ - ~/.torch
210
+ key: cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827
211
+
212
+ windows_cpu_build:
213
+ <<: *windows_cpu
214
+ <<: *version_parameters
215
+ steps:
216
+ - <<: *add_ssh_keys
217
+ - checkout
218
+ - <<: *setup_venv_win
219
+
220
+ # Cache the env directory that contains dependencies
221
+ - restore_cache:
222
+ keys:
223
+ - cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210404
224
+
225
+ - run:
226
+ name: Install Dependencies
227
+ command: |
228
+ pip install certifi --ignore-installed # required on windows to workaround some cert issue
229
+ pip install numpy cython # required on windows before pycocotools
230
+ pip install opencv-python-headless pytest-xdist pycocotools tensorboard onnx
231
+ pip install -U git+https://github.com/facebookresearch/iopath
232
+ pip install -U git+https://github.com/facebookresearch/fvcore
233
+ pip install torch==$env:PYTORCH_VERSION torchvision==$env:TORCHVISION_VERSION -f $env:PYTORCH_INDEX
234
+
235
+ - save_cache:
236
+ paths:
237
+ - env
238
+ key: cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210404
239
+
240
+ - <<: *install_detectron2
241
+ # TODO: unittest fails for now
242
+
243
+ workflows:
244
+ version: 2
245
+ regular_test:
246
+ jobs:
247
+ - linux_cpu_tests:
248
+ name: linux_cpu_tests_pytorch1.10
249
+ pytorch_version: '1.10.0+cpu'
250
+ torchvision_version: '0.11.1+cpu'
251
+ - linux_gpu_tests:
252
+ name: linux_gpu_tests_pytorch1.8
253
+ pytorch_version: '1.8.1+cu111'
254
+ torchvision_version: '0.9.1+cu111'
255
+ - linux_gpu_tests:
256
+ name: linux_gpu_tests_pytorch1.9
257
+ pytorch_version: '1.9+cu111'
258
+ torchvision_version: '0.10+cu111'
259
+ - linux_gpu_tests:
260
+ name: linux_gpu_tests_pytorch1.10
261
+ pytorch_version: '1.10+cu111'
262
+ torchvision_version: '0.11.1+cu111'
263
+ - linux_gpu_tests:
264
+ name: linux_gpu_tests_pytorch1.10_python39
265
+ pytorch_version: '1.10+cu111'
266
+ torchvision_version: '0.11.1+cu111'
267
+ python_version: '3.9.6'
268
+ - windows_cpu_build:
269
+ pytorch_version: '1.10+cpu'
270
+ torchvision_version: '0.11.1+cpu'
approach/ovod/detectron2/.circleci/import-tests.sh ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash -e
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ # Test that import works without building detectron2.
5
+
6
+ # Check that _C is not importable
7
+ python -c "from detectron2 import _C" > /dev/null 2>&1 && {
8
+ echo "This test should be run without building detectron2."
9
+ exit 1
10
+ }
11
+
12
+ # Check that other modules are still importable, even when _C is not importable
13
+ python -c "from detectron2 import modeling"
14
+ python -c "from detectron2 import modeling, data"
15
+ python -c "from detectron2 import evaluation, export, checkpoint"
16
+ python -c "from detectron2 import utils, engine"
approach/ovod/detectron2/.github/CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Code of Conduct
2
+
3
+ Facebook has adopted a Code of Conduct that we expect project participants to adhere to.
4
+ Please read the [full text](https://code.fb.com/codeofconduct/)
5
+ so that you can understand what actions will and will not be tolerated.
approach/ovod/detectron2/.github/CONTRIBUTING.md ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to detectron2
2
+
3
+ ## Issues
4
+ We use GitHub issues to track public bugs and questions.
5
+ Please make sure to follow one of the
6
+ [issue templates](https://github.com/facebookresearch/detectron2/issues/new/choose)
7
+ when reporting any issues.
8
+
9
+ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
10
+ disclosure of security bugs. In those cases, please go through the process
11
+ outlined on that page and do not file a public issue.
12
+
13
+ ## Pull Requests
14
+ We actively welcome pull requests.
15
+
16
+ However, if you're adding any significant features (e.g. > 50 lines), please
17
+ make sure to discuss with maintainers about your motivation and proposals in an issue
18
+ before sending a PR. This is to save your time so you don't spend time on a PR that we'll not accept.
19
+
20
+ We do not always accept new features, and we take the following
21
+ factors into consideration:
22
+
23
+ 1. Whether the same feature can be achieved without modifying detectron2.
24
+ Detectron2 is designed so that you can implement many extensions from the outside, e.g.
25
+ those in [projects](https://github.com/facebookresearch/detectron2/tree/master/projects).
26
+ * If some part of detectron2 is not extensible enough, you can also bring up a more general issue to
27
+ improve it. Such feature request may be useful to more users.
28
+ 2. Whether the feature is potentially useful to a large audience (e.g. an impactful detection paper, a popular dataset,
29
+ a significant speedup, a widely useful utility),
30
+ or only to a small portion of users (e.g., a less-known paper, an improvement not in the object
31
+ detection field, a trick that's not very popular in the community, code to handle a non-standard type of data)
32
+ * Adoption of additional models, datasets, new task are by default not added to detectron2 before they
33
+ receive significant popularity in the community.
34
+ We sometimes accept such features in `projects/`, or as a link in `projects/README.md`.
35
+ 3. Whether the proposed solution has a good design / interface. This can be discussed in the issue prior to PRs, or
36
+ in the form of a draft PR.
37
+ 4. Whether the proposed solution adds extra mental/practical overhead to users who don't
38
+ need such feature.
39
+ 5. Whether the proposed solution breaks existing APIs.
40
+
41
+ To add a feature to an existing function/class `Func`, there are always two approaches:
42
+ (1) add new arguments to `Func`; (2) write a new `Func_with_new_feature`.
43
+ To meet the above criteria, we often prefer approach (2), because:
44
+
45
+ 1. It does not involve modifying or potentially breaking existing code.
46
+ 2. It does not add overhead to users who do not need the new feature.
47
+ 3. Adding new arguments to a function/class is not scalable w.r.t. all the possible new research ideas in the future.
48
+
49
+ When sending a PR, please do:
50
+
51
+ 1. If a PR contains multiple orthogonal changes, split it to several PRs.
52
+ 2. If you've added code that should be tested, add tests.
53
+ 3. For PRs that need experiments (e.g. adding a new model or new methods),
54
+ you don't need to update model zoo, but do provide experiment results in the description of the PR.
55
+ 4. If APIs are changed, update the documentation.
56
+ 5. We use the [Google style docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) in python.
57
+ 6. Make sure your code lints with `./dev/linter.sh`.
58
+
59
+
60
+ ## Contributor License Agreement ("CLA")
61
+ In order to accept your pull request, we need you to submit a CLA. You only need
62
+ to do this once to work on any of Facebook's open source projects.
63
+
64
+ Complete your CLA here: <https://code.facebook.com/cla>
65
+
66
+ ## License
67
+ By contributing to detectron2, you agree that your contributions will be licensed
68
+ under the LICENSE file in the root directory of this source tree.
approach/ovod/detectron2/.github/Detectron2-Logo-Horz.svg ADDED
approach/ovod/detectron2/.github/ISSUE_TEMPLATE.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+
2
+ Please select an issue template from
3
+ https://github.com/facebookresearch/detectron2/issues/new/choose .
4
+
5
+ Otherwise your issue will be closed.
approach/ovod/detectron2/.github/pull_request_template.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Thanks for your contribution!
2
+
3
+ If you're sending a large PR (e.g., >100 lines),
4
+ please open an issue first about the feature / bug, and indicate how you want to contribute.
5
+
6
+ We do not always accept features.
7
+ See https://detectron2.readthedocs.io/notes/contributing.html#pull-requests about how we handle PRs.
8
+
9
+ Before submitting a PR, please run `dev/linter.sh` to lint the code.
10
+
approach/ovod/detectron2/configs/Base-RCNN-C4.yaml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL:
2
+ META_ARCHITECTURE: "GeneralizedRCNN"
3
+ RPN:
4
+ PRE_NMS_TOPK_TEST: 6000
5
+ POST_NMS_TOPK_TEST: 1000
6
+ ROI_HEADS:
7
+ NAME: "Res5ROIHeads"
8
+ DATASETS:
9
+ TRAIN: ("coco_2017_train",)
10
+ TEST: ("coco_2017_val",)
11
+ SOLVER:
12
+ IMS_PER_BATCH: 16
13
+ BASE_LR: 0.02
14
+ STEPS: (60000, 80000)
15
+ MAX_ITER: 90000
16
+ INPUT:
17
+ MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
18
+ VERSION: 2
approach/ovod/detectron2/configs/Base-RCNN-DilatedC5.yaml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL:
2
+ META_ARCHITECTURE: "GeneralizedRCNN"
3
+ RESNETS:
4
+ OUT_FEATURES: ["res5"]
5
+ RES5_DILATION: 2
6
+ RPN:
7
+ IN_FEATURES: ["res5"]
8
+ PRE_NMS_TOPK_TEST: 6000
9
+ POST_NMS_TOPK_TEST: 1000
10
+ ROI_HEADS:
11
+ NAME: "StandardROIHeads"
12
+ IN_FEATURES: ["res5"]
13
+ ROI_BOX_HEAD:
14
+ NAME: "FastRCNNConvFCHead"
15
+ NUM_FC: 2
16
+ POOLER_RESOLUTION: 7
17
+ ROI_MASK_HEAD:
18
+ NAME: "MaskRCNNConvUpsampleHead"
19
+ NUM_CONV: 4
20
+ POOLER_RESOLUTION: 14
21
+ DATASETS:
22
+ TRAIN: ("coco_2017_train",)
23
+ TEST: ("coco_2017_val",)
24
+ SOLVER:
25
+ IMS_PER_BATCH: 16
26
+ BASE_LR: 0.02
27
+ STEPS: (60000, 80000)
28
+ MAX_ITER: 90000
29
+ INPUT:
30
+ MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
31
+ VERSION: 2
approach/ovod/detectron2/configs/Base-RCNN-FPN.yaml ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL:
2
+ META_ARCHITECTURE: "GeneralizedRCNN"
3
+ BACKBONE:
4
+ NAME: "build_resnet_fpn_backbone"
5
+ RESNETS:
6
+ OUT_FEATURES: ["res2", "res3", "res4", "res5"]
7
+ FPN:
8
+ IN_FEATURES: ["res2", "res3", "res4", "res5"]
9
+ ANCHOR_GENERATOR:
10
+ SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map
11
+ ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps)
12
+ RPN:
13
+ IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"]
14
+ PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level
15
+ PRE_NMS_TOPK_TEST: 1000 # Per FPN level
16
+ # Detectron1 uses 2000 proposals per-batch,
17
+ # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue)
18
+ # which is approximately 1000 proposals per-image since the default batch size for FPN is 2.
19
+ POST_NMS_TOPK_TRAIN: 1000
20
+ POST_NMS_TOPK_TEST: 1000
21
+ ROI_HEADS:
22
+ NAME: "StandardROIHeads"
23
+ IN_FEATURES: ["p2", "p3", "p4", "p5"]
24
+ ROI_BOX_HEAD:
25
+ NAME: "FastRCNNConvFCHead"
26
+ NUM_FC: 2
27
+ POOLER_RESOLUTION: 7
28
+ ROI_MASK_HEAD:
29
+ NAME: "MaskRCNNConvUpsampleHead"
30
+ NUM_CONV: 4
31
+ POOLER_RESOLUTION: 14
32
+ DATASETS:
33
+ TRAIN: ("coco_2017_train",)
34
+ TEST: ("coco_2017_val",)
35
+ SOLVER:
36
+ IMS_PER_BATCH: 16
37
+ BASE_LR: 0.02
38
+ STEPS: (60000, 80000)
39
+ MAX_ITER: 90000
40
+ INPUT:
41
+ MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
42
+ VERSION: 2
approach/ovod/detectron2/configs/Base-RetinaNet.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL:
2
+ META_ARCHITECTURE: "RetinaNet"
3
+ BACKBONE:
4
+ NAME: "build_retinanet_resnet_fpn_backbone"
5
+ RESNETS:
6
+ OUT_FEATURES: ["res3", "res4", "res5"]
7
+ ANCHOR_GENERATOR:
8
+ SIZES: !!python/object/apply:eval ["[[x, x * 2**(1.0/3), x * 2**(2.0/3) ] for x in [32, 64, 128, 256, 512 ]]"]
9
+ FPN:
10
+ IN_FEATURES: ["res3", "res4", "res5"]
11
+ RETINANET:
12
+ IOU_THRESHOLDS: [0.4, 0.5]
13
+ IOU_LABELS: [0, -1, 1]
14
+ SMOOTH_L1_LOSS_BETA: 0.0
15
+ DATASETS:
16
+ TRAIN: ("coco_2017_train",)
17
+ TEST: ("coco_2017_val",)
18
+ SOLVER:
19
+ IMS_PER_BATCH: 16
20
+ BASE_LR: 0.01 # Note that RetinaNet uses a different default learning rate
21
+ STEPS: (60000, 80000)
22
+ MAX_ITER: 90000
23
+ INPUT:
24
+ MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
25
+ VERSION: 2
approach/ovod/detectron2/demo/README.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+
2
+ ## Detectron2 Demo
3
+
4
+ We provide a command line tool to run a simple demo of builtin configs.
5
+ The usage is explained in [GETTING_STARTED.md](../GETTING_STARTED.md).
6
+
7
+ See our [blog post](https://ai.facebook.com/blog/-detectron2-a-pytorch-based-modular-object-detection-library-)
8
+ for a high-quality demo generated with this tool.
approach/ovod/detectron2/demo/demo.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ import argparse
3
+ import glob
4
+ import multiprocessing as mp
5
+ import numpy as np
6
+ import os
7
+ import tempfile
8
+ import time
9
+ import warnings
10
+ import cv2
11
+ import tqdm
12
+
13
+ from detectron2.config import get_cfg
14
+ from detectron2.data.detection_utils import read_image
15
+ from detectron2.utils.logger import setup_logger
16
+
17
+ from predictor import VisualizationDemo
18
+
19
+ # constants
20
+ WINDOW_NAME = "COCO detections"
21
+
22
+
23
+ def setup_cfg(args):
24
+ # load config from file and command-line arguments
25
+ cfg = get_cfg()
26
+ # To use demo for Panoptic-DeepLab, please uncomment the following two lines.
27
+ # from detectron2.projects.panoptic_deeplab import add_panoptic_deeplab_config # noqa
28
+ # add_panoptic_deeplab_config(cfg)
29
+ cfg.merge_from_file(args.config_file)
30
+ cfg.merge_from_list(args.opts)
31
+ # Set score_threshold for builtin models
32
+ cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold
33
+ cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold
34
+ cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold
35
+ cfg.freeze()
36
+ return cfg
37
+
38
+
39
+ def get_parser():
40
+ parser = argparse.ArgumentParser(description="Detectron2 demo for builtin configs")
41
+ parser.add_argument(
42
+ "--config-file",
43
+ default="configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml",
44
+ metavar="FILE",
45
+ help="path to config file",
46
+ )
47
+ parser.add_argument("--webcam", action="store_true", help="Take inputs from webcam.")
48
+ parser.add_argument("--video-input", help="Path to video file.")
49
+ parser.add_argument(
50
+ "--input",
51
+ nargs="+",
52
+ help="A list of space separated input images; "
53
+ "or a single glob pattern such as 'directory/*.jpg'",
54
+ )
55
+ parser.add_argument(
56
+ "--output",
57
+ help="A file or directory to save output visualizations. "
58
+ "If not given, will show output in an OpenCV window.",
59
+ )
60
+
61
+ parser.add_argument(
62
+ "--confidence-threshold",
63
+ type=float,
64
+ default=0.5,
65
+ help="Minimum score for instance predictions to be shown",
66
+ )
67
+ parser.add_argument(
68
+ "--opts",
69
+ help="Modify config options using the command-line 'KEY VALUE' pairs",
70
+ default=[],
71
+ nargs=argparse.REMAINDER,
72
+ )
73
+ return parser
74
+
75
+
76
+ def test_opencv_video_format(codec, file_ext):
77
+ with tempfile.TemporaryDirectory(prefix="video_format_test") as dir:
78
+ filename = os.path.join(dir, "test_file" + file_ext)
79
+ writer = cv2.VideoWriter(
80
+ filename=filename,
81
+ fourcc=cv2.VideoWriter_fourcc(*codec),
82
+ fps=float(30),
83
+ frameSize=(10, 10),
84
+ isColor=True,
85
+ )
86
+ [writer.write(np.zeros((10, 10, 3), np.uint8)) for _ in range(30)]
87
+ writer.release()
88
+ if os.path.isfile(filename):
89
+ return True
90
+ return False
91
+
92
+
93
+ if __name__ == "__main__":
94
+ mp.set_start_method("spawn", force=True)
95
+ args = get_parser().parse_args()
96
+ setup_logger(name="fvcore")
97
+ logger = setup_logger()
98
+ logger.info("Arguments: " + str(args))
99
+
100
+ cfg = setup_cfg(args)
101
+
102
+ demo = VisualizationDemo(cfg)
103
+
104
+ if args.input:
105
+ if len(args.input) == 1:
106
+ args.input = glob.glob(os.path.expanduser(args.input[0]))
107
+ assert args.input, "The input path(s) was not found"
108
+ for path in tqdm.tqdm(args.input, disable=not args.output):
109
+ # use PIL, to be consistent with evaluation
110
+ img = read_image(path, format="BGR")
111
+ start_time = time.time()
112
+ predictions, visualized_output = demo.run_on_image(img)
113
+ logger.info(
114
+ "{}: {} in {:.2f}s".format(
115
+ path,
116
+ "detected {} instances".format(len(predictions["instances"]))
117
+ if "instances" in predictions
118
+ else "finished",
119
+ time.time() - start_time,
120
+ )
121
+ )
122
+
123
+ if args.output:
124
+ if os.path.isdir(args.output):
125
+ assert os.path.isdir(args.output), args.output
126
+ out_filename = os.path.join(args.output, os.path.basename(path))
127
+ else:
128
+ assert len(args.input) == 1, "Please specify a directory with args.output"
129
+ out_filename = args.output
130
+ visualized_output.save(out_filename)
131
+ else:
132
+ cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
133
+ cv2.imshow(WINDOW_NAME, visualized_output.get_image()[:, :, ::-1])
134
+ if cv2.waitKey(0) == 27:
135
+ break # esc to quit
136
+ elif args.webcam:
137
+ assert args.input is None, "Cannot have both --input and --webcam!"
138
+ assert args.output is None, "output not yet supported with --webcam!"
139
+ cam = cv2.VideoCapture(0)
140
+ for vis in tqdm.tqdm(demo.run_on_video(cam)):
141
+ cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
142
+ cv2.imshow(WINDOW_NAME, vis)
143
+ if cv2.waitKey(1) == 27:
144
+ break # esc to quit
145
+ cam.release()
146
+ cv2.destroyAllWindows()
147
+ elif args.video_input:
148
+ video = cv2.VideoCapture(args.video_input)
149
+ width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
150
+ height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
151
+ frames_per_second = video.get(cv2.CAP_PROP_FPS)
152
+ num_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
153
+ basename = os.path.basename(args.video_input)
154
+ codec, file_ext = (
155
+ ("x264", ".mkv") if test_opencv_video_format("x264", ".mkv") else ("mp4v", ".mp4")
156
+ )
157
+ if codec == ".mp4v":
158
+ warnings.warn("x264 codec not available, switching to mp4v")
159
+ if args.output:
160
+ if os.path.isdir(args.output):
161
+ output_fname = os.path.join(args.output, basename)
162
+ output_fname = os.path.splitext(output_fname)[0] + file_ext
163
+ else:
164
+ output_fname = args.output
165
+ assert not os.path.isfile(output_fname), output_fname
166
+ output_file = cv2.VideoWriter(
167
+ filename=output_fname,
168
+ # some installation of opencv may not support x264 (due to its license),
169
+ # you can try other format (e.g. MPEG)
170
+ fourcc=cv2.VideoWriter_fourcc(*codec),
171
+ fps=float(frames_per_second),
172
+ frameSize=(width, height),
173
+ isColor=True,
174
+ )
175
+ assert os.path.isfile(args.video_input)
176
+ for vis_frame in tqdm.tqdm(demo.run_on_video(video), total=num_frames):
177
+ if args.output:
178
+ output_file.write(vis_frame)
179
+ else:
180
+ cv2.namedWindow(basename, cv2.WINDOW_NORMAL)
181
+ cv2.imshow(basename, vis_frame)
182
+ if cv2.waitKey(1) == 27:
183
+ break # esc to quit
184
+ video.release()
185
+ if args.output:
186
+ output_file.release()
187
+ else:
188
+ cv2.destroyAllWindows()
approach/ovod/detectron2/demo/predictor.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ import atexit
3
+ import bisect
4
+ import multiprocessing as mp
5
+ from collections import deque
6
+ import cv2
7
+ import torch
8
+
9
+ from detectron2.data import MetadataCatalog
10
+ from detectron2.engine.defaults import DefaultPredictor
11
+ from detectron2.utils.video_visualizer import VideoVisualizer
12
+ from detectron2.utils.visualizer import ColorMode, Visualizer
13
+
14
+
15
+ class VisualizationDemo(object):
16
+ def __init__(self, cfg, instance_mode=ColorMode.IMAGE, parallel=False):
17
+ """
18
+ Args:
19
+ cfg (CfgNode):
20
+ instance_mode (ColorMode):
21
+ parallel (bool): whether to run the model in different processes from visualization.
22
+ Useful since the visualization logic can be slow.
23
+ """
24
+ self.metadata = MetadataCatalog.get(
25
+ cfg.DATASETS.TEST[0] if len(cfg.DATASETS.TEST) else "__unused"
26
+ )
27
+ self.cpu_device = torch.device("cpu")
28
+ self.instance_mode = instance_mode
29
+
30
+ self.parallel = parallel
31
+ if parallel:
32
+ num_gpu = torch.cuda.device_count()
33
+ self.predictor = AsyncPredictor(cfg, num_gpus=num_gpu)
34
+ else:
35
+ self.predictor = DefaultPredictor(cfg)
36
+
37
+ def run_on_image(self, image):
38
+ """
39
+ Args:
40
+ image (np.ndarray): an image of shape (H, W, C) (in BGR order).
41
+ This is the format used by OpenCV.
42
+
43
+ Returns:
44
+ predictions (dict): the output of the model.
45
+ vis_output (VisImage): the visualized image output.
46
+ """
47
+ vis_output = None
48
+ predictions = self.predictor(image)
49
+ # Convert image from OpenCV BGR format to Matplotlib RGB format.
50
+ image = image[:, :, ::-1]
51
+ visualizer = Visualizer(image, self.metadata, instance_mode=self.instance_mode)
52
+ if "panoptic_seg" in predictions:
53
+ panoptic_seg, segments_info = predictions["panoptic_seg"]
54
+ vis_output = visualizer.draw_panoptic_seg_predictions(
55
+ panoptic_seg.to(self.cpu_device), segments_info
56
+ )
57
+ else:
58
+ if "sem_seg" in predictions:
59
+ vis_output = visualizer.draw_sem_seg(
60
+ predictions["sem_seg"].argmax(dim=0).to(self.cpu_device)
61
+ )
62
+ if "instances" in predictions:
63
+ instances = predictions["instances"].to(self.cpu_device)
64
+ vis_output = visualizer.draw_instance_predictions(predictions=instances)
65
+
66
+ return predictions, vis_output
67
+
68
+ def _frame_from_video(self, video):
69
+ while video.isOpened():
70
+ success, frame = video.read()
71
+ if success:
72
+ yield frame
73
+ else:
74
+ break
75
+
76
+ def run_on_video(self, video):
77
+ """
78
+ Visualizes predictions on frames of the input video.
79
+
80
+ Args:
81
+ video (cv2.VideoCapture): a :class:`VideoCapture` object, whose source can be
82
+ either a webcam or a video file.
83
+
84
+ Yields:
85
+ ndarray: BGR visualizations of each video frame.
86
+ """
87
+ video_visualizer = VideoVisualizer(self.metadata, self.instance_mode)
88
+
89
+ def process_predictions(frame, predictions):
90
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
91
+ if "panoptic_seg" in predictions:
92
+ panoptic_seg, segments_info = predictions["panoptic_seg"]
93
+ vis_frame = video_visualizer.draw_panoptic_seg_predictions(
94
+ frame, panoptic_seg.to(self.cpu_device), segments_info
95
+ )
96
+ elif "instances" in predictions:
97
+ predictions = predictions["instances"].to(self.cpu_device)
98
+ vis_frame = video_visualizer.draw_instance_predictions(frame, predictions)
99
+ elif "sem_seg" in predictions:
100
+ vis_frame = video_visualizer.draw_sem_seg(
101
+ frame, predictions["sem_seg"].argmax(dim=0).to(self.cpu_device)
102
+ )
103
+
104
+ # Converts Matplotlib RGB format to OpenCV BGR format
105
+ vis_frame = cv2.cvtColor(vis_frame.get_image(), cv2.COLOR_RGB2BGR)
106
+ return vis_frame
107
+
108
+ frame_gen = self._frame_from_video(video)
109
+ if self.parallel:
110
+ buffer_size = self.predictor.default_buffer_size
111
+
112
+ frame_data = deque()
113
+
114
+ for cnt, frame in enumerate(frame_gen):
115
+ frame_data.append(frame)
116
+ self.predictor.put(frame)
117
+
118
+ if cnt >= buffer_size:
119
+ frame = frame_data.popleft()
120
+ predictions = self.predictor.get()
121
+ yield process_predictions(frame, predictions)
122
+
123
+ while len(frame_data):
124
+ frame = frame_data.popleft()
125
+ predictions = self.predictor.get()
126
+ yield process_predictions(frame, predictions)
127
+ else:
128
+ for frame in frame_gen:
129
+ yield process_predictions(frame, self.predictor(frame))
130
+
131
+
132
+ class AsyncPredictor:
133
+ """
134
+ A predictor that runs the model asynchronously, possibly on >1 GPUs.
135
+ Because rendering the visualization takes considerably amount of time,
136
+ this helps improve throughput a little bit when rendering videos.
137
+ """
138
+
139
+ class _StopToken:
140
+ pass
141
+
142
+ class _PredictWorker(mp.Process):
143
+ def __init__(self, cfg, task_queue, result_queue):
144
+ self.cfg = cfg
145
+ self.task_queue = task_queue
146
+ self.result_queue = result_queue
147
+ super().__init__()
148
+
149
+ def run(self):
150
+ predictor = DefaultPredictor(self.cfg)
151
+
152
+ while True:
153
+ task = self.task_queue.get()
154
+ if isinstance(task, AsyncPredictor._StopToken):
155
+ break
156
+ idx, data = task
157
+ result = predictor(data)
158
+ self.result_queue.put((idx, result))
159
+
160
+ def __init__(self, cfg, num_gpus: int = 1):
161
+ """
162
+ Args:
163
+ cfg (CfgNode):
164
+ num_gpus (int): if 0, will run on CPU
165
+ """
166
+ num_workers = max(num_gpus, 1)
167
+ self.task_queue = mp.Queue(maxsize=num_workers * 3)
168
+ self.result_queue = mp.Queue(maxsize=num_workers * 3)
169
+ self.procs = []
170
+ for gpuid in range(max(num_gpus, 1)):
171
+ cfg = cfg.clone()
172
+ cfg.defrost()
173
+ cfg.MODEL.DEVICE = "cuda:{}".format(gpuid) if num_gpus > 0 else "cpu"
174
+ self.procs.append(
175
+ AsyncPredictor._PredictWorker(cfg, self.task_queue, self.result_queue)
176
+ )
177
+
178
+ self.put_idx = 0
179
+ self.get_idx = 0
180
+ self.result_rank = []
181
+ self.result_data = []
182
+
183
+ for p in self.procs:
184
+ p.start()
185
+ atexit.register(self.shutdown)
186
+
187
+ def put(self, image):
188
+ self.put_idx += 1
189
+ self.task_queue.put((self.put_idx, image))
190
+
191
+ def get(self):
192
+ self.get_idx += 1 # the index needed for this request
193
+ if len(self.result_rank) and self.result_rank[0] == self.get_idx:
194
+ res = self.result_data[0]
195
+ del self.result_data[0], self.result_rank[0]
196
+ return res
197
+
198
+ while True:
199
+ # make sure the results are returned in the correct order
200
+ idx, res = self.result_queue.get()
201
+ if idx == self.get_idx:
202
+ return res
203
+ insert = bisect.bisect(self.result_rank, idx)
204
+ self.result_rank.insert(insert, idx)
205
+ self.result_data.insert(insert, res)
206
+
207
+ def __len__(self):
208
+ return self.put_idx - self.get_idx
209
+
210
+ def __call__(self, image):
211
+ self.put(image)
212
+ return self.get()
213
+
214
+ def shutdown(self):
215
+ for _ in self.procs:
216
+ self.task_queue.put(AsyncPredictor._StopToken())
217
+
218
+ @property
219
+ def default_buffer_size(self):
220
+ return len(self.procs) * 5
approach/ovod/detectron2/detectron2/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from .utils.env import setup_environment
4
+
5
+ setup_environment()
6
+
7
+
8
+ # This line will be programatically read/write by setup.py.
9
+ # Leave them at the bottom of this file and don't touch them.
10
+ __version__ = "0.6"
approach/ovod/detectron2/dev/README.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+
2
+ ## Some scripts for developers to use, include:
3
+
4
+ - `linter.sh`: lint the codebase before commit.
5
+ - `run_{inference,instant}_tests.sh`: run inference/training for a few iterations.
6
+ Note that these tests require 2 GPUs.
7
+ - `parse_results.sh`: parse results from a log file.
approach/ovod/detectron2/dev/linter.sh ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash -e
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ # cd to detectron2 project root
5
+ cd "$(dirname "${BASH_SOURCE[0]}")/.."
6
+
7
+ {
8
+ black --version | grep -E "22\." > /dev/null
9
+ } || {
10
+ echo "Linter requires 'black==22.*' !"
11
+ exit 1
12
+ }
13
+
14
+ ISORT_VERSION=$(isort --version-number)
15
+ if [[ "$ISORT_VERSION" != 4.3* ]]; then
16
+ echo "Linter requires isort==4.3.21 !"
17
+ exit 1
18
+ fi
19
+
20
+ set -v
21
+
22
+ echo "Running isort ..."
23
+ isort -y -sp . --atomic
24
+
25
+ echo "Running black ..."
26
+ black -l 100 .
27
+
28
+ echo "Running flake8 ..."
29
+ if [ -x "$(command -v flake8)" ]; then
30
+ flake8 .
31
+ else
32
+ python3 -m flake8 .
33
+ fi
34
+
35
+ # echo "Running mypy ..."
36
+ # Pytorch does not have enough type annotations
37
+ # mypy detectron2/solver detectron2/structures detectron2/config
38
+
39
+ echo "Running clang-format ..."
40
+ find . -regex ".*\.\(cpp\|c\|cc\|cu\|cxx\|h\|hh\|hpp\|hxx\|tcc\|mm\|m\)" -print0 | xargs -0 clang-format -i
41
+
42
+ command -v arc > /dev/null && arc lint
approach/ovod/detectron2/dev/parse_results.sh ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ # A shell script that parses metrics from the log file.
5
+ # Make it easier for developers to track performance of models.
6
+
7
+ LOG="$1"
8
+
9
+ if [[ -z "$LOG" ]]; then
10
+ echo "Usage: $0 /path/to/log/file"
11
+ exit 1
12
+ fi
13
+
14
+ # [12/15 11:47:32] trainer INFO: Total training time: 12:15:04.446477 (0.4900 s / it)
15
+ # [12/15 11:49:03] inference INFO: Total inference time: 0:01:25.326167 (0.13652186737060548 s / img per device, on 8 devices)
16
+ # [12/15 11:49:03] inference INFO: Total inference pure compute time: .....
17
+
18
+ # training time
19
+ trainspeed=$(grep -o 'Overall training.*' "$LOG" | grep -Eo '\(.*\)' | grep -o '[0-9\.]*')
20
+ echo "Training speed: $trainspeed s/it"
21
+
22
+ # inference time: there could be multiple inference during training
23
+ inferencespeed=$(grep -o 'Total inference pure.*' "$LOG" | tail -n1 | grep -Eo '\(.*\)' | grep -o '[0-9\.]*' | head -n1)
24
+ echo "Inference speed: $inferencespeed s/it"
25
+
26
+ # [12/15 11:47:18] trainer INFO: eta: 0:00:00 iter: 90000 loss: 0.5407 (0.7256) loss_classifier: 0.1744 (0.2446) loss_box_reg: 0.0838 (0.1160) loss_mask: 0.2159 (0.2722) loss_objectness: 0.0244 (0.0429) loss_rpn_box_reg: 0.0279 (0.0500) time: 0.4487 (0.4899) data: 0.0076 (0.0975) lr: 0.000200 max mem: 4161
27
+ memory=$(grep -o 'max[_ ]mem: [0-9]*' "$LOG" | tail -n1 | grep -o '[0-9]*')
28
+ echo "Training memory: $memory MB"
29
+
30
+ echo "Easy to copypaste:"
31
+ echo "$trainspeed","$inferencespeed","$memory"
32
+
33
+ echo "------------------------------"
34
+
35
+ # [12/26 17:26:32] engine.coco_evaluation: copypaste: Task: bbox
36
+ # [12/26 17:26:32] engine.coco_evaluation: copypaste: AP,AP50,AP75,APs,APm,APl
37
+ # [12/26 17:26:32] engine.coco_evaluation: copypaste: 0.0017,0.0024,0.0017,0.0005,0.0019,0.0011
38
+ # [12/26 17:26:32] engine.coco_evaluation: copypaste: Task: segm
39
+ # [12/26 17:26:32] engine.coco_evaluation: copypaste: AP,AP50,AP75,APs,APm,APl
40
+ # [12/26 17:26:32] engine.coco_evaluation: copypaste: 0.0014,0.0021,0.0016,0.0005,0.0016,0.0011
41
+
42
+ echo "COCO Results:"
43
+ num_tasks=$(grep -o 'copypaste:.*Task.*' "$LOG" | sort -u | wc -l)
44
+ # each task has 3 lines
45
+ grep -o 'copypaste:.*' "$LOG" | cut -d ' ' -f 2- | tail -n $((num_tasks * 3))
approach/ovod/detectron2/dev/run_inference_tests.sh ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash -e
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ BIN="python tools/train_net.py"
5
+ OUTPUT="inference_test_output"
6
+ NUM_GPUS=2
7
+
8
+ CFG_LIST=( "${@:1}" )
9
+
10
+ if [ ${#CFG_LIST[@]} -eq 0 ]; then
11
+ CFG_LIST=( ./configs/quick_schedules/*inference_acc_test.yaml )
12
+ fi
13
+
14
+ echo "========================================================================"
15
+ echo "Configs to run:"
16
+ echo "${CFG_LIST[@]}"
17
+ echo "========================================================================"
18
+
19
+
20
+ for cfg in "${CFG_LIST[@]}"; do
21
+ echo "========================================================================"
22
+ echo "Running $cfg ..."
23
+ echo "========================================================================"
24
+ $BIN \
25
+ --eval-only \
26
+ --num-gpus $NUM_GPUS \
27
+ --config-file "$cfg" \
28
+ OUTPUT_DIR $OUTPUT
29
+ rm -rf $OUTPUT
30
+ done
31
+
32
+
33
+ echo "========================================================================"
34
+ echo "Running demo.py ..."
35
+ echo "========================================================================"
36
+ DEMO_BIN="python demo/demo.py"
37
+ COCO_DIR=datasets/coco/val2014
38
+ mkdir -pv $OUTPUT
39
+
40
+ set -v
41
+
42
+ $DEMO_BIN --config-file ./configs/quick_schedules/panoptic_fpn_R_50_inference_acc_test.yaml \
43
+ --input $COCO_DIR/COCO_val2014_0000001933* --output $OUTPUT
44
+ rm -rf $OUTPUT
approach/ovod/detectron2/dev/run_instant_tests.sh ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash -e
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ BIN="python tools/train_net.py"
5
+ OUTPUT="instant_test_output"
6
+ NUM_GPUS=2
7
+
8
+ CFG_LIST=( "${@:1}" )
9
+ if [ ${#CFG_LIST[@]} -eq 0 ]; then
10
+ CFG_LIST=( ./configs/quick_schedules/*instant_test.yaml )
11
+ fi
12
+
13
+ echo "========================================================================"
14
+ echo "Configs to run:"
15
+ echo "${CFG_LIST[@]}"
16
+ echo "========================================================================"
17
+
18
+ for cfg in "${CFG_LIST[@]}"; do
19
+ echo "========================================================================"
20
+ echo "Running $cfg ..."
21
+ echo "========================================================================"
22
+ $BIN --num-gpus $NUM_GPUS --config-file "$cfg" \
23
+ SOLVER.IMS_PER_BATCH $(($NUM_GPUS * 2)) \
24
+ OUTPUT_DIR "$OUTPUT"
25
+ rm -rf "$OUTPUT"
26
+ done
27
+
approach/ovod/detectron2/docs/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ _build
approach/ovod/detectron2/docs/Makefile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Minimal makefile for Sphinx documentation
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ # You can set these variables from the command line.
5
+ SPHINXOPTS =
6
+ SPHINXBUILD = sphinx-build
7
+ SOURCEDIR = .
8
+ BUILDDIR = _build
9
+
10
+ # Put it first so that "make" without argument is like "make help".
11
+ help:
12
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13
+
14
+ .PHONY: help Makefile
15
+
16
+ # Catch-all target: route all unknown targets to Sphinx using the new
17
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18
+ %: Makefile
19
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
approach/ovod/detectron2/docs/README.md ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the docs:
2
+
3
+ The latest documentation built from this directory is available at [detectron2.readthedocs.io](https://detectron2.readthedocs.io/).
4
+ Documents in this directory are not meant to be read on github.
5
+
6
+ # Build the docs:
7
+
8
+ 1. Install detectron2 according to [INSTALL.md](../INSTALL.md).
9
+ 2. Install additional libraries required to build docs:
10
+ - docutils==0.16
11
+ - Sphinx==3.2.0
12
+ - recommonmark==0.6.0
13
+ - sphinx_rtd_theme
14
+
15
+ 3. Run `make html` from this directory.
approach/ovod/detectron2/docs/conf.py ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ # flake8: noqa
5
+
6
+ # Configuration file for the Sphinx documentation builder.
7
+ #
8
+ # This file does only contain a selection of the most common options. For a
9
+ # full list see the documentation:
10
+ # http://www.sphinx-doc.org/en/master/config
11
+
12
+ # -- Path setup --------------------------------------------------------------
13
+
14
+ # If extensions (or modules to document with autodoc) are in another directory,
15
+ # add these directories to sys.path here. If the directory is relative to the
16
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
17
+ #
18
+ import os
19
+ import sys
20
+ from unittest import mock
21
+ from sphinx.domains import Domain
22
+ from typing import Dict, List, Tuple
23
+
24
+ # The theme to use for HTML and HTML Help pages. See the documentation for
25
+ # a list of builtin themes.
26
+ #
27
+ import sphinx_rtd_theme
28
+
29
+
30
+ class GithubURLDomain(Domain):
31
+ """
32
+ Resolve certain links in markdown files to github source.
33
+ """
34
+
35
+ name = "githuburl"
36
+ ROOT = "https://github.com/facebookresearch/detectron2/blob/main/"
37
+ LINKED_DOC = ["tutorials/install", "tutorials/getting_started"]
38
+
39
+ def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
40
+ github_url = None
41
+ if not target.endswith("html") and target.startswith("../../"):
42
+ url = target.replace("../", "")
43
+ github_url = url
44
+ if fromdocname in self.LINKED_DOC:
45
+ # unresolved links in these docs are all github links
46
+ github_url = target
47
+
48
+ if github_url is not None:
49
+ if github_url.endswith("MODEL_ZOO") or github_url.endswith("README"):
50
+ # bug of recommonmark.
51
+ # https://github.com/readthedocs/recommonmark/blob/ddd56e7717e9745f11300059e4268e204138a6b1/recommonmark/parser.py#L152-L155
52
+ github_url += ".md"
53
+ print("Ref {} resolved to github:{}".format(target, github_url))
54
+ contnode["refuri"] = self.ROOT + github_url
55
+ return [("githuburl:any", contnode)]
56
+ else:
57
+ return []
58
+
59
+
60
+ # to support markdown
61
+ from recommonmark.parser import CommonMarkParser
62
+
63
+ sys.path.insert(0, os.path.abspath("../"))
64
+ os.environ["_DOC_BUILDING"] = "True"
65
+ DEPLOY = os.environ.get("READTHEDOCS") == "True"
66
+
67
+
68
+ # -- Project information -----------------------------------------------------
69
+
70
+ # fmt: off
71
+ try:
72
+ import torch # noqa
73
+ except ImportError:
74
+ for m in [
75
+ "torch", "torchvision", "torch.nn", "torch.nn.parallel", "torch.distributed", "torch.multiprocessing", "torch.autograd",
76
+ "torch.autograd.function", "torch.nn.modules", "torch.nn.modules.utils", "torch.utils", "torch.utils.data", "torch.onnx",
77
+ "torchvision", "torchvision.ops",
78
+ ]:
79
+ sys.modules[m] = mock.Mock(name=m)
80
+ sys.modules['torch'].__version__ = "1.7" # fake version
81
+ HAS_TORCH = False
82
+ else:
83
+ try:
84
+ torch.ops.detectron2 = mock.Mock(name="torch.ops.detectron2")
85
+ except:
86
+ pass
87
+ HAS_TORCH = True
88
+
89
+ for m in [
90
+ "cv2", "scipy", "portalocker", "detectron2._C",
91
+ "pycocotools", "pycocotools.mask", "pycocotools.coco", "pycocotools.cocoeval",
92
+ "google", "google.protobuf", "google.protobuf.internal", "onnx",
93
+ "caffe2", "caffe2.proto", "caffe2.python", "caffe2.python.utils", "caffe2.python.onnx", "caffe2.python.onnx.backend",
94
+ ]:
95
+ sys.modules[m] = mock.Mock(name=m)
96
+ # fmt: on
97
+ sys.modules["cv2"].__version__ = "3.4"
98
+
99
+ import detectron2 # isort: skip
100
+
101
+ if HAS_TORCH:
102
+ from detectron2.utils.env import fixup_module_metadata
103
+
104
+ fixup_module_metadata("torch.nn", torch.nn.__dict__)
105
+ fixup_module_metadata("torch.utils.data", torch.utils.data.__dict__)
106
+
107
+
108
+ project = "detectron2"
109
+ copyright = "2019-2020, detectron2 contributors"
110
+ author = "detectron2 contributors"
111
+
112
+ # The short X.Y version
113
+ version = detectron2.__version__
114
+ # The full version, including alpha/beta/rc tags
115
+ release = version
116
+
117
+
118
+ # -- General configuration ---------------------------------------------------
119
+
120
+ # If your documentation needs a minimal Sphinx version, state it here.
121
+ #
122
+ needs_sphinx = "3.0"
123
+
124
+ # Add any Sphinx extension module names here, as strings. They can be
125
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
126
+ # ones.
127
+ extensions = [
128
+ "recommonmark",
129
+ "sphinx.ext.autodoc",
130
+ "sphinx.ext.napoleon",
131
+ "sphinx.ext.intersphinx",
132
+ "sphinx.ext.todo",
133
+ "sphinx.ext.coverage",
134
+ "sphinx.ext.mathjax",
135
+ "sphinx.ext.viewcode",
136
+ "sphinx.ext.githubpages",
137
+ ]
138
+
139
+ # -- Configurations for plugins ------------
140
+ napoleon_google_docstring = True
141
+ napoleon_include_init_with_doc = True
142
+ napoleon_include_special_with_doc = True
143
+ napoleon_numpy_docstring = False
144
+ napoleon_use_rtype = False
145
+ autodoc_inherit_docstrings = False
146
+ autodoc_member_order = "bysource"
147
+
148
+ if DEPLOY:
149
+ intersphinx_timeout = 10
150
+ else:
151
+ # skip this when building locally
152
+ intersphinx_timeout = 0.5
153
+ intersphinx_mapping = {
154
+ "python": ("https://docs.python.org/3.7", None),
155
+ "numpy": ("https://docs.scipy.org/doc/numpy/", None),
156
+ "torch": ("https://pytorch.org/docs/master/", None),
157
+ }
158
+ # -------------------------
159
+
160
+
161
+ # Add any paths that contain templates here, relative to this directory.
162
+ templates_path = ["_templates"]
163
+
164
+ source_suffix = [".rst", ".md"]
165
+
166
+ # The master toctree document.
167
+ master_doc = "index"
168
+
169
+ # The language for content autogenerated by Sphinx. Refer to documentation
170
+ # for a list of supported languages.
171
+ #
172
+ # This is also used if you do content translation via gettext catalogs.
173
+ # Usually you set "language" from the command line for these cases.
174
+ language = None
175
+
176
+ # List of patterns, relative to source directory, that match files and
177
+ # directories to ignore when looking for source files.
178
+ # This pattern also affects html_static_path and html_extra_path.
179
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "build", "README.md", "tutorials/README.md"]
180
+
181
+ # The name of the Pygments (syntax highlighting) style to use.
182
+ pygments_style = "sphinx"
183
+
184
+
185
+ # -- Options for HTML output -------------------------------------------------
186
+
187
+ html_theme = "sphinx_rtd_theme"
188
+ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
189
+
190
+ # Theme options are theme-specific and customize the look and feel of a theme
191
+ # further. For a list of options available for each theme, see the
192
+ # documentation.
193
+ #
194
+ # html_theme_options = {}
195
+
196
+ # Add any paths that contain custom static files (such as style sheets) here,
197
+ # relative to this directory. They are copied after the builtin static files,
198
+ # so a file named "default.css" will overwrite the builtin "default.css".
199
+ html_static_path = ["_static"]
200
+ html_css_files = ["css/custom.css"]
201
+
202
+ # Custom sidebar templates, must be a dictionary that maps document names
203
+ # to template names.
204
+ #
205
+ # The default sidebars (for documents that don't match any pattern) are
206
+ # defined by theme itself. Builtin themes are using these templates by
207
+ # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
208
+ # 'searchbox.html']``.
209
+ #
210
+ # html_sidebars = {}
211
+
212
+
213
+ # -- Options for HTMLHelp output ---------------------------------------------
214
+
215
+ # Output file base name for HTML help builder.
216
+ htmlhelp_basename = "detectron2doc"
217
+
218
+
219
+ # -- Options for LaTeX output ------------------------------------------------
220
+
221
+ latex_elements = {
222
+ # The paper size ('letterpaper' or 'a4paper').
223
+ #
224
+ # 'papersize': 'letterpaper',
225
+ # The font size ('10pt', '11pt' or '12pt').
226
+ #
227
+ # 'pointsize': '10pt',
228
+ # Additional stuff for the LaTeX preamble.
229
+ #
230
+ # 'preamble': '',
231
+ # Latex figure (float) alignment
232
+ #
233
+ # 'figure_align': 'htbp',
234
+ }
235
+
236
+ # Grouping the document tree into LaTeX files. List of tuples
237
+ # (source start file, target name, title,
238
+ # author, documentclass [howto, manual, or own class]).
239
+ latex_documents = [
240
+ (master_doc, "detectron2.tex", "detectron2 Documentation", "detectron2 contributors", "manual")
241
+ ]
242
+
243
+
244
+ # -- Options for manual page output ------------------------------------------
245
+
246
+ # One entry per manual page. List of tuples
247
+ # (source start file, name, description, authors, manual section).
248
+ man_pages = [(master_doc, "detectron2", "detectron2 Documentation", [author], 1)]
249
+
250
+
251
+ # -- Options for Texinfo output ----------------------------------------------
252
+
253
+ # Grouping the document tree into Texinfo files. List of tuples
254
+ # (source start file, target name, title, author,
255
+ # dir menu entry, description, category)
256
+ texinfo_documents = [
257
+ (
258
+ master_doc,
259
+ "detectron2",
260
+ "detectron2 Documentation",
261
+ author,
262
+ "detectron2",
263
+ "One line description of project.",
264
+ "Miscellaneous",
265
+ )
266
+ ]
267
+
268
+
269
+ # -- Options for todo extension ----------------------------------------------
270
+
271
+ # If true, `todo` and `todoList` produce output, else they produce nothing.
272
+ todo_include_todos = True
273
+
274
+
275
+ def autodoc_skip_member(app, what, name, obj, skip, options):
276
+ # we hide something deliberately
277
+ if getattr(obj, "__HIDE_SPHINX_DOC__", False):
278
+ return True
279
+
280
+ # Hide some that are deprecated or not intended to be used
281
+ HIDDEN = {
282
+ "ResNetBlockBase",
283
+ "GroupedBatchSampler",
284
+ "build_transform_gen",
285
+ "apply_transform_gens",
286
+ "TransformGen",
287
+ "apply_augmentations",
288
+ "StandardAugInput",
289
+ "build_batch_data_loader",
290
+ "draw_panoptic_seg_predictions",
291
+ "WarmupCosineLR",
292
+ "WarmupMultiStepLR",
293
+ "downgrade_config",
294
+ "upgrade_config",
295
+ "add_export_config",
296
+ }
297
+ try:
298
+ if name in HIDDEN or (
299
+ hasattr(obj, "__doc__") and obj.__doc__.lower().strip().startswith("deprecated")
300
+ ):
301
+ print("Skipping deprecated object: {}".format(name))
302
+ return True
303
+ except:
304
+ pass
305
+ return skip
306
+
307
+
308
+ _PAPER_DATA = {
309
+ "resnet": ("1512.03385", "Deep Residual Learning for Image Recognition"),
310
+ "fpn": ("1612.03144", "Feature Pyramid Networks for Object Detection"),
311
+ "mask r-cnn": ("1703.06870", "Mask R-CNN"),
312
+ "faster r-cnn": (
313
+ "1506.01497",
314
+ "Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks",
315
+ ),
316
+ "deformconv": ("1703.06211", "Deformable Convolutional Networks"),
317
+ "deformconv2": ("1811.11168", "Deformable ConvNets v2: More Deformable, Better Results"),
318
+ "panopticfpn": ("1901.02446", "Panoptic Feature Pyramid Networks"),
319
+ "retinanet": ("1708.02002", "Focal Loss for Dense Object Detection"),
320
+ "cascade r-cnn": ("1712.00726", "Cascade R-CNN: Delving into High Quality Object Detection"),
321
+ "lvis": ("1908.03195", "LVIS: A Dataset for Large Vocabulary Instance Segmentation"),
322
+ "rrpn": ("1703.01086", "Arbitrary-Oriented Scene Text Detection via Rotation Proposals"),
323
+ "imagenet in 1h": ("1706.02677", "Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour"),
324
+ "xception": ("1610.02357", "Xception: Deep Learning with Depthwise Separable Convolutions"),
325
+ "mobilenet": (
326
+ "1704.04861",
327
+ "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications",
328
+ ),
329
+ "deeplabv3+": (
330
+ "1802.02611",
331
+ "Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation",
332
+ ),
333
+ "dds": ("2003.13678", "Designing Network Design Spaces"),
334
+ "scaling": ("2103.06877", "Fast and Accurate Model Scaling"),
335
+ "fcos": ("2006.09214", "FCOS: A Simple and Strong Anchor-free Object Detector"),
336
+ "rethinking-batchnorm": ("2105.07576", 'Rethinking "Batch" in BatchNorm'),
337
+ "vitdet": ("2203.16527", "Exploring Plain Vision Transformer Backbones for Object Detection"),
338
+ "mvitv2": (
339
+ "2112.01526",
340
+ "MViTv2: Improved Multiscale Vision Transformers for Classification and Detection",
341
+ ),
342
+ "swin": (
343
+ "2103.14030",
344
+ "Swin Transformer: Hierarchical Vision Transformer using Shifted Windows",
345
+ ),
346
+ }
347
+
348
+
349
+ def paper_ref_role(
350
+ typ: str,
351
+ rawtext: str,
352
+ text: str,
353
+ lineno: int,
354
+ inliner,
355
+ options: Dict = {},
356
+ content: List[str] = [],
357
+ ):
358
+ """
359
+ Parse :paper:`xxx`. Similar to the "extlinks" sphinx extension.
360
+ """
361
+ from docutils import nodes, utils
362
+ from sphinx.util.nodes import split_explicit_title
363
+
364
+ text = utils.unescape(text)
365
+ has_explicit_title, title, link = split_explicit_title(text)
366
+ link = link.lower()
367
+ if link not in _PAPER_DATA:
368
+ inliner.reporter.warning("Cannot find paper " + link)
369
+ paper_url, paper_title = "#", link
370
+ else:
371
+ paper_url, paper_title = _PAPER_DATA[link]
372
+ if "/" not in paper_url:
373
+ paper_url = "https://arxiv.org/abs/" + paper_url
374
+ if not has_explicit_title:
375
+ title = paper_title
376
+ pnode = nodes.reference(title, title, internal=False, refuri=paper_url)
377
+ return [pnode], []
378
+
379
+
380
+ def setup(app):
381
+ from recommonmark.transform import AutoStructify
382
+
383
+ app.add_domain(GithubURLDomain)
384
+ app.connect("autodoc-skip-member", autodoc_skip_member)
385
+ app.add_role("paper", paper_ref_role)
386
+ app.add_config_value(
387
+ "recommonmark_config",
388
+ {"enable_math": True, "enable_inline_math": True, "enable_eval_rst": True},
389
+ True,
390
+ )
391
+ app.add_transform(AutoStructify)
approach/ovod/detectron2/docs/index.rst ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. detectron2 documentation master file, created by
2
+ sphinx-quickstart on Sat Sep 21 13:46:45 2019.
3
+ You can adapt this file completely to your liking, but it should at least
4
+ contain the root `toctree` directive.
5
+
6
+ Welcome to detectron2's documentation!
7
+ ======================================
8
+
9
+ .. toctree::
10
+ :maxdepth: 2
11
+
12
+ tutorials/index
13
+ notes/index
14
+ modules/index
approach/ovod/detectron2/docs/notes/benchmarks.md ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Benchmarks
3
+
4
+ Here we benchmark the training speed of a Mask R-CNN in detectron2,
5
+ with some other popular open source Mask R-CNN implementations.
6
+
7
+
8
+ ### Settings
9
+
10
+ * Hardware: 8 NVIDIA V100s with NVLink.
11
+ * Software: Python 3.7, CUDA 10.1, cuDNN 7.6.5, PyTorch 1.5,
12
+ TensorFlow 1.15.0rc2, Keras 2.2.5, MxNet 1.6.0b20190820.
13
+ * Model: an end-to-end R-50-FPN Mask-RCNN model, using the same hyperparameter as the
14
+ [Detectron baseline config](https://github.com/facebookresearch/Detectron/blob/master/configs/12_2017_baselines/e2e_mask_rcnn_R-50-FPN_1x.yaml)
15
+ (it does not have scale augmentation).
16
+ * Metrics: We use the average throughput in iterations 100-500 to skip GPU warmup time.
17
+ Note that for R-CNN-style models, the throughput of a model typically changes during training, because
18
+ it depends on the predictions of the model. Therefore this metric is not directly comparable with
19
+ "train speed" in model zoo, which is the average speed of the entire training run.
20
+
21
+
22
+ ### Main Results
23
+
24
+ ```eval_rst
25
+ +-------------------------------+--------------------+
26
+ | Implementation | Throughput (img/s) |
27
+ +===============================+====================+
28
+ | |D2| |PT| | 62 |
29
+ +-------------------------------+--------------------+
30
+ | mmdetection_ |PT| | 53 |
31
+ +-------------------------------+--------------------+
32
+ | maskrcnn-benchmark_ |PT| | 53 |
33
+ +-------------------------------+--------------------+
34
+ | tensorpack_ |TF| | 50 |
35
+ +-------------------------------+--------------------+
36
+ | simpledet_ |mxnet| | 39 |
37
+ +-------------------------------+--------------------+
38
+ | Detectron_ |C2| | 19 |
39
+ +-------------------------------+--------------------+
40
+ | `matterport/Mask_RCNN`__ |TF| | 14 |
41
+ +-------------------------------+--------------------+
42
+
43
+ .. _maskrcnn-benchmark: https://github.com/facebookresearch/maskrcnn-benchmark/
44
+ .. _tensorpack: https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN
45
+ .. _mmdetection: https://github.com/open-mmlab/mmdetection/
46
+ .. _simpledet: https://github.com/TuSimple/simpledet/
47
+ .. _Detectron: https://github.com/facebookresearch/Detectron
48
+ __ https://github.com/matterport/Mask_RCNN/
49
+
50
+ .. |D2| image:: https://github.com/facebookresearch/detectron2/raw/main/.github/Detectron2-Logo-Horz.svg?sanitize=true
51
+ :height: 15pt
52
+ :target: https://github.com/facebookresearch/detectron2/
53
+ .. |PT| image:: https://pytorch.org/assets/images/logo-icon.svg
54
+ :width: 15pt
55
+ :height: 15pt
56
+ :target: https://pytorch.org
57
+ .. |TF| image:: https://static.nvidiagrid.net/ngc/containers/tensorflow.png
58
+ :width: 15pt
59
+ :height: 15pt
60
+ :target: https://tensorflow.org
61
+ .. |mxnet| image:: https://github.com/dmlc/web-data/raw/master/mxnet/image/mxnet_favicon.png
62
+ :width: 15pt
63
+ :height: 15pt
64
+ :target: https://mxnet.apache.org/
65
+ .. |C2| image:: https://caffe2.ai/static/logo.svg
66
+ :width: 15pt
67
+ :height: 15pt
68
+ :target: https://caffe2.ai
69
+ ```
70
+
71
+
72
+ Details for each implementation:
73
+
74
+ * __Detectron2__: with release v0.1.2, run:
75
+ ```
76
+ python tools/train_net.py --config-file configs/Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x.yaml --num-gpus 8
77
+ ```
78
+
79
+ * __mmdetection__: at commit `b0d845f`, run
80
+ ```
81
+ ./tools/dist_train.sh configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_1x_coco.py 8
82
+ ```
83
+
84
+ * __maskrcnn-benchmark__: use commit `0ce8f6f` with `sed -i 's/torch.uint8/torch.bool/g' **/*.py; sed -i 's/AT_CHECK/TORCH_CHECK/g' **/*.cu`
85
+ to make it compatible with PyTorch 1.5. Then, run training with
86
+ ```
87
+ python -m torch.distributed.launch --nproc_per_node=8 tools/train_net.py --config-file configs/e2e_mask_rcnn_R_50_FPN_1x.yaml
88
+ ```
89
+ The speed we observed is faster than its model zoo, likely due to different software versions.
90
+
91
+ * __tensorpack__: at commit `caafda`, `export TF_CUDNN_USE_AUTOTUNE=0`, then run
92
+ ```
93
+ mpirun -np 8 ./train.py --config DATA.BASEDIR=/data/coco TRAINER=horovod BACKBONE.STRIDE_1X1=True TRAIN.STEPS_PER_EPOCH=50 --load ImageNet-R50-AlignPadding.npz
94
+ ```
95
+
96
+ * __SimpleDet__: at commit `9187a1`, run
97
+ ```
98
+ python detection_train.py --config config/mask_r50v1_fpn_1x.py
99
+ ```
100
+
101
+ * __Detectron__: run
102
+ ```
103
+ python tools/train_net.py --cfg configs/12_2017_baselines/e2e_mask_rcnn_R-50-FPN_1x.yaml
104
+ ```
105
+ Note that many of its ops run on CPUs, therefore the performance is limited.
106
+
107
+ * __matterport/Mask_RCNN__: at commit `3deaec`, apply the following diff, `export TF_CUDNN_USE_AUTOTUNE=0`, then run
108
+ ```
109
+ python coco.py train --dataset=/data/coco/ --model=imagenet
110
+ ```
111
+ Note that many small details in this implementation might be different
112
+ from Detectron's standards.
113
+
114
+ <details>
115
+ <summary>
116
+ (diff to make it use the same hyperparameters - click to expand)
117
+ </summary>
118
+
119
+ ```diff
120
+ diff --git i/mrcnn/model.py w/mrcnn/model.py
121
+ index 62cb2b0..61d7779 100644
122
+ --- i/mrcnn/model.py
123
+ +++ w/mrcnn/model.py
124
+ @@ -2367,8 +2367,8 @@ class MaskRCNN():
125
+ epochs=epochs,
126
+ steps_per_epoch=self.config.STEPS_PER_EPOCH,
127
+ callbacks=callbacks,
128
+ - validation_data=val_generator,
129
+ - validation_steps=self.config.VALIDATION_STEPS,
130
+ + #validation_data=val_generator,
131
+ + #validation_steps=self.config.VALIDATION_STEPS,
132
+ max_queue_size=100,
133
+ workers=workers,
134
+ use_multiprocessing=True,
135
+ diff --git i/mrcnn/parallel_model.py w/mrcnn/parallel_model.py
136
+ index d2bf53b..060172a 100644
137
+ --- i/mrcnn/parallel_model.py
138
+ +++ w/mrcnn/parallel_model.py
139
+ @@ -32,6 +32,7 @@ class ParallelModel(KM.Model):
140
+ keras_model: The Keras model to parallelize
141
+ gpu_count: Number of GPUs. Must be > 1
142
+ """
143
+ + super().__init__()
144
+ self.inner_model = keras_model
145
+ self.gpu_count = gpu_count
146
+ merged_outputs = self.make_parallel()
147
+ diff --git i/samples/coco/coco.py w/samples/coco/coco.py
148
+ index 5d172b5..239ed75 100644
149
+ --- i/samples/coco/coco.py
150
+ +++ w/samples/coco/coco.py
151
+ @@ -81,7 +81,10 @@ class CocoConfig(Config):
152
+ IMAGES_PER_GPU = 2
153
+
154
+ # Uncomment to train on 8 GPUs (default is 1)
155
+ - # GPU_COUNT = 8
156
+ + GPU_COUNT = 8
157
+ + BACKBONE = "resnet50"
158
+ + STEPS_PER_EPOCH = 50
159
+ + TRAIN_ROIS_PER_IMAGE = 512
160
+
161
+ # Number of classes (including background)
162
+ NUM_CLASSES = 1 + 80 # COCO has 80 classes
163
+ @@ -496,29 +499,10 @@ if __name__ == '__main__':
164
+ # *** This training schedule is an example. Update to your needs ***
165
+
166
+ # Training - Stage 1
167
+ - print("Training network heads")
168
+ model.train(dataset_train, dataset_val,
169
+ learning_rate=config.LEARNING_RATE,
170
+ epochs=40,
171
+ - layers='heads',
172
+ - augmentation=augmentation)
173
+ -
174
+ - # Training - Stage 2
175
+ - # Finetune layers from ResNet stage 4 and up
176
+ - print("Fine tune Resnet stage 4 and up")
177
+ - model.train(dataset_train, dataset_val,
178
+ - learning_rate=config.LEARNING_RATE,
179
+ - epochs=120,
180
+ - layers='4+',
181
+ - augmentation=augmentation)
182
+ -
183
+ - # Training - Stage 3
184
+ - # Fine tune all layers
185
+ - print("Fine tune all layers")
186
+ - model.train(dataset_train, dataset_val,
187
+ - learning_rate=config.LEARNING_RATE / 10,
188
+ - epochs=160,
189
+ - layers='all',
190
+ + layers='3+',
191
+ augmentation=augmentation)
192
+
193
+ elif args.command == "evaluate":
194
+ ```
195
+
196
+ </details>
approach/ovod/detectron2/docs/notes/changelog.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log and Backward Compatibility
2
+
3
+ ### Releases
4
+ See release logs at
5
+ [https://github.com/facebookresearch/detectron2/releases](https://github.com/facebookresearch/detectron2/releases)
6
+ for new updates.
7
+
8
+ ### Backward Compatibility
9
+
10
+ Due to the research nature of what the library does, there might be backward incompatible changes.
11
+ But we try to reduce users' disruption by the following ways:
12
+ * APIs listed in [API documentation](https://detectron2.readthedocs.io/modules/index.html), including
13
+ function/class names, their arguments, and documented class attributes, are considered *stable* unless
14
+ otherwise noted in the documentation.
15
+ They are less likely to be broken, but if needed, will trigger a deprecation warning for a reasonable period
16
+ before getting broken, and will be documented in release logs.
17
+ * Others functions/classses/attributes are considered internal, and are more likely to change.
18
+ However, we're aware that some of them may be already used by other projects, and in particular we may
19
+ use them for convenience among projects under `detectron2/projects`.
20
+ For such APIs, we may treat them as stable APIs and also apply the above strategies.
21
+ They may be promoted to stable when we're ready.
22
+ * Projects under "detectron2/projects" or imported with "detectron2.projects" are research projects
23
+ and are all considered experimental.
24
+ * Classes/functions that contain the word "default" or are explicitly documented to produce
25
+ "default behavior" may change their behaviors when new features are added.
26
+
27
+ Despite of the possible breakage, if a third-party project would like to keep up with the latest updates
28
+ in detectron2, using it as a library will still be less disruptive than forking, because
29
+ the frequency and scope of API changes will be much smaller than code changes.
30
+
31
+ To see such changes, search for "incompatible changes" in [release logs](https://github.com/facebookresearch/detectron2/releases).
32
+
33
+ ### Config Version Change Log
34
+
35
+ Detectron2's config version has not been changed since open source.
36
+ There is no need for an open source user to worry about this.
37
+
38
+ * v1: Rename `RPN_HEAD.NAME` to `RPN.HEAD_NAME`.
39
+ * v2: A batch of rename of many configurations before release.
40
+
41
+ ### Silent Regressions in Historical Versions:
42
+
43
+ We list a few silent regressions, since they may silently produce incorrect results and will be hard to debug.
44
+
45
+ * 04/01/2020 - 05/11/2020: Bad accuracy if `TRAIN_ON_PRED_BOXES` is set to True.
46
+ * 03/30/2020 - 04/01/2020: ResNets are not correctly built.
47
+ * 12/19/2019 - 12/26/2019: Using aspect ratio grouping causes a drop in accuracy.
48
+ * - 11/9/2019: Test time augmentation does not predict the last category.
approach/ovod/detectron2/docs/notes/compatibility.md ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Compatibility with Other Libraries
2
+
3
+ ## Compatibility with Detectron (and maskrcnn-benchmark)
4
+
5
+ Detectron2 addresses some legacy issues left in Detectron. As a result, their models
6
+ are not compatible:
7
+ running inference with the same model weights will produce different results in the two code bases.
8
+
9
+ The major differences regarding inference are:
10
+
11
+ - The height and width of a box with corners (x1, y1) and (x2, y2) is now computed more naturally as
12
+ width = x2 - x1 and height = y2 - y1;
13
+ In Detectron, a "+ 1" was added both height and width.
14
+
15
+ Note that the relevant ops in Caffe2 have [adopted this change of convention](https://github.com/pytorch/pytorch/pull/20550)
16
+ with an extra option.
17
+ So it is still possible to run inference with a Detectron2-trained model in Caffe2.
18
+
19
+ The change in height/width calculations most notably changes:
20
+ - encoding/decoding in bounding box regression.
21
+ - non-maximum suppression. The effect here is very negligible, though.
22
+
23
+ - RPN now uses simpler anchors with fewer quantization artifacts.
24
+
25
+ In Detectron, the anchors were quantized and
26
+ [do not have accurate areas](https://github.com/facebookresearch/Detectron/issues/227).
27
+ In Detectron2, the anchors are center-aligned to feature grid points and not quantized.
28
+
29
+ - Classification layers have a different ordering of class labels.
30
+
31
+ This involves any trainable parameter with shape (..., num_categories + 1, ...).
32
+ In Detectron2, integer labels [0, K-1] correspond to the K = num_categories object categories
33
+ and the label "K" corresponds to the special "background" category.
34
+ In Detectron, label "0" means background, and labels [1, K] correspond to the K categories.
35
+
36
+ - ROIAlign is implemented differently. The new implementation is [available in Caffe2](https://github.com/pytorch/pytorch/pull/23706).
37
+
38
+ 1. All the ROIs are shifted by half a pixel compared to Detectron in order to create better image-feature-map alignment.
39
+ See `layers/roi_align.py` for details.
40
+ To enable the old behavior, use `ROIAlign(aligned=False)`, or `POOLER_TYPE=ROIAlign` instead of
41
+ `ROIAlignV2` (the default).
42
+
43
+ 1. The ROIs are not required to have a minimum size of 1.
44
+ This will lead to tiny differences in the output, but should be negligible.
45
+
46
+ - Mask inference function is different.
47
+
48
+ In Detectron2, the "paste_mask" function is different and should be more accurate than in Detectron. This change
49
+ can improve mask AP on COCO by ~0.5% absolute.
50
+
51
+ There are some other differences in training as well, but they won't affect
52
+ model-level compatibility. The major ones are:
53
+
54
+ - We fixed a [bug](https://github.com/facebookresearch/Detectron/issues/459) in
55
+ Detectron, by making `RPN.POST_NMS_TOPK_TRAIN` per-image, rather than per-batch.
56
+ The fix may lead to a small accuracy drop for a few models (e.g. keypoint
57
+ detection) and will require some parameter tuning to match the Detectron results.
58
+ - For simplicity, we change the default loss in bounding box regression to L1 loss, instead of smooth L1 loss.
59
+ We have observed that this tends to slightly decrease box AP50 while improving box AP for higher
60
+ overlap thresholds (and leading to a slight overall improvement in box AP).
61
+ - We interpret the coordinates in COCO bounding box and segmentation annotations
62
+ as coordinates in range `[0, width]` or `[0, height]`. The coordinates in
63
+ COCO keypoint annotations are interpreted as pixel indices in range `[0, width - 1]` or `[0, height - 1]`.
64
+ Note that this affects how flip augmentation is implemented.
65
+
66
+
67
+ [This article](https://ppwwyyxx.com/blog/2021/Where-are-Pixels/)
68
+ explains more details on the above mentioned issues
69
+ about pixels, coordinates, and "+1"s.
70
+
71
+
72
+ ## Compatibility with Caffe2
73
+
74
+ As mentioned above, despite the incompatibilities with Detectron, the relevant
75
+ ops have been implemented in Caffe2.
76
+ Therefore, models trained with detectron2 can be converted in Caffe2.
77
+ See [Deployment](../tutorials/deployment.md) for the tutorial.
78
+
79
+ ## Compatibility with TensorFlow
80
+
81
+ Most ops are available in TensorFlow, although some tiny differences in
82
+ the implementation of resize / ROIAlign / padding need to be addressed.
83
+ A working conversion script is provided by [tensorpack Faster R-CNN](https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN/convert_d2)
84
+ to run a standard detectron2 model in TensorFlow.
approach/ovod/detectron2/docs/notes/contributing.md ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to detectron2
2
+
3
+ ## Issues
4
+ We use GitHub issues to track public bugs and questions.
5
+ Please make sure to follow one of the
6
+ [issue templates](https://github.com/facebookresearch/detectron2/issues/new/choose)
7
+ when reporting any issues.
8
+
9
+ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
10
+ disclosure of security bugs. In those cases, please go through the process
11
+ outlined on that page and do not file a public issue.
12
+
13
+ ## Pull Requests
14
+ We actively welcome pull requests.
15
+
16
+ However, if you're adding any significant features (e.g. > 50 lines), please
17
+ make sure to discuss with maintainers about your motivation and proposals in an issue
18
+ before sending a PR. This is to save your time so you don't spend time on a PR that we'll not accept.
19
+
20
+ We do not always accept new features, and we take the following
21
+ factors into consideration:
22
+
23
+ 1. Whether the same feature can be achieved without modifying detectron2.
24
+ Detectron2 is designed so that you can implement many extensions from the outside, e.g.
25
+ those in [projects](https://github.com/facebookresearch/detectron2/tree/master/projects).
26
+ * If some part of detectron2 is not extensible enough, you can also bring up a more general issue to
27
+ improve it. Such feature request may be useful to more users.
28
+ 2. Whether the feature is potentially useful to a large audience (e.g. an impactful detection paper, a popular dataset,
29
+ a significant speedup, a widely useful utility),
30
+ or only to a small portion of users (e.g., a less-known paper, an improvement not in the object
31
+ detection field, a trick that's not very popular in the community, code to handle a non-standard type of data)
32
+ * Adoption of additional models, datasets, new task are by default not added to detectron2 before they
33
+ receive significant popularity in the community.
34
+ We sometimes accept such features in `projects/`, or as a link in `projects/README.md`.
35
+ 3. Whether the proposed solution has a good design / interface. This can be discussed in the issue prior to PRs, or
36
+ in the form of a draft PR.
37
+ 4. Whether the proposed solution adds extra mental/practical overhead to users who don't
38
+ need such feature.
39
+ 5. Whether the proposed solution breaks existing APIs.
40
+
41
+ To add a feature to an existing function/class `Func`, there are always two approaches:
42
+ (1) add new arguments to `Func`; (2) write a new `Func_with_new_feature`.
43
+ To meet the above criteria, we often prefer approach (2), because:
44
+
45
+ 1. It does not involve modifying or potentially breaking existing code.
46
+ 2. It does not add overhead to users who do not need the new feature.
47
+ 3. Adding new arguments to a function/class is not scalable w.r.t. all the possible new research ideas in the future.
48
+
49
+ When sending a PR, please do:
50
+
51
+ 1. If a PR contains multiple orthogonal changes, split it to several PRs.
52
+ 2. If you've added code that should be tested, add tests.
53
+ 3. For PRs that need experiments (e.g. adding a new model or new methods),
54
+ you don't need to update model zoo, but do provide experiment results in the description of the PR.
55
+ 4. If APIs are changed, update the documentation.
56
+ 5. We use the [Google style docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) in python.
57
+ 6. Make sure your code lints with `./dev/linter.sh`.
58
+
59
+
60
+ ## Contributor License Agreement ("CLA")
61
+ In order to accept your pull request, we need you to submit a CLA. You only need
62
+ to do this once to work on any of Facebook's open source projects.
63
+
64
+ Complete your CLA here: <https://code.facebook.com/cla>
65
+
66
+ ## License
67
+ By contributing to detectron2, you agree that your contributions will be licensed
68
+ under the LICENSE file in the root directory of this source tree.
approach/ovod/detectron2/docs/notes/index.rst ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Notes
2
+ ======================================
3
+
4
+ .. toctree::
5
+ :maxdepth: 2
6
+
7
+ benchmarks
8
+ compatibility
9
+ contributing
10
+ changelog
approach/ovod/detectron2/docs/requirements.txt ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ docutils==0.16
2
+ # https://github.com/sphinx-doc/sphinx/commit/7acd3ada3f38076af7b2b5c9f3b60bb9c2587a3d
3
+ sphinx==3.2.0
4
+ recommonmark==0.6.0
5
+ sphinx_rtd_theme
6
+ # Dependencies here are only those required by import
7
+ termcolor
8
+ numpy
9
+ tqdm
10
+ matplotlib
11
+ termcolor
12
+ yacs
13
+ tabulate
14
+ cloudpickle
15
+ Pillow
16
+ future
17
+ git+https://github.com/facebookresearch/fvcore.git
18
+ https://download.pytorch.org/whl/cpu/torch-1.8.1%2Bcpu-cp37-cp37m-linux_x86_64.whl
19
+ https://download.pytorch.org/whl/cpu/torchvision-0.9.1%2Bcpu-cp37-cp37m-linux_x86_64.whl
20
+ omegaconf>=2.1.0.dev24
21
+ hydra-core>=1.1.0.dev5
22
+ scipy
23
+ timm
24
+ fairscale
approach/ovod/detectron2/docs/tutorials/README.md ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Read the docs:
2
+
3
+ The latest documentation built from this directory is available at [detectron2.readthedocs.io](https://detectron2.readthedocs.io/).
4
+ Documents in this directory are not meant to be read on github.
approach/ovod/detectron2/docs/tutorials/builtin_datasets.md ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Builtin Datasets
2
+
3
+ A dataset can be used by accessing [DatasetCatalog](https://detectron2.readthedocs.io/modules/data.html#detectron2.data.DatasetCatalog)
4
+ for its data, or [MetadataCatalog](https://detectron2.readthedocs.io/modules/data.html#detectron2.data.MetadataCatalog) for its metadata (class names, etc).
5
+ This document explains how to setup the builtin datasets so they can be used by the above APIs.
6
+ [Use Custom Datasets](https://detectron2.readthedocs.io/tutorials/datasets.html) gives a deeper dive on how to use `DatasetCatalog` and `MetadataCatalog`,
7
+ and how to add new datasets to them.
8
+
9
+ Detectron2 has builtin support for a few datasets.
10
+ The datasets are assumed to exist in a directory specified by the environment variable
11
+ `DETECTRON2_DATASETS`.
12
+ Under this directory, detectron2 will look for datasets in the structure described below, if needed.
13
+ ```
14
+ $DETECTRON2_DATASETS/
15
+ coco/
16
+ lvis/
17
+ cityscapes/
18
+ VOC20{07,12}/
19
+ ```
20
+
21
+ You can set the location for builtin datasets by `export DETECTRON2_DATASETS=/path/to/datasets`.
22
+ If left unset, the default is `./datasets` relative to your current working directory.
23
+
24
+ The [model zoo](https://github.com/facebookresearch/detectron2/blob/master/MODEL_ZOO.md)
25
+ contains configs and models that use these builtin datasets.
26
+
27
+ ## Expected dataset structure for [COCO instance/keypoint detection](https://cocodataset.org/#download):
28
+
29
+ ```
30
+ coco/
31
+ annotations/
32
+ instances_{train,val}2017.json
33
+ person_keypoints_{train,val}2017.json
34
+ {train,val}2017/
35
+ # image files that are mentioned in the corresponding json
36
+ ```
37
+
38
+ You can use the 2014 version of the dataset as well.
39
+
40
+ Some of the builtin tests (`dev/run_*_tests.sh`) uses a tiny version of the COCO dataset,
41
+ which you can download with `./datasets/prepare_for_tests.sh`.
42
+
43
+ ## Expected dataset structure for PanopticFPN:
44
+
45
+ Extract panoptic annotations from [COCO website](https://cocodataset.org/#download)
46
+ into the following structure:
47
+ ```
48
+ coco/
49
+ annotations/
50
+ panoptic_{train,val}2017.json
51
+ panoptic_{train,val}2017/ # png annotations
52
+ panoptic_stuff_{train,val}2017/ # generated by the script mentioned below
53
+ ```
54
+
55
+ Install panopticapi by:
56
+ ```
57
+ pip install git+https://github.com/cocodataset/panopticapi.git
58
+ ```
59
+ Then, run `python datasets/prepare_panoptic_fpn.py`, to extract semantic annotations from panoptic annotations.
60
+
61
+ ## Expected dataset structure for [LVIS instance segmentation](https://www.lvisdataset.org/dataset):
62
+ ```
63
+ coco/
64
+ {train,val,test}2017/
65
+ lvis/
66
+ lvis_v0.5_{train,val}.json
67
+ lvis_v0.5_image_info_test.json
68
+ lvis_v1_{train,val}.json
69
+ lvis_v1_image_info_test{,_challenge}.json
70
+ ```
71
+
72
+ Install lvis-api by:
73
+ ```
74
+ pip install git+https://github.com/lvis-dataset/lvis-api.git
75
+ ```
76
+
77
+ To evaluate models trained on the COCO dataset using LVIS annotations,
78
+ run `python datasets/prepare_cocofied_lvis.py` to prepare "cocofied" LVIS annotations.
79
+
80
+ ## Expected dataset structure for [cityscapes](https://www.cityscapes-dataset.com/downloads/):
81
+ ```
82
+ cityscapes/
83
+ gtFine/
84
+ train/
85
+ aachen/
86
+ color.png, instanceIds.png, labelIds.png, polygons.json,
87
+ labelTrainIds.png
88
+ ...
89
+ val/
90
+ test/
91
+ # below are generated Cityscapes panoptic annotation
92
+ cityscapes_panoptic_train.json
93
+ cityscapes_panoptic_train/
94
+ cityscapes_panoptic_val.json
95
+ cityscapes_panoptic_val/
96
+ cityscapes_panoptic_test.json
97
+ cityscapes_panoptic_test/
98
+ leftImg8bit/
99
+ train/
100
+ val/
101
+ test/
102
+ ```
103
+ Install cityscapes scripts by:
104
+ ```
105
+ pip install git+https://github.com/mcordts/cityscapesScripts.git
106
+ ```
107
+
108
+ Note: to create labelTrainIds.png, first prepare the above structure, then run cityscapesescript with:
109
+ ```
110
+ CITYSCAPES_DATASET=/path/to/abovementioned/cityscapes python cityscapesscripts/preparation/createTrainIdLabelImgs.py
111
+ ```
112
+ These files are not needed for instance segmentation.
113
+
114
+ Note: to generate Cityscapes panoptic dataset, run cityscapesescript with:
115
+ ```
116
+ CITYSCAPES_DATASET=/path/to/abovementioned/cityscapes python cityscapesscripts/preparation/createPanopticImgs.py
117
+ ```
118
+ These files are not needed for semantic and instance segmentation.
119
+
120
+ ## Expected dataset structure for [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/index.html):
121
+ ```
122
+ VOC20{07,12}/
123
+ Annotations/
124
+ ImageSets/
125
+ Main/
126
+ trainval.txt
127
+ test.txt
128
+ # train.txt or val.txt, if you use these splits
129
+ JPEGImages/
130
+ ```
131
+
132
+ ## Expected dataset structure for [ADE20k Scene Parsing](http://sceneparsing.csail.mit.edu/):
133
+ ```
134
+ ADEChallengeData2016/
135
+ annotations/
136
+ annotations_detectron2/
137
+ images/
138
+ objectInfo150.txt
139
+ ```
140
+ The directory `annotations_detectron2` is generated by running `python datasets/prepare_ade20k_sem_seg.py`.
approach/ovod/detectron2/docs/tutorials/configs.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Yacs Configs
2
+
3
+ Detectron2 provides a key-value based config system that can be
4
+ used to obtain standard, common behaviors.
5
+
6
+ This system uses YAML and [yacs](https://github.com/rbgirshick/yacs).
7
+ Yaml is a very limited language,
8
+ so we do not expect all features in detectron2 to be available through configs.
9
+ If you need something that's not available in the config space,
10
+ please write code using detectron2's API.
11
+
12
+ With the introduction of a more powerful [LazyConfig system](lazyconfigs.md),
13
+ we no longer add functionality / new keys to the Yacs/Yaml-based config system.
14
+
15
+ ### Basic Usage
16
+
17
+ Some basic usage of the `CfgNode` object is shown here. See more in [documentation](../modules/config.html#detectron2.config.CfgNode).
18
+ ```python
19
+ from detectron2.config import get_cfg
20
+ cfg = get_cfg() # obtain detectron2's default config
21
+ cfg.xxx = yyy # add new configs for your own custom components
22
+ cfg.merge_from_file("my_cfg.yaml") # load values from a file
23
+
24
+ cfg.merge_from_list(["MODEL.WEIGHTS", "weights.pth"]) # can also load values from a list of str
25
+ print(cfg.dump()) # print formatted configs
26
+ with open("output.yaml", "w") as f:
27
+ f.write(cfg.dump()) # save config to file
28
+ ```
29
+
30
+ In addition to the basic Yaml syntax, the config file can
31
+ define a `_BASE_: base.yaml` field, which will load a base config file first.
32
+ Values in the base config will be overwritten in sub-configs, if there are any conflicts.
33
+ We provided several base configs for standard model architectures.
34
+
35
+ Many builtin tools in detectron2 accept command line config overwrite:
36
+ Key-value pairs provided in the command line will overwrite the existing values in the config file.
37
+ For example, [demo.py](../../demo/demo.py) can be used with
38
+ ```sh
39
+ ./demo.py --config-file config.yaml [--other-options] \
40
+ --opts MODEL.WEIGHTS /path/to/weights INPUT.MIN_SIZE_TEST 1000
41
+ ```
42
+
43
+ To see a list of available configs in detectron2 and what they mean,
44
+ check [Config References](../modules/config.html#config-references)
45
+
46
+ ### Configs in Projects
47
+
48
+ A project that lives outside the detectron2 library may define its own configs, which will need to be added
49
+ for the project to be functional, e.g.:
50
+ ```python
51
+ from detectron2.projects.point_rend import add_pointrend_config
52
+ cfg = get_cfg() # obtain detectron2's default config
53
+ add_pointrend_config(cfg) # add pointrend's default config
54
+ # ... ...
55
+ ```
56
+
57
+ ### Best Practice with Configs
58
+
59
+ 1. Treat the configs you write as "code": avoid copying them or duplicating them; use `_BASE_`
60
+ to share common parts between configs.
61
+
62
+ 2. Keep the configs you write simple: don't include keys that do not affect the experimental setting.
approach/ovod/detectron2/docs/tutorials/data_loading.md ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Dataloader
3
+
4
+ Dataloader is the component that provides data to models.
5
+ A dataloader usually (but not necessarily) takes raw information from [datasets](./datasets.md),
6
+ and process them into a format needed by the model.
7
+
8
+ ## How the Existing Dataloader Works
9
+
10
+ Detectron2 contains a builtin data loading pipeline.
11
+ It's good to understand how it works, in case you need to write a custom one.
12
+
13
+ Detectron2 provides two functions
14
+ [build_detection_{train,test}_loader](../modules/data.html#detectron2.data.build_detection_train_loader)
15
+ that create a default data loader from a given config.
16
+ Here is how `build_detection_{train,test}_loader` work:
17
+
18
+ 1. It takes the name of a registered dataset (e.g., "coco_2017_train") and loads a `list[dict]` representing the dataset items
19
+ in a lightweight format. These dataset items are not yet ready to be used by the model (e.g., images are
20
+ not loaded into memory, random augmentations have not been applied, etc.).
21
+ Details about the dataset format and dataset registration can be found in
22
+ [datasets](./datasets.md).
23
+ 2. Each dict in this list is mapped by a function ("mapper"):
24
+ * Users can customize this mapping function by specifying the "mapper" argument in
25
+ `build_detection_{train,test}_loader`. The default mapper is [DatasetMapper](../modules/data.html#detectron2.data.DatasetMapper).
26
+ * The output format of the mapper can be arbitrary, as long as it is accepted by the consumer of this data loader (usually the model).
27
+ The outputs of the default mapper, after batching, follow the default model input format documented in
28
+ [Use Models](./models.html#model-input-format).
29
+ * The role of the mapper is to transform the lightweight representation of a dataset item into a format
30
+ that is ready for the model to consume (including, e.g., read images, perform random data augmentation and convert to torch Tensors).
31
+ If you would like to perform custom transformations to data, you often want a custom mapper.
32
+ 3. The outputs of the mapper are batched (simply into a list).
33
+ 4. This batched data is the output of the data loader. Typically, it's also the input of
34
+ `model.forward()`.
35
+
36
+
37
+ ## Write a Custom Dataloader
38
+
39
+ Using a different "mapper" with `build_detection_{train,test}_loader(mapper=)` works for most use cases
40
+ of custom data loading.
41
+ For example, if you want to resize all images to a fixed size for training, use:
42
+
43
+ ```python
44
+ import detectron2.data.transforms as T
45
+ from detectron2.data import DatasetMapper # the default mapper
46
+ dataloader = build_detection_train_loader(cfg,
47
+ mapper=DatasetMapper(cfg, is_train=True, augmentations=[
48
+ T.Resize((800, 800))
49
+ ]))
50
+ # use this dataloader instead of the default
51
+ ```
52
+ If the arguments of the default [DatasetMapper](../modules/data.html#detectron2.data.DatasetMapper)
53
+ does not provide what you need, you may write a custom mapper function and use it instead, e.g.:
54
+
55
+ ```python
56
+ from detectron2.data import detection_utils as utils
57
+ # Show how to implement a minimal mapper, similar to the default DatasetMapper
58
+ def mapper(dataset_dict):
59
+ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below
60
+ # can use other ways to read image
61
+ image = utils.read_image(dataset_dict["file_name"], format="BGR")
62
+ # See "Data Augmentation" tutorial for details usage
63
+ auginput = T.AugInput(image)
64
+ transform = T.Resize((800, 800))(auginput)
65
+ image = torch.from_numpy(auginput.image.transpose(2, 0, 1))
66
+ annos = [
67
+ utils.transform_instance_annotations(annotation, [transform], image.shape[1:])
68
+ for annotation in dataset_dict.pop("annotations")
69
+ ]
70
+ return {
71
+ # create the format that the model expects
72
+ "image": image,
73
+ "instances": utils.annotations_to_instances(annos, image.shape[1:])
74
+ }
75
+ dataloader = build_detection_train_loader(cfg, mapper=mapper)
76
+ ```
77
+
78
+ If you want to change not only the mapper (e.g., in order to implement different sampling or batching logic),
79
+ `build_detection_train_loader` won't work and you will need to write a different data loader.
80
+ The data loader is simply a
81
+ python iterator that produces [the format](./models.md) that the model accepts.
82
+ You can implement it using any tools you like.
83
+
84
+ No matter what to implement, it's recommended to
85
+ check out [API documentation of detectron2.data](../modules/data) to learn more about the APIs of
86
+ these functions.
87
+
88
+ ## Use a Custom Dataloader
89
+
90
+ If you use [DefaultTrainer](../modules/engine.html#detectron2.engine.defaults.DefaultTrainer),
91
+ you can overwrite its `build_{train,test}_loader` method to use your own dataloader.
92
+ See the [deeplab dataloader](../../projects/DeepLab/train_net.py)
93
+ for an example.
94
+
95
+ If you write your own training loop, you can plug in your data loader easily.
approach/ovod/detectron2/docs/tutorials/datasets.md ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Custom Datasets
2
+
3
+ This document explains how the dataset APIs
4
+ ([DatasetCatalog](../modules/data.html#detectron2.data.DatasetCatalog), [MetadataCatalog](../modules/data.html#detectron2.data.MetadataCatalog))
5
+ work, and how to use them to add custom datasets.
6
+
7
+ Datasets that have builtin support in detectron2 are listed in [builtin datasets](builtin_datasets.md).
8
+ If you want to use a custom dataset while also reusing detectron2's data loaders,
9
+ you will need to:
10
+
11
+ 1. __Register__ your dataset (i.e., tell detectron2 how to obtain your dataset).
12
+ 2. Optionally, __register metadata__ for your dataset.
13
+
14
+ Next, we explain the above two concepts in detail.
15
+
16
+ The [Colab tutorial](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5)
17
+ has a live example of how to register and train on a dataset of custom formats.
18
+
19
+ ### Register a Dataset
20
+
21
+ To let detectron2 know how to obtain a dataset named "my_dataset", users need to implement
22
+ a function that returns the items in your dataset and then tell detectron2 about this
23
+ function:
24
+ ```python
25
+ def my_dataset_function():
26
+ ...
27
+ return list[dict] in the following format
28
+
29
+ from detectron2.data import DatasetCatalog
30
+ DatasetCatalog.register("my_dataset", my_dataset_function)
31
+ # later, to access the data:
32
+ data: List[Dict] = DatasetCatalog.get("my_dataset")
33
+ ```
34
+
35
+ Here, the snippet associates a dataset named "my_dataset" with a function that returns the data.
36
+ The function must return the same data (with same order) if called multiple times.
37
+ The registration stays effective until the process exits.
38
+
39
+ The function can do arbitrary things and should return the data in `list[dict]`, each dict in either
40
+ of the following formats:
41
+ 1. Detectron2's standard dataset dict, described below. This will make it work with many other builtin
42
+ features in detectron2, so it's recommended to use it when it's sufficient.
43
+ 2. Any custom format. You can also return arbitrary dicts in your own format,
44
+ such as adding extra keys for new tasks.
45
+ Then you will need to handle them properly downstream as well.
46
+ See below for more details.
47
+
48
+ #### Standard Dataset Dicts
49
+
50
+ For standard tasks
51
+ (instance detection, instance/semantic/panoptic segmentation, keypoint detection),
52
+ we load the original dataset into `list[dict]` with a specification similar to COCO's annotations.
53
+ This is our standard representation for a dataset.
54
+
55
+ Each dict contains information about one image.
56
+ The dict may have the following fields,
57
+ and the required fields vary based on what the dataloader or the task needs (see more below).
58
+
59
+ ```eval_rst
60
+ .. list-table::
61
+ :header-rows: 1
62
+
63
+ * - Task
64
+ - Fields
65
+ * - Common
66
+ - file_name, height, width, image_id
67
+
68
+ * - Instance detection/segmentation
69
+ - annotations
70
+
71
+ * - Semantic segmentation
72
+ - sem_seg_file_name
73
+
74
+ * - Panoptic segmentation
75
+ - pan_seg_file_name, segments_info
76
+ ```
77
+
78
+ + `file_name`: the full path to the image file.
79
+ + `height`, `width`: integer. The shape of the image.
80
+ + `image_id` (str or int): a unique id that identifies this image. Required by many
81
+ evaluators to identify the images, but a dataset may use it for different purposes.
82
+ + `annotations` (list[dict]): Required by __instance detection/segmentation or keypoint detection__ tasks.
83
+ Each dict corresponds to annotations of one instance in this image, and
84
+ may contain the following keys:
85
+ + `bbox` (list[float], required): list of 4 numbers representing the bounding box of the instance.
86
+ + `bbox_mode` (int, required): the format of bbox. It must be a member of
87
+ [structures.BoxMode](../modules/structures.html#detectron2.structures.BoxMode).
88
+ Currently supports: `BoxMode.XYXY_ABS`, `BoxMode.XYWH_ABS`.
89
+ + `category_id` (int, required): an integer in the range [0, num_categories-1] representing the category label.
90
+ The value num_categories is reserved to represent the "background" category, if applicable.
91
+ + `segmentation` (list[list[float]] or dict): the segmentation mask of the instance.
92
+ + If `list[list[float]]`, it represents a list of polygons, one for each connected component
93
+ of the object. Each `list[float]` is one simple polygon in the format of `[x1, y1, ..., xn, yn]` (n≥3).
94
+ The Xs and Ys are absolute coordinates in unit of pixels.
95
+ + If `dict`, it represents the per-pixel segmentation mask in COCO's compressed RLE format.
96
+ The dict should have keys "size" and "counts". You can convert a uint8 segmentation mask of 0s and
97
+ 1s into such dict by `pycocotools.mask.encode(np.asarray(mask, order="F"))`.
98
+ `cfg.INPUT.MASK_FORMAT` must be set to `bitmask` if using the default data loader with such format.
99
+ + `keypoints` (list[float]): in the format of [x1, y1, v1,..., xn, yn, vn].
100
+ v[i] means the [visibility](http://cocodataset.org/#format-data) of this keypoint.
101
+ `n` must be equal to the number of keypoint categories.
102
+ The Xs and Ys are absolute real-value coordinates in range [0, W or H].
103
+
104
+ (Note that the keypoint coordinates in COCO format are integers in range [0, W-1 or H-1], which is different
105
+ from our standard format. Detectron2 adds 0.5 to COCO keypoint coordinates to convert them from discrete
106
+ pixel indices to floating point coordinates.)
107
+ + `iscrowd`: 0 (default) or 1. Whether this instance is labeled as COCO's "crowd
108
+ region". Don't include this field if you don't know what it means.
109
+
110
+ If `annotations` is an empty list, it means the image is labeled to have no objects.
111
+ Such images will by default be removed from training,
112
+ but can be included using `DATALOADER.FILTER_EMPTY_ANNOTATIONS`.
113
+
114
+ + `sem_seg_file_name` (str):
115
+ The full path to the semantic segmentation ground truth file.
116
+ It should be a grayscale image whose pixel values are integer labels.
117
+ + `pan_seg_file_name` (str):
118
+ The full path to panoptic segmentation ground truth file.
119
+ It should be an RGB image whose pixel values are integer ids encoded using the
120
+ [panopticapi.utils.id2rgb](https://github.com/cocodataset/panopticapi/) function.
121
+ The ids are defined by `segments_info`.
122
+ If an id does not appear in `segments_info`, the pixel is considered unlabeled
123
+ and is usually ignored in training & evaluation.
124
+ + `segments_info` (list[dict]): defines the meaning of each id in panoptic segmentation ground truth.
125
+ Each dict has the following keys:
126
+ + `id` (int): integer that appears in the ground truth image.
127
+ + `category_id` (int): an integer in the range [0, num_categories-1] representing the category label.
128
+ + `iscrowd`: 0 (default) or 1. Whether this instance is labeled as COCO's "crowd region".
129
+
130
+
131
+ ```eval_rst
132
+
133
+ .. note::
134
+
135
+ The PanopticFPN model does not use the panoptic segmentation
136
+ format defined here, but a combination of both instance segmentation and semantic segmentation data
137
+ format. See :doc:`builtin_datasets` for instructions on COCO.
138
+
139
+ ```
140
+
141
+ Fast R-CNN (with pre-computed proposals) models are rarely used today.
142
+ To train a Fast R-CNN, the following extra keys are needed:
143
+
144
+ + `proposal_boxes` (array): 2D numpy array with shape (K, 4) representing K precomputed proposal boxes for this image.
145
+ + `proposal_objectness_logits` (array): numpy array with shape (K, ), which corresponds to the objectness
146
+ logits of proposals in 'proposal_boxes'.
147
+ + `proposal_bbox_mode` (int): the format of the precomputed proposal bbox.
148
+ It must be a member of
149
+ [structures.BoxMode](../modules/structures.html#detectron2.structures.BoxMode).
150
+ Default is `BoxMode.XYXY_ABS`.
151
+
152
+
153
+
154
+ #### Custom Dataset Dicts for New Tasks
155
+
156
+ In the `list[dict]` that your dataset function returns, the dictionary can also have __arbitrary custom data__.
157
+ This will be useful for a new task that needs extra information not covered
158
+ by the standard dataset dicts. In this case, you need to make sure the downstream code can handle your data
159
+ correctly. Usually this requires writing a new `mapper` for the dataloader (see [Use Custom Dataloaders](./data_loading.md)).
160
+
161
+ When designing a custom format, note that all dicts are stored in memory
162
+ (sometimes serialized and with multiple copies).
163
+ To save memory, each dict is meant to contain __small__ but sufficient information
164
+ about each sample, such as file names and annotations.
165
+ Loading full samples typically happens in the data loader.
166
+
167
+ For attributes shared among the entire dataset, use `Metadata` (see below).
168
+ To avoid extra memory, do not save such information inside each sample.
169
+
170
+ ### "Metadata" for Datasets
171
+
172
+ Each dataset is associated with some metadata, accessible through
173
+ `MetadataCatalog.get(dataset_name).some_metadata`.
174
+ Metadata is a key-value mapping that contains information that's shared among
175
+ the entire dataset, and usually is used to interpret what's in the dataset, e.g.,
176
+ names of classes, colors of classes, root of files, etc.
177
+ This information will be useful for augmentation, evaluation, visualization, logging, etc.
178
+ The structure of metadata depends on what is needed from the corresponding downstream code.
179
+
180
+ If you register a new dataset through `DatasetCatalog.register`,
181
+ you may also want to add its corresponding metadata through
182
+ `MetadataCatalog.get(dataset_name).some_key = some_value`, to enable any features that need the metadata.
183
+ You can do it like this (using the metadata key "thing_classes" as an example):
184
+
185
+ ```python
186
+ from detectron2.data import MetadataCatalog
187
+ MetadataCatalog.get("my_dataset").thing_classes = ["person", "dog"]
188
+ ```
189
+
190
+ Here is a list of metadata keys that are used by builtin features in detectron2.
191
+ If you add your own dataset without these metadata, some features may be
192
+ unavailable to you:
193
+
194
+ * `thing_classes` (list[str]): Used by all instance detection/segmentation tasks.
195
+ A list of names for each instance/thing category.
196
+ If you load a COCO format dataset, it will be automatically set by the function `load_coco_json`.
197
+
198
+ * `thing_colors` (list[tuple(r, g, b)]): Pre-defined color (in [0, 255]) for each thing category.
199
+ Used for visualization. If not given, random colors will be used.
200
+
201
+ * `stuff_classes` (list[str]): Used by semantic and panoptic segmentation tasks.
202
+ A list of names for each stuff category.
203
+
204
+ * `stuff_colors` (list[tuple(r, g, b)]): Pre-defined color (in [0, 255]) for each stuff category.
205
+ Used for visualization. If not given, random colors are used.
206
+
207
+ * `ignore_label` (int): Used by semantic and panoptic segmentation tasks. Pixels in ground-truth
208
+ annotations with this category label should be ignored in evaluation. Typically these are "unlabeled"
209
+ pixels.
210
+
211
+ * `keypoint_names` (list[str]): Used by keypoint detection. A list of names for each keypoint.
212
+
213
+ * `keypoint_flip_map` (list[tuple[str]]): Used by keypoint detection. A list of pairs of names,
214
+ where each pair are the two keypoints that should be flipped if the image is
215
+ flipped horizontally during augmentation.
216
+ * `keypoint_connection_rules`: list[tuple(str, str, (r, g, b))]. Each tuple specifies a pair of keypoints
217
+ that are connected and the color (in [0, 255]) to use for the line between them when visualized.
218
+
219
+ Some additional metadata that are specific to the evaluation of certain datasets (e.g. COCO):
220
+
221
+ * `thing_dataset_id_to_contiguous_id` (dict[int->int]): Used by all instance detection/segmentation tasks in the COCO format.
222
+ A mapping from instance class ids in the dataset to contiguous ids in range [0, #class).
223
+ Will be automatically set by the function `load_coco_json`.
224
+
225
+ * `stuff_dataset_id_to_contiguous_id` (dict[int->int]): Used when generating prediction json files for
226
+ semantic/panoptic segmentation.
227
+ A mapping from semantic segmentation class ids in the dataset
228
+ to contiguous ids in [0, num_categories). It is useful for evaluation only.
229
+
230
+ * `json_file`: The COCO annotation json file. Used by COCO evaluation for COCO-format datasets.
231
+ * `panoptic_root`, `panoptic_json`: Used by COCO-format panoptic evaluation.
232
+ * `evaluator_type`: Used by the builtin main training script to select
233
+ evaluator. Don't use it in a new training script.
234
+ You can just provide the [DatasetEvaluator](../modules/evaluation.html#detectron2.evaluation.DatasetEvaluator)
235
+ for your dataset directly in your main script.
236
+
237
+ ```eval_rst
238
+ .. note::
239
+
240
+ In recognition, sometimes we use the term "thing" for instance-level tasks,
241
+ and "stuff" for semantic segmentation tasks.
242
+ Both are used in panoptic segmentation tasks.
243
+ For background on the concept of "thing" and "stuff", see
244
+ `On Seeing Stuff: The Perception of Materials by Humans and Machines
245
+ <http://persci.mit.edu/pub_pdfs/adelson_spie_01.pdf>`_.
246
+ ```
247
+
248
+ ### Register a COCO Format Dataset
249
+
250
+ If your instance-level (detection, segmentation, keypoint) dataset is already a json file in the COCO format,
251
+ the dataset and its associated metadata can be registered easily with:
252
+ ```python
253
+ from detectron2.data.datasets import register_coco_instances
254
+ register_coco_instances("my_dataset", {}, "json_annotation.json", "path/to/image/dir")
255
+ ```
256
+
257
+ If your dataset is in COCO format but need to be further processed, or has extra custom per-instance annotations,
258
+ the [load_coco_json](../modules/data.html#detectron2.data.datasets.load_coco_json)
259
+ function might be useful.
260
+
261
+ ### Update the Config for New Datasets
262
+
263
+ Once you've registered the dataset, you can use the name of the dataset (e.g., "my_dataset" in
264
+ example above) in `cfg.DATASETS.{TRAIN,TEST}`.
265
+ There are other configs you might want to change to train or evaluate on new datasets:
266
+
267
+ * `MODEL.ROI_HEADS.NUM_CLASSES` and `MODEL.RETINANET.NUM_CLASSES` are the number of thing classes
268
+ for R-CNN and RetinaNet models, respectively.
269
+ * `MODEL.ROI_KEYPOINT_HEAD.NUM_KEYPOINTS` sets the number of keypoints for Keypoint R-CNN.
270
+ You'll also need to set [Keypoint OKS](http://cocodataset.org/#keypoints-eval)
271
+ with `TEST.KEYPOINT_OKS_SIGMAS` for evaluation.
272
+ * `MODEL.SEM_SEG_HEAD.NUM_CLASSES` sets the number of stuff classes for Semantic FPN & Panoptic FPN.
273
+ * `TEST.DETECTIONS_PER_IMAGE` controls the maximum number of objects to be detected.
274
+ Set it to a larger number if test images may contain >100 objects.
275
+ * If you're training Fast R-CNN (with precomputed proposals), `DATASETS.PROPOSAL_FILES_{TRAIN,TEST}`
276
+ need to match the datasets. The format of proposal files are documented
277
+ [here](../modules/data.html#detectron2.data.load_proposals_into_dataset).
278
+
279
+ New models
280
+ (e.g. [TensorMask](../../projects/TensorMask),
281
+ [PointRend](../../projects/PointRend))
282
+ often have similar configs of their own that need to be changed as well.
283
+
284
+ ```eval_rst
285
+ .. tip::
286
+
287
+ After changing the number of classes, certain layers in a pre-trained model will become incompatible
288
+ and therefore cannot be loaded to the new model.
289
+ This is expected, and loading such pre-trained models will produce warnings about such layers.
290
+ ```
approach/ovod/detectron2/docs/tutorials/deployment.md ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deployment
2
+
3
+ Models written in Python need to go through an export process to become a deployable artifact.
4
+ A few basic concepts about this process:
5
+
6
+ __"Export method"__ is how a Python model is fully serialized to a deployable format.
7
+ We support the following export methods:
8
+
9
+ * `tracing`: see [pytorch documentation](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html) to learn about it
10
+ * `scripting`: see [pytorch documentation](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html) to learn about it
11
+ * `caffe2_tracing`: replace parts of the model by caffe2 operators, then use tracing.
12
+
13
+ __"Format"__ is how a serialized model is described in a file, e.g.
14
+ TorchScript, Caffe2 protobuf, ONNX format.
15
+ __"Runtime"__ is an engine that loads a serialized model and executes it,
16
+ e.g., PyTorch, Caffe2, TensorFlow, onnxruntime, TensorRT, etc.
17
+ A runtime is often tied to a specific format
18
+ (e.g. PyTorch needs TorchScript format, Caffe2 needs protobuf format).
19
+ We currently support the following combination and each has some limitations:
20
+
21
+ ```eval_rst
22
+ +----------------------------+-------------+-------------+-----------------------------+
23
+ | Export Method | tracing | scripting | caffe2_tracing |
24
+ +============================+=============+=============+=============================+
25
+ | **Formats** | TorchScript | TorchScript | Caffe2, TorchScript, ONNX |
26
+ +----------------------------+-------------+-------------+-----------------------------+
27
+ | **Runtime** | PyTorch | PyTorch | Caffe2, PyTorch |
28
+ +----------------------------+-------------+-------------+-----------------------------+
29
+ | C++/Python inference | ✅ | ✅ | ✅ |
30
+ +----------------------------+-------------+-------------+-----------------------------+
31
+ | Dynamic resolution | ✅ | ✅ | ✅ |
32
+ +----------------------------+-------------+-------------+-----------------------------+
33
+ | Batch size requirement | Constant | Dynamic | Batch inference unsupported |
34
+ +----------------------------+-------------+-------------+-----------------------------+
35
+ | Extra runtime deps | torchvision | torchvision | Caffe2 ops (usually already |
36
+ | | | | |
37
+ | | | | included in PyTorch) |
38
+ +----------------------------+-------------+-------------+-----------------------------+
39
+ | Faster/Mask/Keypoint R-CNN | ✅ | ✅ | ✅ |
40
+ +----------------------------+-------------+-------------+-----------------------------+
41
+ | RetinaNet | ✅ | ✅ | ✅ |
42
+ +----------------------------+-------------+-------------+-----------------------------+
43
+ | PointRend R-CNN | ✅ | ❌ | ❌ |
44
+ +----------------------------+-------------+-------------+-----------------------------+
45
+ | Cascade R-CNN | ✅ | ❌ | ❌ |
46
+ +----------------------------+-------------+-------------+-----------------------------+
47
+
48
+ ```
49
+
50
+ `caffe2_tracing` is going to be deprecated.
51
+ We don't plan to work on additional support for other formats/runtime, but contributions are welcome.
52
+
53
+
54
+ ## Deployment with Tracing or Scripting
55
+
56
+ Models can be exported to TorchScript format, by either
57
+ [tracing or scripting](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html).
58
+ The output model file can be loaded without detectron2 dependency in either Python or C++.
59
+ The exported model often requires torchvision (or its C++ library) dependency for some custom ops.
60
+
61
+ This feature requires PyTorch ≥ 1.8.
62
+
63
+ ### Coverage
64
+ Most official models under the meta architectures `GeneralizedRCNN` and `RetinaNet`
65
+ are supported in both tracing and scripting mode.
66
+ Cascade R-CNN and PointRend are currently supported in tracing.
67
+ Users' custom extensions are supported if they are also scriptable or traceable.
68
+
69
+ For models exported with tracing, dynamic input resolution is allowed, but batch size
70
+ (number of input images) must be fixed.
71
+ Scripting can support dynamic batch size.
72
+
73
+ ### Usage
74
+
75
+ The main export APIs for tracing and scripting are [TracingAdapter](../modules/export.html#detectron2.export.TracingAdapter)
76
+ and [scripting_with_instances](../modules/export.html#detectron2.export.scripting_with_instances).
77
+ Their usage is currently demonstrated in [test_export_torchscript.py](../../tests/test_export_torchscript.py)
78
+ (see `TestScripting` and `TestTracing`)
79
+ as well as the [deployment example](../../tools/deploy).
80
+ Please check that these examples can run, and then modify for your use cases.
81
+ The usage now requires some user effort and necessary knowledge for each model to workaround the limitation of scripting and tracing.
82
+ In the future we plan to wrap these under simpler APIs to lower the bar to use them.
83
+
84
+ ## Deployment with Caffe2-tracing
85
+ We provide [Caffe2Tracer](../modules/export.html#detectron2.export.Caffe2Tracer)
86
+ that performs the export logic.
87
+ It replaces parts of the model with Caffe2 operators,
88
+ and then export the model into Caffe2, TorchScript or ONNX format.
89
+
90
+ The converted model is able to run in either Python or C++ without detectron2/torchvision dependency, on CPU or GPUs.
91
+ It has a runtime optimized for CPU & mobile inference, but not optimized for GPU inference.
92
+
93
+ This feature requires ONNX ≥ 1.6.
94
+
95
+ ### Coverage
96
+
97
+ Most official models under these 3 common meta architectures: `GeneralizedRCNN`, `RetinaNet`, `PanopticFPN`
98
+ are supported. Cascade R-CNN is not supported. Batch inference is not supported.
99
+
100
+ Users' custom extensions under these architectures (added through registration) are supported
101
+ as long as they do not contain control flow or operators not available in Caffe2 (e.g. deformable convolution).
102
+ For example, custom backbones and heads are often supported out of the box.
103
+
104
+ ### Usage
105
+
106
+ The APIs are listed at [the API documentation](../modules/export).
107
+ We provide [export_model.py](../../tools/deploy/) as an example that uses
108
+ these APIs to convert a standard model. For custom models/datasets, you can add them to this script.
109
+
110
+ ### Use the model in C++/Python
111
+
112
+ The model can be loaded in C++ and deployed with
113
+ either Caffe2 or Pytorch runtime.. [C++ examples](../../tools/deploy/) for Mask R-CNN
114
+ are given as a reference. Note that:
115
+
116
+ * Models exported with `caffe2_tracing` method take a special input format
117
+ described in [documentation](../modules/export.html#detectron2.export.Caffe2Tracer).
118
+ This was taken care of in the C++ example.
119
+
120
+ * The converted models do not contain post-processing operations that
121
+ transform raw layer outputs into formatted predictions.
122
+ For example, the C++ examples only produce raw outputs (28x28 masks) from the final
123
+ layers that are not post-processed, because in actual deployment, an application often needs
124
+ its custom lightweight post-processing, so this step is left for users.
125
+
126
+ To help use the Caffe2-format model in python,
127
+ we provide a python wrapper around the converted model, in the
128
+ [Caffe2Model.\_\_call\_\_](../modules/export.html#detectron2.export.Caffe2Model.__call__) method.
129
+ This method has an interface that's identical to the [pytorch versions of models](./models.md),
130
+ and it internally applies pre/post-processing code to match the formats.
131
+ This wrapper can serve as a reference for how to use Caffe2's python API,
132
+ or for how to implement pre/post-processing in actual deployment.
133
+
134
+ ## Conversion to TensorFlow
135
+ [tensorpack Faster R-CNN](https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN/convert_d2)
136
+ provides scripts to convert a few standard detectron2 R-CNN models to TensorFlow's pb format.
137
+ It works by translating configs and weights, therefore only support a few models.
approach/ovod/detectron2/docs/tutorials/extend.md ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Extend Detectron2's Defaults
2
+
3
+ __Research is about doing things in new ways__.
4
+ This brings a tension in how to create abstractions in code,
5
+ which is a challenge for any research engineering project of a significant size:
6
+
7
+ 1. On one hand, it needs to have very thin abstractions to allow for the possibility of doing
8
+ everything in new ways. It should be reasonably easy to break existing
9
+ abstractions and replace them with new ones.
10
+
11
+ 2. On the other hand, such a project also needs reasonably high-level
12
+ abstractions, so that users can easily do things in standard ways,
13
+ without worrying too much about the details that only certain researchers care about.
14
+
15
+ In detectron2, there are two types of interfaces that address this tension together:
16
+
17
+ 1. Functions and classes that take a config (`cfg`) argument
18
+ created from a yaml file
19
+ (sometimes with few extra arguments).
20
+
21
+ Such functions and classes implement
22
+ the "standard default" behavior: it will read what it needs from a given
23
+ config and do the "standard" thing.
24
+ Users only need to load an expert-made config and pass it around, without having to worry about
25
+ which arguments are used and what they all mean.
26
+
27
+ See [Yacs Configs](configs.md) for a detailed tutorial.
28
+
29
+ 2. Functions and classes that have well-defined explicit arguments.
30
+
31
+ Each of these is a small building block of the entire system.
32
+ They require users' expertise to understand what each argument should be,
33
+ and require more effort to stitch together to a larger system.
34
+ But they can be stitched together in more flexible ways.
35
+
36
+ When you need to implement something not supported by the "standard defaults"
37
+ included in detectron2, these well-defined components can be reused.
38
+
39
+ The [LazyConfig system](lazyconfigs.md) relies on such functions and classes.
40
+
41
+ 3. A few functions and classes are implemented with the
42
+ [@configurable](../modules/config.html#detectron2.config.configurable)
43
+ decorator - they can be called with either a config, or with explicit arguments, or a mixture of both.
44
+ Their explicit argument interfaces are currently experimental.
45
+
46
+ As an example, a Mask R-CNN model can be built in the following ways:
47
+
48
+ 1. Config-only:
49
+ ```python
50
+ # load proper yaml config file, then
51
+ model = build_model(cfg)
52
+ ```
53
+
54
+ 2. Mixture of config and additional argument overrides:
55
+ ```python
56
+ model = GeneralizedRCNN(
57
+ cfg,
58
+ roi_heads=StandardROIHeads(cfg, batch_size_per_image=666),
59
+ pixel_std=[57.0, 57.0, 57.0])
60
+ ```
61
+
62
+ 3. Full explicit arguments:
63
+ <details>
64
+ <summary>
65
+ (click to expand)
66
+ </summary>
67
+
68
+ ```python
69
+ model = GeneralizedRCNN(
70
+ backbone=FPN(
71
+ ResNet(
72
+ BasicStem(3, 64, norm="FrozenBN"),
73
+ ResNet.make_default_stages(50, stride_in_1x1=True, norm="FrozenBN"),
74
+ out_features=["res2", "res3", "res4", "res5"],
75
+ ).freeze(2),
76
+ ["res2", "res3", "res4", "res5"],
77
+ 256,
78
+ top_block=LastLevelMaxPool(),
79
+ ),
80
+ proposal_generator=RPN(
81
+ in_features=["p2", "p3", "p4", "p5", "p6"],
82
+ head=StandardRPNHead(in_channels=256, num_anchors=3),
83
+ anchor_generator=DefaultAnchorGenerator(
84
+ sizes=[[32], [64], [128], [256], [512]],
85
+ aspect_ratios=[0.5, 1.0, 2.0],
86
+ strides=[4, 8, 16, 32, 64],
87
+ offset=0.0,
88
+ ),
89
+ anchor_matcher=Matcher([0.3, 0.7], [0, -1, 1], allow_low_quality_matches=True),
90
+ box2box_transform=Box2BoxTransform([1.0, 1.0, 1.0, 1.0]),
91
+ batch_size_per_image=256,
92
+ positive_fraction=0.5,
93
+ pre_nms_topk=(2000, 1000),
94
+ post_nms_topk=(1000, 1000),
95
+ nms_thresh=0.7,
96
+ ),
97
+ roi_heads=StandardROIHeads(
98
+ num_classes=80,
99
+ batch_size_per_image=512,
100
+ positive_fraction=0.25,
101
+ proposal_matcher=Matcher([0.5], [0, 1], allow_low_quality_matches=False),
102
+ box_in_features=["p2", "p3", "p4", "p5"],
103
+ box_pooler=ROIPooler(7, (1.0 / 4, 1.0 / 8, 1.0 / 16, 1.0 / 32), 0, "ROIAlignV2"),
104
+ box_head=FastRCNNConvFCHead(
105
+ ShapeSpec(channels=256, height=7, width=7), conv_dims=[], fc_dims=[1024, 1024]
106
+ ),
107
+ box_predictor=FastRCNNOutputLayers(
108
+ ShapeSpec(channels=1024),
109
+ test_score_thresh=0.05,
110
+ box2box_transform=Box2BoxTransform((10, 10, 5, 5)),
111
+ num_classes=80,
112
+ ),
113
+ mask_in_features=["p2", "p3", "p4", "p5"],
114
+ mask_pooler=ROIPooler(14, (1.0 / 4, 1.0 / 8, 1.0 / 16, 1.0 / 32), 0, "ROIAlignV2"),
115
+ mask_head=MaskRCNNConvUpsampleHead(
116
+ ShapeSpec(channels=256, width=14, height=14),
117
+ num_classes=80,
118
+ conv_dims=[256, 256, 256, 256, 256],
119
+ ),
120
+ ),
121
+ pixel_mean=[103.530, 116.280, 123.675],
122
+ pixel_std=[1.0, 1.0, 1.0],
123
+ input_format="BGR",
124
+ )
125
+ ```
126
+
127
+ </details>
128
+
129
+
130
+ If you only need the standard behavior, the [Beginner's Tutorial](./getting_started.md)
131
+ should suffice. If you need to extend detectron2 to your own needs,
132
+ see the following tutorials for more details:
133
+
134
+ * Detectron2 includes a few standard datasets. To use custom ones, see
135
+ [Use Custom Datasets](./datasets.md).
136
+ * Detectron2 contains the standard logic that creates a data loader for training/testing from a
137
+ dataset, but you can write your own as well. See [Use Custom Data Loaders](./data_loading.md).
138
+ * Detectron2 implements many standard detection models, and provide ways for you
139
+ to overwrite their behaviors. See [Use Models](./models.md) and [Write Models](./write-models.md).
140
+ * Detectron2 provides a default training loop that is good for common training tasks.
141
+ You can customize it with hooks, or write your own loop instead. See [training](./training.md).