mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 19:39:08 +09:00
initial commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
%ADD_CONTENT write matlab structure to H5 file
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function add_content(data, gid, plist, comp, overwrite)
|
||||
import io.HDF.*
|
||||
|
||||
fn = fieldnames(data);
|
||||
for jj=1:length(fn)
|
||||
if isstruct(data.(fn{jj}))
|
||||
fn_sub = fieldnames(data.(fn{jj}));
|
||||
if length(fn_sub) <= 2 && isfield(data.(fn{jj}), 'Value')
|
||||
% found dataset
|
||||
if isfield(data.(fn{jj}), 'Attributes')
|
||||
Attributes.MATLAB_class = class(data.(fn{jj}).Value);
|
||||
write_dataset(data.(fn{jj}).Value, gid, fn{jj}, plist, comp, overwrite, data.(fn{jj}).Attributes);
|
||||
else
|
||||
Attributes.MATLAB_class = class(data.(fn{jj}).Value);
|
||||
write_dataset(data.(fn{jj}).Value, gid, fn{jj}, plist, comp, overwrite, Attributes);
|
||||
end
|
||||
elseif strcmpi(fn{jj}, 'Attributes')
|
||||
% add attributes to group
|
||||
fn_attr = fieldnames(data.Attributes);
|
||||
for ii=1:length(fn_attr)
|
||||
write_attribute(gid, data.Attributes.(fn_attr{ii}), fn_attr{ii});
|
||||
end
|
||||
else
|
||||
% found group
|
||||
gid_new = add_groups(gid, fn{jj}, plist, true);
|
||||
if length(data.(fn{jj})) == 1
|
||||
add_content(data.(fn{jj}), gid_new, plist, comp, overwrite)
|
||||
else
|
||||
fn_names = cell(1,length(data.(fn{jj})));
|
||||
for ii=1:length(data.(fn{jj}))
|
||||
fn_names{ii} = sprintf([fn{jj} '_%d'],ii-1);
|
||||
gid_new_sub = add_groups(gid_new, fn_names{ii}, plist, true);
|
||||
add_content(data.(fn{jj})(ii), gid_new_sub, plist, comp, overwrite);
|
||||
end
|
||||
write_attribute(gid_new, 'structure array', 'MATLAB_class');
|
||||
|
||||
end
|
||||
end
|
||||
else
|
||||
% append dataset
|
||||
Attributes.MATLAB_class = class(data.(fn{jj}));
|
||||
write_dataset(data.(fn{jj}), gid, fn{jj}, plist, comp, overwrite, Attributes);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
%ADD_GROUPS add groups or open them if they exist
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function gid = add_groups(fileID, gpath, plist, varargin)
|
||||
|
||||
if nargin > 3
|
||||
single_data = varargin{1};
|
||||
else
|
||||
single_data = true;
|
||||
end
|
||||
|
||||
if ~single_data
|
||||
gpath_depth = length(gpath);
|
||||
gid{1} = fileID;
|
||||
for ii=1:gpath_depth
|
||||
try
|
||||
gid{end+1} = H5G.open(gid{ii}, gpath{ii}, plist);
|
||||
catch
|
||||
gid{end+1} = H5G.create(gid{ii},gpath{ii},plist,plist,plist);
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
try
|
||||
gid = H5G.open(fileID, gpath, plist);
|
||||
catch
|
||||
gid = H5G.create(fileID,gpath,plist,plist,plist);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
% Determine datatype for HDF files
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function [datatype_h5, data] = get_datatype(data)
|
||||
|
||||
if isempty(data)
|
||||
data = '';
|
||||
end
|
||||
|
||||
switch class(data)
|
||||
case 'uint32'
|
||||
datatype_h5 = 'H5T_STD_U32LE';
|
||||
case 'int32'
|
||||
datatype_h5 = 'H5T_STD_I32LE';
|
||||
case 'int64'
|
||||
datatype_h5 = 'H5T_STD_I64LE';
|
||||
case 'uint64'
|
||||
datatype_h5 = 'H5T_STD_U64LE';
|
||||
case 'double'
|
||||
if isreal(data)
|
||||
datatype_h5 = 'H5T_NATIVE_DOUBLE';
|
||||
else
|
||||
datatype_h5 = 'complex';
|
||||
end
|
||||
case 'single'
|
||||
if isreal(data)
|
||||
datatype_h5 = 'H5T_NATIVE_FLOAT';
|
||||
else
|
||||
datatype_h5 = 'complex';
|
||||
end
|
||||
case 'logical'
|
||||
data = uint32(data);
|
||||
datatype_h5 = 'H5T_STD_U32LE';
|
||||
case 'char'
|
||||
if size(data,1)>1
|
||||
data = cellstr(data);
|
||||
datatype_h5 = 'char_array';
|
||||
else
|
||||
datatype_h5 = 'H5T_UNIX_D32BE'; %'H5T_UNIX_D32LE';
|
||||
end
|
||||
case 'cell'
|
||||
datatype_h5 = 'H5T_C_S1';
|
||||
case 'struct'
|
||||
datatype_h5 = 'struct';
|
||||
|
||||
otherwise
|
||||
error('Unknown data type %s', class(data))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
%HDF5_APPEND_ATTR Append an attribute to a dataset
|
||||
%
|
||||
% file... HDF filename
|
||||
% attr... structure of attributes
|
||||
% loc... location of the dataset that needs to be removed
|
||||
%
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function hdf5_append_attr( file, attr, loc)
|
||||
import io.HDF.*
|
||||
plist = 'H5P_DEFAULT';
|
||||
|
||||
fileID = H5F.open(file,'H5F_ACC_RDWR',plist);
|
||||
|
||||
gpath = strsplit(rm_delimiter(loc), '/');
|
||||
|
||||
gpath_depth = length(gpath);
|
||||
gid{1} = fileID;
|
||||
for ii=1:gpath_depth
|
||||
try
|
||||
gid{end+1} = H5G.open(gid{ii}, gpath{ii}, plist);
|
||||
catch
|
||||
gid{end+1} = H5D.open(gid{ii}, gpath{ii});
|
||||
end
|
||||
end
|
||||
|
||||
fn = fieldnames(attr);
|
||||
for ii=1:length(fn)
|
||||
write_attribute(gid{end}, attr.(fn{ii}), fn{ii}, true);
|
||||
end
|
||||
|
||||
H5F.close(fileID);
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
%HDF5_ATTR_EXISTS check if attribute exists in given file
|
||||
% file... h5 file path
|
||||
% attr... dataset name
|
||||
%
|
||||
% *optional*
|
||||
% gpath... path within the h5 file; default root (/)
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function [ out ] = hdf5_attr_exists(file, name, varargin)
|
||||
|
||||
out = false;
|
||||
|
||||
% load info
|
||||
if nargin > 2
|
||||
h = h5info(file, varargin{1});
|
||||
else
|
||||
h = h5info(file);
|
||||
end
|
||||
|
||||
% loop through datasets and check if name exists
|
||||
for ii=1:numel(h.Attributes)
|
||||
if strcmpi(h.Attributes(ii).Name, name)
|
||||
out = true;
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
%HDF5_CP_FILE copy HDF files
|
||||
% orig_filename... source file
|
||||
% duplicate_filename... target file
|
||||
%
|
||||
% *optional* given as name/value pair
|
||||
% groups... groups to copy; either string or cell of
|
||||
% strings; default: everything in root
|
||||
% copy_type... 'deep', 'normal' or 'shallow' copy;
|
||||
% 'shallow' creates external links in target file;
|
||||
% 'normal' is similar to linux 'cp' command;
|
||||
% 'deep' dereferences all internal and external links;
|
||||
% default: 'shallow'
|
||||
%
|
||||
% EXAMPLES:
|
||||
% hdf5_cp_file('./test.h5', './test_new.h5')
|
||||
% hdf5_cp_file('./test.h5', './test_new.h5', 'copy_type', 'deep');
|
||||
%
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function hdf5_cp_file(orig_filename, duplicate_filename, varargin)
|
||||
import io.HDF.*
|
||||
% take care of input arguments
|
||||
groups = [];
|
||||
copy_type = 'shallow';
|
||||
|
||||
% parse the variable input arguments vararg = cell(0,0);
|
||||
if ~isempty(varargin)
|
||||
for ind = 1:2:length(varargin)
|
||||
name = varargin{ind};
|
||||
value = varargin{ind+1};
|
||||
switch lower(name)
|
||||
case 'groups'
|
||||
groups = value;
|
||||
case 'copy_type'
|
||||
copy_type = value;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
switch copy_type
|
||||
case 'shallow'
|
||||
if isempty(groups)
|
||||
% if no groups are specified, use h5info to get all datasets and groups
|
||||
% from root
|
||||
h = h5info(orig_filename, '/');
|
||||
lng = length(h.Groups);
|
||||
lnd = length(h.Datasets);
|
||||
lna = length(h.Attributes);
|
||||
|
||||
groups = cell([1 lng+lnd]);
|
||||
attributes = [];
|
||||
|
||||
for ii=1:lng
|
||||
groups{ii} = h.Groups(ii).Name;
|
||||
end
|
||||
for ii=1:lnd
|
||||
groups{ii+lng} = h.Datasets(ii).Name;
|
||||
end
|
||||
for ii=1:lna
|
||||
attributes.(h.Attributes(ii).Name) = h.Attributes(ii).Value;
|
||||
if iscell(h.Attributes(ii).Value)
|
||||
attributes.(h.Attributes(ii).Name) = attributes.(h.Attributes(ii).Name){1};
|
||||
end
|
||||
end
|
||||
else
|
||||
attributes = [];
|
||||
end
|
||||
|
||||
|
||||
s = [];
|
||||
if iscell(groups)
|
||||
for ii=1:length(groups)
|
||||
subgrps = strsplit(rm_delimiter(groups{ii}), '/');
|
||||
s = setfield(s, subgrps{:}, ['ext:' orig_filename ':' groups{ii}]);
|
||||
end
|
||||
else
|
||||
s.groups = ['ext:' orig_filename ':' groups];
|
||||
end
|
||||
|
||||
% append attributes
|
||||
if ~isempty(attributes)
|
||||
s.Attributes = attributes;
|
||||
end
|
||||
|
||||
save2hdf5(duplicate_filename, s, 'overwrite', true, 'iscopy', true);
|
||||
|
||||
case 'deep'
|
||||
s = io.HDF.hdf5_load(orig_filename, '-ca');
|
||||
save2hdf5(duplicate_filename, s, 'overwrite', true, 'iscopy', true);
|
||||
|
||||
case 'normal'
|
||||
copyfile(orig_filename, duplicate_filename)
|
||||
|
||||
otherwise
|
||||
error('Unknown copy type!')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,99 @@
|
||||
%HDF5_DSET_EXISTS check if dataset exists in given file
|
||||
% file... h5 file path
|
||||
% dset... dataset name
|
||||
%
|
||||
% *optional*
|
||||
% gpath... path within the h5 file; default root (/)
|
||||
% check_links... include links; default true
|
||||
%
|
||||
% EXAMPLES:
|
||||
% out = io.HDF.hdf5_dset_exists('./recons.h5',
|
||||
% 'object_phase_unwrapped', '/reconstruction', true);
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function [out] = hdf5_dset_exists(file, dset, varargin)
|
||||
|
||||
out = false;
|
||||
|
||||
% load info
|
||||
if nargin > 2
|
||||
h = h5info(file, varargin{1});
|
||||
else
|
||||
h = h5info(file);
|
||||
end
|
||||
|
||||
if nargin > 3
|
||||
check_links = varargin{2};
|
||||
else
|
||||
check_links = true;
|
||||
end
|
||||
|
||||
if nargin > 4
|
||||
check_groups = varargin{3};
|
||||
else
|
||||
check_groups = true;
|
||||
end
|
||||
|
||||
% loop through datasets and check if name exists
|
||||
for ii=1:numel(h.Datasets)
|
||||
if strcmpi(h.Datasets(ii).Name, dset)
|
||||
out = true;
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if check_links
|
||||
for ii=1:numel(h.Links)
|
||||
if strcmpi(h.Links(ii).Name, dset)
|
||||
out = true;
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if check_groups
|
||||
for ii=1:numel(h.Groups)
|
||||
[~, gname] = fileparts(h.Groups(ii).Name);
|
||||
if strcmpi(gname, dset)
|
||||
out = true;
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,642 @@
|
||||
% HDF5_LOAD Load an hdf5 file
|
||||
%
|
||||
% DATA = HDF5_LOAD(filename) reads a complete file hierarchy recursively, with
|
||||
% file name/path being specified by the 'filename' argument
|
||||
%
|
||||
% DATA = HDF5_LOAD(filename, '-a') reads a complete file hierarchy
|
||||
% recursively, including attributes
|
||||
%
|
||||
% DATA = HDF5_LOAD(filename, location) reads a particular group, link, or a single dataset
|
||||
% specified by the 'location' argument
|
||||
%
|
||||
% ATT = HDF5_LOAD(filename, location, '-a') reads all datasets and attributes associated
|
||||
% with a particular location in the file (group, link or dataset)
|
||||
%
|
||||
% ATT = HDF5_LOAD(filename, location, '-ca') reads all datasets and attributes associated
|
||||
% with a particular location in the file (group, link or dataset) and
|
||||
% converts datasets to a specific matlab class based on attribute 'MATLAB_class'
|
||||
%
|
||||
% SLICE = HDF5_LOAD(filename, location, {rowRange, colRange, frameRange, ...}) reads a
|
||||
% portion of a dataset along specified dimentions, where slicing ranges can be defined in
|
||||
% the following ways (negative indexes count from the end of the corresponding dimensions):
|
||||
% range = scalar_index - reads a particular row/col/frame/... (indentical to
|
||||
% 'range = [scalar_index, scalar_index]')
|
||||
% range = [start_index, end_index] - reads all data between start and end
|
||||
% indexes
|
||||
% range = [start_index, Inf] - reads all data from start_index to the last
|
||||
% existing element in the file
|
||||
% range = [], or range is omitted at the end - reads the full range of values for that
|
||||
% dimention (indentical to 'range = [1, Inf]')
|
||||
%
|
||||
% Examples:
|
||||
% hdf5_load('scan_003.hdf5')
|
||||
% hdf5_load('scan_003.hdf5', '/entry/sample/description')
|
||||
% hdf5_load('scan_003.hdf5', '/entry/collection/data/spec', '-a')
|
||||
% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {5})
|
||||
% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {[-100, Inf]})
|
||||
% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {5, [500, Inf], [1, 100]})
|
||||
% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {[], [], [1, 100]})
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group
|
||||
% and the Science IT group, Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function data = hdf5_load(filename, varargin)
|
||||
import io.HDF.*
|
||||
|
||||
load_attr = false;
|
||||
convert2matlab = false;
|
||||
|
||||
narginchk(1, 3);
|
||||
if nargin == 1
|
||||
% Read the complete file hierarchy recursively
|
||||
try
|
||||
info = h5info(filename);
|
||||
info.Name = ''; % a special case of the root group
|
||||
|
||||
catch ME
|
||||
if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr')
|
||||
ME = MException('hdf5_load:h5info', ...
|
||||
strjoin({'File', filename, 'does not exist'}));
|
||||
end
|
||||
|
||||
throwAsCaller(ME);
|
||||
end
|
||||
|
||||
[data, links] = hdf5_loadGroup(filename, info);
|
||||
data = assign_links(data, info, links);
|
||||
|
||||
elseif nargin == 2
|
||||
if any(strcmp(varargin{1}, {'-a', '-ca', '-c'}))
|
||||
% second argument is an attribute flag
|
||||
try
|
||||
info = h5info(filename);
|
||||
info.Name = ''; % a special case of the root group
|
||||
|
||||
catch ME
|
||||
if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr')
|
||||
ME = MException('hdf5_load:h5info', ...
|
||||
strjoin({'File', filename, 'does not exist or is not valid h5 file'}));
|
||||
end
|
||||
|
||||
throwAsCaller(ME);
|
||||
end
|
||||
|
||||
if any(strcmp(varargin{1}, {'-a', '-ca'}))
|
||||
load_attr = true;
|
||||
end
|
||||
|
||||
if any(strcmp(varargin{1}, {'-ca', '-c'}))
|
||||
convert2matlab = true;
|
||||
end
|
||||
|
||||
[data, links] = hdf5_loadGroup(filename, info, convert2matlab, load_attr);
|
||||
data = assign_links(data, info, links, varargin{1});
|
||||
|
||||
else
|
||||
% Read a group or a single dataset
|
||||
location = varargin{1};
|
||||
try
|
||||
info = h5info(filename, location);
|
||||
if strcmp(info.Name, '/') % a special case of the root group
|
||||
info.Name = '';
|
||||
end
|
||||
|
||||
catch ME
|
||||
if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr')
|
||||
ME = MException('hdf5_load:h5info', ...
|
||||
strjoin({'File', filename, 'does not exist'}));
|
||||
|
||||
elseif strcmp(ME.identifier, 'MATLAB:imagesci:h5info:libraryError')
|
||||
ME = MException('hdf5_load:h5info', ...
|
||||
strjoin({'H5Location', location, 'was not found in', filename, 'file'}));
|
||||
end
|
||||
|
||||
throwAsCaller(ME);
|
||||
end
|
||||
|
||||
if isfield(info, 'Groups')
|
||||
% Read a group with its internal hierarchy
|
||||
[data, links] = hdf5_loadGroup(filename, info);
|
||||
data = assign_links(data, info, links);
|
||||
|
||||
elseif isfield(info, 'Datatype')
|
||||
% Read a data set
|
||||
type = info.Datatype.Class;
|
||||
data = hdf5_loadDataset(filename, location, type);
|
||||
|
||||
elseif isfield(info, 'Type')
|
||||
% Read a link
|
||||
data = hdf5_loadLink(info);
|
||||
|
||||
else
|
||||
error('hdf5_load:parse_argument', ...
|
||||
'The 2-nd argument must be a name of a group, dataset, or link');
|
||||
end
|
||||
end
|
||||
|
||||
elseif nargin == 3
|
||||
% Read attributes of a group or a data set, or slices of a data set
|
||||
location = varargin{1};
|
||||
try
|
||||
info = h5info(filename, location);
|
||||
if strcmp(info.Name, '/') % a special case of the root group
|
||||
info.Name = '';
|
||||
end
|
||||
|
||||
catch ME
|
||||
if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr')
|
||||
ME = MException('hdf5_load:h5info', ...
|
||||
strjoin({'File', filename, 'does not exist or is not HDF5 format'}));
|
||||
|
||||
elseif strcmp(ME.identifier, 'MATLAB:imagesci:h5info:libraryError')
|
||||
ME = MException('hdf5_load:h5info', ...
|
||||
strjoin({'H5Location', location, 'was not found in', filename, 'file'}));
|
||||
end
|
||||
|
||||
throwAsCaller(ME);
|
||||
end
|
||||
|
||||
if iscell(varargin{2})
|
||||
% Read slices of a data set
|
||||
slices = varargin{2};
|
||||
|
||||
% Check if the specified location is a data set
|
||||
if ~isfield(info, 'Dataspace')
|
||||
error('hdf5_load:invalid_location', ...
|
||||
'Slicing ranges are not applicable, the location is not a data set');
|
||||
end
|
||||
|
||||
data_size = info.Dataspace.Size;
|
||||
if length(slices) > length(data_size)
|
||||
error('hdf5_load:invalid_slicing', ...
|
||||
'A number of slicing ranges is larger than a dimention of a data set')
|
||||
end
|
||||
|
||||
% Parse ranges
|
||||
startIndex = ones(1, length(data_size));
|
||||
nElements = Inf(1, length(data_size));
|
||||
for i = 1:length(slices)
|
||||
[startIndex(i), nElements(i)] = parse_range(slices{i}, data_size(i));
|
||||
end
|
||||
|
||||
% Read data
|
||||
data = h5read(filename, location, startIndex, nElements);
|
||||
|
||||
elseif any(strcmp(varargin{2}, {'-a', '-ca', '-c'}))
|
||||
% Read attributes and/or convert to matlab structures
|
||||
if any(strcmp(varargin{2}, {'-a', '-ca'}))
|
||||
load_attr = true;
|
||||
end
|
||||
if any(strcmp(varargin{2}, {'-ca', '-c'}))
|
||||
convert2matlab = true;
|
||||
end
|
||||
|
||||
if isfield(info, 'Groups')
|
||||
% Read a group with its internal hierarchy
|
||||
[data, links] = hdf5_loadGroup(filename, info, convert2matlab, load_attr);
|
||||
data = assign_links(data, info, links, varargin{2});
|
||||
|
||||
elseif isfield(info, 'Datatype')
|
||||
% Read a data set
|
||||
type = info.Datatype.Class;
|
||||
dset_val = hdf5_loadDataset(filename, location, type);
|
||||
if load_attr || convert2matlab
|
||||
[dset_attr, ml_class_dset] = hdf5_loadAttributes(info, convert2matlab, load_attr);
|
||||
else
|
||||
ml_class_dset = [];
|
||||
end
|
||||
|
||||
if ~isempty(ml_class_dset)
|
||||
switch ml_class_dset
|
||||
case 'complex'
|
||||
dset_val = dset_val.r + 1i*dset_val.i;
|
||||
|
||||
case 'cell'
|
||||
if ~iscell(dset_val)
|
||||
dset_val = {dset_val};
|
||||
end
|
||||
|
||||
case 'char_array'
|
||||
dset_val = char(dset_val);
|
||||
|
||||
|
||||
otherwise
|
||||
conv2ml = str2func(ml_class_dset);
|
||||
dset_val = conv2ml(dset_val);
|
||||
end
|
||||
end
|
||||
|
||||
if load_attr
|
||||
data.Attributes = dset_attr;
|
||||
data.Value = dset_val;
|
||||
|
||||
else
|
||||
data = dset_val;
|
||||
end
|
||||
|
||||
elseif isfield(info, 'Type')
|
||||
% Read a link
|
||||
data = hdf5_loadLink(info, convert2matlab, load_attr);
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
error('hdf5_load:parse_argument', ...
|
||||
'Incorrect 3-rd argument');
|
||||
end
|
||||
end
|
||||
|
||||
function [data, links] = hdf5_loadGroup(filename, info, varargin)
|
||||
import io.HDF.*
|
||||
|
||||
if nargin > 2
|
||||
convert2matlab = varargin{1};
|
||||
load_attr = varargin{2};
|
||||
else
|
||||
convert2matlab = false;
|
||||
load_attr = false;
|
||||
end
|
||||
|
||||
data = [];
|
||||
|
||||
% Collect links
|
||||
links = info.Links.'; % transform to a row for easier indexing
|
||||
if ~isempty(links)
|
||||
for link_ind = 1:length(links)
|
||||
links(link_ind).Name = [info.Name, '/', links(link_ind).Name];
|
||||
end
|
||||
end
|
||||
|
||||
% Load the datasets
|
||||
for dataset_ind = 1:length(info.Datasets)
|
||||
dset_info = info.Datasets(dataset_ind);
|
||||
dset_name = dset_info.Name;
|
||||
location = [info.Name, '/', dset_name];
|
||||
type = dset_info.Datatype.Class;
|
||||
|
||||
dset_val = hdf5_loadDataset(filename, location, type);
|
||||
|
||||
% Load attributes of a dataset
|
||||
if load_attr || convert2matlab
|
||||
[dset_attr, ml_class_dset] = hdf5_loadAttributes(dset_info, convert2matlab, load_attr);
|
||||
else
|
||||
ml_class_dset = [];
|
||||
end
|
||||
|
||||
if ~isempty(ml_class_dset)
|
||||
switch ml_class_dset
|
||||
case 'complex'
|
||||
dset_val = dset_val.r + 1i*dset_val.i;
|
||||
|
||||
case 'cell'
|
||||
if ~iscell(dset_val)
|
||||
dset_val = {dset_val};
|
||||
end
|
||||
|
||||
case 'char_array'
|
||||
dset_val = char(dset_val);
|
||||
|
||||
otherwise
|
||||
conv2ml = str2func(ml_class_dset);
|
||||
dset_val = conv2ml(dset_val);
|
||||
end
|
||||
end
|
||||
|
||||
if load_attr
|
||||
data.(dset_name).Attributes = dset_attr;
|
||||
data.(dset_name).Value = dset_val;
|
||||
|
||||
else
|
||||
data.(dset_name) = dset_val;
|
||||
end
|
||||
end
|
||||
|
||||
% Load attributes of a group
|
||||
if load_attr || convert2matlab
|
||||
[group_attr, ml_class_group] = hdf5_loadAttributes(info, convert2matlab, load_attr);
|
||||
if load_attr
|
||||
data.Attributes = group_attr;
|
||||
end
|
||||
else
|
||||
ml_class_group = [];
|
||||
end
|
||||
|
||||
% Load the internal groups recursively
|
||||
for group_ind = 1:length(info.Groups)
|
||||
[group_data, child_links] = hdf5_loadGroup(filename, info.Groups(group_ind), convert2matlab, load_attr);
|
||||
|
||||
[~, group_name] = fileparts(info.Groups(group_ind).Name);
|
||||
data.(group_name) = group_data;
|
||||
|
||||
% Aggregate links
|
||||
links = [links, child_links]; %#ok<AGROW> There shouldn't be too many links present
|
||||
end
|
||||
|
||||
if ~isempty(ml_class_group)
|
||||
% convert the groups
|
||||
data_temp = data;
|
||||
data = [];
|
||||
if isfield(data_temp, 'Attributes')
|
||||
data.Attributes = data_temp.Attributes;
|
||||
data_temp = rmfield(data_temp, 'Attributes');
|
||||
fn = fieldnames(data_temp);
|
||||
for group_ind = 1:length(fn)
|
||||
switch ml_class_group
|
||||
case 'cell'
|
||||
data.Value{group_ind} = data_temp.(fn{group_ind});
|
||||
|
||||
case 'structure array'
|
||||
data.Value(group_ind) = data_temp.(fn{group_ind});
|
||||
|
||||
otherwise
|
||||
keyboard
|
||||
end
|
||||
end
|
||||
else
|
||||
fn = fieldnames(data_temp);
|
||||
for group_ind = 1:length(fn)
|
||||
switch ml_class_group
|
||||
case 'cell'
|
||||
data{group_ind} = data_temp.(fn{group_ind});
|
||||
|
||||
case 'structure array'
|
||||
data(group_ind) = data_temp.(fn{group_ind});
|
||||
|
||||
otherwise
|
||||
keyboard
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function [data, ml_class] = hdf5_loadAttributes(info, convert2matlab, load_attr)
|
||||
data = [];
|
||||
ml_class = [];
|
||||
if isfield(info, 'Attributes') % info structure may not contain Attributes field
|
||||
attr_info = info.Attributes;
|
||||
for attr_ind = 1:length(attr_info)
|
||||
attr = attr_info(attr_ind);
|
||||
attr_name = attr.Name;
|
||||
if ~isvarname(attr_name)
|
||||
if ~any(strcmpi({attr_info.Name}, ['MATLAB' attr_name])) && ~strcmpi(attr_name, '_class')
|
||||
warning('Invalid attribute name! Added "MATLAB" prefix to %s.', attr_name)
|
||||
attr_name = ['MATLAB' attr_name];
|
||||
else
|
||||
error('Invalid attribute name.')
|
||||
end
|
||||
end
|
||||
if convert2matlab && strcmpi(attr_name, 'MATLAB_class')
|
||||
ml_class = attr.Value{1};
|
||||
|
||||
elseif load_attr
|
||||
if iscell(attr.Value)
|
||||
data.(attr_name) = attr.Value{1};
|
||||
else
|
||||
data.(attr_name) = attr.Value;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data = hdf5_loadDataset(filename, location, type)
|
||||
if strcmp(type, 'H5T_ENUM')
|
||||
% Workaround for a bug in h5postprocessenums (part of h5read) function
|
||||
data = read_enum(filename, location);
|
||||
|
||||
else
|
||||
data = h5read(filename, location);
|
||||
if iscell(data) && numel(data) == 1 && ischar(data{1})
|
||||
data = data{1}; % utility string unwrapping from a single cell
|
||||
end
|
||||
end
|
||||
|
||||
function data = hdf5_loadLink(link, varargin)
|
||||
|
||||
if nargin > 2
|
||||
convert2matlab = varargin{1};
|
||||
load_attr = varargin{2};
|
||||
else
|
||||
convert2matlab = false;
|
||||
load_attr = false;
|
||||
end
|
||||
|
||||
switch link.Type
|
||||
case {'hard link', 'soft link'}
|
||||
filename = link.Filename;
|
||||
location = link.Value{1};
|
||||
|
||||
case 'external link'
|
||||
filename = absolute_path(link.Value{1}, link.Filename);
|
||||
location = link.Value{2};
|
||||
|
||||
otherwise
|
||||
error('hdf5_load:hdf5_loadLink', ...
|
||||
strjoin({'Unknown link type at', link.Name}));
|
||||
end
|
||||
|
||||
link_info = h5info(filename, location);
|
||||
if strcmp(link_info.Name, '/') % a special case of the root group
|
||||
link_info.Name = '';
|
||||
end
|
||||
|
||||
if isfield(link_info, 'Groups')
|
||||
[data, links] = hdf5_loadGroup(filename, link_info, convert2matlab, load_attr);
|
||||
data = assign_links(data, link_info, links);
|
||||
|
||||
elseif isfield(link_info, 'Datatype')
|
||||
type = link_info.Datatype.Class;
|
||||
data = hdf5_loadDataset(filename, location, type);
|
||||
|
||||
elseif isfield(link_info, 'Type')
|
||||
data = hdf5_loadLink(link_info, convert2matlab, load_attr);
|
||||
|
||||
else
|
||||
error('hdf5_load:hdf5_loadLink', ...
|
||||
strjoin({'A link at', link_info.Name, 'must be a name of a group, dataset, or link'}));
|
||||
end
|
||||
|
||||
function data = assign_links(data, info, links, varargin)
|
||||
import io.HDF.*
|
||||
|
||||
if ~isempty(varargin)
|
||||
flag = varargin{1};
|
||||
else
|
||||
flag = [];
|
||||
end
|
||||
if ~isempty(links)
|
||||
cut_start = length(info.Name) + 1;
|
||||
|
||||
while true
|
||||
resolved_links = false(size(links));
|
||||
|
||||
for ind = 1:length(links)
|
||||
link = links(ind);
|
||||
place = strrep(link.Name(cut_start:end), '/', '.');
|
||||
target = [];
|
||||
target_struc = [];
|
||||
if ~isempty(flag) && contains(flag, 'a') && contains(flag, 'c')
|
||||
target_struc = ['.Value'];
|
||||
end
|
||||
switch link.Type
|
||||
case {'hard link', 'soft link'}
|
||||
try
|
||||
parent = strsplit(link.Value{1}, '/');
|
||||
parent = strjoin(parent(1:end-1), '/');
|
||||
parent_info = h5info(info.Filename, parent);
|
||||
if isfield(parent_info, 'Attributes') && ~isempty(parent_info.Attributes)
|
||||
for ii=1:numel(parent_info.Attributes)
|
||||
if strcmp(parent_info.Attributes(ii).Name, 'MATLAB_class') && ~isempty(flag) && contains(flag, 'c')
|
||||
% get pointer index
|
||||
pnt_indx = strsplit(link.Value{1}, '_');
|
||||
pnt_indx = str2double(pnt_indx(end));
|
||||
target_add = [];
|
||||
switch parent_info.Attributes(ii).Value{1}
|
||||
case 'cell'
|
||||
target_add = sprintf('{%d}', pnt_indx+1);
|
||||
|
||||
case 'structure array'
|
||||
target_add = sprintf('(%d)', pnt_indx+1);
|
||||
|
||||
otherwise
|
||||
keyboard
|
||||
end
|
||||
target = [strrep(parent, '/', '.') target_struc target_add];
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if isempty(target)
|
||||
target = [strrep(link.Value{1}(cut_start:end), '/', '.') target_struc];
|
||||
end
|
||||
|
||||
evalc(['data', place, ' = data', target]);
|
||||
|
||||
catch
|
||||
continue % postpone this link resolution
|
||||
end
|
||||
|
||||
case 'external link'
|
||||
ext_link = absolute_path(link.Value{1}, info.Filename);
|
||||
|
||||
% make sure to reference the same variable in evalc!
|
||||
if ~isempty(flag)
|
||||
target_data = hdf5_load(ext_link, link.Value{2}, flag); %#ok<NASGU>
|
||||
else
|
||||
target_data = hdf5_load(ext_link, link.Value{2});
|
||||
end
|
||||
evalc(['data', place, ' = target_data']);
|
||||
|
||||
otherwise
|
||||
error('hdf5_load:assign_links', ...
|
||||
strjoin({'Unknown link type at', place}));
|
||||
end
|
||||
|
||||
resolved_links(ind) = true;
|
||||
end
|
||||
|
||||
if all(resolved_links)
|
||||
% all links have been assigned
|
||||
return
|
||||
end
|
||||
|
||||
if ~any(resolved_links)
|
||||
% none of the links has been assigned in this iteration
|
||||
error('hdf5_load:assign_links', ...
|
||||
strjoin({'Cannot assign link(s) at', ''}));
|
||||
end
|
||||
|
||||
links = links(~resolved_links);
|
||||
end
|
||||
end
|
||||
|
||||
function filepath = absolute_path(filepath, current_filepath)
|
||||
if ~startsWith(filepath, '/')
|
||||
path = fileparts(current_filepath);
|
||||
filepath = fullfile(path, filepath);
|
||||
end
|
||||
|
||||
function [startVal, nVals] = parse_range(valRange, maxVal)
|
||||
if isempty(valRange) % empty
|
||||
startVal = 1;
|
||||
nVals = Inf;
|
||||
|
||||
elseif isscalar(valRange) % single value
|
||||
if valRange <= -1
|
||||
valRange = maxVal + valRange + 1;
|
||||
end
|
||||
startVal = valRange;
|
||||
nVals = 1;
|
||||
|
||||
elseif isvector(valRange) && numel(valRange) == 2 % vector with two values
|
||||
if valRange(1) <= -1
|
||||
if isinf(valRange(1))
|
||||
valRange(1) = 1; % = -Inf
|
||||
else
|
||||
valRange(1) = maxVal + valRange(1) + 1;
|
||||
end
|
||||
end
|
||||
startVal = valRange(1);
|
||||
|
||||
if valRange(2) <= -1
|
||||
if isinf(valRange(2))
|
||||
valRange(2) = 1; % = -Inf
|
||||
else
|
||||
valRange(2) = maxVal + valRange(2) + 1;
|
||||
end
|
||||
end
|
||||
nVals = valRange(2) - startVal + 1;
|
||||
|
||||
else
|
||||
error('hdf5_load:parse_range', ...
|
||||
'A range should be specified with <= 2 parameters');
|
||||
end
|
||||
|
||||
if startVal < 1 || startVal > maxVal || nVals < 1 || (nVals > maxVal && ~isinf(nVals))
|
||||
error('hdf5_load:parse_range', ...
|
||||
'The resulting range is out of data borders');
|
||||
end
|
||||
|
||||
function data = read_enum(filename, location)
|
||||
file_id = H5F.open(filename);
|
||||
dset_id = H5D.open(file_id, location);
|
||||
type_id = H5D.get_type(dset_id);
|
||||
|
||||
data = H5D.read(dset_id); % numerical member of enumeration
|
||||
data = H5T.enum_nameof(type_id, data); % associated symbol name
|
||||
|
||||
H5T.close(type_id);
|
||||
H5D.close(dset_id);
|
||||
H5F.close(file_id);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
%HDF5_MV_DATA Move data within an HDF5 file
|
||||
% hdf5_mv_data creates a new (UNIX-like) hard link at loc_dest to the dataset at
|
||||
% loc_origin and deletes the hard link to the dataset at loc_origin.
|
||||
%
|
||||
% file... HDF filename
|
||||
% loc_origin... location of the data that needs to be moved
|
||||
% loc_dest... destination and name of the new data
|
||||
%
|
||||
% EXAMPLE:
|
||||
% % move dataset probe from root to group measurements
|
||||
% hdf5_mv_data('./awesome_file.h5', 'probe', 'measurements/probe')
|
||||
%
|
||||
% Please notice that all groups and datasets have to exist before running
|
||||
% the script!
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
function hdf5_mv_data( file, loc_origin, loc_dest)
|
||||
import io.HDF.*
|
||||
plist = 'H5P_DEFAULT';
|
||||
|
||||
fileID = H5F.open(file,'H5F_ACC_RDWR',plist);
|
||||
|
||||
gpath1 = strsplit(rm_delimiter(loc_origin), '/');
|
||||
gid1 = add_groups(fileID, gpath1(1:end-1), plist, false);
|
||||
|
||||
gpath2 = strsplit(rm_delimiter(loc_dest), '/');
|
||||
gid2 = add_groups(fileID, gpath2(1:end-1), plist, false);
|
||||
|
||||
try
|
||||
datasetID = H5D.open(gid1{end}, gpath1{end});
|
||||
dataset = true;
|
||||
catch
|
||||
gid1 = add_groups(fileID,gpath1,plist, false);
|
||||
datasetID = gid1{end};
|
||||
dataset = false;
|
||||
end
|
||||
|
||||
H5O.link(datasetID,gid2{end},gpath2{end},plist,plist);
|
||||
if dataset
|
||||
H5L.delete(gid1{end}, gpath1{end}, plist);
|
||||
else
|
||||
H5L.delete(gid1{end-1}, gpath1{end}, plist);
|
||||
end
|
||||
|
||||
H5F.close(fileID);
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
%HDF5_RM_ATTR Delete attribute(s) from HDF file
|
||||
%
|
||||
% file... HDF filename
|
||||
% loc... location within the HDF file
|
||||
% attr_name... string or cell of strings containing the names of the
|
||||
% obsolete attributes
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function hdf5_rm_attr( file, loc, attr_name)
|
||||
import io.HDF.*
|
||||
|
||||
plist = 'H5P_DEFAULT';
|
||||
% open file
|
||||
fileID = H5F.open(file,'H5F_ACC_RDWR',plist);
|
||||
|
||||
% get group ID
|
||||
gpath = strsplit(rm_delimiter(loc), '/');
|
||||
gid = add_groups(fileID, gpath(1:end-1), plist, false);
|
||||
|
||||
% delete attributes
|
||||
if iscell(attr_name)
|
||||
for ii=1:length(attr_name)
|
||||
H5A.delete(gid{end}, attr_name{ii})
|
||||
end
|
||||
else
|
||||
H5A.delete(gid{end}, attr_name)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
%HDF5_RM_DATA Delete a dataset within an HDF5 file
|
||||
%
|
||||
% file... HDF filename
|
||||
% loc... location of the dataset that needs to be removed
|
||||
%
|
||||
% Please notice that HDF5 does not free the space after removing datasets!
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function hdf5_rm_data( file, loc)
|
||||
import io.HDF.*
|
||||
plist = 'H5P_DEFAULT';
|
||||
|
||||
fileID = H5F.open(file,'H5F_ACC_RDWR',plist);
|
||||
|
||||
gpath1 = strsplit(rm_delimiter(loc), '/');
|
||||
gid1 = add_groups(fileID, gpath1(1:end-1), plist, false);
|
||||
|
||||
H5L.delete(gid1{end}, gpath1{end}, plist);
|
||||
|
||||
H5F.close(fileID);
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,143 @@
|
||||
% Call function without arguments for instructions on how to use it
|
||||
|
||||
% Filename: $RCSfile: hdf5read_main.m,v $
|
||||
%
|
||||
% $Revision: 1.1 $ $Date: 2010/10/02 07:58:50 $
|
||||
% $Author: $
|
||||
% $Tag: $
|
||||
%
|
||||
% Description:
|
||||
% Macro for reading HDF5 files written for example by the EIGER server
|
||||
% program cbd_server
|
||||
%
|
||||
% Note:
|
||||
% So far this is mainly a place holder for a thorough implementation.
|
||||
%
|
||||
% Dependencies:
|
||||
% - image_read_set_default
|
||||
% - fopen_until_exists
|
||||
% - get_hdr_val
|
||||
%
|
||||
%
|
||||
% history:
|
||||
%
|
||||
% September 30th 2010: 1st version
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function [frame,vararg_remain] = hdf5read_main(filename,varargin)
|
||||
import io.HDF.*
|
||||
import io.image_read
|
||||
import utils.fopen_until_exists
|
||||
import utils.get_hdr_val
|
||||
|
||||
% 0: no debug information
|
||||
% 1: some feedback
|
||||
% 2: a lot of information
|
||||
debug_level = 0;
|
||||
|
||||
% initialize return argument
|
||||
frame = struct('header',[], 'data',[]);
|
||||
|
||||
|
||||
% check minimum number of input arguments
|
||||
if (nargin < 1)
|
||||
image_read_sub_help(mfilename,'h5');
|
||||
error('At least the filename has to be specified as input parameter.');
|
||||
end
|
||||
|
||||
% accept cell array with name/value pairs as well
|
||||
no_of_in_arg = nargin;
|
||||
if (nargin == 2)
|
||||
if (isempty(varargin))
|
||||
% ignore empty cell array
|
||||
no_of_in_arg = no_of_in_arg -1;
|
||||
else
|
||||
if (iscell(varargin{1}))
|
||||
% use a filled one given as first and only variable parameter
|
||||
varargin = varargin{1};
|
||||
no_of_in_arg = 1 + length(varargin);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
% check number of input arguments
|
||||
if (rem(no_of_in_arg,2) ~= 1)
|
||||
error('The optional parameters have to be specified as ''name'',value pairs');
|
||||
end
|
||||
|
||||
% set default values for the variable input arguments and parse the named
|
||||
% parameters:
|
||||
vararg = cell(0,0);
|
||||
for ind = 1:2:length(varargin)
|
||||
name = varargin{ind};
|
||||
value = varargin{ind+1};
|
||||
switch name
|
||||
otherwise
|
||||
% pass further arguments on to fopen_until_exists
|
||||
vararg{end+1} = name;
|
||||
vararg{end+1} = value;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
% try to open the data file
|
||||
if (debug_level >= 1)
|
||||
fprintf('Opening %s.\n',filename);
|
||||
end
|
||||
[fid,vararg_remain] = fopen_until_exists(filename,vararg);
|
||||
if (fid < 0)
|
||||
return;
|
||||
end
|
||||
% close input data file
|
||||
fclose(fid);
|
||||
|
||||
% get file header
|
||||
hdr = hdf5info(filename);
|
||||
|
||||
% store part of the file header in the return argument
|
||||
frame.header = {};
|
||||
frame.header{end+1} = 'Exposure_time 1.0';
|
||||
% add the file modification date to the header
|
||||
dir_entry = dir(filename);
|
||||
frame.header{end+1} = [ 'DateTime ' dir_entry.date ];
|
||||
|
||||
% read all data of first data set at once
|
||||
frame.data = hdf5read(hdr.GroupHierarchy(1).Groups(1).Datasets(1));
|
||||
|
||||
if (debug_level >= 2)
|
||||
fprintf('%dx%dx%dx%s data bytes read\n',...
|
||||
size(fdat,1),size(fdat,2),size(fdat,3),size(fdat,4));
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
%RM_DELIMITER makes sure that the path does not start with /
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function path = rm_delimiter(path)
|
||||
% make sure that the path does not start with /
|
||||
if strcmp(path(1), '/')
|
||||
path = path(2:end);
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
%SAVE2HDF5 saves matlab data to a Hierarchical Data Format file (hdf5)
|
||||
%
|
||||
% filename... full path to file, including file extension
|
||||
% data... matlab structure or array or link
|
||||
% data_name... needed if input data is not a matlab structure, needs
|
||||
% to be given as name/value pair
|
||||
%
|
||||
% *optional*
|
||||
% overwrite... replace existing file if it exists
|
||||
% gpath... specify the group to which you want to append the data
|
||||
% (only if data is an array); default root ('/')
|
||||
% Attributes... structure of attributes; will be appended to current
|
||||
% gpath
|
||||
% comp... compression level; default 0 (no compression)
|
||||
% creator... attribute in root; default 'ptycho_recons'
|
||||
%
|
||||
%
|
||||
% If you want to save a structure, everything declared within an 'Attributes'
|
||||
% fieldname will be treated as an attribute to the current group.
|
||||
% If you want to add attributes to a dataset, you have to define your
|
||||
% data within .Value and your attributes within .Attributes.
|
||||
%
|
||||
% A simple structure could look like:
|
||||
% h5_struc = [];
|
||||
% h5_struc.probe_mask = ones(256,256);
|
||||
% h5_struc.Attributes.probe_id = 1;
|
||||
% h5_struc.measurement.n0.diff = fmag(:,:,1);
|
||||
% h5_struc.measurement.n0.Attributes.detector = 0;
|
||||
% h5_struc.measurement.n1.diff.Value = fmag(:,:,2);
|
||||
% h5_struc.measurement.n1.diff.Attributes.slice = 2;
|
||||
%
|
||||
% fmag(:,:,1) will be written to dataset 'diff' in group '/measurement/n0'
|
||||
% fmag(:,:,2) with attribute 'slice' will be written to dataset 'diff' in
|
||||
% group '/measurement/n1'
|
||||
%
|
||||
%
|
||||
% EXAMPLES:
|
||||
% -) if data is a matlab structure:
|
||||
% save2hdf5('./awesome_file.h5', data);
|
||||
% save2hdf5('./awesome_file.h5', data, 'overwrite', true);
|
||||
%
|
||||
%
|
||||
% -) if data is a matlab array:
|
||||
% save2hdf5('./awesome_file.h5', data, 'data_name', data_name);
|
||||
% save2hdf5('./awesome_file.h5', data, 'data_name', 'my_dataset',...
|
||||
% 'gpath', 'group1/group2', 'Attributes', attr_struc);
|
||||
%
|
||||
% -) if data is a link:
|
||||
% currently, only external links ('ext') and internal soft links
|
||||
% ('int_soft') are supported
|
||||
%
|
||||
% external links have to be specified by a single string with
|
||||
% 3 sections: '<link_type>:<file_path>:<target_object>'
|
||||
%
|
||||
% e.g.: 'ext:./awesome_file2.h5:/data'
|
||||
% save2hdf5('./awesome_file.h5',...
|
||||
% 'ext:./awesome_file2.h5:/data', 'data_name', data_name)
|
||||
%
|
||||
% will create a link called $data_name to dataset (or group) '/data'
|
||||
% in './awesome_file2.h5'
|
||||
%
|
||||
% internal links have to be specified by a single string with
|
||||
% 2 sections: '<link_type>:<target_object>'
|
||||
%
|
||||
% e.g.: 'int_soft:/data'
|
||||
% save2hdf5('./awesome_file.h5',...
|
||||
% 'int_soft:/data', 'data_name', data_name, 'gpath', 'g1/g2')
|
||||
%
|
||||
% will create a link called $data_name to dataset (or group) '/data'
|
||||
% in '/g1/g2'
|
||||
%
|
||||
%
|
||||
% Please notice that structures are not supported as attributes, i.e.
|
||||
% h5_struc = [];
|
||||
% h5_struc.attr.probe.probe_id = 1;
|
||||
%
|
||||
% save2hdf5('./awesome_file.h5', h5_struc)
|
||||
%
|
||||
% will crash!
|
||||
%
|
||||
%
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function save2hdf5( filename, data, varargin)
|
||||
import io.HDF.*
|
||||
|
||||
% take care of input arguments
|
||||
overwrite = false;
|
||||
gpath_full = '';
|
||||
attr = [];
|
||||
data_name = '';
|
||||
comp = 0;
|
||||
creator = 'ptycho_recons';
|
||||
iscopy = false;
|
||||
extend_dim = 0;
|
||||
extendable = false;
|
||||
extend_offset = 0;
|
||||
extend_maxdims = 0;
|
||||
|
||||
vararg = cell(0,0);
|
||||
% parse the variable input arguments vararg = cell(0,0);
|
||||
if ~isempty(varargin)
|
||||
for ind = 1:2:length(varargin)
|
||||
name = varargin{ind};
|
||||
value = varargin{ind+1};
|
||||
switch lower(name)
|
||||
case 'data_name'
|
||||
data_name = value;
|
||||
case 'overwrite'
|
||||
overwrite = value;
|
||||
case 'gpath'
|
||||
gpath_full = value;
|
||||
case 'attr'
|
||||
attr = value;
|
||||
case 'comp'
|
||||
comp = value;
|
||||
case 'creator'
|
||||
creator = value;
|
||||
case 'iscopy'
|
||||
iscopy = value;
|
||||
case 'extend_dim'
|
||||
extend_dim = value;
|
||||
case 'extendable'
|
||||
extendable = value;
|
||||
case 'extend_offset'
|
||||
extend_offset = value;
|
||||
case 'extend_maxdims'
|
||||
extend_maxdims = value;
|
||||
|
||||
otherwise
|
||||
vararg{end+1} = name;
|
||||
vararg{end+1} = value;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ~isstruct(data)
|
||||
full_data = false;
|
||||
else
|
||||
full_data = true;
|
||||
end
|
||||
|
||||
if ~isstruct(data) && isempty(data_name)
|
||||
data_name = inputname(2);
|
||||
if isempty(data_name)
|
||||
error('Please specify the data_name.')
|
||||
end
|
||||
end
|
||||
|
||||
if extendable && extend_dim
|
||||
error('Extending the dimension of an unlimited dataset is currently not supported.');
|
||||
end
|
||||
|
||||
plist = 'H5P_DEFAULT';
|
||||
|
||||
%%% create file if it does not exist
|
||||
if exist(filename, 'file')&&~overwrite
|
||||
fileID = H5F.open(filename,'H5F_ACC_RDWR',plist);
|
||||
else
|
||||
fileID = H5F.create(filename,'H5F_ACC_TRUNC','H5P_DEFAULT','H5P_DEFAULT');
|
||||
if ~iscopy
|
||||
write_attribute(fileID, filename, 'filename');
|
||||
write_attribute(fileID, creator,'creator');
|
||||
write_attribute(fileID, datestr(now),'file_time');
|
||||
end
|
||||
end
|
||||
|
||||
if full_data
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%% data as structure %%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
add_content(data, fileID, plist, comp, overwrite)
|
||||
|
||||
|
||||
else
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%
|
||||
%%% data as array %%%
|
||||
%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
% prepare group handles
|
||||
if ~isempty(gpath_full)
|
||||
gpath = strsplit(rm_delimiter(gpath_full), '/');
|
||||
gid = add_groups(fileID, gpath, plist, false);
|
||||
else
|
||||
gid{1} = fileID;
|
||||
end
|
||||
|
||||
% write data to file
|
||||
write_dataset(data, gid{end}, data_name, plist, comp, overwrite, [], extend_dim, extendable, extend_offset, extend_maxdims);
|
||||
|
||||
% append attributes
|
||||
if ~isempty(attr)
|
||||
attr_fn = fieldnames(attr);
|
||||
for ii=1:length(attr_fn)
|
||||
write_attribute(gid{end}, attr.(attr_fn{ii}), attr_fn{ii}, true);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
% close handles
|
||||
H5F.close(fileID);
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
%WRITE_ATTRIBUTE write attribute data_name with value data to ID gid
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function write_attribute(gid, data, data_name, varargin)
|
||||
import io.HDF.*
|
||||
|
||||
if nargin > 3
|
||||
safe = varargin{1};
|
||||
else
|
||||
safe = false;
|
||||
end
|
||||
|
||||
[datatypeID, data] = get_datatype(data);
|
||||
if ischar(data)
|
||||
% The ptycho C++ code expects strings as H5S_SCALAR, so we have to
|
||||
% convert it
|
||||
data = {data};
|
||||
filetype = H5T.copy ('H5T_FORTRAN_S1');
|
||||
H5T.set_size (filetype,'H5T_VARIABLE');
|
||||
memtype = H5T.copy ('H5T_C_S1');
|
||||
H5T.set_size (memtype, 'H5T_VARIABLE');
|
||||
space = H5S.create ('H5S_SCALAR');
|
||||
if safe
|
||||
try
|
||||
attr = H5A.create (gid, data_name, filetype, space, 'H5P_DEFAULT');
|
||||
catch
|
||||
H5A.delete(gid, data_name);
|
||||
attr = H5A.create (gid, data_name, filetype, space, 'H5P_DEFAULT');
|
||||
end
|
||||
else
|
||||
attr = H5A.create (gid, data_name, filetype, space, 'H5P_DEFAULT');
|
||||
end
|
||||
|
||||
H5A.write (attr, memtype, data);
|
||||
|
||||
elseif iscell(data)
|
||||
% If it is a cell, save it as 1D dataset
|
||||
H5T.set_size(datatypeID,'H5T_VARIABLE');
|
||||
agcv = H5ML.get_constant_value('H5S_UNLIMITED');
|
||||
dspace = H5S.create_simple(1,numel(data),agcv);
|
||||
|
||||
plist = H5P.create('H5P_ATTRIBUTE_CREATE');
|
||||
if safe
|
||||
try
|
||||
attr = H5A.create(gid,data_name,datatypeID,dspace,plist);
|
||||
catch
|
||||
H5A.delete(gid, data_name);
|
||||
attr = H5A.create(gid,data_name,datatypeID,dspace,plist);
|
||||
end
|
||||
else
|
||||
attr = H5A.create(gid,data_name,datatypeID,dspace,plist);
|
||||
end
|
||||
H5A.write(attr,'H5ML_DEFAULT',data);
|
||||
|
||||
else
|
||||
|
||||
acpl = H5P.create('H5P_ATTRIBUTE_CREATE');
|
||||
dims = size(data);
|
||||
if length(dims)>1 && dims(2)~=1
|
||||
if dims(1) == 1
|
||||
space_id = H5S.create_simple(dims(1), dims(2), []);
|
||||
else
|
||||
space_id = H5S.create_simple(dims(1), dims, []);
|
||||
end
|
||||
else
|
||||
space_id = H5S.create('H5S_SCALAR');
|
||||
end
|
||||
if safe
|
||||
try
|
||||
attr = H5A.create(gid,data_name,datatypeID,space_id,acpl);
|
||||
catch
|
||||
H5A.delete(gid, data_name);
|
||||
attr = H5A.create(gid,data_name,datatypeID,space_id,acpl);
|
||||
end
|
||||
else
|
||||
attr = H5A.create(gid,data_name,datatypeID,space_id,acpl);
|
||||
end
|
||||
|
||||
|
||||
H5A.write(attr,'H5ML_DEFAULT',data)
|
||||
|
||||
|
||||
end
|
||||
H5A.close(attr);
|
||||
end
|
||||
|
||||
@@ -0,0 +1,370 @@
|
||||
%WRITE_DATASET write dataset data_name, containing data to ID gid
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function write_dataset(data, gid, data_name, plist, varargin)
|
||||
import io.HDF.*
|
||||
|
||||
extend_data = false;
|
||||
link = false;
|
||||
link_type = '';
|
||||
cellstrdata = false;
|
||||
write_data = true;
|
||||
|
||||
if ~isempty(varargin)
|
||||
comp = varargin{1};
|
||||
else
|
||||
comp = true;
|
||||
end
|
||||
if nargin > 5
|
||||
overwrite = varargin{2};
|
||||
else
|
||||
overwrite = true;
|
||||
end
|
||||
|
||||
if nargin > 6
|
||||
data_attr = varargin{3};
|
||||
else
|
||||
data_attr = [];
|
||||
end
|
||||
|
||||
if nargin > 7
|
||||
extend_dim = varargin{4};
|
||||
else
|
||||
extend_dim = 0;
|
||||
end
|
||||
|
||||
if nargin > 8
|
||||
extendable = varargin{5};
|
||||
else
|
||||
extendable = false;
|
||||
end
|
||||
|
||||
if nargin > 9
|
||||
extend_offset = varargin{6};
|
||||
else
|
||||
extend_offset = 0;
|
||||
end
|
||||
|
||||
if nargin > 10
|
||||
extend_maxdims = varargin{7};
|
||||
else
|
||||
extend_maxdims = 0;
|
||||
end
|
||||
|
||||
[datatype, data] = get_datatype(data);
|
||||
filespaceID = [];
|
||||
|
||||
function create_dataspace()
|
||||
|
||||
if extendable
|
||||
unlimited = H5ML.get_constant_value('H5S_UNLIMITED');
|
||||
dims_max = repmat(unlimited, 1, numel(dims));
|
||||
else
|
||||
dims_max = dims;
|
||||
end
|
||||
|
||||
if ~extend_dim
|
||||
if ~extendable
|
||||
dataspaceID = H5S.create_simple(length(dims), fliplr(dims), fliplr(dims_max));
|
||||
else
|
||||
try
|
||||
datasetID = H5D.open(gid, data_name);
|
||||
filespaceID = H5D.get_space(datasetID);
|
||||
[~, spaceDims] = H5S.get_simple_extent_dims(filespaceID);
|
||||
% spaceDims = fliplr(spaceDims);
|
||||
|
||||
start = ones(1,numel(dims))-1;
|
||||
count = dims;
|
||||
|
||||
stride = ones(1, numel(start));
|
||||
boundsEnd = start + (count).*stride;
|
||||
new_dims = fliplr(boundsEnd);
|
||||
H5S.close(filespaceID);
|
||||
H5D.set_extent(datasetID,new_dims);
|
||||
filespaceID = H5D.get_space(datasetID);
|
||||
H5S.select_hyperslab(filespaceID, 'H5S_SELECT_SET', fliplr(start), fliplr(stride), ...
|
||||
fliplr(count), ones(1,length(start)));
|
||||
|
||||
dataspaceID = H5S.create_simple(numel(count),fliplr(count),[]);
|
||||
|
||||
extend_data = true;
|
||||
|
||||
catch
|
||||
dataspaceID = H5S.create_simple(length(dims), fliplr(dims), fliplr(dims_max));
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
try
|
||||
datasetID = H5D.open(gid, data_name);
|
||||
filespaceID = H5D.get_space(datasetID);
|
||||
[~, spaceDims] = H5S.get_simple_extent_dims(filespaceID);
|
||||
spaceDims = fliplr(spaceDims);
|
||||
if extend_offset
|
||||
start = [ones(1,extend_dim-1) extend_offset+1]-1;
|
||||
else
|
||||
start = [ones(1,extend_dim-1) spaceDims(end)+1]-1;
|
||||
end
|
||||
|
||||
if numel(spaceDims) > numel(dims)
|
||||
count = [dims 1];
|
||||
else
|
||||
count = dims;
|
||||
end
|
||||
stride = ones(1, numel(start));
|
||||
boundsEnd = start + (count-1).*stride;
|
||||
if extend_maxdims
|
||||
boundsStart = spaceDims;
|
||||
boundsStart(end) = extend_maxdims;
|
||||
else
|
||||
boundsStart = spaceDims;
|
||||
end
|
||||
new_dims = fliplr(max(boundsStart,boundsEnd+1));
|
||||
H5S.close(filespaceID);
|
||||
H5D.set_extent(datasetID,new_dims);
|
||||
filespaceID = H5D.get_space(datasetID);
|
||||
H5S.select_hyperslab(filespaceID, 'H5S_SELECT_SET', fliplr(start), fliplr(stride), ...
|
||||
fliplr(count), ones(1,length(start)));
|
||||
|
||||
dataspaceID = H5S.create_simple(numel(count),fliplr(count),[]);
|
||||
|
||||
extend_data = true;
|
||||
|
||||
|
||||
catch
|
||||
unlimited = H5ML.get_constant_value('H5S_UNLIMITED');
|
||||
maxdims = [dims(1:extend_dim-1) unlimited];
|
||||
% maxdims = repmat(-1, 1, extend_dim);
|
||||
if numel(maxdims) > numel(dims)
|
||||
dims = [dims 1];
|
||||
end
|
||||
dataspaceID = H5S.create_simple(length(dims), [fliplr(dims)], fliplr(maxdims));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if strcmp(datatype, 'complex')
|
||||
|
||||
%%% prepare compound dataset for complex input data
|
||||
dims = size(data);
|
||||
|
||||
data_temp = data;
|
||||
data = [];
|
||||
data.r = real(data_temp);
|
||||
data.i = imag(data_temp);
|
||||
|
||||
create_dataspace();
|
||||
|
||||
% Create the required data types
|
||||
complexType = H5T.copy(get_datatype(data.r));
|
||||
sz = H5T.get_size(complexType);
|
||||
|
||||
% Create the compound datatype for memory.
|
||||
datatypeID = H5T.create ('H5T_COMPOUND', 2*sz);
|
||||
H5T.insert (datatypeID, 'r',0, complexType);
|
||||
H5T.insert (datatypeID, 'i',sz, complexType);
|
||||
memtype = datatypeID;
|
||||
|
||||
data_attr.MATLAB_class = 'complex';
|
||||
|
||||
|
||||
elseif ischar(data)
|
||||
% check if char is a link
|
||||
ch_entrs = strsplit(data, ':');
|
||||
if length(ch_entrs) >= 2
|
||||
link = true;
|
||||
if strcmp(ch_entrs{1}, 'ext')
|
||||
% prepare external link
|
||||
link_type = 'ext';
|
||||
elseif strcmp(ch_entrs{1}, 'int_soft')
|
||||
% prepare internal soft link
|
||||
link_type = 'int_soft';
|
||||
elseif strcmp(ch_entrs{1}, 'int_hard')
|
||||
% prepare internal hard link
|
||||
link_type = 'int_hard';
|
||||
end
|
||||
else
|
||||
data = {data};
|
||||
datatypeID = H5T.copy ('H5T_FORTRAN_S1');
|
||||
H5T.set_size (datatypeID,'H5T_VARIABLE');
|
||||
memtype = H5T.copy ('H5T_C_S1');
|
||||
H5T.set_size (memtype, 'H5T_VARIABLE');
|
||||
dataspaceID = H5S.create ('H5S_SCALAR');
|
||||
|
||||
end
|
||||
|
||||
elseif iscell(data) || strcmp(datatype, 'char_array')
|
||||
|
||||
|
||||
if iscellstr(data)
|
||||
cellstrdata = true;
|
||||
datatypeID = H5T.copy ('H5T_C_S1');
|
||||
H5T.set_size (datatypeID, 'H5T_VARIABLE');
|
||||
|
||||
dgcv = H5ML.get_constant_value('H5S_UNLIMITED');
|
||||
dataspaceID = H5S.create_simple(1,numel(data),dgcv);
|
||||
memtype = datatypeID;
|
||||
plist_cr = H5P.create('H5P_DATASET_CREATE');
|
||||
H5P.set_chunk(plist_cr,1);
|
||||
if strcmp(datatype, 'char_array')
|
||||
data_attr.MATLAB_class = 'char_array';
|
||||
end
|
||||
else
|
||||
write_data = false;
|
||||
fn_names = cell(1,length(data));
|
||||
cell_gid = add_groups(gid, data_name, plist, true);
|
||||
for ii=1:length(data)
|
||||
fn_names{ii} = sprintf([data_name '_%d'],ii-1);
|
||||
write_dataset(data{ii}, cell_gid, fn_names{ii}, plist, comp, overwrite);
|
||||
end
|
||||
write_attribute(cell_gid, 'cell', 'MATLAB_class');
|
||||
|
||||
|
||||
end
|
||||
|
||||
elseif isstruct(data)
|
||||
|
||||
write_data = false;
|
||||
struct_gid = add_groups(gid, data_name, plist, true);
|
||||
add_content(data, struct_gid, plist, comp, overwrite);
|
||||
|
||||
else
|
||||
datatypeID = H5T.copy(datatype);
|
||||
dims = size(data);
|
||||
if isfield(data_attr, 'save2hdf5DataShape')
|
||||
dims = data_attr.save2hdf5DataShape;
|
||||
end
|
||||
|
||||
% prepare dataspace
|
||||
create_dataspace();
|
||||
|
||||
memtype = 'H5ML_DEFAULT';
|
||||
end
|
||||
|
||||
%%% create groups and write data
|
||||
if comp && ~iscell(data) && ~ischar(data) && write_data || extend_dim || extendable
|
||||
% define compression and chunk size
|
||||
plist_ch = H5P.create('H5P_DATASET_CREATE');
|
||||
if length(dims)>=3
|
||||
chunk_dims = [dims(1) dims(2) ones(1, numel(dims)-2)];
|
||||
else
|
||||
chunk_dims = dims;
|
||||
end
|
||||
|
||||
h5_chunk_dims = fliplr(chunk_dims);
|
||||
H5P.set_chunk(plist_ch,h5_chunk_dims);
|
||||
H5P.set_shuffle(plist_ch);
|
||||
if comp
|
||||
H5P.set_deflate(plist_ch,comp);
|
||||
end
|
||||
|
||||
% Try to create a new dataset. If it exists, try to open it.
|
||||
try
|
||||
if ~extend_data
|
||||
if cellstrdata
|
||||
datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist_cr);
|
||||
else
|
||||
datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist_ch);
|
||||
% create_dataspace();
|
||||
end
|
||||
end
|
||||
catch
|
||||
if ~overwrite
|
||||
try
|
||||
datasetID = H5D.open(gid, data_name);
|
||||
catch
|
||||
error('Could not create dataset %s! Try a different name or overwrite the already existing file.', data_name);
|
||||
end
|
||||
else
|
||||
keyboard
|
||||
error('Dataset %s already exists! Try a different name or overwrite the already existing file.', data_name);
|
||||
end
|
||||
end
|
||||
|
||||
elseif ~link && write_data
|
||||
% Same as above but without compression:
|
||||
% Try to create a new dataset. If it exists, try to open it.
|
||||
try
|
||||
if cellstrdata
|
||||
datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist_cr);
|
||||
else
|
||||
datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist);
|
||||
end
|
||||
catch
|
||||
if ~overwrite
|
||||
try
|
||||
datasetID = H5D.open(gid, data_name);
|
||||
catch
|
||||
error('Could not open dataset %s! Try a different name or overwrite the already existing file.', data_name);
|
||||
end
|
||||
else
|
||||
error('Dataset %s already exists! Try a different name or overwrite the already existing file.', data_name);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if write_data
|
||||
% write data to disk or link data
|
||||
|
||||
if ~link && ~extend_data
|
||||
H5D.write(datasetID,memtype,'H5S_ALL','H5S_ALL',plist ,data);
|
||||
% append attributes if needed
|
||||
if ~isempty(data_attr)
|
||||
fn = fieldnames(data_attr);
|
||||
for ii=1:length(fn)
|
||||
write_attribute(datasetID, data_attr.(fn{ii}), fn{ii}, true);
|
||||
end
|
||||
end
|
||||
H5D.close(datasetID);
|
||||
elseif extend_data
|
||||
H5D.write(datasetID,memtype,dataspaceID, filespaceID, plist, data)
|
||||
elseif strcmp(link_type, 'ext')
|
||||
H5L.create_external(ch_entrs{2},ch_entrs{3},gid,data_name,plist,plist);
|
||||
elseif strcmp(link_type, 'int_hard')
|
||||
error('Currently not supported, sorry!')
|
||||
% H5L.create_hard(ch_entrs{2},'g3',gid1,'g4',plist,plist);
|
||||
elseif strcmp(link_type, 'int_soft')
|
||||
H5L.create_soft(ch_entrs{2},gid,data_name,plist,plist);
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user