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