Multifrequency Categorization

This is a multifrequency categorization technique (see Jech and Michaels, 2006) for echosounder data analysis. It implements an algorithm that classifies the Sv responses that register above a threshold, from each frequency. The threshold can be determined by running a sensitivity analysis of historical data recorded for the species of interest.

The algorithm involves assigning an integer to each frequency. These integers are chosen with the property that any combination of them produces a unique sum. Hence, one could determine which integers (and therefore corresponding frequencies) contributed to that sum. The set of these integers is called the category codes.

For each ping, each frequency contributes to the sum (via its corresponding category code), if the Sv value for that frequency exceeds a given threshold. Otherwise, it contributes nothing.

An output ping constructed from these sums indicates which unique combination of frequencies contributed to those sums. These output pings can then be combined into an echogram for further analysis.

For example, the category codes [1, 3, 7, 13, 29] produce the following sums: 1, 3, 4, 7, 8, 10, 11, 13, 14, 16, 17, 20, 21, 23, 24, 29, 30, 32, 33, 36, 37, 39, 40, 42, 43, 45, 46, 49, 50, 52, 53. If the Sv value for all five frequencies of a ping sample exceeds the threshold, the sum for that sample is 1+3+7+13+29 = 53.

Jech and Michaels (2006) observe that spatial patterns (like the color of category code combinations) can be associated with species of interest or other acoustic objects. As such, a custom color scheme can help in visualizing the results of this method. Read the About color schemes help page, or contact support@echoview.com for assistance.

List of operands

Distinct frequency single beam Sv variables.

Code

"""
Echoview Code Operator source file
========================================================================

Created by Echoview Software, for Echoview® 11.1.25 on 26 October 2020

See the Echoview help file for Code operator documentation and
examples.

NumPy User Guide: https://docs.scipy.org/doc/numpy/user/
NumPy Reference: https://docs.scipy.org/doc/numpy/reference/

Echoview® is a registered trademark of Echoview Software Pty Ltd.
"""

# Authorship information
__author__ = "Echoview Software Pty Ltd. 2020."
__disclaimer__ = (
    "This example code is provided AS IS, without warranty of any "
    "kind, express or implied, including but not limited to the "
    "warranties of merchantability, fitness for a particular purpose "
    "and noninfringement. In no event shall Echoview Software Pty Ltd "
    "be liable for any claim, damages or other liability, arising "
    "from, out of or in connection with the use of this example code."
)
__version__ = "1.0"

# Libraries
from echoview import OperatorBase, MeasurementType
import numpy as np

Threshold = -66.00  # dB
CategoryCodes = [1, 3, 7, 13, 29]


class Operator(OperatorBase):
    '''Multifrequency classification
    ====================================================================
    This program generates an echogram based on the method described
    in Jech & Michaels (2006) for any number of operands.
    Distinct frequency single beam Sv variables.
    The length of the list, CategoryCode, determines how many operands
    will be processed by this Code operator. This should match the
    number of operands provided on the Operands page of the Variable
    Properties dialog box.
    The Code operator referencing this source file replaces 4
    operators per frequency (i.e. 20 operators if there are 5
    frequencies), thereby simplifying the data flow.
    '''

    def result_type(self, input_types):
        '''Accepts single beam Sv operands.
        :param input_types: A list of OperandInput objects, one for
        each operand.
        :returns linear acoustic data.
        '''
        for o_type in input_types:
            if not o_type == MeasurementType.SINGLE_BEAM_SV:
                raise TypeError("Sv Operands expected.")
        if len(input_types) > len(CategoryCodes):
            raise IndexError(
                f"More operands ({len(input_types)}) than category codes ({len(CategoryCode)}).")
        return MeasurementType.SINGLE_BEAM_LINEAR

    def eval(self, inputs):
        '''Given a number of single beam Sv operands, sum the category codes
        corresponding to the frequencies that exceeded the threshold.
        Note: Optimize by using NumPy vectors to process an entire
              ping at a time. A regular `for` loop, iterating through
              each datum comprising the ping, can be significantly
              slower.
        :param inputs: A list of OperandInput objects, one for each
        operand.
        :returns a list of integers, each one is the sum of the
        category codes of the frequencies that exceeded the threshold.
        '''

        # Create an output ping consisting entirely of zeros to sum
        # category codes into. Use the first operand's input ping to
        # determine the size required for the output ping.
        summedCatCodes = np.zeros_like(inputs[0].measurement.data)

        # For each frequency, determine where in the input ping the Sv
        # is greater than or equal the threshold. If it is, accumulate
        # the CategoryCodes into the output ping (summedCatCodes +
        # catCode). Otherwise, the contribution of that frequency is 0
        # (summedCatCodes).
        for freq, catCode in zip(inputs, CategoryCodes):
            summedCatCodes = np.where(freq.measurement.data >=
                                      Threshold, summedCatCodes + catCode, summedCatCodes)

        return summedCatCodes

See also

Code operator
About the Code operator
Using the Code operator