Repository: open-mmlab/mmdeploy

Issue:
[Bug] assert self.num_levels == len(featmap_sizes)
### Checklist

- [X] I have searched related issues but cannot get the expected help.
- [X] 2. I have read the [FAQ documentation](https://github.com/open-mmlab/mmdeploy/tree/main/docs/en/faq.md) but cannot get the expected help.
- [X] 3. The bug has not been fixed in the latest version.

### Describe the bug

When I was running "python tools/test.py", an error occurred:    assert self.num_levels == len(featmap_sizes)
AssertionError.
This error occurs when I run "python tools/test.py" to test pointpillars model, this does not happen when testing centerpoint.

### Reproduction

python tools/test.py /home/mmdeploy/configs/mmdet3d/voxel-detection/voxel-detection_onnxruntime_dynamic.py /home/mmdet3d-v1.1/configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb4-2x_nus-3d.py --model /home/Pointpillars/end2end.onnx --device cuda:0 --speed-test

### Environment

```Shell
06/20 12:47:45 - mmengine - INFO - 06/20 12:47:45 - mmengine - INFO - **********Environmental information**********06/20 12:47:45 - mmengine - INFO - sys.platform: linux06/20 12:47:45 - mmengine - INFO - Python: 3.8.16 | packaged by conda-forge | (default, Feb  1 2023, 16:01:55) [GCC 11.3.0]
06/20 12:47:45 - mmengine - INFO - CUDA available: True
06/20 12:47:45 - mmengine - INFO - numpy_random_seed: 2147483648
06/20 12:47:45 - mmengine - INFO - GPU 0,1,2,3: NVIDIA GeForce RTX 3090
06/20 12:47:45 - mmengine - INFO - CUDA_HOME: /usr/local/cuda-11.1
06/20 12:47:45 - mmengine - INFO - NVCC: Cuda compilation tools, release 11.1, V11.1.105
06/20 12:47:45 - mmengine - INFO - GCC: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
06/20 12:47:45 - mmengine - INFO - PyTorch: 1.10.0+cu111
06/20 12:47:45 - mmengine - INFO - PyTorch compiling details: PyTorch built with:
  - GCC 7.3
  - C++ Version: 201402
  - Intel(R) Math Kernel Library Version 2020.0.0 Product Build 20191122 for Intel(R) 64 architecture applications
  - Intel(R) MKL-DNN v2.2.3 (Git Hash 7336ca9f055cf1bfa13efb658fe15dc9b41f0740)
  - OpenMP 201511 (a.k.a. OpenMP 4.5)
  - LAPACK is enabled (usually provided by MKL)
  - NNPACK is enabled
  - CPU capability usage: AVX512
  - CUDA Runtime 11.1
  - NVCC architecture flags: -gencode;arch=compute_37,code=sm_37;-gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_80,code=sm_80;-gencode;arch=compute_86,code=sm_86
  - CuDNN 8.0.5
  - Magma 2.5.2
  - Build settings: BLAS_INFO=mkl, BUILD_TYPE=Release, CUDA_VERSION=11.1, CUDNN_VERSION=8.0.5, CXX_COMPILER=/opt/rh/devtoolset-7/root/usr/bin/c++, CXX_FLAGS= -Wno-deprecated -fvisibility-inlines-hidden -DUSE_PTHREADPOOL -fopenmp -DNDEBUG -DUSE_KINETO -DUSE_FBGEMM -DUSE_QNNPACK -DUSE_PYTORCH_QNNPACK -DUSE_XNNPACK -DSYMBOLICATE_MOBILE_DEBUG_HANDLE -DEDGE_PROFILER_USE_KINETO -O2 -fPIC -Wno-narrowing -Wall -Wextra -Werror=return-type -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-unused-result -Wno-unused-local-typedefs -Wno-strict-overflow -Wno-strict-aliasing -Wno-error=deprecated-declarations -Wno-stringop-overflow -Wno-psabi -Wno-error=pedantic -Wno-error=redundant-decls -Wno-error=old-style-cast -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Wno-stringop-overflow, LAPACK_INFO=mkl, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, TORCH_VERSION=1.10.0, USE_CUDA=ON, USE_CUDNN=ON, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=ON, USE_NNPACK=ON, USE_OPENMP=ON, 

06/20 12:47:45 - mmengine - INFO - TorchVision: 0.11.0+cu111
06/20 12:47:45 - mmengine - INFO - OpenCV: 4.7.0
06/20 12:47:45 - mmengine - INFO - MMEngine: 0.7.3
06/20 12:47:45 - mmengine - INFO - MMCV: 2.0.0
06/20 12:47:45 - mmengine - INFO - MMCV Compiler: GCC 7.3
06/20 12:47:45 - mmengine - INFO - MMCV CUDA Compiler: 11.1
06/20 12:47:45 - mmengine - INFO - MMDeploy: 1.1.0+439f88b
06/20 12:47:45 - mmengine - INFO - 

06/20 12:47:45 - mmengine - INFO - **********Backend information**********
06/20 12:47:45 - mmengine - INFO - tensorrt:    8.4.2.4
06/20 12:47:45 - mmengine - INFO - tensorrt custom ops: Available
06/20 12:47:45 - mmengine - INFO - ONNXRuntime: None
06/20 12:47:45 - mmengine - INFO - ONNXRuntime-gpu:     1.8.1
06/20 12:47:45 - mmengine - INFO - ONNXRuntime custom ops:      Available
06/20 12:47:45 - mmengine - INFO - pplnn:       None
06/20 12:47:45 - mmengine - INFO - ncnn:        None
06/20 12:47:45 - mmengine - INFO - snpe:        None
06/20 12:47:45 - mmengine - INFO - openvino:    None
06/20 12:47:45 - mmengine - INFO - torchscript: 1.10.0+cu111
06/20 12:47:45 - mmengine - INFO - torchscript custom ops:      NotAvailable
06/20 12:47:45 - mmengine - INFO - rknn-toolkit:        None
06/20 12:47:45 - mmengine - INFO - rknn-toolkit2:       None
06/20 12:47:45 - mmengine - INFO - ascend:      None
06/20 12:47:45 - mmengine - INFO - coreml:      None
06/20 12:47:45 - mmengine - INFO - tvm: None
06/20 12:47:45 - mmengine - INFO - vacc:        None
06/20 12:47:45 - mmengine - INFO - 

06/20 12:47:45 - mmengine - INFO - **********Codebase information**********
06/20 12:47:45 - mmengine - INFO - mmdet:       3.0.0
06/20 12:47:45 - mmengine - INFO - mmseg:       None
06/20 12:47:45 - mmengine - INFO - mmpretrain:  None
06/20 12:47:45 - mmengine - INFO - mmocr:       None
06/20 12:47:45 - mmengine - INFO - mmagic:      None
06/20 12:47:45 - mmengine - INFO - mmdet3d:     1.1.0
06/20 12:47:45 - mmengine - INFO - mmpose:      None
06/20 12:47:45 - mmengine - INFO - mmrotate:    None
06/20 12:47:45 - mmengine - INFO - mmaction:    None
06/20 12:47:45 - mmengine - INFO - mmrazor:     None
```


### Error traceback

```Shell
/home/mmdetection3d/mmdet3d/models/dense_heads/anchor3d_head.py:92: UserWarning: dir_offset and dir_limit_offset will be depressed and be incorporated into box coder in the future
  warnings.warn(
Traceback (most recent call last):
  File "tools/test.py", line 159, in <module>
    main()
  File "tools/test.py", line 150, in main
    runner.test()
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmengine/runner/runner.py", line 1767, in test
    metrics = self.test_loop.run()  # type: ignore
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmengine/runner/loops.py", line 435, in run
    self.run_iter(idx, data_batch)
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/torch/autograd/grad_mode.py", line 28, in decorate_context
    return func(*args, **kwargs)
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmengine/runner/loops.py", line 454, in run_iter
    outputs = self.runner.model.test_step(data_batch)
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmengine/model/base_model/base_model.py", line 145, in test_step
    return self._run_forward(data, mode='predict')  # type: ignore
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmengine/model/base_model/base_model.py", line 340, in _run_forward
    results = self(**data, mode=mode)
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1102, in _call_impl
    return forward_call(*input, **kwargs)
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmdeploy/codebase/mmdet3d/deploy/voxel_detection_model.py", line 97, in forward
    prediction = VoxelDetectionModel.postprocess(
  File "/home/anaconda3/envs/mmdet3d/lib/python3.8/site-packages/mmdeploy/codebase/mmdet3d/deploy/voxel_detection_model.py", line 245, in postprocess
    data_instances_3d = head.predict_by_feat(
  File "/home/mmdetection3d/mmdet3d/models/dense_heads/base_3d_dense_head.py", line 232, in predict_by_feat
    mlvl_priors = self.prior_generator.grid_anchors(
  File "/home/mmdetection3d/mmdet3d/models/task_modules/anchor/anchor_3d_generator.py", line 105, in grid_anchors
    assert self.num_levels == len(featmap_sizes)
AssertionError
```



Discussion:
hi, are you using the official model config and ckpt from mmdet3d?
> hi, are you using the official model config and ckpt from mmdet3d?

yes, i used the official model config and ckpt from mmdet3d.

Resolving patch:
diff --git a/configs/mmdet3d/voxel-detection/voxel-detection_static.py b/configs/mmdet3d/voxel-detection/voxel-detection_static.py
--- a/configs/mmdet3d/voxel-detection/voxel-detection_static.py
+++ b/configs/mmdet3d/voxel-detection/voxel-detection_static.py
@@ -3,4 +3,5 @@
     type='mmdet3d', task='VoxelDetection', model_type='end2end')
 onnx_config = dict(
     input_names=['voxels', 'num_points', 'coors'],
-    output_names=['cls_score', 'bbox_pred', 'dir_cls_pred'])
+    # need to change output_names for head with multi-level features
+    output_names=['cls_score0', 'bbox_pred0', 'dir_cls_pred0'])
diff --git a/mmdeploy/codebase/mmdet3d/deploy/voxel_detection_model.py b/mmdeploy/codebase/mmdet3d/deploy/voxel_detection_model.py
--- a/mmdeploy/codebase/mmdet3d/deploy/voxel_detection_model.py
+++ b/mmdeploy/codebase/mmdet3d/deploy/voxel_detection_model.py
@@ -90,7 +90,14 @@ def forward(self,
         }
 
         outputs = self.wrapper(input_dict)
