Repository: open-mmlab/mmdetection

Issue:
Stacking of masks done in `Pad`
Hey guys,

I run into this error when commenting out the line

```
dict(type='Pad', size_divisor=32),
```

in `train_pipeline` on custom images. It causes this error:

```
Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/pc/.vscode-server/extensions/ms-python.python-2020.1.58038/pythonFiles/lib/python/new_ptvsd/no_wheels/ptvsd/__main__.py", line 45, in <module>
    cli.main()
  File "/home/pc/.vscode-server/extensions/ms-python.python-2020.1.58038/pythonFiles/lib/python/new_ptvsd/no_wheels/ptvsd/../ptvsd/server/cli.py", line 361, in main
    run()
  File "/home/pc/.vscode-server/extensions/ms-python.python-2020.1.58038/pythonFiles/lib/python/new_ptvsd/no_wheels/ptvsd/../ptvsd/server/cli.py", line 203, in run_file
    runpy.run_path(options.target, run_name="__main__")
  File "/usr/lib/python3.6/runpy.py", line 263, in run_path
    pkg_name=pkg_name, script_name=fname)
  File "/usr/lib/python3.6/runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/pc/dev/mmdetection/tools/train.py", line 124, in <module>
    main()
  File "/home/pc/dev/mmdetection/tools/train.py", line 120, in main
    timestamp=timestamp)
  File "/home/pc/dev/mmdetection/mmdet/apis/train.py", line 133, in train_detector
    timestamp=timestamp)
  File "/home/pc/dev/mmdetection/mmdet/apis/train.py", line 319, in _non_dist_train
    runner.run(data_loaders, cfg.workflow, cfg.total_epochs)
  File "/home/pc/dev/venvs/mmdetection/lib/python3.6/site-packages/mmcv/runner/runner.py", line 364, in run
    epoch_runner(data_loaders[i], **kwargs)
  File "/home/pc/dev/venvs/mmdetection/lib/python3.6/site-packages/mmcv/runner/runner.py", line 268, in train
    self.model, data_batch, train_mode=True, **kwargs)
  File "/home/pc/dev/mmdetection/mmdet/apis/train.py", line 100, in batch_processor
    losses = model(**data)
  File "/home/pc/dev/venvs/mmdetection/lib/python3.6/site-packages/torch/nn/modules/module.py", line 532, in __call__
    result = self.forward(*input, **kwargs)
  File "/home/pc/dev/venvs/mmdetection/lib/python3.6/site-packages/torch/nn/parallel/data_parallel.py", line 150, in forward
    return self.module(*inputs[0], **kwargs[0])
  File "/home/pc/dev/venvs/mmdetection/lib/python3.6/site-packages/torch/nn/modules/module.py", line 532, in __call__
    result = self.forward(*input, **kwargs)
  File "/home/pc/dev/mmdetection/mmdet/core/fp16/decorators.py", line 49, in new_func
    return old_func(*args, **kwargs)
  File "/home/pc/dev/mmdetection/mmdet/models/detectors/base.py", line 138, in forward
    return self.forward_train(img, img_meta, **kwargs)
  File "/home/pc/dev/mmdetection/mmdet/models/detectors/two_stage.py", line 254, in forward_train
    sampling_results, gt_masks, self.train_cfg.rcnn)
  File "/home/pc/dev/mmdetection/mmdet/models/mask_heads/fcn_mask_head.py", line 111, in get_target
    gt_masks, rcnn_train_cfg)
  File "/home/pc/dev/mmdetection/mmdet/core/mask/mask_target.py", line 12, in mask_target
    mask_targets = torch.cat(list(mask_targets))
  File "/home/pc/dev/mmdetection/mmdet/core/mask/mask_target.py", line 22, in mask_target_single
    _, maxh, maxw = gt_masks.shape
AttributeError: 'list' object has no attribute 'shape'
```

I already debugged it and as far as I have found out it is because the masks are stacked in [`_pad_masks`](https://github.com/open-mmlab/mmdetection/blob/10c82efb0392fc1a5e1c696a53fe9ca7dfc3cdda/mmdet/datasets/pipelines/transforms.py#L304)

If padding is not included in the pipeline, however, then `gt_masks` is a `list` instead of `ndarray` causing the above error.

Shouldn't the stacking be done in [`_load_masks`](https://github.com/open-mmlab/mmdetection/blob/10c82efb0392fc1a5e1c696a53fe9ca7dfc3cdda/mmdet/datasets/pipelines/loading.py#L82) already to have a more flexible pipeline?


Discussion:
Thanks for the suggestions. It is a known issue and we are preparing a PR to fix it.
Ok, thank you!

Resolving patch:
diff --git a/mmdet/datasets/pipelines/transforms.py b/mmdet/datasets/pipelines/transforms.py
--- a/mmdet/datasets/pipelines/transforms.py
+++ b/mmdet/datasets/pipelines/transforms.py
@@ -156,7 +156,7 @@ def _resize_masks(self, results):
                     mmcv.imresize(mask, mask_size, interpolation='nearest')
                     for mask in results[key]
                 ]
-            results[key] = masks
+            results[key] = np.stack(masks)
 
     def _resize_seg(self, results):
         for key in results.get('seg_fields', []):
@@ -245,10 +245,10 @@ def __call__(self, results):
                                               results['flip_direction'])
             # flip masks
             for key in results.get('mask_fields', []):
-                results[key] = [
+                results[key] = np.stack([
                     mmcv.imflip(mask, direction=results['flip_direction'])
                     for mask in results[key]
-                ]
+                ])
 
             # flip segs
             for key in results.get('seg_fields', []):
@@ -410,7 +410,7 @@ def __call__(self, results):
                     gt_mask = results['gt_masks'][i][crop_y1:crop_y2,
                                                      crop_x1:crop_x2]
                     valid_gt_masks.append(gt_mask)
-                results['gt_masks'] = valid_gt_masks
+                results['gt_masks'] = np.stack(valid_gt_masks)
 
         return results
 
@@ -586,7 +586,7 @@ def __call__(self, results):
                                       0).astype(mask.dtype)
                 expand_mask[top:top + h, left:left + w] = mask
                 expand_gt_masks.append(expand_mask)
-            results['gt_masks'] = expand_gt_masks
+            results['gt_masks'] = np.stack(expand_gt_masks)
 
         # not tested
         if 'gt_semantic_seg' in results:
@@ -678,10 +678,10 @@ def __call__(self, results):
                         results['gt_masks'][i] for i in range(len(mask))
                         if mask[i]
                     ]
-                    results['gt_masks'] = [
+                    results['gt_masks'] = np.stack([
                         gt_mask[patch[1]:patch[3], patch[0]:patch[2]]
                         for gt_mask in valid_masks
-                    ]
+                    ])
 
                 # not tested
                 if 'gt_semantic_seg' in results:


Test patch: