190 | January 2003
191 | """
192 |
193 | seg = seg.astype(np.bool)
194 | seg[seg>0] = 1
195 |
196 | assert np.atleast_3d(seg).shape[2] == 1
197 |
198 | width = seg.shape[1] if width is None else width
199 | height = seg.shape[0] if height is None else height
200 |
201 | h,w = seg.shape[:2]
202 |
203 | ar1 = float(width) / float(height)
204 | ar2 = float(w) / float(h)
205 |
206 | assert not (width>w | height>h | abs(ar1-ar2)>0.01),\
207 | 'Can''t convert %dx%d seg to %dx%d bmap.'%(w,h,width,height)
208 |
209 | e = np.zeros_like(seg)
210 | s = np.zeros_like(seg)
211 | se = np.zeros_like(seg)
212 |
213 | e[:,:-1] = seg[:,1:]
214 | s[:-1,:] = seg[1:,:]
215 | se[:-1,:-1] = seg[1:,1:]
216 |
217 | b = seg^e | seg^s | seg^se
218 | b[-1,:] = seg[-1,:]^e[-1,:]
219 | b[:,-1] = seg[:,-1]^s[:,-1]
220 | b[-1,-1] = 0
221 |
222 | if w == width and h == height:
223 | bmap = b
224 | else:
225 | bmap = np.zeros((height,width))
226 | for x in range(w):
227 | for y in range(h):
228 | if b[y,x]:
229 | j = 1+floor((y-1)+height / h)
230 | i = 1+floor((x-1)+width / h)
231 | bmap[j,i] = 1;
232 |
233 | return bmap
234 |
--------------------------------------------------------------------------------
/utils/my_data_parallel.py:
--------------------------------------------------------------------------------
1 | """
2 | # Code adapted from:
3 | # https://github.com/pytorch/pytorch/blob/master/torch/nn/parallel/data_parallel.py
4 | #
5 | # BSD 3-Clause License
6 | #
7 | # Copyright (c) 2017,
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice, this
14 | # list of conditions and the following disclaimer.
15 | #
16 | # * Redistributions in binary form must reproduce the above copyright notice,
17 | # this list of conditions and the following disclaimer in the documentation
18 | # and/or other materials provided with the distribution.
19 | #
20 | # * Neither the name of the copyright holder nor the names of its
21 | # contributors may be used to endorse or promote products derived from
22 | # this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.s
34 | """
35 |
36 |
37 | import operator
38 | import torch
39 | import warnings
40 | from torch.nn.modules import Module
41 | from torch.nn.parallel.scatter_gather import scatter_kwargs, gather
42 | from torch.nn.parallel.replicate import replicate
43 | from torch.nn.parallel.parallel_apply import parallel_apply
44 |
45 |
46 | def _check_balance(device_ids):
47 | imbalance_warn = """
48 | There is an imbalance between your GPUs. You may want to exclude GPU {} which
49 | has less than 75% of the memory or cores of GPU {}. You can do so by setting
50 | the device_ids argument to DataParallel, or by setting the CUDA_VISIBLE_DEVICES
51 | environment variable."""
52 |
53 | dev_props = [torch.cuda.get_device_properties(i) for i in device_ids]
54 |
55 | def warn_imbalance(get_prop):
56 | values = [get_prop(props) for props in dev_props]
57 | min_pos, min_val = min(enumerate(values), key=operator.itemgetter(1))
58 | max_pos, max_val = max(enumerate(values), key=operator.itemgetter(1))
59 | if min_val / max_val < 0.75:
60 | warnings.warn(imbalance_warn.format(device_ids[min_pos], device_ids[max_pos]))
61 | return True
62 | return False
63 |
64 | if warn_imbalance(lambda props: props.total_memory):
65 | return
66 | if warn_imbalance(lambda props: props.multi_processor_count):
67 | return
68 |
69 |
70 |
71 | def data_parallel(module, inputs, device_ids=None, output_device=None, dim=0, module_kwargs=None, gather=True):
72 | """
73 | Evaluates module(input) in parallel across the GPUs given in device_ids.
74 | This is the functional version of the DataParallel module.
75 | Args:
76 | module: the module to evaluate in parallel
77 | inputs: inputs to the module
78 | device_ids: GPU ids on which to replicate module
79 | output_device: GPU location of the output Use -1 to indicate the CPU.
80 | (default: device_ids[0])
81 | Returns:
82 | a Tensor containing the result of module(input) located on
83 | output_device
84 | """
85 | if not isinstance(inputs, tuple):
86 | inputs = (inputs,)
87 |
88 | if device_ids is None:
89 | device_ids = list(range(torch.cuda.device_count()))
90 |
91 | if output_device is None:
92 | output_device = device_ids[0]
93 |
94 | inputs, module_kwargs = scatter_kwargs(inputs, module_kwargs, device_ids, dim)
95 | if len(device_ids) == 1:
96 | return module(*inputs[0], **module_kwargs[0])
97 | used_device_ids = device_ids[:len(inputs)]
98 | replicas = replicate(module, used_device_ids)
99 | outputs = parallel_apply(replicas, inputs, module_kwargs, used_device_ids)
100 | if gather:
101 | return gather(outputs, output_device, dim)
102 | else:
103 | return outputs
104 |
105 |
106 |
107 | class MyDataParallel(Module):
108 | """
109 | Implements data parallelism at the module level.
110 | This container parallelizes the application of the given module by
111 | splitting the input across the specified devices by chunking in the batch
112 | dimension. In the forward pass, the module is replicated on each device,
113 | and each replica handles a portion of the input. During the backwards
114 | pass, gradients from each replica are summed into the original module.
115 | The batch size should be larger than the number of GPUs used.
116 | See also: :ref:`cuda-nn-dataparallel-instead`
117 | Arbitrary positional and keyword inputs are allowed to be passed into
118 | DataParallel EXCEPT Tensors. All tensors will be scattered on dim
119 | specified (default 0). Primitive types will be broadcasted, but all
120 | other types will be a shallow copy and can be corrupted if written to in
121 | the model's forward pass.
122 | .. warning::
123 | Forward and backward hooks defined on :attr:`module` and its submodules
124 | will be invoked ``len(device_ids)`` times, each with inputs located on
125 | a particular device. Particularly, the hooks are only guaranteed to be
126 | executed in correct order with respect to operations on corresponding
127 | devices. For example, it is not guaranteed that hooks set via
128 | :meth:`~torch.nn.Module.register_forward_pre_hook` be executed before
129 | `all` ``len(device_ids)`` :meth:`~torch.nn.Module.forward` calls, but
130 | that each such hook be executed before the corresponding
131 | :meth:`~torch.nn.Module.forward` call of that device.
132 | .. warning::
133 | When :attr:`module` returns a scalar (i.e., 0-dimensional tensor) in
134 | :func:`forward`, this wrapper will return a vector of length equal to
135 | number of devices used in data parallelism, containing the result from
136 | each device.
137 | .. note::
138 | There is a subtlety in using the
139 | ``pack sequence -> recurrent network -> unpack sequence`` pattern in a
140 | :class:`~torch.nn.Module` wrapped in :class:`~torch.nn.DataParallel`.
141 | See :ref:`pack-rnn-unpack-with-data-parallelism` section in FAQ for
142 | details.
143 | Args:
144 | module: module to be parallelized
145 | device_ids: CUDA devices (default: all devices)
146 | output_device: device location of output (default: device_ids[0])
147 | Attributes:
148 | module (Module): the module to be parallelized
149 | Example::
150 | >>> net = torch.nn.DataParallel(model, device_ids=[0, 1, 2])
151 | >>> output = net(input_var)
152 | """
153 |
154 | # TODO: update notes/cuda.rst when this class handles 8+ GPUs well
155 |
156 | def __init__(self, module, device_ids=None, output_device=None, dim=0, gather=True):
157 | super(MyDataParallel, self).__init__()
158 |
159 | if not torch.cuda.is_available():
160 | self.module = module
161 | self.device_ids = []
162 | return
163 |
164 | if device_ids is None:
165 | device_ids = list(range(torch.cuda.device_count()))
166 | if output_device is None:
167 | output_device = device_ids[0]
168 | self.dim = dim
169 | self.module = module
170 | self.device_ids = device_ids
171 | self.output_device = output_device
172 | self.gather_bool = gather
173 |
174 | _check_balance(self.device_ids)
175 |
176 | if len(self.device_ids) == 1:
177 | self.module.cuda(device_ids[0])
178 |
179 | def forward(self, *inputs, **kwargs):
180 | if not self.device_ids:
181 | return self.module(*inputs, **kwargs)
182 | inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids)
183 | if len(self.device_ids) == 1:
184 | return [self.module(*inputs[0], **kwargs[0])]
185 | replicas = self.replicate(self.module, self.device_ids[:len(inputs)])
186 | outputs = self.parallel_apply(replicas, inputs, kwargs)
187 | if self.gather_bool:
188 | return self.gather(outputs, self.output_device)
189 | else:
190 | return outputs
191 |
192 | def replicate(self, module, device_ids):
193 | return replicate(module, device_ids)
194 |
195 | def scatter(self, inputs, kwargs, device_ids):
196 | return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim)
197 |
198 | def parallel_apply(self, replicas, inputs, kwargs):
199 | return parallel_apply(replicas, inputs, kwargs, self.device_ids[:len(replicas)])
200 |
201 | def gather(self, outputs, output_device):
202 | return gather(outputs, output_device, dim=self.dim)
203 |
204 |
--------------------------------------------------------------------------------
/utils/results_page.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2020 Nvidia Corporation
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of the copyright holder nor the names of its contributors
15 | may be used to endorse or promote products derived from this software
16 | without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 | POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | import glob
32 | import os
33 | import numpy as np
34 |
35 | id2cat = {
36 | 0: 'road',
37 | 1: 'sidewalk',
38 | 2: 'building',
39 | 3: 'wall',
40 | 4: 'fence',
41 | 5: 'pole',
42 | 6: 'traffic_light',
43 | 7: 'traffic_sign',
44 | 8: 'vegetation',
45 | 9: 'terrain',
46 | 10: 'sky',
47 | 11: 'person',
48 | 12: 'rider',
49 | 13: 'car',
50 | 14: 'truck',
51 | 15: 'bus',
52 | 16: 'train',
53 | 17: 'motorcycle',
54 | 18: 'bicycle'}
55 |
56 | # Leaderboard mapillary
57 | sota_iu_results = {
58 | 0: 98.4046,
59 | 1: 85.0224,
60 | 2: 93.6462,
61 | 3: 61.7487,
62 | 4: 63.8885,
63 | 5: 67.6745,
64 | 6: 77.43,
65 | 7: 80.8351,
66 | 8: 93.7341,
67 | 9: 71.8774,
68 | 10: 95.6122,
69 | 11: 86.7228,
70 | 12: 72.7778,
71 | 13: 95.7033,
72 | 14: 79.9019,
73 | 15: 93.0954,
74 | 16: 89.7196,
75 | 17: 72.5731,
76 | 18: 78.2172,
77 | 255: 0}
78 |
79 |
80 | class ResultsPage(object):
81 | '''
82 | This creates an HTML page of embedded images, useful for showing evaluation results.
83 |
84 | Usage:
85 | ip = ImagePage(html_fn)
86 |
87 | # Add a table with N images ...
88 | ip.add_table((img, descr), (img, descr), ...)
89 |
90 | # Generate html page
91 | ip.write_page()
92 | '''
93 |
94 | def __init__(self, experiment_name, html_filename):
95 | self.experiment_name = experiment_name
96 | self.html_filename = html_filename
97 | self.outfile = open(self.html_filename, 'w')
98 | self.items = []
99 |
100 | def _print_header(self):
101 | header = '''
102 |
103 |
104 | Experiment = {}
105 |
106 | '''.format(self.experiment_name)
107 | self.outfile.write(header)
108 |
109 | def _print_footer(self):
110 | self.outfile.write('''
111 | ''')
112 |
113 | def _print_table_header(self, table_name):
114 | table_hdr = ''' {}
115 |
116 | '''.format(table_name)
117 | self.outfile.write(table_hdr)
118 |
119 | def _print_table_footer(self):
120 | table_ftr = '''
121 |
'''
122 | self.outfile.write(table_ftr)
123 |
124 | def _print_table_guts(self, img_fn, descr):
125 | table = '''
126 |
127 |
128 |
129 |
130 | {descr}
131 |
132 | | '''.format(img_fn=img_fn, descr=descr)
133 | self.outfile.write(table)
134 |
135 | def add_table(self, img_label_pairs, table_heading=''):
136 | """
137 | :img_label_pairs: A list of pairs of [img,label]
138 | """
139 | self.items.append([img_label_pairs, table_heading])
140 |
141 | def _write_table(self, table, heading):
142 | img, _descr = table[0]
143 | self._print_table_header(heading)
144 | for img, descr in table:
145 | self._print_table_guts(img, descr)
146 | self._print_table_footer()
147 |
148 | def write_page(self):
149 | self._print_header()
150 |
151 | for table, heading in self.items:
152 | self._write_table(table, heading)
153 |
154 | self._print_footer()
155 |
156 | def _print_page_start(self):
157 | page_start = '''
158 |
159 |
160 | Experiment = EXP_NAME
161 |
171 |
172 | '''
173 | self.outfile.write(page_start)
174 |
175 | def _print_table_start(self, caption, hdr):
176 | self.outfile.write('''
177 | {}
178 | '''.format(caption))
179 | for hdr_col in hdr:
180 | self.outfile.write(' {} | '.format(hdr_col))
181 | self.outfile.write('
')
182 |
183 | def _print_table_row(self, row):
184 | self.outfile.write(' ')
185 | for i in row:
186 | self.outfile.write(' {} | '.format(i))
187 | # Create Links
188 | fp_link = 'false positive Top N'.format(row[
189 | 1])
190 | fn_link = 'false_negative Top N'.format(row[
191 | 1])
192 | self.outfile.write(' {} | '.format(fp_link))
193 | self.outfile.write(' {} | '.format(fn_link))
194 | self.outfile.write('
')
195 |
196 | def _print_table_end(self):
197 | self.outfile.write('
')
198 |
199 | def _print_page_end(self):
200 | self.outfile.write('''
201 |
202 | ''')
203 |
204 | def create_main(self, iu, hist):
205 | self._print_page_start()
206 | #_print_table_style()
207 | # Calculate all of the terms:
208 | iu_false_positive = hist.sum(axis=1) - np.diag(hist)
209 | iu_false_negative = hist.sum(axis=0) - np.diag(hist)
210 | iu_true_positive = np.diag(hist)
211 |
212 | hdr = ("Class ID", "Class", "IoU", "Sota-IU", "TP",
213 | "FP", "FN", "precision", "recall", "", "")
214 | self._print_table_start("Mean IoU Results", hdr)
215 | for iu_score, index in iu:
216 | class_name = id2cat[index]
217 | iu_string = '{:5.2f}'.format(iu_score * 100)
218 | total_pixels = hist.sum()
219 | tp = '{:5.2f}'.format(100 * iu_true_positive[index] / total_pixels)
220 | fp = '{:5.2f}'.format(
221 | iu_false_positive[index] / iu_true_positive[index])
222 | fn = '{:5.2f}'.format(
223 | iu_false_negative[index] / iu_true_positive[index])
224 | precision = '{:5.2f}'.format(
225 | iu_true_positive[index] / (iu_true_positive[index] + iu_false_positive[index]))
226 | recall = '{:5.2f}'.format(
227 | iu_true_positive[index] / (iu_true_positive[index] + iu_false_negative[index]))
228 | sota = '{:5.2f}'.format(sota_iu_results[index])
229 | row = (index, class_name, iu_string, sota,
230 | tp, fp, fn, precision, recall)
231 | self._print_table_row(row)
232 | self._print_table_end()
233 | self._print_page_end()
234 |
235 |
236 | def main():
237 | images = glob.glob('dump_imgs_train/*.png')
238 | images = [i for i in images if 'mask' not in i]
239 |
240 | ip = ResultsPage('test page', 'dd.html')
241 | for img in images:
242 | basename = os.path.splitext(img)[0]
243 | mask_img = basename + '_mask.png'
244 | ip.add_table(((img, 'image'), (mask_img, 'mask')))
245 | ip.write_page()
246 |
--------------------------------------------------------------------------------