Writting data to a UFF file
In this example we show how to write channel and beamformed data into a UFF (Ultrasound File Format) file. The handling couldn't be simpler so this is going to be brief.
by Alfonso Rodriguez-Molares alfonso.r.molares@ntnu.no 15.05.2017
Contents
Getting channel data
The first thing we need to save data into a UFF file is, you guessed it, data. Let us generate some channel data using the fresnel simulator included in the USTB. We won't get into details here. If you want to know more about fresnel you can find some examples under the fresnel folder.
So here we define a 15 angles plane-wave sequence using a 128 elements linear array and a 5.2 MHz pulse. The phantom is a cross of point scatterers.
% phantom x_sca=[zeros(1,7) -15e-3:5e-3:15e-3]; z_sca=[5e-3:5e-3:35e-3 20e-3*ones(1,7)]; N_sca=length(x_sca); pha=uff.phantom(); pha.sound_speed=1540; % speed of sound [m/s] pha.points=[x_sca.', zeros(N_sca,1), z_sca.', ones(N_sca,1)]; % point scatterer position [m] % probe prb=uff.linear_array(); prb.N=128; % number of elements prb.pitch=300e-6; % probe pitch in azimuth [m] prb.element_width=270e-6; % element width [m] prb.element_height=5000e-6; % element height [m] % pulse pul=uff.pulse(); pul.center_frequency=5.2e6; % transducer frequency [MHz] pul.fractional_bandwidth=0.6; % fractional bandwidth [unitless] % sequence N=31; % number of plane waves angles=linspace(-0.3,0.3,N); % angle vector [rad] seq=uff.wave(); for n=1:N seq(n)=uff.wave(); seq(n).source.azimuth=angles(n); seq(n).source.distance=Inf; seq(n).probe=prb; seq(n).sound_speed=pha.sound_speed; end % simulator sim=fresnel(); % setting input data sim.phantom=pha; % phantom sim.pulse=pul; % transmitted pulse sim.probe=prb; % probe sim.sequence=seq; % beam sequence sim.sampling_frequency=41.6e6; % sampling frequency [Hz] % launch the simulation channel_data=sim.go(); % setting dataset name & author information channel_data.name = 'Test for UFF example'; channel_data.author = {'Alfonso Rodriguez-Molares <alfonso.r.molares@ntnu.no>','Arun Nair <anair8@jhu.edu>'}; channel_data.reference = {'www.ustb.no'};
USTB's Fresnel impulse response simulator (v1.0.5) ---------------------------------------------------------------
Getting beamformed data
We will also generate some beamformed data to save into the same UFF file. To do that we define a scanning grid, a beamformer, and we set it to run.
% scan scan=uff.linear_scan(linspace(-20e-3,20e-3,256).', linspace(0e-3,40e-3,256).'); % beamformer bmf=beamformer(); bmf.channel_data=channel_data; bmf.scan=scan; bmf.receive_apodization.window=uff.window.tukey50; bmf.receive_apodization.f_number=1.0; bmf.receive_apodization.apex.distance=Inf; bmf.transmit_apodization.window=uff.window.tukey50; bmf.transmit_apodization.f_number=1.0; bmf.transmit_apodization.apex.distance=Inf; % beamforming b_data=bmf.go({process.das_mex() process.coherent_compounding()}); b_data.plot();
Defining a UFF object
Now that we have data to save we define a uff object to handle the UFF file. We do so by providing the full path (path + filename + extension) to the constructor of the uff class, for instance:
local_path = [ustb_path(),'/data/']; uff_file=uff([local_path,'test02.uff']);
UFF: file C:\Users\alfonsom\Documents\ustb/data/test02.uff not found; it shall be created.
This will open 'test02.uff' file in the current folder. The constructor can take an additional parameter: mode a string that specifies whether the file is meant to be for read-only ("read"), to read and write ("append"), or if we want to overwrite the file ("write"). By default the constructor opens the file in "append" mode.
Saving beamformed data
It's about time we start saving some data. To do so we use the method write of the uff class. We must specify the object we want to save and the name it will have in the uff_data
uff_file.write(b_data,'b_data');
UFF: writting b_data [uff.beamformed_data] at
Now the beamformed data has been saved into the file. If you want to check the contents of the file with a HDF5 viewer such as
But we can check the contents of the file with the index method of uff with
display=true;
index=uff_file.index('/',display);
UFF: Contents of C:\Users\alfonsom\Documents\ustb/data/test02.uff at / - /b_data: b_data [uff.beamformed_data] size(1,1)
uff/index returns a cell with information on the datasets and groups in the specified location, see:
index{:}
ans = location: '/b_data' name: 'b_data' class: 'uff.beamformed_data' size: [2x1 double]
If the flag display is set then the function displays that information on screen. uff/index is not recursive: it only shows the contents of the specified location.
If we try saving the data again with the same name...
try uff_file.write(b_data,'b_data'); catch me fprintf(2,[me.identifier ': ' me.message '\n']); end
UFF: writting b_data [uff.beamformed_data] at MATLAB:imagesci:h5create:datasetAlreadyExists: The dataset '/b_data/scan/x_axis' already exists.
...we get an error. Different datasets must have different names or be placed in different locations. For instance by:
uff_file.write(b_data,'b_data_copy'); uff_file.index('/',display);
UFF: writting b_data_copy [uff.beamformed_data] at UFF: Contents of C:\Users\alfonsom\Documents\ustb/data/test02.uff at / - /b_data: b_data [uff.beamformed_data] size(1,1) - /b_data_copy: b_data_copy [uff.beamformed_data] size(1,1)
It is also possible to save arrays of UFF structures. We can for instance define an array of beamformed data as
b_data_array=uff.beamformed_data(); for n=1:3 b_data_array(n)=uff.beamformed_data(); b_data_array(n).copy(b_data); end
and store the whole array into the UFF file
uff_file.write(b_data_array,'b_data_array'); uff_file.index('/',display);
UFF: writting b_data_array [uff.beamformed_data] at UFF: writting b_data_array_0001 [uff.beamformed_data] at /b_data_array Processed 1/3UFF: writting b_data_array_0002 [uff.beamformed_data] at /Processed 2/3UFF: writting b_data_array_0003 [uff.beamformed_data] at /Processed 3/3 UFF: Contents of C:\Users\alfonsom\Documents\ustb/data/test02.uff at / - /b_data: b_data [uff.beamformed_data] size(1,1) - /b_data_array: b_data_array [uff.beamformed_data] size(1,3) - /b_data_copy: b_data_copy [uff.beamformed_data] size(1,1)
Saving channel data
Saving channel data (or any other uff structure) is exactly as with beamformed data. It might just take a bit more due to the larger amount of data. Here we save scan and channel_data
uff_file.write(scan,'scan'); uff_file.write(channel_data,'channel_data'); uff_file.index('/',display);
UFF: writting channel_data [uff.channel_data] at UFF: writting sequence [uff.wave] at /channel_data Processed 31/31 UFF: writting phantom [uff.phantom] at /channel_data UFF: Contents of C:\Users\alfonsom\Documents\ustb/data/test02.uff at / - /b_data: b_data [uff.beamformed_data] size(1,1) - /b_data_array: b_data_array [uff.beamformed_data] size(1,3) - /b_data_copy: b_data_copy [uff.beamformed_data] size(1,1) - /channel_data: channel_data [uff.channel_data] size(1,1) - /scan: scan [uff.linear_scan] size(1,1)