hysts HF staff commited on
Commit
3fe1151
1 Parent(s): 4e0c895
.gitattributes CHANGED
@@ -1,3 +1,5 @@
 
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
 
1
+ *.jpg filter=lfs diff=lfs merge=lfs -text
2
+ *.png filter=lfs diff=lfs merge=lfs -text
3
  *.7z filter=lfs diff=lfs merge=lfs -text
4
  *.arrow filter=lfs diff=lfs merge=lfs -text
5
  *.bin filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ weights/
.gitmodules ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ [submodule "CBNetV2"]
2
+ path = CBNetV2
3
+ url = https://github.com/VDIGPKU/CBNetV2
.pre-commit-config.yaml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ exclude: ^(CBNetV2/|patch)
2
+ repos:
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v4.2.0
5
+ hooks:
6
+ - id: check-executables-have-shebangs
7
+ - id: check-json
8
+ - id: check-merge-conflict
9
+ - id: check-shebang-scripts-are-executable
10
+ - id: check-toml
11
+ - id: check-yaml
12
+ - id: double-quote-string-fixer
13
+ - id: end-of-file-fixer
14
+ - id: mixed-line-ending
15
+ args: ['--fix=lf']
16
+ - id: requirements-txt-fixer
17
+ - id: trailing-whitespace
18
+ - repo: https://github.com/myint/docformatter
19
+ rev: v1.4
20
+ hooks:
21
+ - id: docformatter
22
+ args: ['--in-place']
23
+ - repo: https://github.com/pycqa/isort
24
+ rev: 5.10.1
25
+ hooks:
26
+ - id: isort
27
+ - repo: https://github.com/pre-commit/mirrors-mypy
28
+ rev: v0.812
29
+ hooks:
30
+ - id: mypy
31
+ args: ['--ignore-missing-imports']
32
+ - repo: https://github.com/google/yapf
33
+ rev: v0.32.0
34
+ hooks:
35
+ - id: yapf
36
+ args: ['--parallel', '--in-place']
37
+ - repo: https://github.com/kynan/nbstripout
38
+ rev: 0.5.0
39
+ hooks:
40
+ - id: nbstripout
41
+ args: ['--extra-keys', 'metadata.interpreter metadata.kernelspec cell.metadata.pycharm']
42
+ - repo: https://github.com/nbQA-dev/nbQA
43
+ rev: 1.3.1
44
+ hooks:
45
+ - id: nbqa-isort
46
+ - id: nbqa-yapf
.style.yapf ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [style]
2
+ based_on_style = pep8
3
+ blank_line_before_nested_class_or_def = false
4
+ spaces_before_comment = 2
5
+ split_before_logical_operator = true
CBNetV2 ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit a546be507af55a17c96dc18a85f86b17656ff814
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: 📉
4
  colorFrom: gray
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 3.0.6
8
  app_file: app.py
9
  pinned: false
10
  ---
 
4
  colorFrom: gray
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: 3.0.5
8
  app_file: app.py
9
  pinned: false
10
  ---
app.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import pathlib
8
+ import subprocess
9
+ import sys
10
+
11
+ if os.getenv('SYSTEM') == 'spaces':
12
+ import mim
13
+
14
+ mim.uninstall('mmcv-full', confirm_yes=True)
15
+ mim.install('mmcv-full==1.5.0', is_yes=True)
16
+
17
+ subprocess.call('pip uninstall -y opencv-python'.split())
18
+ subprocess.call('pip uninstall -y opencv-python-headless'.split())
19
+ subprocess.call('pip install opencv-python-headless==4.5.5.64'.split())
20
+
21
+ subprocess.call('git apply ../patch'.split(), cwd='CBNetV2')
22
+ subprocess.call('mv palette.py CBNetV2/mmdet/core/visualization/'.split())
23
+
24
+ import gradio as gr
25
+ import numpy as np
26
+ import torch
27
+ import torch.nn as nn
28
+
29
+ sys.path.insert(0, 'CBNetV2/')
30
+
31
+ from mmdet.apis import inference_detector, init_detector
32
+
33
+
34
+ def parse_args() -> argparse.Namespace:
35
+ parser = argparse.ArgumentParser()
36
+ parser.add_argument('--device', type=str, default='cpu')
37
+ parser.add_argument('--theme', type=str)
38
+ parser.add_argument('--share', action='store_true')
39
+ parser.add_argument('--port', type=int)
40
+ parser.add_argument('--disable-queue',
41
+ dest='enable_queue',
42
+ action='store_false')
43
+ return parser.parse_args()
44
+
45
+
46
+ class Model:
47
+ def __init__(self, device: str | torch.device):
48
+ self.device = torch.device(device)
49
+ self.models = self._load_models()
50
+ self.model_name = 'Improved HTC (DB-Swin-B)'
51
+
52
+ def _load_models(self) -> dict[str, nn.Module]:
53
+ model_dict = {
54
+ 'Faster R-CNN (DB-ResNet50)': {
55
+ 'config':
56
+ 'CBNetV2/configs/cbnet/faster_rcnn_cbv2d1_r50_fpn_1x_coco.py',
57
+ 'model':
58
+ 'https://github.com/CBNetwork/storage/releases/download/v1.0.0/faster_rcnn_cbv2d1_r50_fpn_1x_coco.pth.zip',
59
+ },
60
+ 'Mask R-CNN (DB-Swin-T)': {
61
+ 'config':
62
+ 'CBNetV2/configs/cbnet/mask_rcnn_cbv2_swin_tiny_patch4_window7_mstrain_480-800_adamw_3x_coco.py',
63
+ 'model':
64
+ 'https://github.com/CBNetwork/storage/releases/download/v1.0.0/mask_rcnn_cbv2_swin_tiny_patch4_window7_mstrain_480-800_adamw_3x_coco.pth.zip',
65
+ },
66
+ # 'Cascade Mask R-CNN (DB-Swin-S)': {
67
+ # 'config':
68
+ # 'CBNetV2/configs/cbnet/cascade_mask_rcnn_cbv2_swin_small_patch4_window7_mstrain_400-1400_adamw_3x_coco.py',
69
+ # 'model':
70
+ # 'https://github.com/CBNetwork/storage/releases/download/v1.0.0/cascade_mask_rcnn_cbv2_swin_small_patch4_window7_mstrain_400-1400_adamw_3x_coco.pth.zip',
71
+ # },
72
+ 'Improved HTC (DB-Swin-B)': {
73
+ 'config':
74
+ 'CBNetV2/configs/cbnet/htc_cbv2_swin_base_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_20e_coco.py',
75
+ 'model':
76
+ 'https://github.com/CBNetwork/storage/releases/download/v1.0.0/htc_cbv2_swin_base22k_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_20e_coco.pth.zip',
77
+ },
78
+ 'Improved HTC (DB-Swin-L)': {
79
+ 'config':
80
+ 'CBNetV2/configs/cbnet/htc_cbv2_swin_large_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_1x_coco.py',
81
+ 'model':
82
+ 'https://github.com/CBNetwork/storage/releases/download/v1.0.0/htc_cbv2_swin_large22k_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_1x_coco.pth.zip',
83
+ },
84
+ 'Improved HTC (DB-Swin-L (TTA))': {
85
+ 'config':
86
+ 'CBNetV2/configs/cbnet/htc_cbv2_swin_large_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_1x_coco.py',
87
+ 'model':
88
+ 'https://github.com/CBNetwork/storage/releases/download/v1.0.0/htc_cbv2_swin_large22k_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_1x_coco.pth.zip',
89
+ },
90
+ }
91
+
92
+ weight_dir = pathlib.Path('weights')
93
+ weight_dir.mkdir(exist_ok=True)
94
+
95
+ def _download(model_name: str, out_dir: pathlib.Path) -> None:
96
+ import zipfile
97
+
98
+ model_url = model_dict[model_name]['model']
99
+ zip_name = model_url.split('/')[-1]
100
+
101
+ out_path = out_dir / zip_name
102
+ if out_path.exists():
103
+ return
104
+ torch.hub.download_url_to_file(model_url, out_path)
105
+
106
+ with zipfile.ZipFile(out_path) as f:
107
+ f.extractall(out_dir)
108
+
109
+ def _get_model_path(model_name: str) -> str:
110
+ model_url = model_dict[model_name]['model']
111
+ model_name = model_url.split('/')[-1][:-4]
112
+ return (weight_dir / model_name).as_posix()
113
+
114
+ for model_name in model_dict:
115
+ _download(model_name, weight_dir)
116
+
117
+ models = {
118
+ key: init_detector(dic['config'],
119
+ _get_model_path(key),
120
+ device=self.device)
121
+ for key, dic in model_dict.items()
122
+ }
123
+ return models
124
+
125
+ def set_model_name(self, name: str) -> None:
126
+ self.model_name = name
127
+
128
+ def detect_and_visualize(
129
+ self, image: np.ndarray,
130
+ score_threshold: float) -> tuple[list[np.ndarray], np.ndarray]:
131
+ out = self.detect(image)
132
+ vis = self.visualize_detection_results(image, out, score_threshold)
133
+ return out, vis
134
+
135
+ def detect(self, image: np.ndarray) -> list[np.ndarray]:
136
+ image = image[:, :, ::-1] # RGB -> BGR
137
+ model = self.models[self.model_name]
138
+ out = inference_detector(model, image)
139
+ return out
140
+
141
+ def visualize_detection_results(
142
+ self,
143
+ image: np.ndarray,
144
+ detection_results: list[np.ndarray],
145
+ score_threshold: float = 0.3) -> np.ndarray:
146
+ image = image[:, :, ::-1] # RGB -> BGR
147
+ model = self.models[self.model_name]
148
+ vis = model.show_result(image,
149
+ detection_results,
150
+ score_thr=score_threshold,
151
+ bbox_color=None,
152
+ text_color=(200, 200, 200),
153
+ mask_color=None)
154
+ return vis[:, :, ::-1] # BGR -> RGB
155
+
156
+
157
+ def set_example_image(example: list) -> dict:
158
+ return gr.Image.update(value=example[0])
159
+
160
+
161
+ def main():
162
+ args = parse_args()
163
+ model = Model(args.device)
164
+
165
+ css = '''
166
+ h1#title {
167
+ text-align: center;
168
+ }
169
+ '''
170
+
171
+ with gr.Blocks(theme=args.theme, css=css) as demo:
172
+ gr.Markdown('''<h1 id="title">VDIGPKU/CBNetV2</h1>
173
+
174
+ This is an unofficial demo for [https://github.com/VDIGPKU/CBNetV2](https://github.com/VDIGPKU/CBNetV2).'''
175
+ )
176
+
177
+ with gr.Row():
178
+ with gr.Column():
179
+ with gr.Row():
180
+ input_image = gr.Image(label='Input Image', type='numpy')
181
+ with gr.Row():
182
+ detector_name = gr.Dropdown(list(model.models.keys()),
183
+ value=model.model_name,
184
+ label='Detector')
185
+ with gr.Row():
186
+ detect_button = gr.Button(value='Detect')
187
+ detection_results = gr.Variable()
188
+ with gr.Column():
189
+ detection_visualization = gr.Image(label='Detection Result',
190
+ type='numpy')
191
+ visualization_score_threshold = gr.Slider(
192
+ 0,
193
+ 1,
194
+ step=0.05,
195
+ value=0.3,
196
+ label='Visualization Score Threshold')
197
+ redraw_button = gr.Button(value='Redraw')
198
+
199
+ with gr.Row():
200
+ paths = sorted(pathlib.Path('images').rglob('*.jpg'))
201
+ example_images = gr.Dataset(components=[input_image],
202
+ samples=[[path.as_posix()]
203
+ for path in paths])
204
+
205
+ gr.Markdown(
206
+ '<center><img src="https://visitor-badge.glitch.me/badge?page_id=hysts.cbnetv2" alt="visitor badge"/></center>'
207
+ )
208
+
209
+ detector_name.change(fn=model.set_model_name,
210
+ inputs=[detector_name],
211
+ outputs=None)
212
+ detect_button.click(fn=model.detect_and_visualize,
213
+ inputs=[
214
+ input_image,
215
+ visualization_score_threshold,
216
+ ],
217
+ outputs=[
218
+ detection_results,
219
+ detection_visualization,
220
+ ])
221
+ redraw_button.click(fn=model.visualize_detection_results,
222
+ inputs=[
223
+ input_image,
224
+ detection_results,
225
+ visualization_score_threshold,
226
+ ],
227
+ outputs=[detection_visualization])
228
+ example_images.click(fn=set_example_image,
229
+ inputs=[example_images],
230
+ outputs=[input_image])
231
+
232
+ demo.launch(
233
+ enable_queue=args.enable_queue,
234
+ server_port=args.port,
235
+ share=args.share,
236
+ )
237
+
238
+
239
+ if __name__ == '__main__':
240
+ main()
images/README.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ These images are freely-usable ones from https://www.pexels.com/.
2
+
3
+ - https://www.pexels.com/photo/assorted-color-kittens-45170/
4
+ - https://www.pexels.com/photo/white-wooden-kitchen-cabinet-1599791/
5
+ - https://www.pexels.com/photo/assorted-books-on-book-shelves-1370295/
6
+ - https://www.pexels.com/photo/pile-of-assorted-varieties-of-vegetables-2255935/
7
+ - https://www.pexels.com/photo/sliced-fruits-on-tray-1132047/
8
+ - https://www.pexels.com/photo/group-of-people-carrying-surfboards-1549196/
9
+ - https://www.pexels.com/photo/aerial-photo-of-vehicles-in-the-city-1031698/
images/pexels-element-digital-1370295.jpg ADDED

