Skip to content

reconstruct

Image reconstruction.

Functions:

Name Description
backproject

Reconstruct an image using the backprojection algorithm.

backproject

backproject(data, tx_position, rx_position, image_x, image_y, image_z=0, c=343.0, rvg=True, complex_image=True, channel_dim='channel', time_dim='time', position_dim='xyz')

Reconstruct an image using the backprojection algorithm.

The backprojection algorithm takes each trace of data and projects it back onto the positions on the reconstruction surface that it could have originated from. For each trace, this results in an arc of data. When summed over all pings and channels, the data constructively adds in true target locations and destructively cancels elsewhere.

Parameters:

Name Type Description Default
data DataArray

The data to backproject.

required
tx_position DataArray

The position of the transmitter for each trace in data.

required
rx_position DataArray

The position of each receiver for each trace in data.

required
image_x ArrayLike

The x coordinates of the focal points to reconstruct at. This must be one-dimensional.

required
image_y ArrayLike

The y coordinates of the focal points to reconstruct at. This must be one-dimensional.

required
image_z ArrayLike

The z coordinates of the focal points to reconstruct at. This may be either scalar if z is constant for all focal points, or a two-dimensional array giving a value for each focal point.

0
c float

The speed of sound to use during reconstruction.

343.0
rvg bool

Whether to apply a range-varying gain during reconstruction to compensate for spherical spreading losses. Note that backprojection has an inherent one-way gain. Setting this parameter to True applies the remaining one-way gain to complete this compensation. Setting it to False does not apply the remaining gain, leaving the image with its inherent compensation.

True
complex_image bool

If True and data is real, use scipy.signal.hilbert to convert it to an analytic signal before backprojecting. If False, use the data array as given.

True
channel_dim str

The dimension of data and rx_position corresponding to the receiver channels.

'channel'
time_dim str

The dimension of data corresponding to the sampling times.

'time'
position_dim str

The dimension of tx_position and rx_position corresponding to the position coordinates.

'xyz'

Returns:

Name Type Description
image DataArray

The complex image formed at the given focal points. This will be two-dimensional with the requested x and y coordinates.

Source code in src/airsonar/processing/reconstruct.py
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def backproject(
    data: xr.DataArray,
    tx_position: xr.DataArray,
    rx_position: xr.DataArray,
    image_x: ArrayLike,
    image_y: ArrayLike,
    image_z: ArrayLike = 0,
    c: float = 343.0,
    rvg: bool = True,
    complex_image: bool = True,
    channel_dim: str = "channel",
    time_dim: str = "time",
    position_dim: str = "xyz",
) -> xr.DataArray:
    """Reconstruct an image using the backprojection algorithm.

    The backprojection algorithm takes each trace of data and projects it back onto the
    positions on the reconstruction surface that it could have originated from. For each
    trace, this results in an arc of data. When summed over all pings and channels, the
    data constructively adds in true target locations and destructively cancels
    elsewhere.

    Parameters
    ----------
    data
        The data to backproject.
    tx_position
        The position of the transmitter for each trace in `data`.
    rx_position
        The position of each receiver for each trace in `data`.
    image_x
        The x coordinates of the focal points to reconstruct at. This must be
        one-dimensional.
    image_y
        The y coordinates of the focal points to reconstruct at. This must be
        one-dimensional.
    image_z
        The z coordinates of the focal points to reconstruct at. This may be either
        scalar if z is constant for all focal points, or a two-dimensional array giving
        a value for each focal point.
    c
        The speed of sound to use during reconstruction.
    rvg
        Whether to apply a range-varying gain during reconstruction to compensate for
        spherical spreading losses. Note that backprojection has an inherent one-way
        gain. Setting this parameter to True applies the remaining one-way gain to
        complete this compensation. Setting it to False does not apply the remaining
        gain, leaving the image with its inherent compensation.
    complex_image
        If True and `data` is real, use [scipy.signal.hilbert][] to convert it to an
        analytic signal before backprojecting. If False, use the `data` array as given.
    channel_dim
        The dimension of `data` and `rx_position` corresponding to the receiver
        channels.
    time_dim
        The dimension of `data` corresponding to the sampling times.
    position_dim
        The dimension of `tx_position` and `rx_position` corresponding to the position
        coordinates.

    Returns
    -------
    image : xr.DataArray
        The complex image formed at the given focal points. This will be two-dimensional
        with the requested x and y coordinates.

    """
    # Check and convert coordinates to DataArrays.
    image_x = _ensure_1d(image_x, "x")
    image_y = _ensure_1d(image_y, "y")

    # Coerce z positions to either a float or a 2D DataArray.
    if not isinstance(image_z, xr.DataArray):
        image_z_arr = np.asarray(image_z)
        if image_z_arr.ndim == 2:
            image_z = xr.DataArray(image_z_arr, coords=[image_x, image_y])
        elif image_z_arr.ndim != 0:
            raise ValueError("image_z must be a float or a 2D array")

    # Decide on the output datatype based on the input and the complex_image flag.
    if data.dtype.kind == "c":
        output_dtype = data.dtype
    elif complex_image:
        output_dtype = complex  # type:ignore[assignment]
    else:
        output_dtype = data.dtype

    # Call the low-level ufunc on each block.
    image = xr.apply_ufunc(
        _backproject,
        data,
        tx_position,
        rx_position,
        input_core_dims=[
            [channel_dim, time_dim],
            [position_dim],
            [channel_dim, position_dim],
        ],
        kwargs={
            "image_x": image_x.values,
            "image_y": image_y.values,
            "image_z": image_z.values if isinstance(image_z, xr.DataArray) else image_z,
            "c": c,
            "t": data[time_dim].values,
            "complex_image": complex_image,
            "rvg": rvg,
        },
        output_core_dims=[["__image_x__", "__image_y__"]],
        dask="parallelized",
        dask_gufunc_kwargs={
            "meta": [np.empty(0, dtype=output_dtype)],
            "output_sizes": {"__image_x__": len(image_x), "__image_y__": len(image_y)},
        },
        keep_attrs=True,
    )

    # Sum over non-image dimensions.
    if image.ndim > 2:
        image = image.sum(image.dims[:-2])

    # And assign the dimensions and coordinates of the focal points.
    image = image.assign_coords(
        __image_x__=image_x.values,
        __image_y__=image_y.values,
    ).rename(
        __image_x__=image_x.dims[0],
        __image_y__=image_y.dims[0],
    )

    return image