docstr: :param bboxes: (N, num_boxes, 4)
  :param pro_features: (N, num_boxes, feat_channels)
docstr_tok: [":","param","bboxes",":","(","N",",","num_boxes",",","4",")",":","param","pro_features",":","(","N",",","num_boxes",",","feat_channels",")"]
code: def forward(self, features, bboxes, pro_features, pooler, time_emb):
      """
      :param bboxes: (N, num_boxes, 4)
      :param pro_features: (N, num_boxes, feat_channels)
      """
  
      N, num_boxes = bboxes.shape[:2]
  
      # roi_feature.
      proposal_boxes = list()
      for b in range(N):
          proposal_boxes.append(bboxes[b])
      rois = bbox2roi(proposal_boxes)
  
      roi_features = pooler(features, rois)
  
      if pro_features is None:
          pro_features = roi_features.view(
              N, num_boxes, self.feat_channels, -1
          ).mean(-1)
  
      roi_features = roi_features.view(
          N * num_boxes, self.feat_channels, -1
      ).permute(2, 0, 1)
  
      # self_att.
      pro_features = pro_features.view(
          N, num_boxes, self.feat_channels
      ).permute(1, 0, 2)
      pro_features2 = self.self_attn(
          pro_features, pro_features, value=pro_features
      )[0]
      pro_features = pro_features + self.dropout1(pro_features2)
      pro_features = self.norm1(pro_features)
  
      # inst_interact.
      pro_features = (
          pro_features.view(num_boxes, N, self.feat_channels)
          .permute(1, 0, 2)
          .reshape(1, N * num_boxes, self.feat_channels)
      )
      pro_features2 = self.inst_interact(pro_features, roi_features)
      pro_features = pro_features + self.dropout2(pro_features2)
      obj_features = self.norm2(pro_features)
  
      # obj_feature.
      obj_features2 = self.linear2(
          self.dropout(self.activation(self.linear1(obj_features)))
      )
      obj_features = obj_features + self.dropout3(obj_features2)
      obj_features = self.norm3(obj_features)
  
      fc_feature = obj_features.transpose(0, 1).reshape(
          N * num_boxes, -1
      )
  
      scale_shift = self.block_time_mlp(time_emb)
      scale_shift = torch.repeat_interleave(
          scale_shift, num_boxes, dim=0
      )
      scale, shift = scale_shift.chunk(2, dim=1)
      fc_feature = fc_feature * (scale + 1) + shift
  
      cls_feature = fc_feature.clone()
      reg_feature = fc_feature.clone()
      for cls_layer in self.cls_module:
          cls_feature = cls_layer(cls_feature)
      for reg_layer in self.reg_module:
          reg_feature = reg_layer(reg_feature)
      class_logits = self.class_logits(cls_feature)
      bboxes_deltas = self.bboxes_delta(reg_feature)
      pred_bboxes = self.apply_deltas(bboxes_deltas, bboxes.view(-1, 4))
  
      return (
          class_logits.view(N, num_boxes, -1),
          pred_bboxes.view(N, num_boxes, -1),
          obj_features,
      )
  
imports: import copy
  import math
  import random
  import warnings
  from typing import Tuple
  import torch
  import torch.nn
  import torch.nn.functional
  from mmcv.cnn import build_activation_layer
  from mmcv.ops import batched_nms
  from mmengine.structures import InstanceData
  from torch import Tensor
  from mmdet.registry import MODELS, TASK_UTILS
  from mmdet.structures import SampleList
  from mmdet.structures.bbox import bbox2roi, bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh, get_box_wh, scale_boxes
  from mmdet.utils import InstanceList
label_desc: Use image node_id 2 for calling the SingleDiffusionDetHead obj's underlying member method code with example usage: obj.forward(features, bboxes, pro_features, pooler, time_emb) and returns: class_logits,  pred_bboxes,  obj_features