Git LFS Details

  • SHA256: 5b2885e968ebf6b8e84ecab708d953a5feede203f488038ce3c9cb5bcfc52562
  • Pointer size: 131 Bytes
  • Size of remote file: 738 kB
images/pexels-elle-hughes-1549196.jpg ADDED

Git LFS Details

  • SHA256: 6d3854839113cc0570044ac87be9d7116b1d1ffe0ba6c81eff60923fa9030b25
  • Pointer size: 131 Bytes
  • Size of remote file: 609 kB
images/pexels-jean-van-der-meulen-1599791.jpg ADDED

Git LFS Details

  • SHA256: 0f34538e583afda98666158a40154b09316757d89b465b318e182a1758859a88
  • Pointer size: 131 Bytes
  • Size of remote file: 333 kB
images/pexels-mark-stebnicki-2255935.jpg ADDED

Git LFS Details

  • SHA256: 778b11664deb3c332345a327444898d776b50b4038e39c8ef79e416dc183f752
  • Pointer size: 131 Bytes
  • Size of remote file: 417 kB
images/pexels-oleksandr-pidvalnyi-1031698.jpg ADDED

Git LFS Details

  • SHA256: 2de03892c6f6624a45fc67a283aeb33d4534fecc76e51d83e43fe40b58ce2351
  • Pointer size: 131 Bytes
  • Size of remote file: 670 kB
images/pexels-pixabay-45170.jpg ADDED

Git LFS Details

  • SHA256: 114e83cc896e9df6476fd682c8d0dc8fafb76e8cf1da5f6396280040081ee1ca
  • Pointer size: 131 Bytes
  • Size of remote file: 681 kB
images/pexels-trang-doan-1132047.jpg ADDED

Git LFS Details

  • SHA256: e0eaec57002e3fbb1199d5ddc09df277fd168fc88b36359c2a9d8ef20ea18c28
  • Pointer size: 131 Bytes
  • Size of remote file: 519 kB
