Doppler
Detailed example of a color and power doppler on raw RF real data, scanning a rotating disk. Data can be found in MUST directory Few steps will be seen very quickly, as we assume you’ve already checked out the DAS example.
Beamforming process
We first read the data (‘must’ reader), initialize the scan we’d like to observe, and initialize a basic DAS beamformer.
1import numpy as np
2import cupy as cp
3from ultraspy.io.reader import Reader
4from ultraspy.scan import GridScan
5from ultraspy.beamformers.das import DelayAndSum
6import ultraspy as us
7
8# Load the data
9reader = Reader('/path/to/rotating_disk.mat', 'must')
10
11# Medium to observe
12x = np.linspace(-12.5, 12.5, 250) * 1e-3
13z = np.linspace(10, 35, 250) * 1e-3
14scan = GridScan(x, z)
15
16# DAS Beamformer, with the I/Q option set to True, as we need to work on
17# phase-shifts for Doppler
18beamformer = DelayAndSum(is_iq=True)
19beamformer.automatic_setup(reader.acquisition_info, reader.probe)
So far, nothing new but the is_iq option. The loaded data is RFs, but
in order to perform Doppler operations, we’ll have to convert them into I/Qs,
thus we need to warn the beamformer to set itself in the I/Qs algorithms mode.
1 # Send the whole packet to GPU, as complex data so we can store I/Qs
2 d_data = cp.asarray(reader.data, np.complex64)
3
4 # Conversion, required some info about the acquisition
5 info = beamformer.setup
6 us.rf2iq(d_data, info['central_freq'], info['sampling_freq'],
7 beamformer.t0)
1 # Conversion to I/Qs, required some info about the acquisition
2 info = beamformer.setup
3 data = us.cpu.rf2iq(data, info['central_freq'],
4 info['sampling_freq'], beamformer.t0)
Then we need to beamform the whole packet at once:
1 d_beamformed_packet = beamformer.beamform_packet(d_data, scan)
1 beamformed_packet = beamformer.beamform_packet(data, scan)
Warning
Note that the beamform_packet might require a big volume of memory,
if the packet is too large, consider to split it in bunches of iterates
through frames.
Doppler Imaging
And voilà! We have everything we need! Time to actually compute the doppler maps now. For the Color Doppler, the nyquist velocity needs to be computed beforehand. The Power Doppler on the other hand can be computed solely with the beamformed I/Qs.
1 # We need to know the nyquist velocity for the color-map
2 nyquist = info['sound_speed'] * info['prf'] / (4 * info['central_freq'])
3
4 # Doppler maps
5 d_color_map = us.get_color_doppler_map(d_beamformed_packet, nyquist)
6 d_power_map = us.get_power_doppler_map(d_beamformed_packet)
7 color_map = d_color_map.get()
8 power_map = d_power_map.get()
1 # We need to know the nyquist velocity for the color-map
2 nyquist = info['sound_speed'] * info['prf'] / (4 * info['central_freq'])
3
4 # Doppler maps
5 color_map = us.cpu.get_color_doppler_map(beamformed_packet, nyquist)
6 power_map = us.cpu.get_power_doppler_map(beamformed_packet)
This is enough for the Doppler imaging, let’s compute the B-Mode of one of the frame (let’s say the last one) for better visualization.
1 d_last_beamformed = d_beamformed_packet[..., -1].copy()
2 d_envelope = beamformer.compute_envelope(d_last_beamformed, scan)
3 us.to_b_mode(d_envelope)
4 b_mode = d_envelope.get()
1 last_beamformed = beamformed_packet[..., -1].copy()
2 envelope = beamformer.compute_envelope(last_beamformed, scan)
3 b_mode = us.to_b_mode(envelope)
And we’re done! We can now display the final result. For the color-map, it is common to use the power estimations as a threshold for nicer visualizations. Here, we’ll set it to -20dB.
1import matplotlib.pyplot as plt
2
3# Power threshold for color map
4power_threshold = -20
5doppler_colormap = us.get_doppler_colormap()
6
7# Display init
8extent = [x * 1e3 for x in [x[0], x[-1], z[-1], z[0]]] # In mm
9fig, axes = plt.subplots(1, 2)
10
11# Color map, we show the B-Mode first, then the masked color-map
12color_map = np.ma.masked_where(power_map < power_threshold, color_map)
13axes[0].imshow(b_mode.T, extent=extent, cmap='gray', clim=[-60, 0])
14im1 = axes[0].imshow(color_map.T, extent=extent, cmap=doppler_colormap,
15 clim=[-nyquist, nyquist])
16fig.colorbar(im1, ax=axes[0])
17axes[0].set_title('Color map (doppler velocity (m/s))')
18
19# Power map
20im2 = axes[1].imshow(power_map.T, extent=extent, cmap='hot', clim=[-30, 0])
21axes[1].set_title('Power map (dB)')
22fig.colorbar(im2, ax=axes[1])
23
24plt.show()
There we go :) Feel free to have a look to the next tutorial (about metrics) if you want to see another application of ultraspy.