Wiki

Clone wiki

UFF / other_formats / USTB HDF5 read_write

HDF5 read/write

Reading from and writting to HDF5 is done through the functions uff.read_object and uff.write_object. For instance the code

#!matlab

    my_probe = uff.linear_array('N',128,'pitch',300e-6);
    uff.write_object('./my_uff_file.uff',my_probe,'my_beautiful_probe');

dumps the probe data in a HDF5 file named 'my_uff_file.uff'. Within the file the data is saved in a data structure called 'my_beautiful_probe'. We can check the contents of the file with the function '''uff.index'''

#!matlab

uff.index('./my_uff_file.uff','/',true);
UFF: Contents of ./my_uff_file.uff at /
   - /my_beautiful_probe: my_beautiful_probe [uff.linear_array] size(1,1)

To read the data from the UFF file it suffices to

#!matlab

my_other_probe=uff.read_object('./my_uff_file.uff','/my_beautiful_probe');

uff.write_object

#!matlab

uff.write_object(filename, object, name, location, verbose)

This function checks the object class. In case the object is a numeric value (double, single) or character (char) the function dumps the data in the provided location. In case the object is a uff class then the routine will recursively call itself for each parameter in the ''object'' class. Dependent properties are skipped. The dataset type and size are stored as attributes in the HDF5.

You can find the code of the uff.write_object function below

#!matlab

function dumped_objects = write_object(filename,object,name,location,verbose)
%% WRITE_OBJECT  Writes object into location
%
%   This UFF method writes the object provided into the specified location
%   of a UFF file.
%
%   WRITE(filename, object, name, location, verbose)
%
%   Parameters:
%       filename    Name and path to the UFF file
%       object      Object to be written into the UFF file
%       name        Name of the object within the file
%       location    Location within the UFF file
%       verbose     Flag to get text messages
%
%   Example:
%       chdat = uff.channel_data();
%       uff.write_object('test.uff',chdat,'channel_data');
%
%   See also UFF.WRITE, UFF.INDEX

if(nargin<1)||isempty(filename) error('Missing UFF filename'); end
if(nargin<2)||isempty(object) error('Missing object to write in UFF file'); end
if(nargin<3)||isempty(name) error('Missing name for the HDF5 group'); end
if(nargin<4) location=[]; end
if nargin<5||isempty(verbose) verbose=true; end

% check if path to file exists
[pathstr,~] = fileparts(filename);
if ~exist(pathstr,'file')
    error(sprintf('UFF: The path %s does not exist.',pathstr));
end

% if file doesn't exist -> we write the current version
if ~(exist(filename,'file')==2)
    if verbose fprintf('UFF: creating file %s\n',filename); end
    attr_details.Name = 'version';
    attr_details.AttachedTo = '/';
    attr_details.AttachType = 'group';
    hdf5write(filename, attr_details, uff.version);
end

% check if version matches
file_version=h5readatt(filename, '/','version');  % read file version
file_version=file_version{1};                       % from cell to string
file_version=file_version(int32(file_version)>0);   % removing 0's from 0-terminated strings
if ~strcmp(file_version, uff.version)
    error(sprintf('UFF: Unsupported file version (%s). Current UFF version (%s). The file you want to write to is obsolete, create a new file instead.',file_version,uff.version));
end

% check if location exist
if ~isempty(uff.index(filename,[location '/' name],false));
    choice = tools.dialog_timeout(5,sprintf('UFF: %s already exists in the file. Overwrite? (y/n)',[location '/' name]), ...
        'USTB', ...
        'Yes','No','No');
    if ~strcmp(choice,'Yes')
        fprintf(1,'%s not written\n',name);
        return;
    else
        fid = H5F.open(filename,'H5F_ACC_RDWR','H5P_DEFAULT');
        H5L.delete(fid,[location '/' name],'H5P_DEFAULT');
        H5F.close(fid);
    end
end

