From 21156f8869481a06f945b541de1332731a495787 Mon Sep 17 00:00:00 2001 From: Nicola Vigano <nicola.vigano@esrf.fr> Date: Wed, 17 Feb 2016 16:26:29 +0100 Subject: [PATCH] ODF Solvers: round of improvements, and added real-space sampling to uvw solver Signed-off-by: Nicola Vigano <nicola.vigano@esrf.fr> --- zUtil_Deformation/GtGrainODFuvwSolver.m | 235 ++++++++++++------ zUtil_Deformation/GtGrainODFwSolver.m | 133 ++++------ .../gtDefComputeGrainODFNearField.m | 2 +- 3 files changed, 205 insertions(+), 165 deletions(-) diff --git a/zUtil_Deformation/GtGrainODFuvwSolver.m b/zUtil_Deformation/GtGrainODFuvwSolver.m index f0fad513..b1351326 100644 --- a/zUtil_Deformation/GtGrainODFuvwSolver.m +++ b/zUtil_Deformation/GtGrainODFuvwSolver.m @@ -1,22 +1,26 @@ classdef GtGrainODFuvwSolver < GtGrainODFwSolver properties + voxel_centers = []; end methods (Access = public) - function self = GtGrainODFuvwSolver(parameters) - self = self@GtGrainODFwSolver(parameters); + function self = GtGrainODFuvwSolver(parameters, varargin) + self = self@GtGrainODFwSolver(parameters, varargin{:}); end end methods (Access = public) % Low Level API - function build_sinogram(self, bls, oversize) + function build_sinogram(self) + bls = self.sampler.bl(self.sampler.selected); + num_blobs = numel(bls); blob_dephs = arrayfun(@(x)size(x.intm, 3), bls); blob_dephs = reshape(blob_dephs, [], 1); - num_ws = max(blob_dephs) + 2; - if (exist('oversize', 'var')) - num_ws = round(num_ws * oversize); - end + + delta_omegas = self.sampler.get_omega_deviations(); + chosen_depts = max(blob_dephs, delta_omegas(self.sampler.selected)); + + num_ws = max(chosen_depts) + 2; self.size_sino = [size(bls(1).intm, 1), size(bls(1).intm, 2), num_ws, num_blobs]; self.sino = zeros(self.size_sino); @@ -29,22 +33,41 @@ classdef GtGrainODFuvwSolver < GtGrainODFwSolver self.sino(:, :, ints_interval, ii_b) = masked_blob; end self.sino = reshape(self.sino, [], 1); + + if (self.verbose) + fprintf('Sino size: [%d, %d, %d, %d]\n', self.size_sino); + end end - function deg_res = guess_resolution(self, ref_gr, sampler) - deg_res_omega = guess_resolution@GtGrainODFwSolver(self, ref_gr, sampler); + function chose_voxel_centers(self, over_sampling) + if (~exist('over_sampling', 'var') || isempty(over_sampling)) + over_sampling = 1; + end + + recgeo = self.parameters.recgeo(self.sampler.detector_index); + ref_gr = self.sampler.get_reference_grain(); + proj = ref_gr.proj(self.sampler.detector_index); + r_space_vol_size = [proj.vol_size_y, proj.vol_size_x, proj.vol_size_z]; + + max_dists_from_center = (r_space_vol_size - 1) / 2; + num_voxels = prod(r_space_vol_size); + + self.voxel_centers = zeros(num_voxels, 3); + + counter = 1; - bls = sampler.bl(sampler.selected); + for ss_x = linspace(-max_dists_from_center(1), max_dists_from_center(1), r_space_vol_size(1)) + for ss_y = linspace(-max_dists_from_center(2), max_dists_from_center(2), r_space_vol_size(2)) + for ss_z = linspace(-max_dists_from_center(3), max_dists_from_center(3), r_space_vol_size(3)) - bl_centers = [sum(cat(1, bls(:).bbuim), 2), sum(cat(1, bls(:).bbvim), 2)] / 2; - gc_det_pos = gtGeoGrainCenterSam2Det(ref_gr.center, ref_gr.allblobs.omega, self.parameters); - dists = sqrt(sum((bl_centers - gc_det_pos) .^ 2, 2)); + offset_voxel = [ss_x, ss_y, ss_z] .* recgeo.voxsize; - % Higest resolution in ETA - deg_res_eta = min(2 .* atan2d(0.5, dists)); + self.voxel_centers(counter, :) = ref_gr.center + offset_voxel; - % We ake the lowest resolution between the two - deg_res = max(deg_res_eta, deg_res_omega); + counter = counter + 1; + end + end + end end function build_projection_matrices(self, bls, bl_selected) @@ -53,12 +76,10 @@ classdef GtGrainODFuvwSolver < GtGrainODFwSolver else om_step = self.parameters.labgeo.omstep; end - fprintf('Computing projection matrices..') + fprintf('Computing projection matrices: ') c = tic(); num_ws = self.get_num_ws(); - num_orients = numel(self.grid_gr); - bls_bbws = cat(1, bls(:).bbwim); min_w_conds = bls_bbws(:, 1) - self.pre_paddings + 1; max_w_conds = min_w_conds + num_ws - 1; @@ -66,87 +87,139 @@ classdef GtGrainODFuvwSolver < GtGrainODFwSolver bls_bbus = cat(1, bls(:).bbuim); bls_bbvs = cat(1, bls(:).bbvim); - b_us = []; - b_vs = []; - b_ws = []; - b_cs = []; - b_is = []; - b_os = []; - - for ii_o = 1:num_orients - ab = self.grid_gr{ii_o}.allblobs; - % W part - ws = ab.omega(bl_selected) / om_step; - - min_ws = floor(ws); - max_ws = min_ws + 1; - - max_w_cs = ws - min_ws; - min_w_cs = 1 - max_w_cs; - - % UV part - if (isfield(ab, 'detector')) - uv = ab.detector(1).uvw(bl_selected, 1:2); - else - uv = ab.uvw(bl_selected, 1:2); - end - min_uvs = floor(uv); - max_uvs = min_uvs + 1; + grid_gr = self.sampler.get_orientations(); + num_orients = numel(grid_gr); + + det_ind = self.sampler.detector_index; + + % We make now the approximation that by moving in XYZ inside the + % sample, the projection moves like if the incidence was always + % perpendicular. In reality, given small tilts of the detector or + % small scattering angles, this is hardly violated. + self.chose_voxel_centers(); + + num_voxels = size(self.voxel_centers, 1); + self.S = cell(num_voxels, 1); + self.St = cell(num_voxels, 1); + + shift_grs = cell(num_voxels, 1); + ref_gr = self.sampler.get_reference_grain(); + + % computing shifts + for ii_v = 1:num_voxels + shift_grs{ii_v} = ref_gr; + shift_grs{ii_v}.center = self.voxel_centers(ii_v, :); + end + ref_omind = ref_gr.allblobs.omind; + ref_inc = self.sampler.ondet(self.sampler.included); + shift_grs = gtCalculateGrain_p(shift_grs, self.parameters, ... + 'ref_omind', ref_omind, 'included', ref_inc); + + ref_uv = ref_gr.allblobs.detector(det_ind).uvw(ref_inc(bl_selected), 1:2); + + for ii_v = 1:num_voxels + b_us = []; + b_vs = []; + b_ws = []; + b_cs = []; + b_is = []; + b_os = []; + + shift_uv = shift_grs{ii_v}.allblobs.detector(det_ind).uvw(bl_selected, 1:2) - ref_uv; + + num_chars = fprintf('%03d/%03d', ii_v, num_voxels); + + for ii_o = 1:num_orients + ab = grid_gr{ii_o}.allblobs; + % W part + ws = ab.omega(bl_selected) / om_step; - max_uv_cs = uv - min_uvs; - min_uv_cs = 1 - max_uv_cs; + min_ws = floor(ws); + max_ws = min_ws + 1; - % Acceptance criteria - ok_w_mins = (min_ws >= min_w_conds) & (min_ws <= max_w_conds); - ok_w_maxs = (max_ws >= min_w_conds) & (max_ws <= max_w_conds) & (max_w_cs > eps('single')); + max_w_cs = ws - min_ws; + min_w_cs = 1 - max_w_cs; - ok_u_mins = (min_uvs(:, 1) >= bls_bbus(:, 1)) & (min_uvs(:, 1) <= bls_bbus(:, 2)); - ok_u_maxs = (max_uvs(:, 1) >= bls_bbus(:, 1)) & (max_uvs(:, 1) <= bls_bbus(:, 2)) & (max_uv_cs(:, 1) > eps('single')); + % UV part + uv = ab.detector(det_ind).uvw(bl_selected, 1:2) + shift_uv; - ok_v_mins = (min_uvs(:, 2) >= bls_bbvs(:, 1)) & (min_uvs(:, 2) <= bls_bbvs(:, 2)); - ok_v_maxs = (max_uvs(:, 2) >= bls_bbvs(:, 1)) & (max_uvs(:, 2) <= bls_bbvs(:, 2)) & (max_uv_cs(:, 2) > eps('single')); + min_uvs = floor(uv); + max_uvs = min_uvs + 1; - u_oks = [ok_u_mins, ok_u_maxs]; - v_oks = [ok_v_mins, ok_v_maxs]; - w_oks = [ok_w_mins, ok_w_maxs]; + max_uv_cs = uv - min_uvs; + min_uv_cs = 1 - max_uv_cs; - u_cs = [min_uv_cs(:, 1), max_uv_cs(:, 1)]; - v_cs = [min_uv_cs(:, 2), max_uv_cs(:, 2)]; - w_cs = [min_w_cs, max_w_cs]; + % Acceptance criteria + ok_w_mins = (min_ws >= min_w_conds) & (min_ws <= max_w_conds); + ok_w_maxs = (max_ws >= min_w_conds) & (max_ws <= max_w_conds) & (max_w_cs > eps('single')); - pos_us = [(min_uvs(:, 1) - bls_bbus(:, 1) + 1), (max_uvs(:, 1) - bls_bbus(:, 1) + 1)]; - pos_vs = [(min_uvs(:, 2) - bls_bbvs(:, 1) + 1), (max_uvs(:, 2) - bls_bbvs(:, 1) + 1)]; - pos_ws = [(min_ws - bls_bbws(:, 1) + self.pre_paddings), (max_ws - bls_bbws(:, 1) + self.pre_paddings)]; + ok_u_mins = (min_uvs(:, 1) >= bls_bbus(:, 1)) & (min_uvs(:, 1) <= bls_bbus(:, 2)); + ok_u_maxs = (max_uvs(:, 1) >= bls_bbus(:, 1)) & (max_uvs(:, 1) <= bls_bbus(:, 2)) & (max_uv_cs(:, 1) > eps('single')); - for u_ii = 1:2 - for v_ii = 1:2 - for w_ii = 1:2 - indx = find(u_oks(:, u_ii) & v_oks(:, v_ii) & w_oks(:, w_ii)); - b_us = [b_us; pos_us(indx, u_ii)]; - b_vs = [b_vs; pos_vs(indx, v_ii)]; - b_ws = [b_ws; pos_ws(indx, w_ii)]; + ok_v_mins = (min_uvs(:, 2) >= bls_bbvs(:, 1)) & (min_uvs(:, 2) <= bls_bbvs(:, 2)); + ok_v_maxs = (max_uvs(:, 2) >= bls_bbvs(:, 1)) & (max_uvs(:, 2) <= bls_bbvs(:, 2)) & (max_uv_cs(:, 2) > eps('single')); - b_cs = [b_cs; u_cs(indx, u_ii) .* v_cs(indx, v_ii) .* w_cs(indx, w_ii)]; + u_oks = [ok_u_mins, ok_u_maxs]; + v_oks = [ok_v_mins, ok_v_maxs]; + w_oks = [ok_w_mins, ok_w_maxs]; - b_is = [b_is; indx]; - b_os = [b_os; ii_o(ones(numel(indx), 1))]; + u_cs = [min_uv_cs(:, 1), max_uv_cs(:, 1)]; + v_cs = [min_uv_cs(:, 2), max_uv_cs(:, 2)]; + w_cs = [min_w_cs, max_w_cs]; + + pos_us = [(min_uvs(:, 1) - bls_bbus(:, 1) + 1), (max_uvs(:, 1) - bls_bbus(:, 1) + 1)]; + pos_vs = [(min_uvs(:, 2) - bls_bbvs(:, 1) + 1), (max_uvs(:, 2) - bls_bbvs(:, 1) + 1)]; + pos_ws = [(min_ws - bls_bbws(:, 1) + self.pre_paddings), (max_ws - bls_bbws(:, 1) + self.pre_paddings)]; + + for u_ii = 1:2 + for v_ii = 1:2 + for w_ii = 1:2 + indx = find(u_oks(:, u_ii) & v_oks(:, v_ii) & w_oks(:, w_ii)); + b_us = [b_us; pos_us(indx, u_ii)]; + b_vs = [b_vs; pos_vs(indx, v_ii)]; + b_ws = [b_ws; pos_ws(indx, w_ii)]; + + b_cs = [b_cs; u_cs(indx, u_ii) .* v_cs(indx, v_ii) .* w_cs(indx, w_ii)]; + + b_is = [b_is; indx]; + b_os = [b_os; ii_o(ones(numel(indx), 1))]; + end end end end - end - sino_indx = sub2ind(self.size_sino, b_us, b_vs, b_ws, b_is); + sino_indx = sub2ind(self.size_sino, b_us, b_vs, b_ws, b_is); - self.S = sparse( ... - sino_indx, b_os, b_cs, ... - numel(self.sino), num_orients); + self.S{ii_v} = sparse( ... + sino_indx, b_os, b_cs, ... + numel(self.sino), num_orients); - self.St = self.S'; - fprintf('\b\b: Done in %f seconds.\n', toc(c)); + self.St{ii_v} = self.S{ii_v}'; + + fprintf(repmat('\b', [1 num_chars])); + end + fprintf('Done in %f seconds.\n', toc(c)); + end + + function vol = get_volume(self) + vol = reshape(sum(self.volume, 2), self.size_volume); end end methods (Access = protected) + function y = fp(self, x) + y = self.S{1} * x(:, 1); + for ii_s = 2:numel(self.S) + y = y + self.S{ii_s} * x(:, ii_s); + end + end + + function y = bp(self, x) + for ii_s = numel(self.St):-1:1 + y(:, ii_s) = self.St{ii_s} * x; + end + end + function num_ws = get_num_ws(self) num_ws = self.size_sino(3); end diff --git a/zUtil_Deformation/GtGrainODFwSolver.m b/zUtil_Deformation/GtGrainODFwSolver.m index c70710b7..54d632d0 100644 --- a/zUtil_Deformation/GtGrainODFwSolver.m +++ b/zUtil_Deformation/GtGrainODFwSolver.m @@ -2,7 +2,7 @@ classdef GtGrainODFwSolver < handle properties parameters; - grid_gr; + sampler; volume = []; size_volume = []; @@ -29,48 +29,37 @@ classdef GtGrainODFwSolver < handle self = parse_pv_pairs(self, varargin); end - function vol = solve_synthetic(self, ref_gr, gvdm, algorithm, lambda) - sampler = self.build_orientation_sampling_synthetic(ref_gr, 1.3); - bls = sampler.bl(sampler.selected); - - self.build_sinogram(bls, 1.1); - self.build_projection_matrices(bls, sampler.selected); + function vol = solve(self, ref_gr, varargin) + % FUNCTION vol = solve(self, ref_gr, varargin) + % + % INPUT (varargin): + % 'algorithm': 'sirt' + % 'lambda': 1e-2 + % 'ospace_oversize': 1.1 + % 'det_index': 1 + % + + conf = struct( ... + 'algorithm', 'sirt', ... + 'lambda', 1e-2, ... + 'ospace_oversize', 1.1, ... + 'det_index', 1 ); + conf = parse_pv_pairs(conf, varargin); + + self.build_orientation_sampling(ref_gr, conf.ospace_oversize, conf.det_index); + bls = self.sampler.bl(self.sampler.selected); + + self.build_sinogram(); + self.build_projection_matrices(bls, self.sampler.selected); self.build_projection_weights(); - if (~exist('algorithm', 'var')) - algorithm = 'cplsnn'; - end - switch(lower(algorithm)) - case 'sirt' - self.solve_sirt(); - case 'cplsnn' - self.solve_cplsnn(); - case 'cplsl1nn' - self.solve_cplsl1nn(lambda); - case 'cpl1nn' - self.solve_cpl1nn(); - end - vol = self.get_volume(); - end - - function vol = solve_experimental(self, ref_gr, algorithm, lambda) - sampler = self.build_orientation_sampling_experimental(ref_gr, 1.05); - bls = sampler.bl(sampler.selected); - - self.build_sinogram(bls, 1.2); - self.build_projection_matrices(bls, sampler.selected); - self.build_projection_weights(); - - if (~exist('algorithm', 'var')) - algorithm = 'cplsnn'; - end - switch(lower(algorithm)) + switch(lower(conf.algorithm)) case 'sirt' self.solve_sirt(); case 'cplsnn' self.solve_cplsnn(); case 'cplsl1nn' - self.solve_cplsl1nn(lambda); + self.solve_cplsl1nn(conf.lambda); case 'cpl1nn' self.solve_cpl1nn(); end @@ -79,53 +68,24 @@ classdef GtGrainODFwSolver < handle end methods (Access = public) % Low Level API - function sampler = build_orientation_sampling_synthetic(self, ref_gr, oversize) - sampler = GtOrientationSampling(self.parameters, ref_gr, 'verbose', self.verbose); - sampler.make_grid_estim_ODF_resoluion('cubic', true, oversize); - self.grid_gr = sampler.get_orientations(); - self.size_volume = size(sampler.lattice.gr); + function build_orientation_sampling(self, ref_gr, oversize, det_index) + self.sampler = GtOrientationSampling(self.parameters, ref_gr, ... + 'verbose', self.verbose, 'detector_index', det_index); + self.sampler.make_grid_estim_ODF_resoluion('cubic', 0, oversize); + self.size_volume = size(self.sampler.lattice.gr); end - function sampler = build_orientation_sampling_experimental(self, ref_gr, oversize) - sampler = GtOrientationSampling(self.parameters, ref_gr, 'verbose', self.verbose); - sampler.make_grid_estim_ODF_resoluion('cubic', true, oversize); - self.grid_gr = sampler.get_orientations(); - self.size_volume = size(sampler.lattice.gr); - end - - function deg_res = guess_resolution(self, ref_gr, sampler) - if (~isfield(self.parameters.labgeo, 'omstep')) - omega_step = gtGetOmegaStepDeg(self.parameters); - else - omega_step = self.parameters.labgeo.omstep; - end - - included = sampler.ondet(sampler.included); - selected = included(sampler.selected); - selected_etas = ref_gr.allblobs.eta(selected); - inv_lorentz = abs(cosd(selected_etas)); - - % Gives the highest resolution in OMEGA - deg_res = min(omega_step .* inv_lorentz); - end + function build_sinogram(self) + bls = self.sampler.bl(self.sampler.selected); - function build_sinogram(self, bls, oversize) num_blobs = numel(bls); -% % Should be worked on! -% ints_w = arrayfun(@(x){squeeze(sum(sum(x.intm, 1), 2))}, bls); -% real_blob_limits = cellfun(@(x){... -% [max(find(x, 1, 'first'), 1), ... -% min(find(x, 1, 'last'), numel(x))]... -% }, ints_w); -% real_blob_limits = cat(1, real_blob_limits{:}); -% -% blob_dephs = real_blob_limits(:, 2) - real_blob_limits(:, 1) + 1; blob_dephs = arrayfun(@(x)size(x.intm, 3), bls); blob_dephs = reshape(blob_dephs, [], 1); - num_ws = max(blob_dephs) + 2; - if (exist('oversize', 'var')) - num_ws = round(num_ws * oversize); - end + + delta_omegas = self.sampler.get_omega_deviations(); + chosen_depts = max(blob_dephs, delta_omegas(self.sampler.selected)); + + num_ws = max(chosen_depts) + 2; self.sino = zeros(num_ws, num_blobs); self.size_sino = size(self.sino); @@ -138,6 +98,10 @@ classdef GtGrainODFwSolver < handle self.sino(ints_interval, ii_b) = squeeze(sum(sum(masked_blob, 1), 2)); end self.sino = reshape(self.sino, [], 1); + + if (self.verbose) + fprintf('Sino size: [%d, %d]\n', self.size_sino); + end end function sino = get_sinogram(self) @@ -146,7 +110,7 @@ classdef GtGrainODFwSolver < handle function comp_sino = get_computed_sinogram(self) if (~isempty(self.volume)) - comp_sino = self.S * self.volume; + comp_sino = self.fp(self.volume); comp_sino = reshape(comp_sino, self.size_sino); else error('GtGrainODFSolver:no_reconstruction', ... @@ -159,11 +123,13 @@ classdef GtGrainODFwSolver < handle end function or = get_orientations(self) - or = reshape(self.grid_gr, self.size_volume); + grid_gr = self.sampler.get_orientations(); + or = reshape(grid_gr, self.size_volume); end function r_vecs = get_R_vectors(self) - r_vecs = [self.grid_gr{:}]; + grid_gr = self.sampler.get_orientations(); + r_vecs = [grid_gr{:}]; r_vecs = cat(1, r_vecs(:).R_vector); end @@ -177,8 +143,6 @@ classdef GtGrainODFwSolver < handle c = tic(); num_ws = self.get_num_ws(); - num_orients = numel(self.grid_gr); - bls_bbws = cat(1, bls(:).bbwim); min_conds = bls_bbws(:, 1) - self.pre_paddings + 1; max_conds = min_conds + num_ws - 1; @@ -188,8 +152,11 @@ classdef GtGrainODFwSolver < handle b_is = []; b_os = []; + grid_gr = self.sampler.get_orientations(); + num_orients = numel(grid_gr); + for ii_o = 1:num_orients - ab = self.grid_gr{ii_o}.allblobs; + ab = grid_gr{ii_o}.allblobs; ws = ab.omega(bl_selected) / om_step; min_ws = floor(ws); diff --git a/zUtil_Deformation/gtDefComputeGrainODFNearField.m b/zUtil_Deformation/gtDefComputeGrainODFNearField.m index ac0e4b22..032c78ec 100644 --- a/zUtil_Deformation/gtDefComputeGrainODFNearField.m +++ b/zUtil_Deformation/gtDefComputeGrainODFNearField.m @@ -16,7 +16,7 @@ function [odfw, odfw_R_vectors] = gtDefComputeGrainODFNearField(phase_id, grain_ sprintf('phase_%02d', phase_id)); gr = gtLoadGrain(phase_id, grain_id); - odfw = sol.solve_experimental(gr); + odfw = sol.solve(gr); odfw_R_vectors = sol.get_R_vectors(); -- GitLab