palette.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file is copied from https://github.com/open-mmlab/mmdetection/blob/v2.24.1/mmdet/core/visualization/palette.py
3
+ The LICENSE of mmdetection is the following:
4
+
5
+ ```
6
+ Copyright 2018-2023 OpenMMLab. All rights reserved.
7
+
8
+ Apache License
9
+ Version 2.0, January 2004
10
+ http://www.apache.org/licenses/
11
+
12
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
13
+
14
+ 1. Definitions.
15
+
16
+ "License" shall mean the terms and conditions for use, reproduction,
17
+ and distribution as defined by Sections 1 through 9 of this document.
18
+
19
+ "Licensor" shall mean the copyright owner or entity authorized by
20
+ the copyright owner that is granting the License.
21
+
22
+ "Legal Entity" shall mean the union of the acting entity and all
23
+ other entities that control, are controlled by, or are under common
24
+ control with that entity. For the purposes of this definition,
25
+ "control" means (i) the power, direct or indirect, to cause the
26
+ direction or management of such entity, whether by contract or
27
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
28
+ outstanding shares, or (iii) beneficial ownership of such entity.
29
+
30
+ "You" (or "Your") shall mean an individual or Legal Entity
31
+ exercising permissions granted by this License.
32
+
33
+ "Source" form shall mean the preferred form for making modifications,
34
+ including but not limited to software source code, documentation
35
+ source, and configuration files.
36
+
37
+ "Object" form shall mean any form resulting from mechanical
38
+ transformation or translation of a Source form, including but
39
+ not limited to compiled object code, generated documentation,
40
+ and conversions to other media types.
41
+
42
+ "Work" shall mean the work of authorship, whether in Source or
43
+ Object form, made available under the License, as indicated by a
44
+ copyright notice that is included in or attached to the work
45
+ (an example is provided in the Appendix below).
46
+
47
+ "Derivative Works" shall mean any work, whether in Source or Object
48
+ form, that is based on (or derived from) the Work and for which the
49
+ editorial revisions, annotations, elaborations, or other modifications
50
+ represent, as a whole, an original work of authorship. For the purposes
51
+ of this License, Derivative Works shall not include works that remain
52
+ separable from, or merely link (or bind by name) to the interfaces of,
53
+ the Work and Derivative Works thereof.
54
+
55
+ "Contribution" shall mean any work of authorship, including
56
+ the original version of the Work and any modifications or additions
57
+ to that Work or Derivative Works thereof, that is intentionally
58
+ submitted to Licensor for inclusion in the Work by the copyright owner
59
+ or by an individual or Legal Entity authorized to submit on behalf of
60
+ the copyright owner. For the purposes of this definition, "submitted"
61
+ means any form of electronic, verbal, or written communication sent
62
+ to the Licensor or its representatives, including but not limited to
63
+ communication on electronic mailing lists, source code control systems,
64
+ and issue tracking systems that are managed by, or on behalf of, the
65
+ Licensor for the purpose of discussing and improving the Work, but
66
+ excluding communication that is conspicuously marked or otherwise
67
+ designated in writing by the copyright owner as "Not a Contribution."
68
+
69
+ "Contributor" shall mean Licensor and any individual or Legal Entity
70
+ on behalf of whom a Contribution has been received by Licensor and
71
+ subsequently incorporated within the Work.
72
+
73
+ 2. Grant of Copyright License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ copyright license to reproduce, prepare Derivative Works of,
77
+ publicly display, publicly perform, sublicense, and distribute the
78
+ Work and such Derivative Works in Source or Object form.
79
+
80
+ 3. Grant of Patent License. Subject to the terms and conditions of
81
+ this License, each Contributor hereby grants to You a perpetual,
82
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
83
+ (except as stated in this section) patent license to make, have made,
84
+ use, offer to sell, sell, import, and otherwise transfer the Work,
85
+ where such license applies only to those patent claims licensable
86
+ by such Contributor that are necessarily infringed by their
87
+ Contribution(s) alone or by combination of their Contribution(s)
88
+ with the Work to which such Contribution(s) was submitted. If You
89
+ institute patent litigation against any entity (including a
90
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
91
+ or a Contribution incorporated within the Work constitutes direct
92
+ or contributory patent infringement, then any patent licenses
93
+ granted to You under this License for that Work shall terminate
94
+ as of the date such litigation is filed.
95
+
96
+ 4. Redistribution. You may reproduce and distribute copies of the
97
+ Work or Derivative Works thereof in any medium, with or without
98
+ modifications, and in Source or Object form, provided that You
99
+ meet the following conditions:
100
+
101
+ (a) You must give any other recipients of the Work or
102
+ Derivative Works a copy of this License; and
103
+
104
+ (b) You must cause any modified files to carry prominent notices
105
+ stating that You changed the files; and
106
+
107
+ (c) You must retain, in the Source form of any Derivative Works
108
+ that You distribute, all copyright, patent, trademark, and
109
+ attribution notices from the Source form of the Work,
110
+ excluding those notices that do not pertain to any part of
111
+ the Derivative Works; and
112
+
113
+ (d) If the Work includes a "NOTICE" text file as part of its
114
+ distribution, then any Derivative Works that You distribute must
115
+ include a readable copy of the attribution notices contained
116
+ within such NOTICE file, excluding those notices that do not
117
+ pertain to any part of the Derivative Works, in at least one
118
+ of the following places: within a NOTICE text file distributed
119
+ as part of the Derivative Works; within the Source form or
120
+ documentation, if provided along with the Derivative Works; or,
121
+ within a display generated by the Derivative Works, if and
122
+ wherever such third-party notices normally appear. The contents
123
+ of the NOTICE file are for informational purposes only and
124
+ do not modify the License. You may add Your own attribution
125
+ notices within Derivative Works that You distribute, alongside
126
+ or as an addendum to the NOTICE text from the Work, provided
127
+ that such additional attribution notices cannot be construed
128
+ as modifying the License.
129
+
130
+ You may add Your own copyright statement to Your modifications and
131
+ may provide additional or different license terms and conditions
132
+ for use, reproduction, or distribution of Your modifications, or
133
+ for any such Derivative Works as a whole, provided Your use,
134
+ reproduction, and distribution of the Work otherwise complies with
135
+ the conditions stated in this License.
136
+
137
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
138
+ any Contribution intentionally submitted for inclusion in the Work
139
+ by You to the Licensor shall be under the terms and conditions of
140
+ this License, without any additional terms or conditions.
141
+ Notwithstanding the above, nothing herein shall supersede or modify
142
+ the terms of any separate license agreement you may have executed
143
+ with Licensor regarding such Contributions.
144
+
145
+ 6. Trademarks. This License does not grant permission to use the trade
146
+ names, trademarks, service marks, or product names of the Licensor,
147
+ except as required for reasonable and customary use in describing the
148
+ origin of the Work and reproducing the content of the NOTICE file.
149
+
150
+ 7. Disclaimer of Warranty. Unless required by applicable law or
151
+ agreed to in writing, Licensor provides the Work (and each
152
+ Contributor provides its Contributions) on an "AS IS" BASIS,
153
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
154
+ implied, including, without limitation, any warranties or conditions
155
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
156
+ PARTICULAR PURPOSE. You are solely responsible for determining the
157
+ appropriateness of using or redistributing the Work and assume any
158
+ risks associated with Your exercise of permissions under this License.
159
+
160
+ 8. Limitation of Liability. In no event and under no legal theory,
161
+ whether in tort (including negligence), contract, or otherwise,
162
+ unless required by applicable law (such as deliberate and grossly
163
+ negligent acts) or agreed to in writing, shall any Contributor be
164
+ liable to You for damages, including any direct, indirect, special,
165
+ incidental, or consequential damages of any character arising as a
166
+ result of this License or out of the use or inability to use the
167
+ Work (including but not limited to damages for loss of goodwill,
168
+ work stoppage, computer failure or malfunction, or any and all
169
+ other commercial damages or losses), even if such Contributor
170
+ has been advised of the possibility of such damages.
171
+
172
+ 9. Accepting Warranty or Additional Liability. While redistributing
173
+ the Work or Derivative Works thereof, You may choose to offer,
174
+ and charge a fee for, acceptance of support, warranty, indemnity,
175
+ or other liability obligations and/or rights consistent with this
176
+ License. However, in accepting such obligations, You may act only
177
+ on Your own behalf and on Your sole responsibility, not on behalf
178
+ of any other Contributor, and only if You agree to indemnify,
179
+ defend, and hold each Contributor harmless for any liability
180
+ incurred by, or claims asserted against, such Contributor by reason
181
+ of your accepting any such warranty or additional liability.
182
+
183
+ END OF TERMS AND CONDITIONS
184
+
185
+ APPENDIX: How to apply the Apache License to your work.
186
+
187
+ To apply the Apache License to your work, attach the following
188
+ boilerplate notice, with the fields enclosed by brackets "[]"
189
+ replaced with your own identifying information. (Don't include
190
+ the brackets!) The text should be enclosed in the appropriate
191
+ comment syntax for the file format. We also recommend that a
192
+ file or class name and description of purpose be included on the
193
+ same "printed page" as the copyright notice for easier
194
+ identification within third-party archives.
195
+
196
+ Copyright 2018-2023 OpenMMLab.
197
+
198
+ Licensed under the Apache License, Version 2.0 (the "License");
199
+ you may not use this file except in compliance with the License.
200
+ You may obtain a copy of the License at
201
+
202
+ http://www.apache.org/licenses/LICENSE-2.0
203
+
204
+ Unless required by applicable law or agreed to in writing, software
205
+ distributed under the License is distributed on an "AS IS" BASIS,
206
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
207
+ See the License for the specific language governing permissions and
208
+ limitations under the License.
209
+ ```
210
+ """
211
+ # Copyright (c) OpenMMLab. All rights reserved.
212
+ import mmcv
213
+ import numpy as np
214
+
215
+
216
+ def palette_val(palette):
217
+ """Convert palette to matplotlib palette.
218
+
219
+ Args:
220
+ palette List[tuple]: A list of color tuples.
221
+
222
+ Returns:
223
+ List[tuple[float]]: A list of RGB matplotlib color tuples.
224
+ """
225
+ new_palette = []
226
+ for color in palette:
227
+ color = [c / 255 for c in color]
228
+ new_palette.append(tuple(color))
229
+ return new_palette
230
+
231
+
232
+ def get_palette(palette, num_classes):
233
+ """Get palette from various inputs.
234
+
235
+ Args:
236
+ palette (list[tuple] | str | tuple | :obj:`Color`): palette inputs.
237
+ num_classes (int): the number of classes.
238
+
239
+ Returns:
240
+ list[tuple[int]]: A list of color tuples.
241
+ """
242
+ assert isinstance(num_classes, int)
243
+
244
+ if isinstance(palette, list):
245
+ dataset_palette = palette
246
+ elif isinstance(palette, tuple):
247
+ dataset_palette = [palette] * num_classes
248
+ elif palette == 'random' or palette is None:
249
+ state = np.random.get_state()
250
+ # random color
251
+ np.random.seed(42)
252
+ palette = np.random.randint(0, 256, size=(num_classes, 3))
253
+ np.random.set_state(state)
254
+ dataset_palette = [tuple(c) for c in palette]
255
+ elif palette == 'coco':
256
+ from mmdet.datasets import CocoDataset, CocoPanopticDataset
257
+ dataset_palette = CocoDataset.PALETTE
258
+ if len(dataset_palette) < num_classes:
259
+ dataset_palette = CocoPanopticDataset.PALETTE
260
+ elif palette == 'citys':
261
+ from mmdet.datasets import CityscapesDataset
262
+ dataset_palette = CityscapesDataset.PALETTE
263
+ elif palette == 'voc':
264
+ from mmdet.datasets import VOCDataset
265
+ dataset_palette = VOCDataset.PALETTE
266
+ elif mmcv.is_str(palette):
267
+ dataset_palette = [mmcv.color_val(palette)[::-1]] * num_classes
268
+ else:
269
+ raise TypeError(f'Invalid type for palette: {type(palette)}')
270
+
271
+ assert len(dataset_palette) >= num_classes, \
272
+ 'The length of palette should not be less than `num_classes`.'
273
+ return dataset_palette
patch ADDED
@@ -0,0 +1,834 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ diff --git a/configs/cbnet/cascade_rcnn_cbv2d1_r2_101_mdconv_fpn_20e_fp16_ms400-1400_giou_4conv1f_coco.py b/configs/cbnet/cascade_rcnn_cbv2d1_r2_101_mdconv_fpn_20e_fp16_ms400-1400_giou_4conv1f_coco.py
2
+ index 167d4379..7c0bd239 100644
3
+ --- a/configs/cbnet/cascade_rcnn_cbv2d1_r2_101_mdconv_fpn_20e_fp16_ms400-1400_giou_4conv1f_coco.py
4
+ +++ b/configs/cbnet/cascade_rcnn_cbv2d1_r2_101_mdconv_fpn_20e_fp16_ms400-1400_giou_4conv1f_coco.py
5
+ @@ -2,9 +2,9 @@ _base_ = '../res2net/cascade_rcnn_r2_101_fpn_20e_coco.py'
6
+
7
+ model = dict(
8
+ backbone=dict(
9
+ - type='CBRes2Net',
10
+ + type='CBRes2Net',
11
+ cb_del_stages=1,
12
+ - cb_inplanes=[64, 256, 512, 1024, 2048],
13
+ + cb_inplanes=[64, 256, 512, 1024, 2048],
14
+ dcn=dict(type='DCNv2', deform_groups=1, fallback_on_stride=False),
15
+ stage_with_dcn=(False, True, True, True)
16
+ ),
17
+ @@ -28,7 +28,7 @@ model = dict(
18
+ target_stds=[0.1, 0.1, 0.2, 0.2]),
19
+ reg_class_agnostic=False,
20
+ reg_decoded_bbox=True,
21
+ - norm_cfg=dict(type='SyncBN', requires_grad=True),
22
+ + norm_cfg=dict(type='BN', requires_grad=True),
23
+ loss_cls=dict(
24
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
25
+ loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
26
+ @@ -47,7 +47,7 @@ model = dict(
27
+ target_stds=[0.05, 0.05, 0.1, 0.1]),
28
+ reg_class_agnostic=False,
29
+ reg_decoded_bbox=True,
30
+ - norm_cfg=dict(type='SyncBN', requires_grad=True),
31
+ + norm_cfg=dict(type='BN', requires_grad=True),
32
+ loss_cls=dict(
33
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
34
+ loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
35
+ @@ -66,7 +66,7 @@ model = dict(
36
+ target_stds=[0.033, 0.033, 0.067, 0.067]),
37
+ reg_class_agnostic=False,
38
+ reg_decoded_bbox=True,
39
+ - norm_cfg=dict(type='SyncBN', requires_grad=True),
40
+ + norm_cfg=dict(type='BN', requires_grad=True),
41
+ loss_cls=dict(
42
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
43
+ loss_bbox=dict(type='GIoULoss', loss_weight=10.0))
44
+ diff --git a/configs/cbnet/htc_cbv2_swin_base_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_20e_coco.py b/configs/cbnet/htc_cbv2_swin_base_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_20e_coco.py
45
+ index 51edfd62..a7434c5d 100644
46
+ --- a/configs/cbnet/htc_cbv2_swin_base_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_20e_coco.py
47
+ +++ b/configs/cbnet/htc_cbv2_swin_base_patch4_window7_mstrain_400-1400_giou_4conv1f_adamw_20e_coco.py
48
+ @@ -18,7 +18,7 @@ model = dict(
49
+ target_stds=[0.1, 0.1, 0.2, 0.2]),
50
+ reg_class_agnostic=True,
51
+ reg_decoded_bbox=True,
52
+ - norm_cfg=dict(type='SyncBN', requires_grad=True),
53
+ + norm_cfg=dict(type='BN', requires_grad=True),
54
+ loss_cls=dict(
55
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
56
+ loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
57
+ @@ -37,7 +37,7 @@ model = dict(
58
+ target_stds=[0.05, 0.05, 0.1, 0.1]),
59
+ reg_class_agnostic=True,
60
+ reg_decoded_bbox=True,
61
+ - norm_cfg=dict(type='SyncBN', requires_grad=True),
62
+ + norm_cfg=dict(type='BN', requires_grad=True),
63
+ loss_cls=dict(
64
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
65
+ loss_bbox=dict(type='GIoULoss', loss_weight=10.0)),
66
+ @@ -56,7 +56,7 @@ model = dict(
67
+ target_stds=[0.033, 0.033, 0.067, 0.067]),
68
+ reg_class_agnostic=True,
69
+ reg_decoded_bbox=True,
70
+ - norm_cfg=dict(type='SyncBN', requires_grad=True),
71
+ + norm_cfg=dict(type='BN', requires_grad=True),
72
+ loss_cls=dict(
73
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
74
+ loss_bbox=dict(type='GIoULoss', loss_weight=10.0))
75
+ diff --git a/mmdet/__init__.py b/mmdet/__init__.py
76
+ index 646ee84e..9e846286 100644
77
+ --- a/mmdet/__init__.py
78
+ +++ b/mmdet/__init__.py
79
+ @@ -20,9 +20,9 @@ mmcv_maximum_version = '1.4.0'
80
+ mmcv_version = digit_version(mmcv.__version__)
81
+
82
+
83
+ -assert (mmcv_version >= digit_version(mmcv_minimum_version)
84
+ - and mmcv_version <= digit_version(mmcv_maximum_version)), \
85
+ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \
86
+ - f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.'
87
+ +#assert (mmcv_version >= digit_version(mmcv_minimum_version)
88
+ +# and mmcv_version <= digit_version(mmcv_maximum_version)), \
89
+ +# f'MMCV=={mmcv.__version__} is used but incompatible. ' \
90
+ +# f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.'
91
+
92
+ __all__ = ['__version__', 'short_version']
93
+ diff --git a/mmdet/core/mask/structures.py b/mmdet/core/mask/structures.py
94
+ index 6f5a62ae..a9d0ebb4 100644
95
+ --- a/mmdet/core/mask/structures.py
96
+ +++ b/mmdet/core/mask/structures.py
97
+ @@ -1,3 +1,4 @@
98
+ +# Copyright (c) OpenMMLab. All rights reserved.
99
+ from abc import ABCMeta, abstractmethod
100
+
101
+ import cv2
102
+ @@ -528,6 +529,21 @@ class BitmapMasks(BaseInstanceMasks):
103
+ self = cls(masks, height=height, width=width)
104
+ return self
105
+
106
+ + def get_bboxes(self):
107
+ + num_masks = len(self)
108
+ + boxes = np.zeros((num_masks, 4), dtype=np.float32)
109
+ + x_any = self.masks.any(axis=1)
110
+ + y_any = self.masks.any(axis=2)
111
+ + for idx in range(num_masks):
112
+ + x = np.where(x_any[idx, :])[0]
113
+ + y = np.where(y_any[idx, :])[0]
114
+ + if len(x) > 0 and len(y) > 0:
115
+ + # use +1 for x_max and y_max so that the right and bottom
116
+ + # boundary of instance masks are fully included by the box
117
+ + boxes[idx, :] = np.array([x[0], y[0], x[-1] + 1, y[-1] + 1],
118
+ + dtype=np.float32)
119
+ + return boxes
120
+ +
121
+
122
+ class PolygonMasks(BaseInstanceMasks):
123
+ """This class represents masks in the form of polygons.
124
+ @@ -637,8 +653,8 @@ class PolygonMasks(BaseInstanceMasks):
125
+ resized_poly = []
126
+ for p in poly_per_obj:
127
+ p = p.copy()
128
+ - p[0::2] *= w_scale
129
+ - p[1::2] *= h_scale
130
+ + p[0::2] = p[0::2] * w_scale
131
+ + p[1::2] = p[1::2] * h_scale
132
+ resized_poly.append(p)
133
+ resized_masks.append(resized_poly)
134
+ resized_masks = PolygonMasks(resized_masks, *out_shape)
135
+ @@ -690,8 +706,8 @@ class PolygonMasks(BaseInstanceMasks):
136
+ for p in poly_per_obj:
137
+ # pycocotools will clip the boundary
138
+ p = p.copy()
139
+ - p[0::2] -= bbox[0]
140
+ - p[1::2] -= bbox[1]
141
+ + p[0::2] = p[0::2] - bbox[0]
142
+ + p[1::2] = p[1::2] - bbox[1]
143
+ cropped_poly_per_obj.append(p)
144
+ cropped_masks.append(cropped_poly_per_obj)
145
+ cropped_masks = PolygonMasks(cropped_masks, h, w)
146
+ @@ -736,12 +752,12 @@ class PolygonMasks(BaseInstanceMasks):
147
+ p = p.copy()
148
+ # crop
149
+ # pycocotools will clip the boundary
150
+ - p[0::2] -= bbox[0]
151
+ - p[1::2] -= bbox[1]
152
+ + p[0::2] = p[0::2] - bbox[0]
153
+ + p[1::2] = p[1::2] - bbox[1]
154
+
155
+ # resize
156
+ - p[0::2] *= w_scale
157
+ - p[1::2] *= h_scale
158
+ + p[0::2] = p[0::2] * w_scale
159
+ + p[1::2] = p[1::2] * h_scale
160
+ resized_mask.append(p)
161
+ resized_masks.append(resized_mask)
162
+ return PolygonMasks(resized_masks, *out_shape)
163
+ @@ -944,6 +960,7 @@ class PolygonMasks(BaseInstanceMasks):
164
+ a list of vertices, in CCW order.
165
+ """
166
+ from scipy.stats import truncnorm
167
+ +
168
+ # Generate around the unit circle
169
+ cx, cy = (0.0, 0.0)
170
+ radius = 1
171
+ @@ -1019,6 +1036,24 @@ class PolygonMasks(BaseInstanceMasks):
172
+ self = cls(masks, height, width)
173
+ return self
174
+
175
+ + def get_bboxes(self):
176
+ + num_masks = len(self)
177
+ + boxes = np.zeros((num_masks, 4), dtype=np.float32)
178
+ + for idx, poly_per_obj in enumerate(self.masks):
179
+ + # simply use a number that is big enough for comparison with
180
+ + # coordinates
181
+ + xy_min = np.array([self.width * 2, self.height * 2],
182
+ + dtype=np.float32)
183
+ + xy_max = np.zeros(2, dtype=np.float32)
184
+ + for p in poly_per_obj:
185
+ + xy = np.array(p).reshape(-1, 2).astype(np.float32)
186
+ + xy_min = np.minimum(xy_min, np.min(xy, axis=0))
187
+ + xy_max = np.maximum(xy_max, np.max(xy, axis=0))
188
+ + boxes[idx, :2] = xy_min
189
+ + boxes[idx, 2:] = xy_max
190
+ +
191
+ + return boxes
192
+ +
193
+
194
+ def polygon_to_bitmap(polygons, height, width):
195
+ """Convert masks from the form of polygons to bitmaps.
196
+ @@ -1035,3 +1070,33 @@ def polygon_to_bitmap(polygons, height, width):
197
+ rle = maskUtils.merge(rles)
198
+ bitmap_mask = maskUtils.decode(rle).astype(np.bool)
199
+ return bitmap_mask
200
+ +
201
+ +
202
+ +def bitmap_to_polygon(bitmap):
203
+ + """Convert masks from the form of bitmaps to polygons.
204
+ +
205
+ + Args:
206
+ + bitmap (ndarray): masks in bitmap representation.
207
+ +
208
+ + Return:
209
+ + list[ndarray]: the converted mask in polygon representation.
210
+ + bool: whether the mask has holes.
211
+ + """
212
+ + bitmap = np.ascontiguousarray(bitmap).astype(np.uint8)
213
+ + # cv2.RETR_CCOMP: retrieves all of the contours and organizes them
214
+ + # into a two-level hierarchy. At the top level, there are external
215
+ + # boundaries of the components. At the second level, there are
216
+ + # boundaries of the holes. If there is another contour inside a hole
217
+ + # of a connected component, it is still put at the top level.
218
+ + # cv2.CHAIN_APPROX_NONE: stores absolutely all the contour points.
219
+ + outs = cv2.findContours(bitmap, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
220
+ + contours = outs[-2]
221
+ + hierarchy = outs[-1]
222
+ + if hierarchy is None:
223
+ + return [], False
224
+ + # hierarchy[i]: 4 elements, for the indexes of next, previous,
225
+ + # parent, or nested contours. If there is no corresponding contour,
226
+ + # it will be -1.
227
+ + with_hole = (hierarchy.reshape(-1, 4)[:, 3] >= 0).any()
228
+ + contours = [c.reshape(-1, 2) for c in contours]
229
+ + return contours, with_hole
230
+ diff --git a/mmdet/core/visualization/image.py b/mmdet/core/visualization/image.py
231
+ index 5a148384..66f82a38 100644
232
+ --- a/mmdet/core/visualization/image.py
233
+ +++ b/mmdet/core/visualization/image.py
234
+ @@ -1,3 +1,5 @@
235
+ +# Copyright (c) OpenMMLab. All rights reserved.
236
+ +import cv2
237
+ import matplotlib.pyplot as plt
238
+ import mmcv
239
+ import numpy as np
240
+ @@ -5,17 +7,25 @@ import pycocotools.mask as mask_util
241
+ from matplotlib.collections import PatchCollection
242
+ from matplotlib.patches import Polygon
243
+
244
+ +#from mmdet.core.evaluation.panoptic_utils import INSTANCE_OFFSET
245
+ +from ..mask.structures import bitmap_to_polygon
246
+ from ..utils import mask2ndarray
247
+ +from .palette import get_palette, palette_val
248
+ +
249
+ +__all__ = [
250
+ + 'color_val_matplotlib', 'draw_masks', 'draw_bboxes', 'draw_labels',
251
+ + 'imshow_det_bboxes', 'imshow_gt_det_bboxes'
252
+ +]
253
+
254
+ EPS = 1e-2
255
+
256
+
257
+ def color_val_matplotlib(color):
258
+ """Convert various input in BGR order to normalized RGB matplotlib color
259
+ - tuples,
260
+ + tuples.
261
+
262
+ Args:
263
+ - color (:obj:`Color`/str/tuple/int/ndarray): Color inputs
264
+ + color (:obj`Color` | str | tuple | int | ndarray): Color inputs.
265
+
266
+ Returns:
267
+ tuple[float]: A tuple of 3 normalized floats indicating RGB channels.
268
+ @@ -25,9 +35,177 @@ def color_val_matplotlib(color):
269
+ return tuple(color)
270
+
271
+
272
+ +def _get_adaptive_scales(areas, min_area=800, max_area=30000):
273
+ + """Get adaptive scales according to areas.
274
+ +
275
+ + The scale range is [0.5, 1.0]. When the area is less than
276
+ + ``'min_area'``, the scale is 0.5 while the area is larger than
277
+ + ``'max_area'``, the scale is 1.0.
278
+ +
279
+ + Args:
280
+ + areas (ndarray): The areas of bboxes or masks with the
281
+ + shape of (n, ).
282
+ + min_area (int): Lower bound areas for adaptive scales.
283
+ + Default: 800.
284
+ + max_area (int): Upper bound areas for adaptive scales.
285
+ + Default: 30000.
286
+ +
287
+ + Returns:
288
+ + ndarray: The adaotive scales with the shape of (n, ).
289
+ + """
290
+ + scales = 0.5 + (areas - min_area) / (max_area - min_area)
291
+ + scales = np.clip(scales, 0.5, 1.0)
292
+ + return scales
293
+ +
294
+ +
295
+ +def _get_bias_color(base, max_dist=30):
296
+ + """Get different colors for each masks.
297
+ +
298
+ + Get different colors for each masks by adding a bias
299
+ + color to the base category color.
300
+ + Args:
301
+ + base (ndarray): The base category color with the shape
302
+ + of (3, ).
303
+ + max_dist (int): The max distance of bias. Default: 30.
304
+ +
305
+ + Returns:
306
+ + ndarray: The new color for a mask with the shape of (3, ).
307
+ + """
308
+ + new_color = base + np.random.randint(
309
+ + low=-max_dist, high=max_dist + 1, size=3)
310
+ + return np.clip(new_color, 0, 255, new_color)
311
+ +
312
+ +
313
+ +def draw_bboxes(ax, bboxes, color='g', alpha=0.8, thickness=2):
314
+ + """Draw bounding boxes on the axes.
315
+ +
316
+ + Args:
317
+ + ax (matplotlib.Axes): The input axes.
318
+ + bboxes (ndarray): The input bounding boxes with the shape
319
+ + of (n, 4).
320
+ + color (list[tuple] | matplotlib.color): the colors for each
321
+ + bounding boxes.
322
+ + alpha (float): Transparency of bounding boxes. Default: 0.8.
323
+ + thickness (int): Thickness of lines. Default: 2.
324
+ +
325
+ + Returns:
326
+ + matplotlib.Axes: The result axes.
327
+ + """
328
+ + polygons = []
329
+ + for i, bbox in enumerate(bboxes):
330
+ + bbox_int = bbox.astype(np.int32)
331
+ + poly = [[bbox_int[0], bbox_int[1]], [bbox_int[0], bbox_int[3]],
332
+ + [bbox_int[2], bbox_int[3]], [bbox_int[2], bbox_int[1]]]
333
+ + np_poly = np.array(poly).reshape((4, 2))
334
+ + polygons.append(Polygon(np_poly))
335
+ + p = PatchCollection(
336
+ + polygons,
337
+ + facecolor='none',
338
+ + edgecolors=color,
339
+ + linewidths=thickness,
340
+ + alpha=alpha)
341
+ + ax.add_collection(p)
342
+ +
343
+ + return ax
344
+ +
345
+ +
346
+ +def draw_labels(ax,
347
+ + labels,
348
+ + positions,
349
+ + scores=None,
350
+ + class_names=None,
351
+ + color='w',
352
+ + font_size=8,
353
+ + scales=None,
354
+ + horizontal_alignment='left'):
355
+ + """Draw labels on the axes.
356
+ +
357
+ + Args:
358
+ + ax (matplotlib.Axes): The input axes.
359
+ + labels (ndarray): The labels with the shape of (n, ).
360
+ + positions (ndarray): The positions to draw each labels.
361
+ + scores (ndarray): The scores for each labels.
362
+ + class_names (list[str]): The class names.
363
+ + color (list[tuple] | matplotlib.color): The colors for labels.
364
+ + font_size (int): Font size of texts. Default: 8.
365
+ + scales (list[float]): Scales of texts. Default: None.
366
+ + horizontal_alignment (str): The horizontal alignment method of
367
+ + texts. Default: 'left'.
368
+ +
369
+ + Returns:
370
+ + matplotlib.Axes: The result axes.
371
+ + """
372
+ + for i, (pos, label) in enumerate(zip(positions, labels)):
373
+ + label_text = class_names[
374
+ + label] if class_names is not None else f'class {label}'
375
+ + if scores is not None:
376
+ + label_text += f'|{scores[i]:.02f}'
377
+ + text_color = color[i] if isinstance(color, list) else color
378
+ +
379
+ + font_size_mask = font_size if scales is None else font_size * scales[i]
380
+ + ax.text(
381
+ + pos[0],
382
+ + pos[1],
383
+ + f'{label_text}',
384
+ + bbox={
385
+ + 'facecolor': 'black',
386
+ + 'alpha': 0.8,
387
+ + 'pad': 0.7,
388
+ + 'edgecolor': 'none'
389
+ + },
390
+ + color=text_color,
391
+ + fontsize=font_size_mask,
392
+ + verticalalignment='top',
393
+ + horizontalalignment=horizontal_alignment)
394
+ +
395
+ + return ax
396
+ +
397
+ +
398
+ +def draw_masks(ax, img, masks, color=None, with_edge=True, alpha=0.8):
399
+ + """Draw masks on the image and their edges on the axes.
400
+ +
401
+ + Args:
402
+ + ax (matplotlib.Axes): The input axes.
403
+ + img (ndarray): The image with the shape of (3, h, w).
404
+ + masks (ndarray): The masks with the shape of (n, h, w).
405
+ + color (ndarray): The colors for each masks with the shape
406
+ + of (n, 3).
407
+ + with_edge (bool): Whether to draw edges. Default: True.
408
+ + alpha (float): Transparency of bounding boxes. Default: 0.8.
409
+ +
410
+ + Returns:
411
+ + matplotlib.Axes: The result axes.
412
+ + ndarray: The result image.
413
+ + """
414
+ + taken_colors = set([0, 0, 0])
415
+ + if color is None:
416
+ + random_colors = np.random.randint(0, 255, (masks.size(0), 3))
417
+ + color = [tuple(c) for c in random_colors]
418
+ + color = np.array(color, dtype=np.uint8)
419
+ + polygons = []
420
+ + for i, mask in enumerate(masks):
421
+ + if with_edge:
422
+ + contours, _ = bitmap_to_polygon(mask)
423
+ + polygons += [Polygon(c) for c in contours]
424
+ +
425
+ + color_mask = color[i]
426
+ + while tuple(color_mask) in taken_colors:
427
+ + color_mask = _get_bias_color(color_mask)
428
+ + taken_colors.add(tuple(color_mask))
429
+ +
430
+ + mask = mask.astype(bool)
431
+ + img[mask] = img[mask] * (1 - alpha) + color_mask * alpha
432
+ +
433
+ + p = PatchCollection(
434
+ + polygons, facecolor='none', edgecolors='w', linewidths=1, alpha=0.8)
435
+ + ax.add_collection(p)
436
+ +
437
+ + return ax, img
438
+ +
439
+ +
440
+ def imshow_det_bboxes(img,
441
+ - bboxes,
442
+ - labels,
443
+ + bboxes=None,
444
+ + labels=None,
445
+ segms=None,
446
+ class_names=None,
447
+ score_thr=0,
448
+ @@ -35,7 +213,7 @@ def imshow_det_bboxes(img,
449
+ text_color='green',
450
+ mask_color=None,
451
+ thickness=2,
452
+ - font_size=13,
453
+ + font_size=8,
454
+ win_name='',
455
+ show=True,
456
+ wait_time=0,
457
+ @@ -43,43 +221,51 @@ def imshow_det_bboxes(img,
458
+ """Draw bboxes and class labels (with scores) on an image.
459
+
460
+ Args:
461
+ - img (str or ndarray): The image to be displayed.
462
+ + img (str | ndarray): The image to be displayed.
463
+ bboxes (ndarray): Bounding boxes (with scores), shaped (n, 4) or
464
+ (n, 5).
465
+ labels (ndarray): Labels of bboxes.
466
+ - segms (ndarray or None): Masks, shaped (n,h,w) or None
467
+ + segms (ndarray | None): Masks, shaped (n,h,w) or None.
468
+ class_names (list[str]): Names of each classes.
469
+ - score_thr (float): Minimum score of bboxes to be shown. Default: 0
470
+ - bbox_color (str or tuple(int) or :obj:`Color`):Color of bbox lines.
471
+ - The tuple of color should be in BGR order. Default: 'green'
472
+ - text_color (str or tuple(int) or :obj:`Color`):Color of texts.
473
+ - The tuple of color should be in BGR order. Default: 'green'
474
+ - mask_color (str or tuple(int) or :obj:`Color`, optional):
475
+ - Color of masks. The tuple of color should be in BGR order.
476
+ - Default: None
477
+ - thickness (int): Thickness of lines. Default: 2
478
+ - font_size (int): Font size of texts. Default: 13
479
+ - show (bool): Whether to show the image. Default: True
480
+ - win_name (str): The window name. Default: ''
481
+ + score_thr (float): Minimum score of bboxes to be shown. Default: 0.
482
+ + bbox_color (list[tuple] | tuple | str | None): Colors of bbox lines.
483
+ + If a single color is given, it will be applied to all classes.
484
+ + The tuple of color should be in RGB order. Default: 'green'.
485
+ + text_color (list[tuple] | tuple | str | None): Colors of texts.
486
+ + If a single color is given, it will be applied to all classes.
487
+ + The tuple of color should be in RGB order. Default: 'green'.
488
+ + mask_color (list[tuple] | tuple | str | None, optional): Colors of
489
+ + masks. If a single color is given, it will be applied to all
490
+ + classes. The tuple of color should be in RGB order.
491
+ + Default: None.
492
+ + thickness (int): Thickness of lines. Default: 2.
493
+ + font_size (int): Font size of texts. Default: 13.
494
+ + show (bool): Whether to show the image. Default: True.
495
+ + win_name (str): The window name. Default: ''.
496
+ wait_time (float): Value of waitKey param. Default: 0.
497
+ out_file (str, optional): The filename to write the image.
498
+ - Default: None
499
+ + Default: None.
500
+
501
+ Returns:
502
+ ndarray: The image with bboxes drawn on it.
503
+ """
504
+ - assert bboxes.ndim == 2, \
505
+ + assert bboxes is None or bboxes.ndim == 2, \
506
+ f' bboxes ndim should be 2, but its ndim is {bboxes.ndim}.'
507
+ assert labels.ndim == 1, \
508
+ f' labels ndim should be 1, but its ndim is {labels.ndim}.'
509
+ - assert bboxes.shape[0] == labels.shape[0], \
510
+ - 'bboxes.shape[0] and labels.shape[0] should have the same length.'
511
+ - assert bboxes.shape[1] == 4 or bboxes.shape[1] == 5, \
512
+ + assert bboxes is None or bboxes.shape[1] == 4 or bboxes.shape[1] == 5, \
513
+ f' bboxes.shape[1] should be 4 or 5, but its {bboxes.shape[1]}.'
514
+ + assert bboxes is None or bboxes.shape[0] <= labels.shape[0], \
515
+ + 'labels.shape[0] should not be less than bboxes.shape[0].'
516
+ + assert segms is None or segms.shape[0] == labels.shape[0], \
517
+ + 'segms.shape[0] and labels.shape[0] should have the same length.'
518
+ + assert segms is not None or bboxes is not None, \
519
+ + 'segms and bboxes should not be None at the same time.'
520
+ +
521
+ img = mmcv.imread(img).astype(np.uint8)
522
+
523
+ if score_thr > 0:
524
+ - assert bboxes.shape[1] == 5
525
+ + assert bboxes is not None and bboxes.shape[1] == 5
526
+ scores = bboxes[:, -1]
527
+ inds = scores > score_thr
528
+ bboxes = bboxes[inds, :]
529
+ @@ -87,25 +273,6 @@ def imshow_det_bboxes(img,
530
+ if segms is not None:
531
+ segms = segms[inds, ...]
532
+
533
+ - mask_colors = []
534
+ - if labels.shape[0] > 0:
535
+ - if mask_color is None:
536
+ - # random color
537
+ - np.random.seed(42)
538
+ - mask_colors = [
539
+ - np.random.randint(0, 256, (1, 3), dtype=np.uint8)
540
+ - for _ in range(max(labels) + 1)
541
+ - ]
542
+ - else:
543
+ - # specify color
544
+ - mask_colors = [
545
+ - np.array(mmcv.color_val(mask_color)[::-1], dtype=np.uint8)
546
+ - ] * (
547
+ - max(labels) + 1)
548
+ -
549
+ - bbox_color = color_val_matplotlib(bbox_color)
550
+ - text_color = color_val_matplotlib(text_color)
551
+ -
552
+ img = mmcv.bgr2rgb(img)
553
+ width, height = img.shape[1], img.shape[0]
554
+ img = np.ascontiguousarray(img)
555
+ @@ -123,44 +290,64 @@ def imshow_det_bboxes(img,
556
+ ax = plt.gca()
557
+ ax.axis('off')
558
+
559
+ - polygons = []
560
+ - color = []
561
+ - for i, (bbox, label) in enumerate(zip(bboxes, labels)):
562
+ - bbox_int = bbox.astype(np.int32)
563
+ - poly = [[bbox_int[0], bbox_int[1]], [bbox_int[0], bbox_int[3]],
564
+ - [bbox_int[2], bbox_int[3]], [bbox_int[2], bbox_int[1]]]
565
+ - np_poly = np.array(poly).reshape((4, 2))
566
+ - polygons.append(Polygon(np_poly))
567
+ - color.append(bbox_color)
568
+ - label_text = class_names[
569
+ - label] if class_names is not None else f'class {label}'
570
+ - if len(bbox) > 4:
571
+ - label_text += f'|{bbox[-1]:.02f}'
572
+ - ax.text(
573
+ - bbox_int[0],
574
+ - bbox_int[1],
575
+ - f'{label_text}',
576
+ - bbox={
577
+ - 'facecolor': 'black',
578
+ - 'alpha': 0.8,
579
+ - 'pad': 0.7,
580
+ - 'edgecolor': 'none'
581
+ - },
582
+ - color=text_color,
583
+ - fontsize=font_size,
584
+ - verticalalignment='top',
585
+ - horizontalalignment='left')
586
+ - if segms is not None:
587
+ - color_mask = mask_colors[labels[i]]
588
+ - mask = segms[i].astype(bool)
589
+ - img[mask] = img[mask] * 0.5 + color_mask * 0.5
590
+ + max_label = int(max(labels) if len(labels) > 0 else 0)
591
+ + text_palette = palette_val(get_palette(text_color, max_label + 1))
592
+ + text_colors = [text_palette[label] for label in labels]
593
+ +
594
+ + num_bboxes = 0
595
+ + if bboxes is not None:
596
+ + num_bboxes = bboxes.shape[0]
597
+ + bbox_palette = palette_val(get_palette(bbox_color, max_label + 1))
598
+ + colors = [bbox_palette[label] for label in labels[:num_bboxes]]
599
+ + draw_bboxes(ax, bboxes, colors, alpha=0.8, thickness=thickness)
600
+ +
601
+ + horizontal_alignment = 'left'
602
+ + positions = bboxes[:, :2].astype(np.int32) + thickness
603
+ + areas = (bboxes[:, 3] - bboxes[:, 1]) * (bboxes[:, 2] - bboxes[:, 0])
604
+ + scales = _get_adaptive_scales(areas)
605
+ + scores = bboxes[:, 4] if bboxes.shape[1] == 5 else None
606
+ + draw_labels(
607
+ + ax,
608
+ + labels[:num_bboxes],
609
+ + positions,
610
+ + scores=scores,
611
+ + class_names=class_names,
612
+ + color=text_colors,
613
+ + font_size=font_size,
614
+ + scales=scales,
615
+ + horizontal_alignment=horizontal_alignment)
616
+ +
617
+ + if segms is not None:
618
+ + mask_palette = get_palette(mask_color, max_label + 1)
619
+ + colors = [mask_palette[label] for label in labels]
620
+ + colors = np.array(colors, dtype=np.uint8)
621
+ + draw_masks(ax, img, segms, colors, with_edge=True)
622
+ +
623
+ + if num_bboxes < segms.shape[0]:
624
+ + segms = segms[num_bboxes:]
625
+ + horizontal_alignment = 'center'
626
+ + areas = []
627
+ + positions = []
628
+ + for mask in segms:
629
+ + _, _, stats, centroids = cv2.connectedComponentsWithStats(
630
+ + mask.astype(np.uint8), connectivity=8)
631
+ + largest_id = np.argmax(stats[1:, -1]) + 1
632
+ + positions.append(centroids[largest_id])
633
+ + areas.append(stats[largest_id, -1])
634
+ + areas = np.stack(areas, axis=0)
635
+ + scales = _get_adaptive_scales(areas)
636
+ + draw_labels(
637
+ + ax,
638
+ + labels[num_bboxes:],
639
+ + positions,
640
+ + class_names=class_names,
641
+ + color=text_colors,
642
+ + font_size=font_size,
643
+ + scales=scales,
644
+ + horizontal_alignment=horizontal_alignment)
645
+
646
+ plt.imshow(img)
647
+
648
+ - p = PatchCollection(
649
+ - polygons, facecolor='none', edgecolors=color, linewidths=thickness)
650
+ - ax.add_collection(p)
651
+ -
652
+ stream, _ = canvas.print_to_buffer()
653
+ buffer = np.frombuffer(stream, dtype='uint8')
654
+ img_rgba = buffer.reshape(height, width, 4)
655
+ @@ -191,12 +378,12 @@ def imshow_gt_det_bboxes(img,
656
+ result,
657
+ class_names=None,
658
+ score_thr=0,
659
+ - gt_bbox_color=(255, 102, 61),
660
+ - gt_text_color=(255, 102, 61),
661
+ - gt_mask_color=(255, 102, 61),
662
+ - det_bbox_color=(72, 101, 241),
663
+ - det_text_color=(72, 101, 241),
664
+ - det_mask_color=(72, 101, 241),
665
+ + gt_bbox_color=(61, 102, 255),
666
+ + gt_text_color=(200, 200, 200),
667
+ + gt_mask_color=(61, 102, 255),
668
+ + det_bbox_color=(241, 101, 72),
669
+ + det_text_color=(200, 200, 200),
670
+ + det_mask_color=(241, 101, 72),
671
+ thickness=2,
672
+ font_size=13,
673
+ win_name='',
674
+ @@ -206,54 +393,75 @@ def imshow_gt_det_bboxes(img,
675
+ """General visualization GT and result function.
676
+
677
+ Args:
678
+ - img (str or ndarray): The image to be displayed.)
679
+ + img (str | ndarray): The image to be displayed.
680
+ annotation (dict): Ground truth annotations where contain keys of
681
+ - 'gt_bboxes' and 'gt_labels' or 'gt_masks'
682
+ - result (tuple[list] or list): The detection result, can be either
683
+ + 'gt_bboxes' and 'gt_labels' or 'gt_masks'.
684
+ + result (tuple[list] | list): The detection result, can be either
685
+ (bbox, segm) or just bbox.
686
+ class_names (list[str]): Names of each classes.
687
+ - score_thr (float): Minimum score of bboxes to be shown. Default: 0
688
+ - gt_bbox_color (str or tuple(int) or :obj:`Color`):Color of bbox lines.
689
+ - The tuple of color should be in BGR order. Default: (255, 102, 61)
690
+ - gt_text_color (str or tuple(int) or :obj:`Color`):Color of texts.
691
+ - The tuple of color should be in BGR order. Default: (255, 102, 61)
692
+ - gt_mask_color (str or tuple(int) or :obj:`Color`, optional):
693
+ - Color of masks. The tuple of color should be in BGR order.
694
+ - Default: (255, 102, 61)
695
+ - det_bbox_color (str or tuple(int) or :obj:`Color`):Color of bbox lines.
696
+ - The tuple of color should be in BGR order. Default: (72, 101, 241)
697
+ - det_text_color (str or tuple(int) or :obj:`Color`):Color of texts.
698
+ - The tuple of color should be in BGR order. Default: (72, 101, 241)
699
+ - det_mask_color (str or tuple(int) or :obj:`Color`, optional):
700
+ - Color of masks. The tuple of color should be in BGR order.
701
+ - Default: (72, 101, 241)
702
+ - thickness (int): Thickness of lines. Default: 2
703
+ - font_size (int): Font size of texts. Default: 13
704
+ - win_name (str): The window name. Default: ''
705
+ - show (bool): Whether to show the image. Default: True
706
+ + score_thr (float): Minimum score of bboxes to be shown. Default: 0.
707
+ + gt_bbox_color (list[tuple] | tuple | str | None): Colors of bbox lines.
708
+ + If a single color is given, it will be applied to all classes.
709
+ + The tuple of color should be in RGB order. Default: (61, 102, 255).
710
+ + gt_text_color (list[tuple] | tuple | str | None): Colors of texts.
711
+ + If a single color is given, it will be applied to all classes.
712
+ + The tuple of color should be in RGB order. Default: (200, 200, 200).
713
+ + gt_mask_color (list[tuple] | tuple | str | None, optional): Colors of
714
+ + masks. If a single color is given, it will be applied to all classes.
715
+ + The tuple of color should be in RGB order. Default: (61, 102, 255).
716
+ + det_bbox_color (list[tuple] | tuple | str | None):Colors of bbox lines.
717
+ + If a single color is given, it will be applied to all classes.
718
+ + The tuple of color should be in RGB order. Default: (241, 101, 72).
719
+ + det_text_color (list[tuple] | tuple | str | None):Colors of texts.
720
+ + If a single color is given, it will be applied to all classes.
721
+ + The tuple of color should be in RGB order. Default: (200, 200, 200).
722
+ + det_mask_color (list[tuple] | tuple | str | None, optional): Color of
723
+ + masks. If a single color is given, it will be applied to all classes.
724
+ + The tuple of color should be in RGB order. Default: (241, 101, 72).
725
+ + thickness (int): Thickness of lines. Default: 2.
726
+ + font_size (int): Font size of texts. Default: 13.
727
+ + win_name (str): The window name. Default: ''.
728
+ + show (bool): Whether to show the image. Default: True.
729
+ wait_time (float): Value of waitKey param. Default: 0.
730
+ out_file (str, optional): The filename to write the image.
731
+ - Default: None
732
+ + Default: None.
733
+
734
+ Returns:
735
+ ndarray: The image with bboxes or masks drawn on it.
736
+ """
737
+ assert 'gt_bboxes' in annotation
738
+ assert 'gt_labels' in annotation
739
+ - assert isinstance(
740
+ - result,
741
+ - (tuple, list)), f'Expected tuple or list, but get {type(result)}'
742
+ + assert isinstance(result, (tuple, list, dict)), 'Expected ' \
743
+ + f'tuple or list or dict, but get {type(result)}'
744
+
745
+ + gt_bboxes = annotation['gt_bboxes']
746
+ + gt_labels = annotation['gt_labels']
747
+ gt_masks = annotation.get('gt_masks', None)
748
+ if gt_masks is not None:
749
+ gt_masks = mask2ndarray(gt_masks)
750
+
751
+ + gt_seg = annotation.get('gt_semantic_seg', None)
752
+ + if gt_seg is not None:
753
+ + pad_value = 255 # the padding value of gt_seg
754
+ + sem_labels = np.unique(gt_seg)
755
+ + all_labels = np.concatenate((gt_labels, sem_labels), axis=0)
756
+ + all_labels, counts = np.unique(all_labels, return_counts=True)
757
+ + stuff_labels = all_labels[np.logical_and(counts < 2,
758
+ + all_labels != pad_value)]
759
+ + stuff_masks = gt_seg[None] == stuff_labels[:, None, None]
760
+ + gt_labels = np.concatenate((gt_labels, stuff_labels), axis=0)
761
+ + gt_masks = np.concatenate((gt_masks, stuff_masks.astype(np.uint8)),
762
+ + axis=0)
763
+ + # If you need to show the bounding boxes,
764
+ + # please comment the following line
765
+ + # gt_bboxes = None
766
+ +
767
+ img = mmcv.imread(img)
768
+
769
+ img = imshow_det_bboxes(
770
+ img,
771
+ - annotation['gt_bboxes'],
772
+ - annotation['gt_labels'],
773
+ + gt_bboxes,
774
+ + gt_labels,
775
+ gt_masks,
776
+ class_names=class_names,
777
+ bbox_color=gt_bbox_color,
778
+ @@ -264,25 +472,38 @@ def imshow_gt_det_bboxes(img,
779
+ win_name=win_name,
780
+ show=False)
781
+
782
+ - if isinstance(result, tuple):
783
+ - bbox_result, segm_result = result
784
+ - if isinstance(segm_result, tuple):
785
+ - segm_result = segm_result[0] # ms rcnn
786
+ + if not isinstance(result, dict):
787
+ + if isinstance(result, tuple):
788
+ + bbox_result, segm_result = result
789
+ + if isinstance(segm_result, tuple):
790
+ + segm_result = segm_result[0] # ms rcnn
791
+ + else:
792
+ + bbox_result, segm_result = result, None
793
+ +
794
+ + bboxes = np.vstack(bbox_result)
795
+ + labels = [
796
+ + np.full(bbox.shape[0], i, dtype=np.int32)
797
+ + for i, bbox in enumerate(bbox_result)
798
+ + ]
799
+ + labels = np.concatenate(labels)
800
+ +
801
+ + segms = None
802
+ + if segm_result is not None and len(labels) > 0: # non empty
803
+ + segms = mmcv.concat_list(segm_result)
804
+ + segms = mask_util.decode(segms)
805
+ + segms = segms.transpose(2, 0, 1)
806
+ else:
807
+ - bbox_result, segm_result = result, None
808
+ -
809
+ - bboxes = np.vstack(bbox_result)
810
+ - labels = [
811
+ - np.full(bbox.shape[0], i, dtype=np.int32)
812
+ - for i, bbox in enumerate(bbox_result)
813
+ - ]
814
+ - labels = np.concatenate(labels)
815
+ -
816
+ - segms = None
817
+ - if segm_result is not None and len(labels) > 0: # non empty
818
+ - segms = mmcv.concat_list(segm_result)
819
+ - segms = mask_util.decode(segms)
820
+ - segms = segms.transpose(2, 0, 1)
821
+ + assert class_names is not None, 'We need to know the number ' \
822
+ + 'of classes.'
823
+ + VOID = len(class_names)
824
+ + bboxes = None
825
+ + pan_results = result['pan_results']
826
+ + # keep objects ahead
827
+ + ids = np.unique(pan_results)[::-1]
828
+ + legal_indices = ids != VOID
829
+ + ids = ids[legal_indices]
830
+ + labels = np.array([id % INSTANCE_OFFSET for id in ids], dtype=np.int64)
831
+ + segms = (pan_results[None] == ids[:, None, None])
832
+
833
+ img = imshow_det_bboxes(
834
+ img,
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ mmcv-full==1.5.0
2
+ mmdet==2.24.1
3
+ numpy==1.22.4
4
+ opencv-python-headless==4.5.5.64
5
+ openmim==0.1.5
6
+ timm==0.5.4
7
+ torch==1.11.0
8
+ torchvision==0.12.0