docs: def deblend_sources(data, segment_img, npixels, filter_kernel=None,
                      labels=None, nlevels=32, contrast=0.001,
                      mode='exponential', connectivity=8, relabel=True):
      """
      Deblend overlapping sources labeled in a segmentation image.
  
      Sources are deblended using a combination of multi-thresholding and
      `watershed segmentation
      <https://en.wikipedia.org/wiki/Watershed_(image_processing)>`_.  In
      order to deblend sources, they must be separated enough such that
      there is a saddle between them.
  
      Parameters
      ----------
      data : array_like
          The 2D array of the image.
  
      segment_img : `~photutils.segmentation.SegmentationImage` or array_like (int)
          A 2D segmentation image, either as a
          `~photutils.segmentation.SegmentationImage` object or an
          `~numpy.ndarray`, with the same shape as ``data`` where sources
          are labeled by different positive integer values.  A value of
          zero is reserved for the background.
  
      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.
  
      filter_kernel : array-like (2D) or `~astropy.convolution.Kernel2D`, optional
          The 2D array of the kernel used to filter the image before
          thresholding.  Filtering the image will smooth the noise and
          maximize detectability of objects with a shape similar to the
          kernel.
  
      labels : int or array-like of int, optional
          The label numbers to deblend.  If `None` (default), then all
          labels in the segmentation image will be deblended.
  
      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 difference of about 7.5.
  
      mode : {'exponential', 'linear'}, optional
          The mode used in defining the spacing between the
          multi-thresholding levels (see the ``nlevels`` keyword).  The
          default is 'exponential'.
  
      connectivity : {8, 4}, optional
          The type of pixel connectivity used in determining how pixels
          are grouped into a detected source.  The options are 8 (default)
          or 4.  8-connected pixels touch along their edges or corners.
          4-connected pixels touch along their edges.  For reference,
          SExtractor uses 8-connected pixels.
  
      relabel : bool
          If `True` (default), then the segmentation image will be
          relabeled such that the labels are in consecutive order starting
          from 1.
  
      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.
  
      See Also
      --------
      :func:`photutils.detect_sources`
      """
  
      if not isinstance(segment_img, SegmentationImage):
          segment_img = SegmentationImage(segment_img)
  
      if segment_img.shape != data.shape:
          raise ValueError('The data and segmentation image must have '
                           'the same shape')
  
      if labels is None:
          labels = segment_img.labels
      labels = np.atleast_1d(labels)
      segment_img.check_labels(labels)
  
      data = filter_data(data, filter_kernel, mode='constant', fill_value=0.0)
  
      last_label = segment_img.max_label
      segm_deblended = deepcopy(segment_img)
      for label in labels:
          source_slice = segment_img.slices[segment_img.get_index(label)]
          source_data = data[source_slice]
          source_segm = SegmentationImage(np.copy(
              segment_img.data[source_slice]))
          source_segm.keep_labels(label)    # include only one label
          source_deblended = _deblend_source(
              source_data, source_segm, npixels, nlevels=nlevels,
              contrast=contrast, mode=mode, connectivity=connectivity)
  
          if not np.array_equal(source_deblended.data.astype(bool),
                                source_segm.data.astype(bool)):
              raise ValueError('Deblending failed for source "{0}".  Please '
                               'ensure you used the same pixel connectivity '
                               'in detect_sources and deblend_sources.  If '
                               'this issue persists, then please inform the '
                               'developers.'.format(label))
  
          if source_deblended.nlabels > 1:
              # replace the original source with the deblended source
              source_mask = (source_deblended.data > 0)
              segm_tmp = segm_deblended.data
              segm_tmp[source_slice][source_mask] = (
                  source_deblended.data[source_mask] + last_label)
              segm_deblended.data = segm_tmp    # needed to call data setter
              last_label += source_deblended.nlabels
  
      if relabel:
          segm_deblended.relabel_consecutive()
  
      return segm_deblended