docs: def prune_contours(self, area_thresh=1000.0, dist_thresh=20,
                         preserve_topology=True):
          """Removes all white connected components with area less than area_thresh.
          Parameters
          ----------
          area_thresh : float
              The minimum area for which a white connected component will not be
              zeroed out.
          dist_thresh : int
              If a connected component is within dist_thresh of the top of the
              image, it will not be pruned out, regardless of its area.
          Returns
          -------
          :obj:`BinaryImage`
              The new pruned binary image.
          """
          # get all contours (connected components) from the binary image
          contours, hierarchy = cv2.findContours(
              self.data.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
          num_contours = len(contours)
          middle_pixel = np.array(self.shape)[:2] / 2
          middle_pixel = middle_pixel.reshape(1, 2)
          center_contour = None
          pruned_contours = []
  
          # find which contours need to be pruned
          for i in range(num_contours):
              area = cv2.contourArea(contours[i])
              if area > area_thresh:
                  # check close to origin
                  fill = np.zeros([self.height, self.width, 3])
                  cv2.fillPoly(
                      fill,
                      pts=[
                          contours[i]],
                      color=(
                          BINARY_IM_MAX_VAL,
                          BINARY_IM_MAX_VAL,
                          BINARY_IM_MAX_VAL))
                  nonzero_px = np.where(fill > 0)
                  nonzero_px = np.c_[nonzero_px[0], nonzero_px[1]]
                  dists = ssd.cdist(middle_pixel, nonzero_px)
                  min_dist = np.min(dists)
                  pruned_contours.append((contours[i], min_dist))
  
          if len(pruned_contours) == 0:
              return None
  
          pruned_contours.sort(key=lambda x: x[1])
  
          # keep all contours within some distance of the top
          num_contours = len(pruned_contours)
          keep_indices = [0]
          source_coords = pruned_contours[0][0].squeeze().astype(np.float32)
          for i in range(1, num_contours):
              target_coords = pruned_contours[i][0].squeeze().astype(np.float32)
              dists = ssd.cdist(source_coords, target_coords)
              min_dist = np.min(dists)
              if min_dist < dist_thresh:
                  keep_indices.append(i)
  
          # keep the top num_areas pruned contours
          keep_indices = np.unique(keep_indices)
          pruned_contours = [pruned_contours[i][0] for i in keep_indices]
  
          # mask out bad areas in the image
          pruned_data = np.zeros([self.height, self.width, 3])
          for contour in pruned_contours:
              cv2.fillPoly(
                  pruned_data,
                  pts=[contour],
                  color=(
                      BINARY_IM_MAX_VAL,
                      BINARY_IM_MAX_VAL,
                      BINARY_IM_MAX_VAL))
          pruned_data = pruned_data[:, :, 0]  # convert back to one channel
  
          # preserve topology of original image
          if preserve_topology:
              orig_zeros = np.where(self.data == 0)
              pruned_data[orig_zeros[0], orig_zeros[1]] = 0
          return BinaryImage(pruned_data.astype(np.uint8), self._frame)