<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def source_properties(data, segment_img, error=None, mask=None, background=None, filter_kernel=None, wcs=None, labels=None):
 """ Calculate photometry and morphological properties of sources defined by a labeled segmentation image. Parameters data : array_like or `~astropy.units.Quantity` The 2D array from which to calculate the source photometry and properties. ``data`` should be background-subtracted. Non-finite ``data`` values (e.g. NaN or inf) are automatically masked. segment_img : `SegmentationImage` or array_like (int) A 2D segmentation image, either as a `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. error : array_like or `~astropy.units.Quantity`, optional The total error array corresponding to the input ``data`` array. ``error`` is assumed to include *all* sources of error, including the Poisson error of the sources (see `~photutils.utils.calc_total_error`) . ``error`` must have the same shape as the input ``data``. Non-finite ``error`` values (e.g. NaN or inf) are not automatically masked, unless they are at the same position of non-finite values in the input ``data`` array. Such pixels can be masked using the ``mask`` keyword. See the Notes section below for details on the error propagation. mask : array_like (bool), optional A boolean mask with the same shape as ``data`` where a `True` value indicates the corresponding element of ``data`` is masked. Masked data are excluded from all calculations. Non-finite values (e.g. NaN or inf) in the input ``data`` are automatically masked. background : float, array_like, or `~astropy.units.Quantity`, optional The background level that was *previously* present in the input ``data``. ``background`` may either be a scalar value or a 2D image with the same shape as the input ``data``. Inputting the ``background`` merely allows for its properties to be measured within each source segment. The input ``background`` does *not* get subtracted from the input ``data``, which should already be background-subtracted. Non-finite ``background`` values (e.g. NaN or inf) are not automatically masked, unless they are at the same position of non-finite values in the input ``data`` array. Such pixels can be masked using the ``mask`` keyword. filter_kernel : array-like (2D) or `~astropy.convolution.Kernel2D`, optional The 2D array of the kernel used to filter the data prior to calculating the source centroid and morphological parameters. The kernel should be the same one used in defining the source segments, i.e. the detection image (e.g., see :func:`~photutils.detect_sources`). If `None`, then the unfiltered ``data`` will be used instead. wcs : `~astropy.wcs.WCS` The WCS transformation to use. If `None`, then any sky-based properties will be set to `None`. labels : int, array-like (1D, int) The segmentation labels for which to calculate source properties. If `None` (default), then the properties will be calculated for all labeled sources. Returns ------- output : `SourceCatalog` instance A `SourceCatalog` instance containing the properties of each source. Notes ----- `SExtractor`_'s centroid and morphological parameters are always calculated from a filtered "detection" image, i.e. the image used to define the segmentation image. The usual downside of the filtering is the sources will be made more circular than they actually are. If you wish to reproduce `SExtractor`_ centroid and morphology results, then input a filtered and background-subtracted "detection" image into the ``filtered_data`` keyword. If ``filtered_data`` is `None`, then the unfiltered ``data`` will be used for the source centroid and morphological parameters. Negative data values (``filtered_data`` or ``data``) within the source segment are set to zero when calculating morphological properties based on image moments. Negative values could occur, for example, if the segmentation image was defined from a different image (e.g., different bandpass) or if the background was oversubtracted. Note that `~photutils.SourceProperties.source_sum` always includes the contribution of negative ``data`` values. The input ``error`` is assumed to include *all* sources of error, including the Poisson error of the sources. `~photutils.SourceProperties.source_sum_err` is simply the quadrature sum of the pixel-wise total errors over the non-masked pixels within the source segment: .. math:: \\Delta F = \\sqrt{\\sum_{i \\in S} \\sigma_{\\mathrm{tot}, i}^2} where :math:`\\Delta F` is `~photutils.SourceProperties.source_sum_err`, :math:`S` are the non-masked pixels in the source segment, and :math:`\\sigma_{\\mathrm{tot}, i}` is the input ``error`` array. .. _SExtractor: http://www.astromatic.net/software/sextractor See Also -------- SegmentationImage, SourceProperties, detect_sources Examples -------- [[ 0. 1. 2. 3.] [ 4. 5. 6. 7.] [ 8. 9. 10. 11.] [12. 13. 14. 15.]] Print some properties of the first object (labeled with ``1`` in the segmentation image):
 1 <Quantity [0.8, 0.2] pix> 5.0 <Quantity 3. pix2> 4.0 Print some properties of the second object (labeled with ``2`` in the segmentation image):
 2 <Quantity [2.36363636, 2.09090909] pix> <Quantity 5.41421356 pix> <Quantity -0.74175931 rad> """


    if not isinstance(segment_img, SegmentationImage):
        segment_img = SegmentationImage(segment_img)

    if segment_img.shape != data.shape:
        raise ValueError('segment_img and data must have the same shape.')

    # filter the data once, instead of repeating for each source
    if filter_kernel is not None:
        filtered_data = filter_data(data, filter_kernel, mode='constant',
                                    fill_value=0.0, check_normalization=True)
    else:
        filtered_data = None

    if labels is None:
        labels = segment_img.labels
    labels = np.atleast_1d(labels)

    sources_props = []
    for label in labels:
        if label not in segment_img.labels:
            warnings.warn('label {} is not in the segmentation image.'
                          .format(label), AstropyUserWarning)
            continue  # skip invalid labels

        sources_props.append(SourceProperties(
            data, segment_img, label, filtered_data=filtered_data,
            error=error, mask=mask, background=background, wcs=wcs))

    if len(sources_props) == 0:
        raise ValueError('No sources are defined.')

    return SourceCatalog(sources_props, wcs=wcs)