data_mapper

PURPOSE ^

DATA_MAPPER maps img.params data to elem or node data

SYNOPSIS ^

function img = data_mapper(img, reverse)

DESCRIPTION ^

DATA_MAPPER maps img.params data to elem or node data
 img = data_mapper(img) will only work if there is only a single
 params data field on the img, throwing an error otherwise.
 For multi-parametrization images, img.data_mapper must be specified. It
 can be either a name of the appropriate parametrization data field on the img, or
 a function that will handle the data differently.

 To avoid confusion, the parametrization currently in use by image data will be
 specified in a descriptive string tag img.current_params

 img = data_mapper(img, TRUE) will reverse the process updating
 the current parametrization with the data from img.elem_data or img.node_data.

 Examples:
  imdl = mk_common_model('a2c2',8);
  c = 3*ones(length(imdl.fwd_model.elems),1);
  img = eidors_obj('image','fwd_model',imdl.fwd_model);
  img.conductivity.elem_data = c;
  img = data_mapper(img);

  img.resistivity.elem_data = 1./c;
  img.data_mapper = 'resistivity';
  img = data_mapper(img);

  img.data_mapper = 'some_function_that_takes_an_image';
  img = data_mapper(img);

 KNOWN ISSUES:
   - doesn't fully support image arrays

 See also: MK_IMAGE, SUPPORTED_PARAMS

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function img = data_mapper(img, reverse)
0002 %DATA_MAPPER maps img.params data to elem or node data
0003 % img = data_mapper(img) will only work if there is only a single
0004 % params data field on the img, throwing an error otherwise.
0005 % For multi-parametrization images, img.data_mapper must be specified. It
0006 % can be either a name of the appropriate parametrization data field on the img, or
0007 % a function that will handle the data differently.
0008 %
0009 % To avoid confusion, the parametrization currently in use by image data will be
0010 % specified in a descriptive string tag img.current_params
0011 %
0012 % img = data_mapper(img, TRUE) will reverse the process updating
0013 % the current parametrization with the data from img.elem_data or img.node_data.
0014 %
0015 % Examples:
0016 %  imdl = mk_common_model('a2c2',8);
0017 %  c = 3*ones(length(imdl.fwd_model.elems),1);
0018 %  img = eidors_obj('image','fwd_model',imdl.fwd_model);
0019 %  img.conductivity.elem_data = c;
0020 %  img = data_mapper(img);
0021 %
0022 %  img.resistivity.elem_data = 1./c;
0023 %  img.data_mapper = 'resistivity';
0024 %  img = data_mapper(img);
0025 %
0026 %  img.data_mapper = 'some_function_that_takes_an_image';
0027 %  img = data_mapper(img);
0028 %
0029 % KNOWN ISSUES:
0030 %   - doesn't fully support image arrays
0031 %
0032 % See also: MK_IMAGE, SUPPORTED_PARAMS
0033 
0034 % (C) 2012 Bartlomiej Grychtol.
0035 % Licenced under GPL version 2 or 3
0036 % $Id: data_mapper.m 6926 2024-05-31 15:34:13Z bgrychtol $
0037 
0038 if ischar(img) && strcmp(img,'UNIT_TEST'); img = do_unit_test; return; end
0039 
0040 if nargin < 2
0041    reverse = false;
0042 elseif ischar(reverse) && strcmpi(reverse, 'pack')
0043    reverse = false;
0044 else % 'unpack'
0045    reverse = true;
0046 end
0047 % need to handle image arrays
0048 for i = 1:length(img)
0049    if reverse
0050       tmp(i) = unpack_params(img(i));
0051    else
0052       tmp(i) = pack_params(img(i));
0053    end
0054    tmp(i) = eidors_obj('image',tmp(i));
0055 end
0056 img = tmp;
0057 end
0058 
0059 function img = pack_params(img);
0060 
0061 flds = fieldnames(img);
0062 prms = ismember(flds, supported_params);
0063 
0064 switch sum(prms)
0065    case 0
0066       % no params found, check for elem_data or node_data
0067       if ~( isfield(img,'elem_data') || isfield(img,'node_data'))
0068          error('No params, elem_data or node_data found on img');
0069       else
0070          if isfield(img,'current_params') && ~isempty(img.current_params)
0071                 eidors_msg('@@@ Careful! Image already mapped. Doing nothing',4);
0072            return;
0073          end
0074 
0075          eidors_msg('@@@ No params present on img. Assuming conductivity',4);
0076 % STUPID MATLAB CAN'T KEEP SYNTAX STRAIGHT BETWEEN VERSIONS
0077 %        img = setfield(img,{},'current_params', 'conductivity');
0078 % NEED TO DO THIS, UGLY INEFFICIENT CODE
0079          for i=1:length(img)
0080             img(i).current_params = 'conductivity';
0081          end
0082       end
0083       return      % returns img
0084    case 1
0085       ph = find(prms);
0086       if isfield(img.(flds{ph}),'func')
0087          % let the function handle the whole process
0088          img = feval(img.(flds{ph}).func, img);
0089          return
0090       else
0091          img = pack_params_using_params_str(img, flds{ph});
0092       end
0093    otherwise
0094       % multiple params
0095       try
0096          prms_fun = img.data_mapper;
0097       catch
0098          error('Multiple params found on img require: img.data_mapper = function or {"string","array"}');
0099       end
0100       if ~isa(prms_fun, 'function_handle') && ismember(prms_fun,flds);
0101          img = pack_params_using_params_str(img, prms_fun); % assume a string array
0102       else
0103          try
0104             img = feval(prms_fun,img);
0105          catch
0106             error('img.data_mapper not understood');
0107          end
0108       end
0109 
0110 end
0111 end
0112 
0113 function img = pack_params_using_params_str(img, prms)
0114 
0115 prms_flds = fieldnames(img.(prms));
0116 for i = 1:length(prms_flds)
0117    if isfield(img, prms_flds{i})
0118       eidors_msg('@@@ Overwriting img.%s',prms_flds{i},4);
0119       warning('EIDORS:OverwritingData', 'Overwriting img.%s',prms_flds{i});
0120    end
0121    % allow elem_data/node_data to be scalar
0122    if strcmp(prms_flds{i},'elem_data') && numel(img.(prms).elem_data) == 1
0123       n_elem = calc_num_elems(img.fwd_model);
0124       img.elem_data = ones(n_elem,1);
0125       img.elem_data(:) = img.(prms).elem_data;
0126    elseif strcmp(prms_flds{i},'node_data')
0127       img.node_data = ones(size(img.fwd_model.nodes,1),1);
0128       img.node_data(:) = img.(prms).node_data;
0129    else
0130       img.(prms_flds{i}) = img.(prms).(prms_flds{i});
0131    end
0132 end
0133 img.current_params = prms;
0134 end
0135 
0136 function n = calc_num_elems(fmdl)
0137 if isfield(fmdl, 'coarse2fine')
0138    n = size(fmdl.coarse2fine,2);
0139 else
0140    n = length(fmdl.elems);
0141 end
0142 end
0143 
0144 function img = copy_data_to_params(img, prms)
0145 prms_flds = fieldnames(img.(prms));
0146 try
0147    for i = 1:length(prms_flds)
0148       img.(prms).(prms_flds{i}) = img.(prms_flds{i});
0149       img = rmfield(img, prms_flds{i});
0150    end
0151    img = rmfield(img, 'current_params');
0152 catch
0153    error('Fields specified by img.%s missing from img.',prms);
0154 end
0155 end
0156 
0157 function img = unpack_params(img)
0158 try
0159    curprms = img.current_params;
0160 catch
0161    if isfield(img,'elem_data') || isfield(img, 'node_data')
0162       return % old type image, nothing to do
0163    else
0164       error('img.current_params required');
0165    end
0166 end
0167 if ismember(curprms, fieldnames(img))
0168    img = copy_data_to_params(img,curprms);
0169 elseif strcmp(curprms,'conductivity')
0170    % current_params is conductivity, but there's no img.conductivity
0171    if ~any(ismember(fieldnames(img),supported_params))
0172       % we're dealing with an old params-oblivious image
0173       % nothing to do
0174       img = rmfield(img, 'current_params');
0175    else
0176       error('Cannot reverse %s mapping',curprms);
0177    end
0178 else
0179    try
0180       % user-provided data_mapper must provide the reverse
0181       img = feval(curprms,img,1);
0182    catch
0183       error('data_mapper %s failed to reverse', curprms);
0184    end
0185 end
0186 end
0187 
0188 function pass = do_unit_test
0189    pass = 1;
0190    pass = do_unit_test_legacy() && pass;
0191    pass = do_unit_test_undo()   && pass;
0192    disp('');
0193    if pass
0194       disp('TEST: overall PASS');
0195    else
0196       error('TEST: overall FAIL');
0197    end
0198 end
0199 
0200 function pass = do_unit_test_legacy
0201    pass = 1;
0202    ll = eidors_msg('log_level');
0203    eidors_msg('log_level',3);
0204 
0205    imdl = mk_common_model('a2c2',8);
0206    c = 3*ones(length(imdl.fwd_model.elems),1);
0207    img = eidors_obj('image','test_image','fwd_model',imdl.fwd_model);
0208    img.conductivity.elem_data = c;
0209 
0210    fprintf('TEST: mv img.conductivity.elem_data to img.elem_data\n');
0211    imgi = data_mapper(img, 'pack');
0212    if ~isfield(imgi, 'elem_data')
0213      imgi
0214      disp('TEST FAIL: missing elem_data');
0215      pass = 0;
0216    end
0217 
0218    imgi2 = data_mapper(img, 'pack'); % give a msg at log_level 3
0219 
0220    imgii = data_mapper(imgi,'unpack'); % unpack
0221    if ~isequaln(imgii, img)
0222       img
0223       imgii
0224       fprintf('TEST FAIL: pack to unpack returned different result\n');
0225       pass = 0;
0226    end
0227 
0228    img.resistivity.elem_data = 1./c;
0229    img.data_mapper = 'resistivity';
0230    img = data_mapper(img);
0231    if abs(img.elem_data(1) - 1/3) > 1e-4
0232       img.elem_data(1)
0233       pass = 0;
0234       disp('TEST FAIL: expected img.elem_data to be ~1/3');
0235    end
0236 
0237    img.data_mapper = @unit_test_passthrough; % some function that takes an image
0238    imgf = data_mapper(img);
0239    % if this works without failing, then we are happy
0240    if ~isequaln(imgf, img)
0241       img
0242       imgf
0243       fprintf('TEST FAIL: pass-through function failed\n');
0244       pass = 0;
0245    end
0246 
0247    eidors_msg('log_level',ll);
0248    if ~pass
0249       disp('TEST: ---------------------------------------');
0250    end
0251 end
0252 
0253 function img = unit_test_passthrough(img1,rev)
0254   img = img1;
0255 end
0256 
0257 function pass = do_unit_test_undo
0258    pass = 1;
0259 
0260    imdl = mk_common_model('c2c', 16);
0261    img = mk_image(imdl);
0262    d = img.elem_data;
0263    img0 = rmfield(img, 'elem_data');
0264 
0265    disp('TEST: img.elem_data');
0266    img = img0; img.elem_data = d;
0267    pass = test_undo(img, d, 'elem_data') && pass;
0268 
0269    disp('TEST: img.node_data');
0270    img = img0; img.node_data = d;
0271    pass = test_undo(img, d, 'node_data') && pass;
0272 
0273    disp('TEST: img.conductivity.elem_data');
0274    img = img0; img.conductivity.elem_data = d;
0275    pass = test_undo(img, d, 'elem_data') && pass;
0276 % TODO AB below here blows up
0277 %   disp('TEST: img.conductivity.node_data');
0278 %   img = img0; img.conductivity.node_data = d;
0279 %   pass = test_undo(img, d) && pass;
0280 
0281 %   disp('TEST: img.resistivity.node_data');
0282 %   img = img0; img.resistivity.node_data = d;
0283 %   pass = test_undo(img, d) && pass;
0284 
0285 %   disp('TEST: img.conductivity');
0286 %   img = img0; img.conductivity = d; img.params_mapper = 'conductivity';
0287 %   pass = test_undo(img, d) && pass;
0288 end
0289 
0290 % img, d=data, dl=data location (once packed)
0291 function pass = test_undo(img, d, dl)
0292    pass = 1;
0293    imgp = data_mapper(img);
0294    imgup = data_mapper(imgp,'unpack');
0295    if ~isequaln(img, imgup)
0296       img
0297       imgp
0298       imgup
0299       disp('TEST FAIL: the initial image and the image after being packed, then unpacked differ');
0300       pass = 0;
0301    end
0302    if ~isequaln(imgp.(dl), d)
0303       disp(['TEST FAIL: the packed data imgp.' dl ' seems broken']);
0304       pass = 0;
0305    end
0306    if ~pass
0307       disp('TEST: ---------------------------------------');
0308    end
0309 end

Generated on Sun 29-Dec-2024 11:41:59 by m2html © 2005