//  [python]
def visualize_results_vis(self, first_frame_idx, batched_inputs, targets, image_size, out_size, is_last):
        # batch size is 1 here
        targets_per_video = targets[0]
        file_names = batched_inputs[0]['file_names'] 
        video_len = len(file_names)
        video_id = file_names[0].split('/')[-2]
        

        save_dir = os.path.join(self.output_dir, 'inference/vis_'+targets_per_video['dataset_name'], video_id)
        os.makedirs(save_dir, exist_ok=True)

        first_frame_idx = min(first_frame_idx + self.num_frames, video_len) - targets_per_video['mask_logits'].shape[1]
        
        if is_last:
            pred_masks = targets_per_video['mask_logits']   # NTHW
            pred_scores = targets_per_video['logits'].mean(1)  # NK
        else:
            pred_masks = targets_per_video['mask_logits'][:, :self.num_frames_window_output] # NTHW
            pred_scores = targets_per_video['logits'][:, :self.num_frames_window_output].mean(1)  # NK
        pred_masks = pred_masks[:, :, :image_size[0], :image_size[1]]
        if pred_masks.nelement() == 0:
            print(
                f"Non objects are detected from frame {first_frame_idx} to {first_frame_idx+self.num_frames_window_output}:", 
                pred_masks.shape
            )
            return 
        
        ids = torch.as_tensor(targets_per_video["ids"], device=self.device)
        if image_size != out_size:
            pred_masks = F.interpolate(
                pred_masks.float(),
                out_size,
                mode='bilinear',
                align_corners=False
            ).gt(0.).float()
        
        pred_scores = pred_scores.cpu()
        pred_masks = pred_masks.cpu()

        for t, m in enumerate(pred_masks.transpose(0, 1)):
            file_name = file_names[first_frame_idx + t].split('/')[-1]
            img = batched_inputs[0]["image"][first_frame_idx + t]
            img = F.interpolate(
                img[None].float(),
                out_size,
                mode="bilinear",
                align_corners=False
            ).squeeze(0).long()
            img = img.permute(1, 2, 0).cpu().to(torch.uint8)
            visualizer = VisualizerFrame(
                img, metadata=self.metadata, 
                class_names=None
            )
            
            scores, classes = pred_scores.max(-1)
            is_low_score = scores < 0.02
            m[is_low_score] = 0

            results = Instances(out_size)
            results.pred_masks = m.cpu()
            results.scores = scores.tolist()
            results.pred_classes = classes   # +1? should start from 1

            save_path = '/'.join([save_dir, file_name.replace('.png', '.jpg')])
            VisImage = visualizer.draw_instance_predictions(results)
            VisImage.save(save_path)
            
        # is_last = True
        if is_last:
            # save all frames to a .avi video
            out = cv2.VideoWriter(
                '/'.join([save_dir, video_id + '.avi']),
                cv2.VideoWriter_fourcc(*'DIVX'),
                fps=10,
                frameSize=(out_size[1], out_size[0])
            )

            file_names = glob.glob('/'.join([save_dir, "*.jpg"]))
            file_names = sorted(file_names, key=lambda f: int(f.split("/")[-1].split('_')[-1].replace('.jpg', '')))
            for file_name in file_names:
                out.write(cv2.imread(file_name))
            out.release()
            print(f"save all frames with inst. seg. into {video_id}.avi")