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 5112 2015-06-14 13:00:41Z aadler $
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 end
0055 img = tmp;
0056 end
0057 
0058 function img = pack_params(img);
0059 
0060 flds = fieldnames(img);
0061 prms = ismember(flds, supported_params);
0062 
0063 switch sum(prms)
0064    case 0
0065       % no params found, check for elem_data or node_data
0066       if ~( isfield(img,'elem_data') || isfield(img,'node_data'))
0067          error('No params, elem_data or node_data found on img');
0068       else
0069          if isfield(img,'current_params') && ~isempty(img.current_params)
0070                 eidors_msg('@@@ Careful! Image already mapped. Doing nothing',4);
0071            return;
0072          end
0073 
0074          eidors_msg('@@@ No params present on img. Assuming conductivity',4);
0075 % STUPID MATLAB CAN'T KEEP SYNTAX STRAIGHT BETWEEN VERSIONS
0076 %        img = setfield(img,{},'current_params', 'conductivity');
0077 % NEED TO DO THIS, UGLY INEFFICIENT CODE
0078          for i=1:length(img)
0079             img(i).current_params = 'conductivity';
0080          end
0081       end
0082       return      % returns img
0083    case 1
0084       ph = find(prms);
0085       if isfield(img.(flds{ph}),'func')
0086          % let the function handle the whole process
0087          img = feval(img.(flds{ph}).func, img);
0088          return
0089       else
0090          img = pack_params_using_params_str(img, flds{ph});
0091       end
0092    otherwise
0093       % multiple params
0094       try
0095          prms_fun = img.data_mapper;
0096       catch
0097          error('Multiple params found on img require: img.data_mapper = function or {"string","array"}');
0098       end
0099       if ~isa(prms_fun, 'function_handle') && ismember(prms_fun,flds);
0100          img = pack_params_using_params_str(img, prms_fun); % assume a string array
0101       else
0102          try
0103             img = feval(prms_fun,img);
0104          catch
0105             error('img.data_mapper not understood');
0106          end
0107       end
0108 
0109 end
0110 end
0111 
0112 function img = pack_params_using_params_str(img, prms)
0113 
0114 prms_flds = fieldnames(img.(prms));
0115 for i = 1:length(prms_flds)
0116    if isfield(img, prms_flds{i})
0117       eidors_msg('@@@ Overwriting img.%s',prms_flds{i},4);
0118       warning('EIDORS:OverwritingData', 'Overwriting img.%s',prms_flds{i});
0119    end
0120    % allow elem_data/node_data to be scalar
0121    if strcmp(prms_flds{i},'elem_data') && numel(img.(prms).elem_data) == 1
0122       n_elem = calc_num_elems(img.fwd_model);
0123       img.elem_data = ones(n_elem,1);
0124       img.elem_data(:) = img.(prms).elem_data;
0125    elseif strcmp(prms_flds{i},'node_data')
0126       img.node_data = ones(size(img.fwd_model.nodes,1),1);
0127       img.node_data(:) = img.(prms).node_data;
0128    else
0129       img.(prms_flds{i}) = img.(prms).(prms_flds{i});
0130    end
0131 end
0132 img.current_params = prms;
0133 end
0134 
0135 function n = calc_num_elems(fmdl)
0136 if isfield(fmdl, 'coarse2fine')
0137    n = size(fmdl.coarse2fine,2);
0138 else
0139    n = length(fmdl.elems);
0140 end
0141 end
0142 
0143 function img = copy_data_to_params(img, prms)
0144 prms_flds = fieldnames(img.(prms));
0145 try
0146    for i = 1:length(prms_flds)
0147       img.(prms).(prms_flds{i}) = img.(prms_flds{i});
0148       img = rmfield(img, prms_flds{i});
0149    end
0150    img = rmfield(img, 'current_params');
0151 catch
0152    error('Fields specified by img.%s missing from img.',prms);
0153 end
0154 end
0155 
0156 function img = unpack_params(img)
0157 try
0158    curprms = img.current_params;
0159 catch
0160    if isfield(img,'elem_data') || isfield(img, 'node_data')
0161       return % old type image, nothing to do
0162    else
0163       error('img.current_params required');
0164    end
0165 end
0166 if ismember(curprms, fieldnames(img))
0167    img = copy_data_to_params(img,curprms);
0168 elseif strcmp(curprms,'conductivity')
0169    % current_params is conductivity, but there's no img.conductivity
0170    if ~any(ismember(fieldnames(img),supported_params))
0171       % we're dealing with an old params-oblivious image
0172       % nothing to do
0173       img = rmfield(img, 'current_params');
0174    else
0175       error('Cannot reverse %s mapping',curprms);
0176    end
0177 else
0178    try
0179       % user-provided data_mapper must provide the reverse
0180       img = feval(curprms,img,1);
0181    catch
0182       error('data_mapper %s failed to reverse', curprms);
0183    end
0184 end
0185 end
0186 
0187 function pass = do_unit_test
0188    pass = 1;
0189    pass = do_unit_test_legacy() && pass;
0190    pass = do_unit_test_undo()   && pass;
0191    disp('');
0192    if pass
0193       disp('TEST: overall PASS');
0194    else
0195       error('TEST: overall FAIL');
0196    end
0197 end
0198 
0199 function pass = do_unit_test_legacy
0200    pass = 1;
0201    ll = eidors_msg('log_level');
0202    eidors_msg('log_level',3);
0203 
0204    imdl = mk_common_model('a2c2',8);
0205    c = 3*ones(length(imdl.fwd_model.elems),1);
0206    img = eidors_obj('image','test_image','fwd_model',imdl.fwd_model);
0207    img.conductivity.elem_data = c;
0208 
0209    fprintf('TEST: mv img.conductivity.elem_data to img.elem_data\n');
0210    imgi = data_mapper(img, 'pack');
0211    if ~isfield(imgi, 'elem_data')
0212      imgi
0213      disp('TEST FAIL: missing elem_data');
0214      pass = 0;
0215    end
0216 
0217    imgi2 = data_mapper(img, 'pack'); % give a msg at log_level 3
0218 
0219    imgii = data_mapper(imgi,'unpack'); % unpack
0220    if ~isequaln(imgii, img)
0221       img
0222       imgii
0223       fprintf('TEST FAIL: pack to unpack returned different result\n');
0224       pass = 0;
0225    end
0226 
0227    img.resistivity.elem_data = 1./c;
0228    img.data_mapper = 'resistivity';
0229    img = data_mapper(img);
0230    if abs(img.elem_data(1) - 1/3) > 1e-4
0231       img.elem_data(1)
0232       pass = 0;
0233       disp('TEST FAIL: expected img.elem_data to be ~1/3');
0234    end
0235 
0236    img.data_mapper = @unit_test_passthrough; % some function that takes an image
0237    imgf = data_mapper(img);
0238    % if this works without failing, then we are happy
0239    if ~isequaln(imgf, img)
0240       img
0241       imgf
0242       fprintf('TEST FAIL: pass-through function failed\n');
0243       pass = 0;
0244    end
0245 
0246    eidors_msg('log_level',ll);
0247    if ~pass
0248       disp('TEST: ---------------------------------------');
0249    end
0250 end
0251 
0252 function img = unit_test_passthrough(img1,rev)
0253   img = img1;
0254 end
0255 
0256 function pass = do_unit_test_undo
0257    pass = 1;
0258 
0259    imdl = mk_common_model('c2c', 16);
0260    img = mk_image(imdl);
0261    d = img.elem_data;
0262    img0 = rmfield(img, 'elem_data');
0263 
0264    disp('TEST: img.elem_data');
0265    img = img0; img.elem_data = d;
0266    pass = test_undo(img, d, 'elem_data') && pass;
0267 
0268    disp('TEST: img.node_data');
0269    img = img0; img.node_data = d;
0270    pass = test_undo(img, d, 'node_data') && pass;
0271 
0272    disp('TEST: img.conductivity.elem_data');
0273    img = img0; img.conductivity.elem_data = d;
0274    pass = test_undo(img, d, 'elem_data') && pass;
0275 % TODO AB below here blows up
0276 %   disp('TEST: img.conductivity.node_data');
0277 %   img = img0; img.conductivity.node_data = d;
0278 %   pass = test_undo(img, d) && pass;
0279 
0280 %   disp('TEST: img.resistivity.node_data');
0281 %   img = img0; img.resistivity.node_data = d;
0282 %   pass = test_undo(img, d) && pass;
0283 
0284 %   disp('TEST: img.conductivity');
0285 %   img = img0; img.conductivity = d; img.params_mapper = 'conductivity';
0286 %   pass = test_undo(img, d) && pass;
0287 end
0288 
0289 % img, d=data, dl=data location (once packed)
0290 function pass = test_undo(img, d, dl)
0291    pass = 1;
0292    imgp = data_mapper(img);
0293    imgup = data_mapper(imgp,'unpack');
0294    if ~isequaln(img, imgup)
0295       img
0296       imgp
0297       imgup
0298       disp('TEST FAIL: the initial image and the image after being packed, then unpacked differ');
0299       pass = 0;
0300    end
0301    if ~isequaln(imgp.(dl), d)
0302       disp(['TEST FAIL: the packed data imgp.' dl ' seems broken']);
0303       pass = 0;
0304    end
0305    if ~pass
0306       disp('TEST: ---------------------------------------');
0307    end
0308 end

Generated on Fri 30-Dec-2022 19:44:54 by m2html © 2005