docstr: The main logic:
  # foreach record(image):
  #   foreach scale:
  #     1 generate chips by chip size and stride for each scale
  #     2 get pos chips
  #     - validate boxes: current scale; h,w >= 1
  #     - find pos chips greedily by valid gt boxes in each scale
  #     - for every valid gt box, find its corresponding pos chips in each scale
  #     3 get neg chips
  #     - If given proposals, find neg boxes in them which are not in pos chips
  #     - If got neg boxes in last step, we find neg chips and assign neg boxes to neg chips such as 2.
  # 4 sample neg chips if too much each image
  #   transform this image-scale annotations to chips(pos chips&neg chips) annotations
  
  :param records, standard coco_record but with extra key `proposals`(Px4), which are predicted by stage1
                  model and maybe have neg boxes in them.
  :return: new_records, list of dict like
  {
      'im_file': 'fake_image1.jpg',
      'im_id': np.array([1]),  # new _global_chip_id as im_id
      'h': h,  # chip height
      'w': w,  # chip width
      'is_crowd': is_crowd,  # Nx1 -> Mx1
      'gt_class': gt_class,  # Nx1 -> Mx1
      'gt_bbox': gt_bbox,  # Nx4 -> Mx4, 4 represents [x1,y1,x2,y2]
      'gt_poly': gt_poly,  # [None]xN -> [None]xM
      'chip': [x1, y1, x2, y2]  # added
  }
  
  Attention:
  ------------------------------>x
  |
  |    (x1,y1)------
  |       |        |
  |       |        |
  |       |        |
  |       |        |
  |       |        |
  |       ----------
  |                 (x2,y2)
  |
  ↓
  y
  
  If we use [x1, y1, x2, y2] to represent boxes or chips,
  (x1,y1) is the left-top point which is in the box,
  but (x2,y2) is the right-bottom point which is not in the box.
  So x1 in [0, w-1], x2 in [1, w], y1 in [0, h-1], y2 in [1,h].
  And you can use x2-x1 to get width, and you can use image[y1:y2, x1:x2] to get the box area.