switch class(object)
    case {'double' 'single'}
        if isreal(object)
            h5create(filename,[location '/' name], size(object), 'Datatype', 'single', 'ChunkSize',size(object));
            h5write(filename,[location '/' name], single(object));
            h5writeatt(filename,[location '/' name],'class',class(object));
            h5writeatt(filename,[location '/' name],'name',name);
            h5writeatt(filename,[location '/' name],'imaginary',0);
            h5writeatt(filename,[location '/' name],'complex',0);
            dumped_objects=1;
        else
            % real
            h5create(filename,[location '/' name '/real'], size(object), 'Datatype', 'single', 'ChunkSize',size(object));
            h5write(filename,[location '/' name '/real'], single(real(object)));
            h5writeatt(filename,[location '/' name '/real'],'class',class(object));
            h5writeatt(filename,[location '/' name '/real'],'name',name);
            h5writeatt(filename,[location '/' name '/real'],'imaginary',0);

            % imag
            h5create(filename,[location '/' name '/imag'], size(object), 'Datatype', 'single', 'ChunkSize',size(object));
            h5write(filename,[location '/' name '/imag'], single(imag(object)));
            h5writeatt(filename,[location '/' name '/imag'],'class',class(object));
            h5writeatt(filename,[location '/' name '/imag'],'name',name);
            h5writeatt(filename,[location '/' name '/imag'],'imaginary',1);

            % group attributes
            h5writeatt(filename,[location '/' name],'class',class(object));
            h5writeatt(filename,[location '/' name],'name',name);
            h5writeatt(filename,[location '/' name],'complex',1);
            dumped_objects=1;
        end
    case 'char'
        h5create(filename,[location '/' name], size(object), 'Datatype', 'single', 'ChunkSize',size(object));
        h5write(filename,[location '/' name], uint16(object));
        h5writeatt(filename,[location '/' name],'class',class(object));
        h5writeatt(filename,[location '/' name],'name',name);
        dumped_objects=1;
    case 'uff.window'
        h5create(filename,[location '/' name], size(object), 'Datatype', 'single', 'ChunkSize',size(object));
        h5write(filename,[location '/' name], single(object));
        h5writeatt(filename,[location '/' name],'class',class(object));
        h5writeatt(filename,[location '/' name],'name',name);
        dumped_objects=1;
    case 'cell'
        % call write for all members in the cell
        dumped_objects=0;
        for n=1:numel(object)
            dumped_objects=dumped_objects+uff.write_object(filename,object{n}, [name '_' sprintf('%04d',n)],[location '/' name], verbose);
        end

        % group attributes
        if dumped_objects
            h5writeatt(filename,[location '/' name],'class',class(object));
            h5writeatt(filename,[location '/' name],'name',name);
            h5writeatt(filename,[location '/' name],'array',1);
            h5writeatt(filename,[location '/' name],'size',size(object));
        end
    otherwise
        % UFF structures
        if (findstr('uff.',class(object)))
            if numel(object)>1

                if verbose fprintf('UFF: writting %s [%s] at %s ',name,class(object),location); end
                % call write for all members in the array
                dumped_objects=0;
                previous_msg='';
                for n=1:numel(object)
                    dumped_objects=dumped_objects+uff.write_object(filename,object(n), [name '_' sprintf('%04d',n)],[location '/' name],verbose);
                    if verbose previous_msg = tools.text_progress_bar(100*n/numel(object),previous_msg); end
                end
                if verbose fprintf('\n'); end

                % group attributes
                if dumped_objects
                    h5writeatt(filename,[location '/' name],'class',class(object));
                    h5writeatt(filename,[location '/' name],'name',name);
                    h5writeatt(filename,[location '/' name],'array',1);
                    h5writeatt(filename,[location '/' name],'size',size(object));
                end
            else
                % here we process non-array UFF structures
                switch class(object)
                    case {'uff.channel_data' 'uff.beamformed_data' 'uff.phantom'}
                        if verbose
                            if isempty(location)
                                fprintf('UFF: writting %s [%s] at %s\n',name,class(object),'/');
                            else
                                fprintf('UFF: writting %s [%s] at %s\n',name,class(object),location);
                            end
                        end
                end

                % dump all fields in struct (or properties in class)
                dumped_objects=0;
                field_list = fieldnames(object);
                eval(['mco = ?' class(object) ';']);
                plist = mco.PropertyList;
                for f=1:length(field_list)

                    % check if the property is dependent
                    copy=false;
                    for p=1:length(plist)
                        if strcmp(plist(p).Name,field_list{f})
                            copy=~plist(p).Dependent;
                            continue;
                        end
                    end

                    % if it isn't dependent or empty we write it
                    if copy
                        prop=object.(field_list{f});
                        if numel(prop)
                            uff.write_object(filename, prop, field_list{f}, [location '/' name], verbose);
                            dumped_objects=dumped_objects+1;
                        end
                    end
                end

                if dumped_objects>0
                    h5writeatt(filename,[location '/' name],'class',class(object));
                    h5writeatt(filename,[location '/' name],'name',name);
                    h5writeatt(filename,[location '/' name],'size',size(object));
                    h5writeatt(filename,[location '/' name],'array',0);
                end
            end
        else warning(sprintf('Class %s not supported by UFF; skipping write.',class(object))); end
end

uff.read_object

#!matlab

object = uff.read_object(filename, location, verbose)

This function checks the location provided, where an attribute should define the class of the saved object. An object of said class is defined, and the contents of the file will be copied to the object instance. If the object class is a numeric value (double, single) or character (char) the function just dumps the data to the new object. If the dumped object was a uff class then the routine will recursively call itself for each parameter in the object class. The dataset type and size are retrieved from attributes in the HDF5 datasets.

You can find the code of the uff.read_object function below

