docs: def _deblend_source(data, segment_img, npixels, nlevels=32, contrast=0.001,
                      mode='exponential', connectivity=8):
      """
      Deblend a single labeled source.
  
      Parameters
      ----------
      data : array_like
          The 2D array of the image.  The should be a cutout for a single
          source.  ``data`` should already be smoothed by the same filter
          used in :func:`~photutils.detect_sources`, if applicable.
  
      segment_img : `~photutils.segmentation.SegmentationImage`
          A cutout `~photutils.segmentation.SegmentationImage` object with
          the same shape as ``data``.  ``segment_img`` should contain only
          *one* source label.
  
      npixels : int
          The number of connected pixels, each greater than ``threshold``,
          that an object must have to be detected.  ``npixels`` must be a
          positive integer.
  
      nlevels : int, optional
          The number of multi-thresholding levels to use.  Each source
          will be re-thresholded at ``nlevels``, spaced exponentially or
          linearly (see the ``mode`` keyword), between its minimum and
          maximum values within the source segment.
  
      contrast : float, optional
          The fraction of the total (blended) source flux that a local
          peak must have to be considered as a separate object.
          ``contrast`` must be between 0 and 1, inclusive.  If ``contrast
          = 0`` then every local peak will be made a separate object
          (maximum deblending).  If ``contrast = 1`` then no deblending
          will occur.  The default is 0.001, which will deblend sources with
          a magnitude differences of about 7.5.
  
      mode : {'exponential', 'linear'}, optional
          The mode used in defining the spacing between the
          multi-thresholding levels (see the ``nlevels`` keyword).
  
      connectivity : {4, 8}, optional
          The type of pixel connectivity used in determining how pixels
          are grouped into a detected source.  The options are 4 or 8
          (default).  4-connected pixels touch along their edges.
          8-connected pixels touch along their edges or corners.  For
          reference, SExtractor uses 8-connected pixels.
  
      Returns
      -------
      segment_image : `~photutils.segmentation.SegmentationImage`
          A 2D segmentation image, with the same shape as ``data``, where
          sources are marked by different positive integer values.  A
          value of zero is reserved for the background.
      """
  
      from scipy import ndimage
      from skimage.morphology import watershed
  
      if nlevels < 1:
          raise ValueError('nlevels must be >= 1, got "{0}"'.format(nlevels))
      if contrast < 0 or contrast > 1:
          raise ValueError('contrast must be >= 0 or <= 1, got '
                           '"{0}"'.format(contrast))
  
      if connectivity == 4:
          selem = ndimage.generate_binary_structure(2, 1)
      elif connectivity == 8:
          selem = ndimage.generate_binary_structure(2, 2)
      else:
          raise ValueError('Invalid connectivity={0}.  '
                           'Options are 4 or 8'.format(connectivity))
  
      segm_mask = (segment_img.data > 0)
      source_values = data[segm_mask]
      source_min = np.nanmin(source_values)
      source_max = np.nanmax(source_values)
      if source_min == source_max:
          return segment_img     # no deblending
      if mode == 'exponential' and source_min < 0:
          warnings.warn('Source "{0}" contains negative values, setting '
                        'deblending mode to "linear"'.format(
                            segment_img.labels[0]), AstropyUserWarning)
          mode = 'linear'
      source_sum = float(np.nansum(source_values))
  
      steps = np.arange(1., nlevels + 1)
      if mode == 'exponential':
          if source_min == 0:
              source_min = source_max * 0.01
          thresholds = source_min * ((source_max / source_min) **
                                     (steps / (nlevels + 1)))
      elif mode == 'linear':
          thresholds = source_min + ((source_max - source_min) /
                                     (nlevels + 1)) * steps
      else:
          raise ValueError('"{0}" is an invalid mode; mode must be '
                           '"exponential" or "linear"')
  
      # suppress NoDetectionsWarning during deblending
      warnings.filterwarnings('ignore', category=NoDetectionsWarning)
  
      # create top-down tree of local peaks
      segm_tree = []
      mask = ~segm_mask
      for level in thresholds[::-1]:
          segm_tmp = detect_sources(data, level, npixels=npixels,
                                    connectivity=connectivity, mask=mask)
  
          if segm_tmp is None or segm_tmp.nlabels < 2:
              continue
  
          fluxes = []
          for i in segm_tmp.labels:
              fluxes.append(np.nansum(data[segm_tmp == i]))
          idx = np.where((np.array(fluxes) / source_sum) >= contrast)[0]
          if len(idx >= 2):
              segm_tree.append(segm_tmp)
  
      nbranch = len(segm_tree)
      if nbranch == 0:
          return segment_img
      else:
          for j in range(nbranch - 1, 0, -1):
              intersect_mask = (segm_tree[j].data *
                                segm_tree[j - 1].data).astype(bool)
              intersect_labels = np.unique(segm_tree[j].data[intersect_mask])
  
              if segm_tree[j - 1].nlabels <= len(intersect_labels):
                  segm_tree[j - 1] = segm_tree[j]
              else:
                  # If a higher tree level has more peaks than in the
                  # intersected label(s) with the level below, then remove
                  # the intersected label(s) in the lower level, add the
                  # higher level, and relabel.
                  segm_tree[j].remove_labels(intersect_labels)
                  new_segments = segm_tree[j].data + segm_tree[j - 1].data
                  new_segm, nsegm = ndimage.label(new_segments)
                  segm_tree[j - 1] = SegmentationImage(new_segm)
  
          return SegmentationImage(watershed(-data, segm_tree[0].data,
                                             mask=segment_img.data.astype(bool),
                                             connectivity=selem))