.. _doppler_example: 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 :ref:`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. .. code-block:: python :linenos: import numpy as np import cupy as cp from ultraspy.io.reader import Reader from ultraspy.scan import GridScan from ultraspy.beamformers.das import DelayAndSum import ultraspy as us # Load the data reader = Reader('/path/to/rotating_disk.mat', 'must') # Medium to observe x = np.linspace(-12.5, 12.5, 250) * 1e-3 z = np.linspace(10, 35, 250) * 1e-3 scan = GridScan(x, z) # DAS Beamformer, with the I/Q option set to True, as we need to work on # phase-shifts for Doppler beamformer = DelayAndSum(is_iq=True) beamformer.automatic_setup(reader.acquisition_info, reader.probe) So far, nothing new but the :code:`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. .. tabs:: .. group-tab:: GPU version .. code-block:: python :linenos: # Send the whole packet to GPU, as complex data so we can store I/Qs d_data = cp.asarray(reader.data, np.complex64) # Conversion, required some info about the acquisition info = beamformer.setup us.rf2iq(d_data, info['central_freq'], info['sampling_freq'], beamformer.t0) .. group-tab:: CPU version .. code-block:: python :linenos: # Conversion to I/Qs, required some info about the acquisition info = beamformer.setup data = us.cpu.rf2iq(data, info['central_freq'], info['sampling_freq'], beamformer.t0) Then we need to beamform the whole packet at once: .. tabs:: .. group-tab:: GPU version .. code-block:: python :linenos: d_beamformed_packet = beamformer.beamform_packet(d_data, scan) .. group-tab:: CPU version .. code-block:: python :linenos: beamformed_packet = beamformer.beamform_packet(data, scan) .. warning:: Note that the :code:`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. .. tabs:: .. group-tab:: GPU version .. code-block:: python :linenos: # We need to know the nyquist velocity for the color-map nyquist = info['sound_speed'] * info['prf'] / (4 * info['central_freq']) # Doppler maps d_color_map = us.get_color_doppler_map(d_beamformed_packet, nyquist) d_power_map = us.get_power_doppler_map(d_beamformed_packet) color_map = d_color_map.get() power_map = d_power_map.get() .. group-tab:: CPU version .. code-block:: python :linenos: # We need to know the nyquist velocity for the color-map nyquist = info['sound_speed'] * info['prf'] / (4 * info['central_freq']) # Doppler maps color_map = us.cpu.get_color_doppler_map(beamformed_packet, nyquist) 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. .. tabs:: .. group-tab:: GPU version .. code-block:: python :linenos: d_last_beamformed = d_beamformed_packet[..., -1].copy() d_envelope = beamformer.compute_envelope(d_last_beamformed, scan) us.to_b_mode(d_envelope) b_mode = d_envelope.get() .. group-tab:: CPU version .. code-block:: python :linenos: last_beamformed = beamformed_packet[..., -1].copy() envelope = beamformer.compute_envelope(last_beamformed, scan) 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. .. code-block:: python :linenos: import matplotlib.pyplot as plt # Power threshold for color map power_threshold = -20 doppler_colormap = us.get_doppler_colormap() # Display init extent = [x * 1e3 for x in [x[0], x[-1], z[-1], z[0]]] # In mm fig, axes = plt.subplots(1, 2) # Color map, we show the B-Mode first, then the masked color-map color_map = np.ma.masked_where(power_map < power_threshold, color_map) axes[0].imshow(b_mode.T, extent=extent, cmap='gray', clim=[-60, 0]) im1 = axes[0].imshow(color_map.T, extent=extent, cmap=doppler_colormap, clim=[-nyquist, nyquist]) fig.colorbar(im1, ax=axes[0]) axes[0].set_title('Color map (doppler velocity (m/s))') # Power map im2 = axes[1].imshow(power_map.T, extent=extent, cmap='hot', clim=[-30, 0]) axes[1].set_title('Power map (dB)') fig.colorbar(im2, ax=axes[1]) plt.show() .. image:: ../images/doppler.png :width: 800 There we go :) Feel free to have a look to the next tutorial (about metrics) if you want to see another application of ultraspy.