-
+        num_level = len(outputs) // 3
+        new_outputs = dict(
+            cls_score=[outputs[f'cls_score{i}'] for i in range(num_level)],
+            bbox_pred=[outputs[f'bbox_pred{i}'] for i in range(num_level)],
+            dir_cls_pred=[
+                outputs[f'dir_cls_pred{i}'] for i in range(num_level)
+            ])
+        outputs = new_outputs
         if data_samples is None:
             return outputs
 
@@ -239,9 +246,9 @@ def postprocess(model_cfg: Union[str, Config],
 
         if not hasattr(head, 'task_heads'):
             data_instances_3d = head.predict_by_feat(
-                cls_scores=[cls_score],
-                bbox_preds=[bbox_pred],
-                dir_cls_preds=[dir_cls_pred],
+                cls_scores=cls_score,
+                bbox_preds=bbox_pred,
+                dir_cls_preds=dir_cls_pred,
                 batch_input_metas=batch_input_metas,
                 cfg=cfg)
 
diff --git a/mmdeploy/codebase/mmdet3d/models/mvx_two_stage.py b/mmdeploy/codebase/mmdet3d/models/mvx_two_stage.py
--- a/mmdeploy/codebase/mmdet3d/models/mvx_two_stage.py
+++ b/mmdeploy/codebase/mmdet3d/models/mvx_two_stage.py
@@ -2,6 +2,7 @@
 import torch
 
 from mmdeploy.core import FUNCTION_REWRITER
+from mmdeploy.utils import get_ir_config
 
 
 @FUNCTION_REWRITER.register_rewriter(
@@ -52,10 +53,21 @@ def mvxtwostagedetector__forward(self, inputs: list, **kwargs):
         inputs (list): input list comprises voxels, num_points and coors
 
     Returns:
-        bbox (Tensor): Decoded bbox after nms
-        scores (Tensor): bbox scores
-        labels (Tensor): bbox labels
+        tuple: A tuple of classification scores, bbox and direction
+            classification prediction.
+
+            - cls_scores (list[Tensor]): Classification scores for all
+                scale levels, each is a 4D-tensor, the channels number
+                is num_base_priors * num_classes.
+            - bbox_preds (list[Tensor]): Box energies / deltas for all
+                scale levels, each is a 4D-tensor, the channels number
+                is num_base_priors * C.
+            - dir_cls_preds (list[Tensor|None]): Direction classification
+                predictions for all scale levels, each is a 4D-tensor,
+                the channels number is num_base_priors * 2.
     """
+    ctx = FUNCTION_REWRITER.get_context()
+    deploy_cfg = ctx.cfg
     batch_inputs_dict = {
         'voxels': {
             'voxels': inputs[0],
@@ -82,5 +94,18 @@ def mvxtwostagedetector__forward(self, inputs: list, **kwargs):
         dir_scores = torch.cat(dir_scores, dim=1)
         return scores, bbox_preds, dir_scores
     else:
-        cls_score, bbox_pred, dir_cls_pred = outs[0][0], outs[1][0], outs[2][0]
-        return cls_score, bbox_pred, dir_cls_pred
+        preds = []
+        expect_names = []
+        for i in range(len(outs[0])):
+            preds += [outs[0][i], outs[1][i], outs[2][i]]
+            expect_names += [
+                f'cls_score{i}', f'bbox_pred{i}', f'dir_cls_pred{i}'
+            ]
+        # check if output_names is set correctly.
+        onnx_cfg = get_ir_config(deploy_cfg)
+        output_names = onnx_cfg['output_names']
+        if output_names != list(expect_names):
+            raise RuntimeError(f'`output_names` should be {expect_names} '
+                               f'but given {output_names}\n'
+                               f'Deploy config:\n{deploy_cfg.pretty_text}')
+        return tuple(preds)


Test patch: