code: def nms_2to3D(dets, thresh):
      """
      Merges 2D boxes to 3D cubes. Therefore, boxes of all slices are projected into one slices. An adaptation of Non-maximum surpression
      is applied, where clusters are found (like in NMS) with an extra constrained, that surpressed boxes have to have 'connected'
      z-coordinates w.r.t the core slice (cluster center, highest scoring box). 'connected' z-coordinates are determined
      as the z-coordinates with predictions until the first coordinate, where no prediction was found.
  
      example: a cluster of predictions was found overlap > iou thresh in xy (like NMS). The z-coordinate of the highest
      scoring box is 50. Other predictions have 23, 46, 48, 49, 51, 52, 53, 56, 57.
      Only the coordinates connected with 50 are clustered to one cube: 48, 49, 51, 52, 53. (46 not because nothing was
      found in 47, so 47 is a 'hole', which interrupts the connection). Only the boxes corresponding to these coordinates
      are surpressed. All others are kept for building of further clusters.
  
      This algorithm works better with a certain min_confidence of predictions, because low confidence (e.g. noisy/cluttery)
      predictions can break the relatively strong assumption of defining cubes' z-boundaries at the first 'hole' in the cluster.
  
      :param dets: (n_detections, (y1, x1, y2, x2, scores, slice_id)
      :param thresh: iou matchin threshold (like in NMS).
      :return: keep: (n_keep) 1D tensor of indices to be kept.
      :return: keep_z: (n_keep, [z1, z2]) z-coordinates to be added to boxes, which are kept in order to form cubes.
      """
      y1 = dets[:, 0]
      x1 = dets[:, 1]
      y2 = dets[:, 2]
      x2 = dets[:, 3]
      scores = dets[:, -2]
      slice_id = dets[:, -1]
  
      areas = (x2 - x1 + 1) * (y2 - y1 + 1)
      order = scores.argsort()[::-1]
  
      keep = []
      keep_z = []
  
      while (
          order.size > 0
      ):  # order is the sorted index.  maps order to index o[1] = 24 (rank1, ix 24)
          i = order[0]  # pop higehst scoring element
          xx1 = np.maximum(x1[i], x1[order])
          yy1 = np.maximum(y1[i], y1[order])
          xx2 = np.minimum(x2[i], x2[order])
          yy2 = np.minimum(y2[i], y2[order])
  
          w = np.maximum(0.0, xx2 - xx1 + 1)
          h = np.maximum(0.0, yy2 - yy1 + 1)
          inter = w * h
  
          ovr = inter / (areas[i] + areas[order] - inter)
          matches = np.argwhere(
              ovr > thresh
          )  # get all the elements that match the current box and have a lower score
  
          slice_ids = slice_id[order[matches]]
          core_slice = slice_id[int(i)]
          upper_wholes = [
              ii
              for ii in np.arange(core_slice, np.max(slice_ids))
              if ii not in slice_ids
          ]
          lower_wholes = [
              ii
              for ii in np.arange(np.min(slice_ids), core_slice)
              if ii not in slice_ids
          ]
          max_valid_slice_id = (
              np.min(upper_wholes)
              if len(upper_wholes) > 0
              else np.max(slice_ids)
          )
          min_valid_slice_id = (
              np.max(lower_wholes)
              if len(lower_wholes) > 0
              else np.min(slice_ids)
          )
          z_matches = matches[
              (slice_ids <= max_valid_slice_id)
              & (slice_ids >= min_valid_slice_id)
          ]
  
          z1 = np.min(slice_id[order[z_matches]]) - 1
          z2 = np.max(slice_id[order[z_matches]]) + 1
  
          keep.append(i)
          keep_z.append([z1, z2])
          order = np.delete(order, z_matches, axis=0)
  
      return keep, keep_z
  
imports: import os
  import numpy
  import torch
  from scipy.stats import norm
  from collections import OrderedDict
  from multiprocessing import Pool
  import pickle
  import pandas
label_desc: Use image node_id 4 for calling a global function with example usage: nms_2to3D(dets, thresh) and returns: keep,  keep_z