<gh_stars>1-10
import torch
import torch.nn as nn
import torch.nn.functional as F

# from mmdet.core import bbox2result, bbox2roi, build_assigner, build_sampler
from ..builder import DETECTORS, build_backbone, build_head, build_neck
from .base import BaseDetector
from mmcv.cnn import xavier_init, kaiming_init
import pdb
import copy


class MuAttention(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.dim = dim
        self.hidden_weight_rgb = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.hidden_weight_t = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.softmax = nn.Softmax(dim = -1)

        self.init_weights()


    def init_weights(self):
        kaiming_init(self.hidden_weight_rgb)
        kaiming_init(self.hidden_weight_t)


    def forward(self, x1, x2):
        B, C, w, h = x1.shape
        w1 = self.hidden_weight_rgb(x1)
        w2 = self.hidden_weight_t(x2)
        x = self.softmax(w1.view(B, C, -1)).view(B, C, w, h) * x1 + self.softmax(w2.view(B, C, -1)).view(B, C, w, h) * x2
        return x




class MuAttention_single(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.dim = dim * 2
        self.hidden_weight = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.hidden = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.softmax = nn.Softmax(dim = -1)
        self.proj_back = nn.Conv2d(self.dim, self.dim // 2, 3, 1, 1)

        self.init_weights()


    def init_weights(self):
        kaiming_init(self.hidden_weight)
        kaiming_init(self.proj_back)
        kaiming_init(self.hidden)


    def forward(self, x1, x2):
        x = torch.cat([x1, x2], dim=1)
        B_, C, w, h = x.shape
        weight = self.hidden_weight(x)
        weight = self.softmax(weight.view(B_, C, -1)).view(B_, C, w, h)
        x = self.proj_back(weight * self.hidden(x))
        return x





@DETECTORS.register_module()
class TwoStreamTwoStageDetector(BaseDetector):
    """Base class for two-stage detectors.

    Two-stage detectors typically consisting of a region proposal network and a
    task-specific regression head.
    """

    def __init__(self,
                 backbone_1,
                 backbone_2,
                 neck=None,
                 rpn_head=None,
                 roi_head=None,
                 train_cfg=None,
                 test_cfg=None,
                 pretrained=None,
                 fc_flag = False,
                 loca = None):
        super(TwoStreamTwoStageDetector, self).__init__()
        self.backbone_1 = build_backbone(backbone_1)
        self.backbone_2 = build_backbone(backbone_2)
        self.loca = loca

        if neck is not None:
            self.neck = build_neck(neck)
            if loca == 'fpn':
                self.neck_t = copy.deepcopy(self.neck)

        if rpn_head is not None:
            rpn_train_cfg = train_cfg.rpn if train_cfg is not None else None
            rpn_head_ = rpn_head.copy()
            rpn_head_.update(train_cfg=rpn_train_cfg, test_cfg=test_cfg.rpn)
            self.rpn_head = build_head(rpn_head_)

        if roi_head is not None:
            # update train and test cfg here for now
            # TODO: refactor assigner & sampler
            rcnn_train_cfg = train_cfg.rcnn if train_cfg is not None else None
            roi_head.update(train_cfg=rcnn_train_cfg)
            roi_head.update(test_cfg=test_cfg.rcnn)
            self.roi_head = build_head(roi_head)

        self.train_cfg = train_cfg
        self.test_cfg = test_cfg
        self.fc_flag = fc_flag

        if self.fc_flag:
            if self.loca == 'fpn':
                self.fc_rgb = nn.Linear(neck['out_channels'],16)
                self.fc_trm = nn.Linear(neck['out_channels'],16)
            else:
                self.fc_rgb = nn.Linear(neck['in_channels'][-1],16)
                self.fc_trm = nn.Linear(neck['in_channels'][-1],16)
            self.fc = nn.Linear(32,2)

        self.init_weights(pretrained=pretrained)

            

    @property
    def with_rpn(self):
        """bool: whether the detector has RPN"""
        return hasattr(self, 'rpn_head') and self.rpn_head is not None

    @property
    def with_roi_head(self):
        """bool: whether the detector has a RoI head"""
        return hasattr(self, 'roi_head') and self.roi_head is not None


    def init_weights(self, pretrained=None):
        """Initialize the weights in detector.

        Args:
            pretrained (str, optional): Path to pre-trained weights.
                Defaults to None.
        """
        super(TwoStreamTwoStageDetector, self).init_weights(pretrained)
        self.backbone_1.init_weights(pretrained=pretrained)
        self.backbone_2.init_weights(pretrained=pretrained)
        if self.with_neck:
            if isinstance(self.neck, nn.Sequential):
                for m in self.neck:
                    m.init_weights()
                if self.loca == 'fpn':
                    for m in self.neck_t:
                        m.init_weights()
            else:
                self.neck.init_weights()
                if self.loca == 'fpn':
                    self.neck_t.init_weights()
        if self.with_rpn:
            self.rpn_head.init_weights()
        if self.with_roi_head:
            self.roi_head.init_weights(pretrained)
        if self.fc_flag:
            xavier_init(self.fc_rgb, distribution='uniform')
            xavier_init(self.fc_trm, distribution='uniform')
            xavier_init(self.fc, distribution='uniform')

    def extract_feat(self, img):
        """Directly extract features from the backbone+neck."""
        img_1, img_2 = torch.split(img, 3, dim=1)
        x_1 = self.backbone_1(img_1)
        x_2 = self.backbone_2(img_2)
        if self.loca == 'fpn':
            if self.with_neck:
                x_1 = self.neck(x_1)
                x_2 = self.neck_t(x_2)
            if self.fc_flag:
                bs, c, w, h = x_1[-1].shape
                w_1 = F.relu(torch.mean(x_1[-1].view(bs,c,-1),-1)).view(bs,c)
                w_2 = F.relu(torch.mean(x_2[-1].view(bs,c,-1),-1)).view(bs,c)
                w_all = torch.cat((F.relu(self.fc_rgb(w_1)), F.relu(self.fc_trm(w_2))),dim=1)
                w_all = F.relu(self.fc(w_all))
                w_all = F.softmax(w_all,dim=1)
                #pdb.set_trace()
                x = tuple([w_all[:,0].view(bs,1,1,1) * x_1[i] + w_all[:,1].view(bs,1,1,1) * x_2[i] for i in range(min(len(x_1),len(x_2)))])
            else:
                x = tuple([0.5*(x_1[i] + x_2[i]) for i in range(min(len(x_1), len(x_2)))])
        else:
            if self.fc_flag:
                bs, c, w, h = x_1[-1].shape
                w_1 = F.relu(torch.mean(x_1[-1].view(bs,c,-1),-1)).view(bs,c)
                w_2 = F.relu(torch.mean(x_2[-1].view(bs,c,-1),-1)).view(bs,c)
                w_all = torch.cat((F.relu(self.fc_rgb(w_1)), F.relu(self.fc_trm(w_2))),dim=1)
                w_all = F.relu(self.fc(w_all))
                w_all = F.softmax(w_all,dim=1)
                #pdb.set_trace()
                x = tuple([w_all[:,0].view(bs,1,1,1) * x_1[i] + w_all[:,1].view(bs,1,1,1) * x_2[i] for i in range(min(len(x_1),len(x_2)))])
            else:
                x = tuple([0.5*(x_1[i] + x_2[i]) for i in range(min(len(x_1), len(x_2)))])
            if self.with_neck:
                x = self.neck(x)
        
        return x

    def forward_dummy(self, img):
        """Used for computing network flops.

        See `mmdetection/tools/get_flops.py`
        """
        outs = ()
        # backbone
        x = self.extract_feat(img)
        # rpn
        if self.with_rpn:
            rpn_outs = self.rpn_head(x)
            outs = outs + (rpn_outs, )
        
        proposals = torch.randn(1000, 4).to(img.device)
        # roi_head
        roi_outs = self.roi_head.forward_dummy(x, proposals)
        outs = outs + (roi_outs, )
        return outs

    def forward_train(self,
                      img,
                      img_metas,
                      gt_bboxes,
                      gt_labels,
                      gt_bboxes_ignore=None,
                      gt_masks=None,
                      proposals=None,
                      **kwargs):
        """
        Args:
            img (Tensor): of shape (N, C, H, W) encoding input images.
                Typically these should be mean centered and std scaled.

            img_metas (list[dict]): list of image info dict where each dict
                has: 'img_shape', 'scale_factor', 'flip', and may also contain
                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.
                For details on the values of these keys see
                `mmdet/datasets/pipelines/formatting.py:Collect`.

            gt_bboxes (list[Tensor]): Ground truth bboxes for each image with
                shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.

            gt_labels (list[Tensor]): class indices corresponding to each box

            gt_bboxes_ignore (None | list[Tensor]): specify which bounding
                boxes can be ignored when computing the loss.

            gt_masks (None | Tensor) : true segmentation masks for each box
                used if the architecture supports a segmentation task.

            proposals : override rpn proposals with custom proposals. Use when
                `with_rpn` is False.

        Returns:
            dict[str, Tensor]: a dictionary of loss components
        """
        x = self.extract_feat(img)

        losses = dict()

        # RPN forward and loss
        if self.with_rpn:
            proposal_cfg = self.train_cfg.get('rpn_proposal',
                                              self.test_cfg.rpn)
            rpn_losses, proposal_list = self.rpn_head.forward_train(
                x,
                img_metas,
                gt_bboxes,
                gt_labels=None,
                gt_bboxes_ignore=gt_bboxes_ignore,
                proposal_cfg=proposal_cfg)
            losses.update(rpn_losses)
        else:
            proposal_list = proposals

        roi_losses = self.roi_head.forward_train(x, img_metas, proposal_list,
                                                 gt_bboxes, gt_labels,
                                                 gt_bboxes_ignore, gt_masks,
                                                 **kwargs)
        losses.update(roi_losses)

        return losses

    async def async_simple_test(self,
                                img,
                                img_meta,
                                proposals=None,
                                rescale=False):
        """Async test without augmentation."""
        assert self.with_bbox, 'Bbox head must be implemented.'
        x = self.extract_feat(img)

        if proposals is None:
            proposal_list = await self.rpn_head.async_simple_test_rpn(
                x, img_meta)
        else:
            proposal_list = proposals

        return await self.roi_head.async_simple_test(
            x, proposal_list, img_meta, rescale=rescale)

    def simple_test(self, img, img_metas, proposals=None, rescale=False):
        """Test without augmentation."""
        assert self.with_bbox, 'Bbox head must be implemented.'

        x = self.extract_feat(img)

        if proposals is None:
            proposal_list = self.rpn_head.simple_test_rpn(x, img_metas)
        else:
            proposal_list = proposals

        return self.roi_head.simple_test(
            x, proposal_list, img_metas, rescale=rescale)

    def aug_test(self, imgs, img_metas, rescale=False):
        """Test with augmentations.

        If rescale is False, then returned bboxes and masks will fit the scale
        of imgs[0].
        """
        # recompute feats to save memory
        x = self.extract_feats(imgs)
        proposal_list = self.rpn_head.aug_test_rpn(x, img_metas)
        return self.roi_head.aug_test(
            x, proposal_list, img_metas, rescale=rescale)

import torch
import torch.nn as nn
import torch.nn.functional as F

# from mmdet.core import bbox2result, bbox2roi, build_assigner, build_sampler
from ..builder import DETECTORS, build_backbone, build_head, build_neck
from .base import BaseDetector
from mmcv.cnn import xavier_init, kaiming_init
import pdb
import copy


class MuAttention(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.dim = dim
        self.hidden_weight_rgb = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.hidden_weight_t = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.softmax = nn.Softmax(dim = -1)

        self.init_weights()


    def init_weights(self):
        kaiming_init(self.hidden_weight_rgb)
        kaiming_init(self.hidden_weight_t)


    def forward(self, x1, x2):
        B, C, w, h = x1.shape
        w1 = self.hidden_weight_rgb(x1)
        w2 = self.hidden_weight_t(x2)
        x = self.softmax(w1.view(B, C, -1)).view(B, C, w, h) * x1 + self.softmax(w2.view(B, C, -1)).view(B, C, w, h) * x2
        return x




class MuAttention_single(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.dim = dim * 2
        self.hidden_weight = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.hidden = nn.Conv2d(self.dim, self.dim, 3, 1, 1)
        self.softmax = nn.Softmax(dim = -1)
        self.proj_back = nn.Conv2d(self.dim, self.dim // 2, 3, 1, 1)

        self.init_weights()


    def init_weights(self):
        kaiming_init(self.hidden_weight)
        kaiming_init(self.proj_back)
        kaiming_init(self.hidden)


    def forward(self, x1, x2):
        x = torch.cat([x1, x2], dim=1)
        B_, C, w, h = x.shape
        weight = self.hidden_weight(x)
        weight = self.softmax(weight.view(B_, C, -1)).view(B_, C, w, h)
        x = self.proj_back(weight * self.hidden(x))
        return x





@DETECTORS.register_module()
class TwoStreamTwoStageDetector(BaseDetector):
    """Base class for two-stage detectors.

    Two-stage detectors typically consisting of a region proposal network and a
    task-specific regression head.
    """

    def __init__(self,
                 backbone_1,
                 backbone_2,
                 neck=None,
                 rpn_head=None,
                 roi_head=None,
                 train_cfg=None,
                 test_cfg=None,
                 pretrained=None,
                 fc_flag = False,
                 loca = None):
        super(TwoStreamTwoStageDetector, self).__init__()
        self.backbone_1 = build_backbone(backbone_1)
        self.backbone_2 = build_backbone(backbone_2)
        self.loca = loca

        if neck is not None:
            self.neck = build_neck(neck)
            if loca == 'fpn':
                self.neck_t = copy.deepcopy(self.neck)

        if rpn_head is not None:
            rpn_train_cfg = train_cfg.rpn if train_cfg is not None else None
            rpn_head_ = rpn_head.copy()
            rpn_head_.update(train_cfg=rpn_train_cfg, test_cfg=test_cfg.rpn)
            self.rpn_head = build_head(rpn_head_)

        if roi_head is not None:
            # update train and test cfg here for now
            # TODO: refactor assigner & sampler
            rcnn_train_cfg = train_cfg.rcnn if train_cfg is not None else None
            roi_head.update(train_cfg=rcnn_train_cfg)
            roi_head.update(test_cfg=test_cfg.rcnn)
            self.roi_head = build_head(roi_head)

        self.train_cfg = train_cfg
        self.test_cfg = test_cfg
        self.fc_flag = fc_flag

        if self.fc_flag:
            if self.loca == 'fpn':
                self.fc_rgb = nn.Linear(neck['out_channels'],16)
                self.fc_trm = nn.Linear(neck['out_channels'],16)
            else:
                self.fc_rgb = nn.Linear(neck['in_channels'][-1],16)
                self.fc_trm = nn.Linear(neck['in_channels'][-1],16)
            self.fc = nn.Linear(32,2)

        self.init_weights(pretrained=pretrained)

            

    @property
    def with_rpn(self):
        """bool: whether the detector has RPN"""
        return hasattr(self, 'rpn_head') and self.rpn_head is not None

    @property
    def with_roi_head(self):
        """bool: whether the detector has a RoI head"""
        return hasattr(self, 'roi_head') and self.roi_head is not None


    def init_weights(self, pretrained=None):
        """Initialize the weights in detector.

        Args:
            pretrained (str, optional): Path to pre-trained weights.
                Defaults to None.
        """
        super(TwoStreamTwoStageDetector, self).init_weights(pretrained)
        self.backbone_1.init_weights(pretrained=pretrained)
        self.backbone_2.init_weights(pretrained=pretrained)
        if self.with_neck:
            if isinstance(self.neck, nn.Sequential):
                for m in self.neck:
                    m.init_weights()
                if self.loca == 'fpn':
                    for m in self.neck_t:
                        m.init_weights()
            else:
                self.neck.init_weights()
                if self.loca == 'fpn':
                    self.neck_t.init_weights()
        if self.with_rpn:
            self.rpn_head.init_weights()
        if self.with_roi_head:
            self.roi_head.init_weights(pretrained)
        if self.fc_flag:
            xavier_init(self.fc_rgb, distribution='uniform')
            xavier_init(self.fc_trm, distribution='uniform')
            xavier_init(self.fc, distribution='uniform')

    def extract_feat(self, img):
        """Directly extract features from the backbone+neck."""
        img_1, img_2 = torch.split(img, 3, dim=1)
        x_1 = self.backbone_1(img_1)
        x_2 = self.backbone_2(img_2)
        if self.loca == 'fpn':
            if self.with_neck:
                x_1 = self.neck(x_1)
                x_2 = self.neck_t(x_2)
            if self.fc_flag:
                bs, c, w, h = x_1[-1].shape
                w_1 = F.relu(torch.mean(x_1[-1].view(bs,c,-1),-1)).view(bs,c)
                w_2 = F.relu(torch.mean(x_2[-1].view(bs,c,-1),-1)).view(bs,c)
                w_all = torch.cat((F.relu(self.fc_rgb(w_1)), F.relu(self.fc_trm(w_2))),dim=1)
                w_all = F.relu(self.fc(w_all))
                w_all = F.softmax(w_all,dim=1)
                #pdb.set_trace()
                x = tuple([w_all[:,0].view(bs,1,1,1) * x_1[i] + w_all[:,1].view(bs,1,1,1) * x_2[i] for i in range(min(len(x_1),len(x_2)))])
            else:
                x = tuple([0.5*(x_1[i] + x_2[i]) for i in range(min(len(x_1), len(x_2)))])
        else:
            if self.fc_flag:
                bs, c, w, h = x_1[-1].shape
                w_1 = F.relu(torch.mean(x_1[-1].view(bs,c,-1),-1)).view(bs,c)
                w_2 = F.relu(torch.mean(x_2[-1].view(bs,c,-1),-1)).view(bs,c)
                w_all = torch.cat((F.relu(self.fc_rgb(w_1)), F.relu(self.fc_trm(w_2))),dim=1)
                w_all = F.relu(self.fc(w_all))
                w_all = F.softmax(w_all,dim=1)
                #pdb.set_trace()
                x = tuple([w_all[:,0].view(bs,1,1,1) * x_1[i] + w_all[:,1].view(bs,1,1,1) * x_2[i] for i in range(min(len(x_1),len(x_2)))])
            else:
                x = tuple([0.5*(x_1[i] + x_2[i]) for i in range(min(len(x_1), len(x_2)))])
            if self.with_neck:
                x = self.neck(x)
        
        return x

    def forward_dummy(self, img):
        """Used for computing network flops.

        See `mmdetection/tools/get_flops.py`
        """
        outs = ()
        # backbone
        x = self.extract_feat(img)
        # rpn
        if self.with_rpn:
            rpn_outs = self.rpn_head(x)
            outs = outs + (rpn_outs, )
        
        proposals = torch.randn(1000, 4).to(img.device)
        # roi_head
        roi_outs = self.roi_head.forward_dummy(x, proposals)
        outs = outs + (roi_outs, )
        return outs

    def forward_train(self,
                      img,
                      img_metas,
                      gt_bboxes,
                      gt_labels,
                      gt_bboxes_ignore=None,
                      gt_masks=None,
                      proposals=None,
                      **kwargs):
        """
        Args:
            img (Tensor): of shape (N, C, H, W) encoding input images.
                Typically these should be mean centered and std scaled.

            img_metas (list[dict]): list of image info dict where each dict
                has: 'img_shape', 'scale_factor', 'flip', and may also contain
                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.
                For details on the values of these keys see
                `mmdet/datasets/pipelines/formatting.py:Collect`.

            gt_bboxes (list[Tensor]): Ground truth bboxes for each image with
                shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.

            gt_labels (list[Tensor]): class indices corresponding to each box

            gt_bboxes_ignore (None | list[Tensor]): specify which bounding
                boxes can be ignored when computing the loss.

            gt_masks (None | Tensor) : true segmentation masks for each box
                used if the architecture supports a segmentation task.

            proposals : override rpn proposals with custom proposals. Use when
                `with_rpn` is False.

        Returns:
            dict[str, Tensor]: a dictionary of loss components
        """
        x = self.extract_feat(img)

        losses = dict()

        # RPN forward and loss
        if self.with_rpn:
            proposal_cfg = self.train_cfg.get('rpn_proposal',
                                              self.test_cfg.rpn)
            rpn_losses, proposal_list = self.rpn_head.forward_train(
                x,
                img_metas,
                gt_bboxes,
                gt_labels=None,
                gt_bboxes_ignore=gt_bboxes_ignore,
                proposal_cfg=proposal_cfg)
            losses.update(rpn_losses)
        else:
            proposal_list = proposals

        roi_losses = self.roi_head.forward_train(x, img_metas, proposal_list,
                                                 gt_bboxes, gt_labels,
                                                 gt_bboxes_ignore, gt_masks,
                                                 **kwargs)
        losses.update(roi_losses)

        return losses

    async def async_simple_test(self,
                                img,
                                img_meta,
                                proposals=None,
                                rescale=False):
        """Async test without augmentation."""
        assert self.with_bbox, 'Bbox head must be implemented.'
        x = self.extract_feat(img)

        if proposals is None:
            proposal_list = await self.rpn_head.async_simple_test_rpn(
                x, img_meta)
        else:
            proposal_list = proposals

        return await self.roi_head.async_simple_test(
            x, proposal_list, img_meta, rescale=rescale)

    def simple_test(self, img, img_metas, proposals=None, rescale=False):
        """Test without augmentation."""
        assert self.with_bbox, 'Bbox head must be implemented.'

        x = self.extract_feat(img)

        if proposals is None:
            proposal_list = self.rpn_head.simple_test_rpn(x, img_metas)
        else:
            proposal_list = proposals

        return self.roi_head.simple_test(
            x, proposal_list, img_metas, rescale=rescale)

    def aug_test(self, imgs, img_metas, rescale=False):
        """Test with augmentations.

        If rescale is False, then returned bboxes and masks will fit the scale
        of imgs[0].
        """
        # recompute feats to save memory
        x = self.extract_feats(imgs)
        proposal_list = self.rpn_head.aug_test_rpn(x, img_metas)
        return self.roi_head.aug_test(
            x, proposal_list, img_metas, rescale=rescale)
# from mmdet.core import bbox2result, bbox2roi, build_assigner, build_sampler Base class for two-stage detectors.      Two-stage detectors typically consisting of a region proposal network and a     task-specific regression head. # update train and test cfg here for now # TODO: refactor assigner & sampler bool: whether the detector has RPN bool: whether the detector has a RoI head Initialize the weights in detector.          Args:             pretrained (str, optional): Path to pre-trained weights.                 Defaults to None. Directly extract features from the backbone+neck. #pdb.set_trace() #pdb.set_trace() Used for computing network flops.          See `mmdetection/tools/get_flops.py` # backbone # rpn # roi_head Args:             img (Tensor): of shape (N, C, H, W) encoding input images.                 Typically these should be mean centered and std scaled.              img_metas (list[dict]): list of image info dict where each dict                 has: 'img_shape', 'scale_factor', 'flip', and may also contain                 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.                 For details on the values of these keys see                 `mmdet/datasets/pipelines/formatting.py:Collect`.              gt_bboxes (list[Tensor]): Ground truth bboxes for each image with                 shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format.              gt_labels (list[Tensor]): class indices corresponding to each box              gt_bboxes_ignore (None | list[Tensor]): specify which bounding                 boxes can be ignored when computing the loss.              gt_masks (None | Tensor) : true segmentation masks for each box                 used if the architecture supports a segmentation task.              proposals : override rpn proposals with custom proposals. Use when                 `with_rpn` is False.          Returns:             dict[str, Tensor]: a dictionary of loss components # RPN forward and loss Async test without augmentation. Test without augmentation. Test with augmentations.          If rescale is False, then returned bboxes and masks will fit the scale         of imgs[0]. # recompute feats to save memory