#!matlab

function object = read_object(filename, location, verbose)
%% READ_OBJECT  Reads object from location
%
%   This UFF method delivers the object stored in the specified location
%   of a UFF file.
%
%   READ_OBJECT(filename, location, verbose)
%
%   Parameters:
%       filename    Name and path to the UFF file
%       location    Location within the UFF file
%       verbose     Flag to get text messages
%
%   Example:
%       chndata=uff.read_object('test.uff','/channel_data');
%
%   See also UFF.READ, UFF.WRITE, UFF.INFO

flag_v10X=false;

if nargin<2||isempty(location) location='/'; end
if nargin<3 verbose=true; end

% check if path to file exists
[pathstr,~] = fileparts(filename);
if ~exist(pathstr,'file')
    error(sprintf('UFF: The path %s does not exist.',pathstr));
end

% check if file exists
if ~(exist(filename,'file')==2)
    error(sprintf('UFF: The file %s does not exist.',filename));
end

% check if version matches
file_version=h5readatt(filename, '/','version');    % read file version
file_version=file_version{1};                       % from cell to string
file_version=file_version(int32(file_version)>0);   % removing 0's from 0-terminated strings
if ~strcmp(file_version, uff.version)
    % Flags to enable backcompatibility. 
    if strcmp(file_version, 'v1.0.0')||strcmp(file_version, 'v1.0.1')
        flag_v10X=true;
    else
        error(sprintf('UFF: Unsupported file version (%s). Current UFF version (%s). Please choose a new file instead.',file_version,uff.version));
    end
end

% check if location exist
if isempty(uff.index(filename,location,false));
    error('UFF: The location does not exists');
end

% check if batch reading
if nargin<2 || strcmp(location,'/')
    % we read everything in the main location
    item=uff.index(filename,'/');
    if length(item) object={}; end
    for n=1:length(item)
        object{n}=uff.read_object(filename,item{n}.location,verbose);
    end
else

    % checking name and class
    try
        data_name=h5readatt(filename, location ,'name');
        class_name=h5readatt(filename, location ,'class');
    catch me
        if isempty(findstr(me.message,'can''t locate attribute:'))
            me.rethrow;
        else
            warning(['UFF: unnamed locations not supported. Skipping ' location '.']);
            object=[];
            return;
        end
    end

    switch class_name
        case {'double' 'single'}
            if ~h5readatt(filename, location, 'complex')
                object=h5read(filename, location);
            else
                object=h5read(filename, [ location '/real' ])+...
                    1i*h5read(filename, [ location '/imag' ]);
            end
        case 'char'
            object=char(h5read(filename, location));
        case 'uff.window'
            object=uff.window(h5read(filename, location ));
        case 'cell'
            data_size=h5readatt(filename, location ,'size');
            N=prod(data_size);
            if(N>0)
                item=uff.index(filename, location );
                if length(item)~=N error('Size attribute does not match number of subgroups'); end
                object={};
                for n=1:N
                    object{n}=uff.read_object(filename,item{n}.location,verbose);
                end
                reshape(object,data_size.');
            end
        otherwise
            % rest of UFF structures
            if (findstr('uff.',class_name))
                switch class_name
                    case {'uff.channel_data' 'uff.beamformed_data' 'uff.phantom'}
                        if verbose fprintf('UFF: reading %s [%s]\n',data_name,class_name); end
                end
                data_size=h5readatt(filename, location ,'size');
                N=prod(data_size);
                if(N>1)
                    item=uff.index(filename, location );
                    if length(item)~=N error('Size attribute does not match number of subgroups'); end
                    if verbose fprintf('UFF: reading %s [%s] ',data_name,class_name); end
                    previous_msg = '';
                    for n=1:N
                        object(n)=uff.read_object(filename,item{n}.location,verbose);
                        if verbose previous_msg = tools.text_progress_bar(100*n/N,previous_msg); end
                    end
                    if verbose fprintf('\n'); end
                    reshape(object,data_size.');
                else
                    object=feval(class_name);

                    % add properties
                    prop=uff.index(filename, location);
                    for m=1:length(prop)
                        % exceptions from backcompatibility
                        if flag_v10X&&strcmp(class_name,'uff.apodization')&&strcmp(prop{m}.name,'apex')
                            object.('origo')=uff.read_object(filename,prop{m}.location,verbose);                        
                        elseif flag_v10X&&strcmp(class_name,'uff.apodization')&&strcmp(prop{m}.name,'scan')
                            object.('focus')=uff.read_object(filename,prop{m}.location,verbose);                        
                        else
                            object.(prop{m}.name)=uff.read_object(filename,prop{m}.location,verbose);
                        end
                    end
                end
            else
                warning(sprintf('Class %s not supported by UFF; skipping write.',class(value)));
                object=[];
            end
    end
end

Updated