docstr_tok: ["The","main","logic",":","#","foreach","record","(","image",")",":","#","foreach","scale",":","#","1","generate","chips","by","chip","size","and","stride","for","each","scale","#","2","get","pos","chips","#","-","validate","boxes",":","current","scale",";","h",",","w",">","=","1","#","-","find","pos","chips","greedily","by","valid","gt","boxes","in","each","scale","#","-","for","every","valid","gt","box",",","find","its","corresponding","pos","chips","in","each","scale","#","3","get","neg","chips","#","-","If","given","proposals",",","find","neg","boxes","in","them","which","are","not","in","pos","chips","#","-","If","got","neg","boxes","in","last","step",",","we","find","neg","chips","and","assign","neg","boxes","to","neg","chips","such","as","2",".","#","4","sample","neg","chips","if","too","much","each","image","#","transform","this","image-scale","annotations","to","chips","(","pos","chips","&","neg","chips",")","annotations",":","param","records",",","standard","coco_record","but","with","extra","key","`","proposals","`","(","Px4",")",",","which","are","predicted","by","stage1","model","and","maybe","have","neg","boxes","in","them",".",":","return",":","new_records",",","list","of","dict","like","{","'im_file","'",":","'fake_image1.jpg","'",",","'im_id","'",":","np.array","(","[","1","]",")",",","#","new","_global_chip_id","as","im_id","'","h","'",":","h",",","#","chip","height","'","w","'",":","w",",","#","chip","width","'is_crowd","'",":","is_crowd",",","#","Nx1","-",">","Mx1","'gt_class","'",":","gt_class",",","#","Nx1","-",">","Mx1","'gt_bbox","'",":","gt_bbox",",","#","Nx4","-",">","Mx4",",","4","represents","[","x1",",","y1",",","x2",",","y2","]","'gt_poly","'",":","gt_poly",",","#","[","None","]","xN","-",">","[","None","]","xM","'chip","'",":","[","x1",",","y1",",","x2",",","y2","]","#","added","}","Attention",":","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--",">","x","|","|","(","x1",",","y1",")","--","--","--","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","--","--","--","--","--","|","(","x2",",","y2",")","|","\u2193","y","If","we","use","[","x1",",","y1",",","x2",",","y2","]","to","represent","boxes","or","chips",",","(","x1",",","y1",")","is","the","left-top","point","which","is","in","the","box",",","but","(","x2",",","y2",")","is","the","right-bottom","point","which","is","not","in","the","box",".","So","x1","in","[","0",",","w-1","]",",","x2","in","[","1",",","w","]",",","y1","in","[","0",",","h-1","]",",","y2","in","[","1",",","h","]",".","And","you","can","use","x2-x1","to","get","width",",","and","you","can","use","image","[","y1",":","y2",",","x1",":","x2","]","to","get","the","box","area","."]
code: def crop_anno_records(self, records: List[dict]):
      """
      The main logic:
      # foreach record(image):
      #   foreach scale:
      #     1 generate chips by chip size and stride for each scale
      #     2 get pos chips
      #     - validate boxes: current scale; h,w >= 1
      #     - find pos chips greedily by valid gt boxes in each scale
      #     - for every valid gt box, find its corresponding pos chips in each scale
      #     3 get neg chips
      #     - If given proposals, find neg boxes in them which are not in pos chips
      #     - If got neg boxes in last step, we find neg chips and assign neg boxes to neg chips such as 2.
      # 4 sample neg chips if too much each image
      #   transform this image-scale annotations to chips(pos chips&neg chips) annotations
  
      :param records, standard coco_record but with extra key `proposals`(Px4), which are predicted by stage1
                      model and maybe have neg boxes in them.
      :return: new_records, list of dict like
      {
          'im_file': 'fake_image1.jpg',
          'im_id': np.array([1]),  # new _global_chip_id as im_id
          'h': h,  # chip height
          'w': w,  # chip width
          'is_crowd': is_crowd,  # Nx1 -> Mx1
          'gt_class': gt_class,  # Nx1 -> Mx1
          'gt_bbox': gt_bbox,  # Nx4 -> Mx4, 4 represents [x1,y1,x2,y2]
          'gt_poly': gt_poly,  # [None]xN -> [None]xM
          'chip': [x1, y1, x2, y2]  # added
      }
  
      Attention:
      ------------------------------>x
      |
      |    (x1,y1)------
      |       |        |
      |       |        |
      |       |        |
      |       |        |
      |       |        |
      |       ----------
      |                 (x2,y2)
      |
      ↓
      y
  
      If we use [x1, y1, x2, y2] to represent boxes or chips,
      (x1,y1) is the left-top point which is in the box,
      but (x2,y2) is the right-bottom point which is not in the box.
      So x1 in [0, w-1], x2 in [1, w], y1 in [0, h-1], y2 in [1,h].
      And you can use x2-x1 to get width, and you can use image[y1:y2, x1:x2] to get the box area.
      """
  
      self.chip_records = []
      self._global_chip_id = 1
      for r in records:
          self._cur_im_pos_chips = (
              []
          )  # element: (chip, boxes_idx), chip is [x1, y1, x2, y2], boxes_ids is List[int]
          self._cur_im_neg_chips = []  # element: (chip, neg_box_num)
          for scale_i in range(self.scale_num):
              self._get_current_scale_parameters(scale_i, r)
  
              # Cx4
              chips = self._create_chips(
                  r["h"], r["w"], self._cur_scale
              )
  
              # # dict: chipid->[box_id, ...]
              pos_chip2boxes_idx = self._get_valid_boxes_and_pos_chips(
                  r["gt_bbox"], chips
              )
  
              # dict: chipid->neg_box_num
              neg_chip2box_num = self._get_neg_boxes_and_chips(
                  chips,
                  list(pos_chip2boxes_idx.keys()),
                  r.get("proposals", None),
              )
  
              self._add_to_cur_im_chips(
                  chips, pos_chip2boxes_idx, neg_chip2box_num
              )
  
          cur_image_records = self._trans_all_chips2annotations(r)
          self.chip_records.extend(cur_image_records)
      return self.chip_records
  
imports: import copy
  import math
  import random
  import numpy
  from copy import deepcopy
  from typing import List, Tuple
  from collections import defaultdict
  from .chip_box_utils import nms, transform_chip_boxes2image_boxes
  from .chip_box_utils import find_chips_to_cover_overlaped_boxes
  from .chip_box_utils import transform_chip_box
  from .chip_box_utils import intersection_over_box
label_desc: Use image node_id 2 for calling the AnnoCropper obj's underlying member method code with example usage: obj.crop_anno_records(records) and returns: self