0001 function [SNRmean, SE, debug] = calc_image_SNR(imdl, hyperparameter, doPlot)
0002 
0003 
0004 
0005 
0006 
0007 
0008 
0009 
0010 
0011 
0012 
0013 
0014 
0015 
0016 
0017 
0018 
0019 
0020 
0021 
0022 
0023 
0024 
0025 
0026 
0027 
0028 
0029 
0030 
0031 
0032 
0033 
0034 
0035 
0036 
0037 
0038 
0039 
0040 
0041 
0042 
0043 
0044 
0045 
0046 
0047 
0048 
0049 citeme(mfilename);
0050 
0051 
0052 
0053 ROI_SCALING_FACTOR = 0.5; 
0054 
0055 N_DESIRED_TARGETS = 200;
0056 
0057 NORM_TGT_RADIUS = 0.05; 
0058 
0059 TEZ_REGION_THRESH = 0.25;  
0060 
0061 
0062 
0063 if ~exist('doPlot', 'var') || isempty(doPlot)
0064     doPlot = false;
0065 end
0066 if isfield(imdl.hyperparameter, 'roi_scaling_factor') && ~isempty(imdl.hyperparameter.roi_scaling_factor)
0067     ROI_SCALING_FACTOR = imdl.hyperparameter.roi_scaling_factor;
0068 end
0069 assert(ROI_SCALING_FACTOR < 1, 'ROI must be scaled smaller than effective model');
0070 if isfield(imdl.hyperparameter, 'n_targets') && ~isempty(imdl.hyperparameter.n_targets)
0071     N_DESIRED_TARGETS = imdl.hyperparameter.n_targets;
0072 end
0073 if isfield(imdl.hyperparameter, 'target_radius') && ~isempty(imdl.hyperparameter.target_radius)
0074     NORM_TGT_RADIUS = imdl.hyperparameter.target_radius;
0075 end
0076 if isfield(imdl.hyperparameter, 'xyzr_targets') && ~isempty(imdl.hyperparameter.xyzr_targets)
0077     xyzr_targets = imdl.hyperparameter.xyzr_targets;
0078 else
0079     xyzr_targets = [];
0080 end
0081 
0082 
0083 
0084 if nargin>=2 && numel(hyperparameter) == 1 && ~isempty(hyperparameter)
0085     imdl.hyperparameter.value = hyperparameter;
0086     
0087     try; imdl.hyperparameter = rmfield(imdl.hyperparameter,'func'); end
0088 end
0089 
0090 
0091 
0092 if isfield(imdl, 'rec_model')
0093     RecMdl = imdl.rec_model;   
0094 else
0095     RecMdl = imdl.fwd_model;
0096 end
0097 MdlCtr = mean(RecMdl.nodes,1);
0098 
0099 if isempty(xyzr_targets)
0100     
0101     
0102     
0103     
0104 
0105     
0106     if isfield(RecMdl, 'mdl_slice_mapper') && isfield(RecMdl.mdl_slice_mapper, 'level')
0107         ElectrodeLevel = RecMdl.mdl_slice_mapper.level;    
0108     elseif isfield(RecMdl, 'mdl_slice_mapper') && isfield(RecMdl.mdl_slice_mapper, 'z_pts')
0109         ElectrodeLevel = RecMdl.mdl_slice_mapper.z_pts;    
0110         error('3D not supported yet, please seed targets manually via xyzr_targets');
0111     else
0112         if isfield(RecMdl.electrode(1), 'pos')
0113             elec_loc = cell2mat(cellfun(@(x) mean(x)', {RecMdl.electrode.pos}, 'uniformoutput', false))';
0114         elseif isfield(RecMdl.electrode(1), 'nodes')
0115             for i=1:length(RecMdl.electrode)
0116                enodesi = RecMdl.electrode(i).nodes;
0117                elec_loc(i,:) = mean( RecMdl.nodes( enodesi,:),1 );
0118             end  
0119         else
0120             error('not supported!');
0121         end
0122         ElectrodeLevel = mean(elec_loc,1);
0123     end
0124     
0125     
0126     
0127     
0128     
0129     RecMdlShrunk = RecMdl;
0130     RecMdlShrunk.nodes = RecMdlShrunk.nodes - repmat(MdlCtr, size(RecMdlShrunk.nodes, 1), 1);
0131     RecMdlShrunk.nodes = RecMdlShrunk.nodes * ROI_SCALING_FACTOR;
0132     RecMdlShrunk.nodes = RecMdlShrunk.nodes + repmat(MdlCtr, size(RecMdlShrunk.nodes, 1), 1);
0133 
0134     
0135     BoundaryNodes = find_boundary(RecMdlShrunk);
0136     
0137     
0138     Boundary = order_loop(RecMdlShrunk.nodes(BoundaryNodes, :));
0139     Boundary = [Boundary; Boundary(1,:)];
0140     
0141 
0142     
0143     AreaPoly = polyarea(Boundary(:,1), Boundary(:,2));
0144     Bounds = [max(Boundary); min(Boundary)];
0145     AreaRect = prod([Bounds(1,:) - Bounds(2,:)]);
0146     nUniformTgts = AreaRect * (N_DESIRED_TARGETS / AreaPoly);    
0147 
0148     
0149     RoiSize = abs(diff(Bounds,[], 1));
0150     
0151     ScaleX = RoiSize(1) / RoiSize(2);
0152     ScaleY = 1./ScaleX;                 
0153     
0154     [xx, yy] = meshgrid(linspace(Bounds(2,1), Bounds(1,1), ceil(ScaleX * sqrt(nUniformTgts))), ...
0155                         linspace(Bounds(2,2), Bounds(1,2), ceil(ScaleY * sqrt(nUniformTgts))));
0156     IsInside = inpolygon(xx(:), yy(:), Boundary(:,1), Boundary(:,2));
0157     nTgts = sum(IsInside);
0158     assert(abs((nTgts - N_DESIRED_TARGETS)/N_DESIRED_TARGETS) < 0.15, 'Cannot make desired number of targets');
0159     xx = xx(IsInside);
0160     yy = yy(IsInside);    
0161     zz = ones(size(xx))*ElectrodeLevel(3);
0162 else
0163     
0164     xx = xyzr_targets(1,:)';
0165     yy = xyzr_targets(2,:)';
0166     zz = xyzr_targets(3,:)';
0167 end
0168 
0169 
0170 BoundsFull = [max(RecMdl.nodes); min(RecMdl.nodes)];
0171 Rmodel = (max(BoundsFull(1,:) - BoundsFull(2,:)))/2;    
0172 Rtarget = Rmodel * NORM_TGT_RADIUS;
0173 rr = ones(size(xx))*Rtarget;
0174 
0175 if ~isempty(xyzr_targets)
0176     try
0177         rr = xyzr_targets(4,:)';    
0178     end
0179 end
0180     
0181 
0182 img = mk_image(imdl.fwd_model, 1);
0183 if elem_dim(imdl.fwd_model) == 3
0184     xyzr = [xx yy zz rr]';  
0185 elseif elem_dim(imdl.fwd_model) == 2
0186     xyzr = [xx yy rr]';    
0187 else
0188     error('unsupported dimensions');
0189 end
0190 [vh, vi, xyzrOut, c2f] = simulate_movement(img, xyzr);
0191 NotAssigned = ~ismember(xyzr', xyzrOut', 'rows');
0192 assert(sum(NotAssigned) == 0, 'Error: target(s) got missing...');
0193 vd = vi - repmat(vh, 1, size(vi,2));
0194 
0195 
0196 
0197 RM = get_RM(imdl);
0198 
0199 RecMdlVols = get_elem_volume(RecMdl);
0200 
0201 
0202 
0203 
0204 
0205 
0206 imgrs = RM*vd;
0207 imgrsNorm = imgrs;
0208 imgrsNorm = imgrsNorm ./ repmat(max(imgrsNorm, [], 1), size(imgrsNorm,1), 1);
0209 TEZs = double((imgrsNorm > TEZ_REGION_THRESH));
0210 
0211 clear imgrsNorm;
0212 
0213 ElemVols = spdiags(RecMdlVols, 0, length(RecMdlVols), length(RecMdlVols));
0214   
0215 
0216 
0217 Z = ElemVols * TEZs;
0218 Vtez = sum(Z,1);                
0219 Vt = 4/3 * pi * rr.^3;          
0220 
0221 
0222 K = Vtez ./ Vt';                
0223 
0224 Ztilde = Z .* repmat(K, size(Z,1), 1);
0225 
0226 
0227 TEZsNonNorm = Z; 
0228 
0229 TEZs = TEZs ./ repmat(sum(TEZs,1), size(TEZs,1), 1);  
0230 
0231 
0232 
0233 
0234 if isfield(imdl.hyperparameter, 'SigmaN') && ~isempty(imdl.hyperparameter.SigmaN)
0235     SigmaN = imdl.hyperparameter.SigmaN;    
0236 else
0237     SigmaN = speye(size(RM,2));        
0238 end
0239 
0240 Signal = diag(Ztilde' * imgrs);    
0241 VarPerPixel = diag(RM*SigmaN*RM');      
0242 Noise = sqrt(Z' * VarPerPixel);      
0243 
0244 
0245 SNRs = Signal ./ Noise;                 
0246 SNRmean = mean(SNRs); 
0247 SE = std(SNRs);
0248 
0249 if isnan(SNRmean)
0250     SNRmean = -inf;
0251 elseif isinf(SNRmean)
0252     keyboard;
0253 end
0254 
0255 try
0256   eidors_msg('SNR = %e (hp=%e)', SNRmean, imdl.hyperparameter.value, 1);
0257 end
0258 
0259 
0260 
0261 if nargout >= 3
0262     
0263     ArInFrac = diag((TEZsNonNorm'*abs(RM*vd).^2) ./ (repmat(RecMdlVols, 1, size(TEZs,2))'*abs(RM*vd).^2));    
0264     debug.ArInFrac = ArInFrac;
0265     
0266     debug.SNRs = SNRs;
0267     debug.Signal = Signal;
0268     debug.Noise = Noise;
0269     debug.TEZs = TEZs;
0270     debug.TEZsNoNorm = TEZsNonNorm;
0271     
0272     debug.RoiBounds = Bounds;
0273     debug.RoiBoundary = Boundary;
0274     debug.MdlCtr = MdlCtr;
0275     debug.BoundsFull = BoundsFull;
0276     
0277     imgrsNorm = imgrs;
0278     imgrsNorm = imgrsNorm ./ repmat(max(imgrsNorm, [], 1), size(imgrsNorm,1), 1);
0279     debug.meanInNormImg = diag(TEZs'*imgrsNorm);
0280 end
0281 
0282 
0283 
0284 if doPlot
0285     fig = figure;
0286     set(fig, 'position', [  182         700        1531         313]);
0287     
0288     if isfield(imdl.fwd_model, 'coarse2fine')
0289         MapTgts2Img = imdl.fwd_model.coarse2fine'*c2f;
0290     else
0291         try
0292             imdl.fwd_model.coarse2fine = mk_coarse_fine_mapping(imdl.fwd_model, imdl.rec_model);
0293             MapTgts2Img = imdl.fwd_model.coarse2fine'*c2f;
0294         catch
0295             warning('Unable to make c2f mapping');
0296             MapTgts2Img = c2f;
0297         end
0298     end
0299     MapTgts2Img = MapTgts2Img ./ repmat(sum(MapTgts2Img,2), 1, size(MapTgts2Img, 2));
0300     MapTgts2Img(isnan(MapTgts2Img(:))) = 0;
0301     
0302     img = mk_image(RecMdl, nan);
0303     img.elem_data = MapTgts2Img * SNRs;
0304     SnrImg = calc_slices(img);
0305     
0306     img.elem_data = nan(size(img.elem_data));
0307     img.elem_data = MapTgts2Img * Signal;
0308     AmpSens = img.elem_data;
0309     AmpSensImg = calc_slices(img);
0310     
0311     img.elem_data = nan(size(img.elem_data));
0312     img.elem_data = MapTgts2Img * Noise;
0313     NoiseSens = img.elem_data;
0314     NoiseSensImg = calc_slices(img);
0315     
0316     sp1 = subplot(131);
0317     imagescnan(SnrImg);
0318     title(['SNR image: ', num2str(SNRmean, '%0.2d')]);
0319     colorbar; colormap jet;
0320 
0321     
0322     sp2 = subplot(132);
0323     imagescnan(AmpSensImg);
0324     title(['Amplitude response: ', num2str(nanmean(AmpSens(:)), '%0.2d')]);
0325     colorbar;
0326 
0327     
0328     sp3 = subplot(133);
0329     imagescnan(NoiseSensImg);
0330     title(['Noise sensitivity: ', num2str(nanmean(NoiseSens(:)), '%0.2d')]);
0331     colorbar;
0332 
0333     if doPlot < 2
0334         linkaxes([sp1 sp2 sp3], 'xy');
0335     end
0336    
0337     if doPlot == 2
0338        
0339        sp1 = subplot(131);
0340        imgTmp = img;
0341        iTgt = round(size(vd,2)/2);
0342        imgTmp.elem_data = RM*vd(:,iTgt);
0343        hh = show_fem(imgTmp);
0344        set(hh, 'edgecolor', 'none');
0345        hold on; 
0346        circle(xyzr(1:2, iTgt), xyzr(3, iTgt), 100, 'k');
0347        circle(xyzr(1:2, iTgt), Rtarget, 100, 'k');
0348        axis equal;
0349     end
0350     
0351     
0352     if doPlot == 3
0353        sp3 = subplot(133); 
0354        cla;
0355        hh = show_fem(RecMdl);
0356        set(hh, 'edgecolor', 'none')
0357        hold on;
0358        plot(Boundary(:,1), Boundary(:,2), '.-k');
0359        plot(xyzr(1,:), xyzr(2,:), '.r');
0360        plot(xyzr(1,:), xyzr(2,:), 'or');
0361     end
0362     
0363     if doPlot == 4
0364         
0365        sp2 = subplot(132);
0366        cla;
0367        imgN = mk_image(imdl,0);
0368        imgN.elem_data = VarPerPixel;
0369        imgN.elem_data(sum(MapTgts2Img,2) == 0) = 0;
0370        imagescnan(calc_slices(imgN));
0371        title(['Pixel-wise NoiseSens: ', num2str(nanmean(imgN.elem_data(:)), '%0.2d')]);
0372        colorbar;        
0373     end
0374     
0375     if doPlot == 5
0376         sp1 = subplot(131);
0377         hist(SNRs); 
0378         xlim([0 max(xlim())]);
0379     end
0380     
0381 end
0382 
0383 end
0384 
0385