Exemplo n.º 1
0
def _significant_features(radar,
                          field,
                          gatefilter=None,
                          min_size=None,
                          size_bins=75,
                          size_limits=(0, 300),
                          structure=None,
                          remove_size_field=True,
                          fill_value=None,
                          size_field=None,
                          debug=False,
                          verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if size_field is None:
        size_field = '{}_feature_size'.format(field)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Parse binary structuring element
    if structure is None:
        structure = ndimage.generate_binary_structure(2, 1)

    # Initialize echo feature size array
    size_data = np.zeros_like(radar.fields[field]['data'],
                              subok=False,
                              dtype=np.int32)

    # Loop over all sweeps
    feature_sizes = []
    for sweep in radar.iter_slice():

        # Parse radar sweep data and define only valid gates
        is_valid_gate = ~radar.fields[field]['data'][sweep].mask

        # Label the connected features in radar sweep data and create index
        # array which defines each unique label (feature)
        labels, nlabels = ndimage.label(is_valid_gate,
                                        structure=structure,
                                        output=None)
        index = np.arange(1, nlabels + 1, 1)

        if debug:
            print 'Number of unique features for {}: {}'.format(sweep, nlabels)

        # Compute the size (in radar gates) of each echo feature
        # Check for case where no echo features are found, e.g., no data in
        # sweep
        if nlabels > 0:
            sweep_sizes = ndimage.labeled_comprehension(
                is_valid_gate, labels, index, np.count_nonzero, np.int32, 0)
            feature_sizes.append(sweep_sizes)

            # Set each label (feature) to its total size (in radar gates)
            for label, size in zip(index, sweep_sizes):
                size_data[sweep][labels == label] = size

    # Stack sweep echo feature sizes
    feature_sizes = np.hstack(feature_sizes)

    # Compute histogram of echo feature sizes, bin centers and bin
    # width
    counts, bin_edges = np.histogram(feature_sizes,
                                     bins=size_bins,
                                     range=size_limits,
                                     normed=False,
                                     weights=None,
                                     density=False)
    bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
    bin_width = np.diff(bin_edges).mean()

    if debug:
        print 'Bin width: {} gate(s)'.format(bin_width)

    # Compute the peak of the echo feature size distribution
    # We expect the peak of the echo feature size distribution to be close to 1
    # radar gate
    peak_size = bin_centers[counts.argmax()] - bin_width / 2.0

    if debug:
        print 'Feature size at peak: {} gate(s)'.format(peak_size)

    # Determine the first instance when the count (sample size) for an echo
    # feature size bin reaches 0 after the distribution peak
    # This will define the minimum echo feature size
    is_zero_size = np.logical_and(bin_centers > peak_size,
                                  np.isclose(counts, 0, atol=1.0e-5))
    min_size = bin_centers[is_zero_size].min() - bin_width / 2.0

    if debug:
        _range = [0.0, min_size]
        print 'Insignificant feature size range: {} gates'.format(_range)

    # Mask invalid feature sizes, e.g., zero-size features
    size_data = np.ma.masked_equal(size_data, 0, copy=False)
    size_data.set_fill_value(fill_value)

    # Add echo feature size field to radar
    size_dict = {
        'data': size_data.astype(np.int32),
        'standard_name': size_field,
        'long_name': '',
        '_FillValue': size_data.fill_value,
        'units': 'unitless',
    }
    radar.add_field(size_field, size_dict, replace_existing=True)

    # Update gate filter
    gatefilter.include_above(size_field, min_size, op='and', inclusive=False)

    # Remove eacho feature size field
    if remove_size_field:
        radar.fields.pop(size_field, None)

    return gatefilter
Exemplo n.º 2
0
def significant_detection(radar,
                          gatefilter=None,
                          remove_small_features=True,
                          size_bins=75,
                          size_limits=(0, 300),
                          fill_holes=False,
                          dilate=False,
                          structure=None,
                          iterations=1,
                          rays_wrap_around=False,
                          min_ncp=None,
                          ncp_field=None,
                          detect_field=None,
                          debug=False,
                          verbose=False):
    """
    Determine the significant detection of a radar. Note that significant
    detection can still include other non-meteorological echoes that the user
    may still have to remove further down the processing chain.

    Parameters
    ----------
    radar : Radar
        Radar object used to determine the appropriate GateFilter.
    gatefilter : GateFilter, optional
        If None, all radar gates will initially be assumed valid.
    remove_small_features : bool, optional
        True to remove insignificant echo features (e.g., salt and pepper
        noise) from significant detection mask.
    size_bins : int, optional
        Number of bins used to bin echo feature sizes and thus define its
        distribution.
    size_limits : list or tuple, optional
        Limits of the echo feature size distribution. The upper limit needs to
        be large enough to include the minimum feature size.
    fill_holes : bool, optional
        Fill any holes in the significant detection mask. For most radar
        volumes this should not be used since the default structuring element
        will automatically fill any sized hole.
    dilate : bool, optional
        Use binary dilation to fill in edges of the significant detection mask.
    structure : array_like, optional
        The binary structuring element used for all morphology routines. See
        SciPy's ndimage documentation for more information.
    iterations : int, optional
        The number of iterations to repeat binary dilation. If iterations is
        less than 1, binary dilation is repeated until the result does not
        change anymore.
    rays_wrap_around : bool, optional
        Whether the rays at the beginning and end of a sweep are connected
        (e.g., PPI VCP).
    min_ncp : float, optional
        Minimum normalized coherent power (signal quality) value used to
        indicate a significant echo.
    ncp_field : str, optional
        Minimum normalized coherent power (signal quality) field name. The
        default uses the Py-ART configuation file.
    detect_field : str, optional
        Radar significant detection mask field name.
    debug : bool, optional
        True to print debugging information, False to suppress.
    verbose : bool, optional
        True to print progress information, False to suppress.

    Returns
    -------
    gatefilter : GateFilter
        Py-ART GateFilter object indicating which radar gates are valid and
        invalid.
    """

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if detect_field is None:
        detect_field = 'significant_detection_mask'

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Exclude gates with poor signal quality
    if min_ncp is not None and ncp_field in radar.fields:
        gatefilter.include_above(ncp_field, min_ncp, op='and', inclusive=True)

    detect_dict = {
        'data': gatefilter.gate_included.astype(np.int8),
        'long_name': 'Radar significant detection mask',
        'standard_name': 'significant_detection_mask',
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = no significant detection, 1 = significant detection',
    }
    radar.add_field(detect_field, detect_dict, replace_existing=True)

    # Remove insignificant features from significant detection mask
    if remove_small_features:
        basic_fixes._binary_significant_features(radar,
                                                 detect_field,
                                                 size_bins=size_bins,
                                                 size_limits=size_limits,
                                                 structure=structure,
                                                 debug=debug,
                                                 verbose=verbose)

    # Fill holes in significant detection mask
    if fill_holes:
        basic_fixes._binary_fill(radar, detect_field, structure=structure)

    # Dilate significant detection mask
    if dilate:
        basic_fixes._binary_dilation(radar,
                                     detect_field,
                                     structure=structure,
                                     iterations=iterations,
                                     debug=debug,
                                     verbose=verbose)

    # Update gate filter
    gatefilter.include_equal(detect_field, 1, op='new')

    return gatefilter
Exemplo n.º 3
0
def significant_detection(
        radar, gatefilter=None, remove_small_features=True, size_bins=75,
        size_limits=(0, 300), fill_holes=False, dilate=False, structure=None,
        iterations=1, rays_wrap_around=False, min_ncp=None, ncp_field=None,
        detect_field=None, debug=False, verbose=False):
    """
    Determine the significant detection of a radar. Note that significant
    detection can still include other non-meteorological echoes that the user
    may still have to remove further down the processing chain.

    Parameters
    ----------
    radar : Radar
        Radar object used to determine the appropriate GateFilter.
    gatefilter : GateFilter, optional
        If None, all radar gates will initially be assumed valid.
    remove_small_features : bool, optional
        True to remove insignificant echo features (e.g., salt and pepper
        noise) from significant detection mask.
    size_bins : int, optional
        Number of bins used to bin echo feature sizes and thus define its
        distribution.
    size_limits : list or tuple, optional
        Limits of the echo feature size distribution. The upper limit needs to
        be large enough to include the minimum feature size.
    fill_holes : bool, optional
        Fill any holes in the significant detection mask. For most radar
        volumes this should not be used since the default structuring element
        will automatically fill any sized hole.
    dilate : bool, optional
        Use binary dilation to fill in edges of the significant detection mask.
    structure : array_like, optional
        The binary structuring element used for all morphology routines. See
        SciPy's ndimage documentation for more information.
    iterations : int, optional
        The number of iterations to repeat binary dilation. If iterations is
        less than 1, binary dilation is repeated until the result does not
        change anymore.
    rays_wrap_around : bool, optional
        Whether the rays at the beginning and end of a sweep are connected
        (e.g., PPI VCP).
    min_ncp : float, optional
        Minimum normalized coherent power (signal quality) value used to
        indicate a significant echo.
    ncp_field : str, optional
        Minimum normalized coherent power (signal quality) field name. The
        default uses the Py-ART configuation file.
    detect_field : str, optional
        Radar significant detection mask field name.
    debug : bool, optional
        True to print debugging information, False to suppress.
    verbose : bool, optional
        True to print progress information, False to suppress.

    Returns
    -------
    gatefilter : GateFilter
        Py-ART GateFilter object indicating which radar gates are valid and
        invalid.
    """

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if detect_field is None:
        detect_field = 'significant_detection_mask'

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Exclude gates with poor signal quality
    if min_ncp is not None and ncp_field in radar.fields:
        gatefilter.include_above(ncp_field, min_ncp, op='and', inclusive=True)

    detect_dict = {
        'data': gatefilter.gate_included.astype(np.int8),
        'long_name': 'Radar significant detection mask',
        'standard_name': 'significant_detection_mask',
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = no significant detection, 1 = significant detection',
    }
    radar.add_field(detect_field, detect_dict, replace_existing=True)

    # Remove insignificant features from significant detection mask
    if remove_small_features:
        basic_fixes._binary_significant_features(
            radar, detect_field, size_bins=size_bins, size_limits=size_limits,
            structure=structure, debug=debug, verbose=verbose)

    # Fill holes in significant detection mask
    if fill_holes:
        basic_fixes._binary_fill(radar, detect_field, structure=structure)

    # Dilate significant detection mask
    if dilate:
        basic_fixes._binary_dilation(
            radar, detect_field, structure=structure, iterations=iterations,
            debug=debug, verbose=verbose)

    # Update gate filter
    gatefilter.include_equal(detect_field, 1, op='new')

    return gatefilter
Exemplo n.º 4
0
def _significant_features(
        radar, field, gatefilter=None, min_size=None, size_bins=75,
        size_limits=(0, 300), structure=None, remove_size_field=True,
        fill_value=None, size_field=None, debug=False, verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if size_field is None:
        size_field = '{}_feature_size'.format(field)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Parse binary structuring element
    if structure is None:
        structure = ndimage.generate_binary_structure(2, 1)

    # Initialize echo feature size array
    size_data = np.zeros_like(
        radar.fields[field]['data'], subok=False, dtype=np.int32)

    # Loop over all sweeps
    feature_sizes = []
    for sweep in radar.iter_slice():

        # Parse radar sweep data and define only valid gates
        is_valid_gate = ~radar.fields[field]['data'][sweep].mask

        # Label the connected features in radar sweep data and create index
        # array which defines each unique label (feature)
        labels, nlabels = ndimage.label(
            is_valid_gate, structure=structure, output=None)
        index = np.arange(1, nlabels + 1, 1)

        if debug:
            print 'Number of unique features for {}: {}'.format(sweep, nlabels)

        # Compute the size (in radar gates) of each echo feature
        # Check for case where no echo features are found, e.g., no data in
        # sweep
        if nlabels > 0:
            sweep_sizes = ndimage.labeled_comprehension(
                is_valid_gate, labels, index, np.count_nonzero, np.int32, 0)
            feature_sizes.append(sweep_sizes)

            # Set each label (feature) to its total size (in radar gates)
            for label, size in zip(index, sweep_sizes):
                size_data[sweep][labels == label] = size

    # Stack sweep echo feature sizes
    feature_sizes = np.hstack(feature_sizes)

    # Compute histogram of echo feature sizes, bin centers and bin
    # width
    counts, bin_edges = np.histogram(
        feature_sizes, bins=size_bins, range=size_limits, normed=False,
        weights=None, density=False)
    bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
    bin_width = np.diff(bin_edges).mean()

    if debug:
        print 'Bin width: {} gate(s)'.format(bin_width)

    # Compute the peak of the echo feature size distribution
    # We expect the peak of the echo feature size distribution to be close to 1
    # radar gate
    peak_size = bin_centers[counts.argmax()] - bin_width / 2.0

    if debug:
        print 'Feature size at peak: {} gate(s)'.format(peak_size)

    # Determine the first instance when the count (sample size) for an echo
    # feature size bin reaches 0 after the distribution peak
    # This will define the minimum echo feature size
    is_zero_size = np.logical_and(
        bin_centers > peak_size, np.isclose(counts, 0, atol=1.0e-5))
    min_size = bin_centers[is_zero_size].min() - bin_width / 2.0

    if debug:
        _range = [0.0, min_size]
        print 'Insignificant feature size range: {} gates'.format(_range)

    # Mask invalid feature sizes, e.g., zero-size features
    size_data = np.ma.masked_equal(size_data, 0, copy=False)
    size_data.set_fill_value(fill_value)

    # Add echo feature size field to radar
    size_dict = {
        'data': size_data.astype(np.int32),
        'standard_name': size_field,
        'long_name': '',
        '_FillValue': size_data.fill_value,
        'units': 'unitless',
    }
    radar.add_field(size_field, size_dict, replace_existing=True)

    # Update gate filter
    gatefilter.include_above(size_field, min_size, op='and', inclusive=False)

    # Remove eacho feature size field
    if remove_size_field:
        radar.fields.pop(size_field, None)

    return gatefilter
Exemplo n.º 5
0
def significant_features(
        radar, fields, gatefilter=None, size_bins=100, size_limits=(0, 400),
        structure=None, save_size_field=False, fill_value=None, debug=False,
        verbose=False):
    """
    Determine significant radar echo features on a sweep by sweep basis by
    computing the size each echo feature. Here an echo feature is defined as
    multiple connected radar gates with valid data, where the connection
    structure is defined by the user.

    Parameters
    ----------
    radar : Radar
        Py-ART Radar containing
    fields : str or list or tuple
        Radar fields to be used to identify significant echo featues.
    gatefilter : GateFilter
        Py-ART GateFilter instance.
    size_bins : int, optional
        Number of size bins used to bin feature size distribution.
    size_limits : list or tuple, optional
        Lower and upper limits of the feature size distribution. This together
        with size_bins defines the bin width of the feature size distribution.
    structure : array_like, optional
        Binary structuring element used to define connected radar gates. The
        default defines a structuring element in which diagonal radar gates are
        not considered connected.
    save_size_field : bool, optional
        True to save size fields in the radar object, False to discard.
    debug : bool, optional
        True to print debugging information, False to suppress.


    Returns
    -------
    gf : GateFilter
        Py-ART GateFilter.

    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse radar fields
    if isinstance(fields, str):
        fields = [fields]

    # Parse gate filter
    if gatefilter is None:
        gf = GateFilter(radar, exclude_based=False)

    # Parse binary structuring element
    if structure is None:
        structure = ndimage.generate_binary_structure(2, 1)

    for field in fields:

        if verbose:
            print 'Processing echo features: {}'.format(field)

        # Initialize echo feature size array
        size_data = np.zeros_like(
            radar.fields[field]['data'], subok=False, dtype=np.int32)

        feature_sizes = []
        for sweep, _slice in enumerate(radar.iter_slice()):

            # Parse radar sweep data and define only valid gates
            data = radar.get_field(sweep, field, copy=False)
            is_valid_gate = ~np.ma.getmaskarray(data)

            # Label the connected features in the radar sweep data and create
            # index array which defines each unique label (feature)
            labels, nlabels = ndimage.label(
                is_valid_gate, structure=structure, output=None)
            index = np.arange(1, nlabels + 1, 1)

            if debug:
                print 'Unique features in sweep {}: {}'.format(sweep, nlabels)

            # Compute the size (in radar gates) of each echo feature
            # Check for case where no echo features are found, e.g., no data in
            # sweep
            if nlabels > 0:
                sweep_sizes = ndimage.labeled_comprehension(
                    is_valid_gate, labels, index, np.count_nonzero,
                    np.int32, 0)
                feature_sizes.append(sweep_sizes)

                # Set each label (feature) to its total size (in radar gates)
                for label, size in zip(index, sweep_sizes):
                    size_data[_slice][labels == label] = size

        # Stack sweep echo feature sizes
        feature_sizes = np.hstack(feature_sizes)

        # Bin and compute feature size occurrences
        counts, bin_edges = np.histogram(
            feature_sizes, bins=size_bins, range=size_limits, normed=False,
            weights=None, density=False)
        bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
        bin_width = np.diff(bin_edges).mean()

        if debug:
            print 'Bin width: {} gate(s)'.format(bin_width)

        # Compute the peak of the echo feature size distribution. We expect the
        # peak of the echo feature size distribution to be close to 1 radar
        # gate
        peak_size = bin_centers[counts.argmax()] - bin_width / 2.0

        if debug:
            print 'Feature size at peak: {} gate(s)'.format(peak_size)

        # Determine the first instance when the count (sample size) for an echo
        # feature size bin reaches 0 after the distribution peak. This will
        # define the minimum echo feature size
        is_zero_size = np.logical_and(
            bin_centers > peak_size, np.isclose(counts, 0, atol=1.0e-1))
        min_size = bin_centers[is_zero_size].min() - bin_width / 2.0

        if debug:
            _range = [0.0, min_size]
            print 'Insignificant feature size range: {} gates'.format(_range)

        # Mask invalid feature sizes, e.g., zero-size features
        size_data = np.ma.masked_equal(size_data, 0, copy=False)
        size_data.set_fill_value(fill_value)

        # Parse echo feature size field name
        size_field = '{}_feature_size'.format(field)

        # Add echo feature size field to radar
        size_dict = {
            'data': size_data.astype(np.int32),
            'long_name': 'Echo feature size in number of radar gates',
            '_FillValue': size_data.fill_value,
            'units': 'unitless',
            'comment': None,
            }
        radar.add_field(size_field, size_dict, replace_existing=True)

        # Update gate filter
        gf.include_above(size_field, min_size, op='and', inclusive=False)

        # Remove eacho feature size field if specified
        if not save_size_field:
            radar.fields.pop(size_field, None)

    return gf