Skip to content
Snippets Groups Projects
Gt6DVolumeToBlobProjector.m 12.9 KiB
Newer Older
classdef Gt6DVolumeToBlobProjector < Gt6DVolumeProjector
    properties
        offsets = {};
        ss_offsets = {};
        blobs_depths = [];

        % Weights
        forwardWeights = {};
        backwardWeights = {};

        blob_sinogram_c_functions = true;
    end

    methods (Access = public)
        function self = Gt6DVolumeToBlobProjector(initialVolumes, detector_size, blobs_depths, varargin)
            self = self@Gt6DVolumeProjector(initialVolumes, detector_size, varargin{:});

            self.blobs_depths = blobs_depths;

            self.statistics.add_task('fproj_create_sum', 'FP Create Blob time');
            self.statistics.add_task('bproj_create_sum', 'BP Create Blob time');

            self.statistics.add_task('fproj_permute_sum', 'FP Permute Blob time');
            self.statistics.add_task('bproj_permute_sum', 'BP Permute Blob time');

            self.statistics.add_task('fproj_permute_ss_single', 'SS FP Permute Blob time (single)');
            self.statistics.add_task('bproj_permute_ss_single', 'SS BP Permute Blob time (single)');

            self.statistics.add_task('fproj_pre_weight_sum', 'FP Pre-weight time');
            self.statistics.add_task('bproj_pre_weight_sum', 'BP Pre-weight time');

            self.statistics.add_task('fproj_post_weight_sum', 'FP Post-weight time');
            self.statistics.add_task('bproj_post_weight_sum', 'BP Post-weight time');
        end

        function backwardWeights = getColumnsSum(self)
            numBlobs = numel(self.blobs_depths);
            blobs = cell(1, numBlobs);
            for m = 1:numBlobs
                blobs{m} = ones(self.proj_size(1), ...
                    self.blobs_depths(m), self.proj_size(2));
            end

            backwardWeights = self.backprojectVolume(blobs);
        end
    end

    methods (Access = protected)
        function blobs = projectVolume(self, volumes)
        % We get volumes in input and we want the blobs as output, but the
        % output from the base function is stacks of sinograms relative to
        % the geometry of the volumes, so we need to actually decompose the
        % output and recompose the slices into blobs

            sinograms = self.projectVolume@Gt6DVolumeProjector(volumes);

            numBlobs = numel(self.blobs_depths);
            self.statistics.tic('fproj_create_sum');
            if (self.blob_sinogram_c_functions)
                blobs_sizes = cell(1, numBlobs);
                for m = 1:numBlobs
                    blobs_sizes{m} = [self.proj_size(1), ...
                        self.blobs_depths(m), self.proj_size(2)];
                end
            else
                blobs = cell(1, numBlobs);
                for m = 1:numBlobs
                    blobs{m} = zeros(self.proj_size(1), ...
                        self.blobs_depths(m), self.proj_size(2), 'single');
                end
            end
            self.statistics.toc('fproj_create_sum');

            self.statistics.tic('fproj_permute_sum');
            if (self.blob_sinogram_c_functions)
                blobs = internal_cell_sinograms_to_blobs(blobs_sizes, sinograms, self.offsets);
            else
                try
                    numSinograms = numel(sinograms);
                    for n = 1:numSinograms
                        sino_offsets = self.offsets{n}.sino_offsets(:);
                        blob_offsets = self.offsets{n}.blob_offsets(:);
                        proj_offsets = self.offsets{n}.proj_offsets(:);
                        proj_coeffs = self.offsets{n}.proj_coeffs(:);
                        for m = 1:numel(proj_offsets)
                            p = proj_offsets(m);
                            b = blob_offsets(m);
                            c = proj_coeffs(m);
                            s = sino_offsets(m);
                            blobs{b}(:, p, :) = ...
                                blobs{b}(:, p, :) + c * sinograms{n}(:, s, :);
                        end
                    end
                catch mexc
                    fprintf('\n\nError!!\n')
                    fprintf('  indices: b %d, p %d, n %d, s %d, m %d\n', ...
                        b, p, n, s, m)
                    fprintf('  num blobs %d, num sinos %d\n', ...
                        numel(blobs), numSinograms)
                    fprintf('  size blob (%d %d %d), size sino (%d %d %d)\n\n', ...
                        size(blobs{b}), size(sinograms{n}))
                    fprintf('  Limits over p: [%d, %d]\n', ...
                        min(proj_offsets), max(proj_offsets))
                    fprintf('  Limits over s: [%d, %d]\n\n', ...
                        min(sino_offsets), max(sino_offsets))
                    rethrow(mexc)
                end
            end
            self.statistics.toc('fproj_permute_sum');

            if (~isempty(self.ss_offsets))
                blobs = self.projectVolumeSS(blobs, volumes);
            end
        end

        function blobs = projectVolumeSS(self, blobs, volumes)
        % We get volumes in input and we want the blobs as output, but the
        % output from the base function is stacks of sinograms relative to
        % the geometry of the volumes, so we need to actually decompose the
        % output and recompose the slices into blobs

            num_ss_geoms = numel(self.ss_geometries);
            for n = 1:num_ss_geoms
                ss_sino = self.projectSingleVolumeSS(volumes, n);

                self.statistics.tic('fproj_permute_ss_single');
                if (self.blob_sinogram_c_functions)
                    coeffs = self.ss_offsets{n};
                    blobs = internal_cell_single_sinogram_to_blobs(blobs, ss_sino, coeffs);
                else
                    try
                        sino_offsets = self.ss_offsets{n}.sino_offsets(:);
                        blob_offsets = self.ss_offsets{n}.blob_offsets(:);
                        proj_offsets = self.ss_offsets{n}.proj_offsets(:);
                        proj_coeffs = self.ss_offsets{n}.proj_coeffs(:);
                        for m = 1:numel(proj_offsets)
                            p = proj_offsets(m);
                            b = blob_offsets(m);
                            c = proj_coeffs(m);
                            s = sino_offsets(m);
                            blobs{b}(:, p, :) = ...
                                blobs{b}(:, p, :) + c * ss_sino(:, s, :);
                        end
                    catch mexc
                        fprintf('\n\nError!!\n')
                        fprintf('  indices: b %d, p %d, n %d, s %d, m %d\n', ...
                            b, p, n, s, m)
                        fprintf('  num blobs %d, num sinos %d\n', ...
                            numel(blobs), numel(self.ss_offsets))
                        fprintf('  size blob (%d %d %d), size sino (%d %d %d)\n\n', ...
                            size(blobs{b}), size(ss_sino))
                        fprintf('  Limits over p: [%d, %d]\n', ...
                            min(proj_offsets), max(proj_offsets))
                        fprintf('  Limits over s: [%d, %d]\n\n', ...
                            min(sino_offsets), max(sino_offsets))
                        rethrow(mexc)
                    end
                end
                self.statistics.toc('fproj_permute_ss_single');
            end

            num_geoms = numel(self.geometries);
            renorm_factor = num_geoms / (num_geoms + num_ss_geoms);

            blobs = internal_cell_prod_scalar_assign(blobs, renorm_factor);
        end

        function volumes = backprojectVolume(self, blobs)
        % Similar problem to the projection function, with the only
        % difference that in this case the input is blobs and we want to
        % transform them into stack of sinograms

            numSinograms = numel(self.astra_geometries);
            self.statistics.tic('bproj_create_sum');
            if (self.blob_sinogram_c_functions)
                sinograms_sizes = cell(1, numSinograms);
                for n = 1:numSinograms
                    sinograms_sizes{n} = [self.proj_size(1), ...
                        size(self.geometries{n}, 1), self.proj_size(2)];
                end
            else
                sinograms = cell(1, numSinograms);
                for n = 1:numSinograms
                    sinograms{n} = zeros(self.proj_size(1), ...
                        size(self.geometries{n}, 1), self.proj_size(2), 'single');
                end
            end
            self.statistics.toc('bproj_create_sum');

            self.statistics.tic('bproj_permute_sum');
            if (self.blob_sinogram_c_functions)
                sinograms = internal_cell_blobs_to_sinograms(sinograms_sizes, blobs, self.offsets);
            else
                for n = 1:numSinograms
                    sino_offsets = self.offsets{n}.sino_offsets(:);
                    blob_offsets = self.offsets{n}.blob_offsets(:);
                    proj_offsets = self.offsets{n}.proj_offsets(:);
                    proj_coeffs = self.offsets{n}.proj_coeffs(:);
                    for m = 1:numel(proj_offsets)
                        p = proj_offsets(m);
                        b = blob_offsets(m);
                        c = proj_coeffs(m);
                        s = sino_offsets(m);
                        sinograms{n}(:, s, :) = ...
                            sinograms{n}(:, s, :) + c * blobs{b}(:, p, :);
                    end
                end
            end
            self.statistics.toc('bproj_permute_sum');

            volumes = self.backprojectVolume@Gt6DVolumeProjector(sinograms);

            if (~isempty(self.ss_offsets))
                volumes = self.backprojectVolumeSS(volumes, blobs);
            end
        end

        function volumes = backprojectVolumeSS(self, volumes, blobs)
        % Similar problem to the projection function, with the only
        % difference that in this case the input is blobs and we want to
        % transform them into stack of sinograms

            numSinograms = numel(self.ss_geometries);
            if (self.blob_sinogram_c_functions)
                sinograms_sizes = cell(1, numSinograms);
                for n = 1:numSinograms
                    sinograms_sizes{n} = [self.proj_size(1), ...
                        size(self.ss_geometries{n}, 1), self.proj_size(2)];
                end
            end

            num_ss_geoms = numel(self.ss_geometries);
            for n = 1:num_ss_geoms
                self.statistics.tic('bproj_permute_ss_single');
                if (self.blob_sinogram_c_functions)
                    ss_sino = internal_cell_blobs_to_single_sinogram( ...
                        sinograms_sizes{n}, blobs, self.ss_offsets{n});
                else
                    ss_sino = zeros(self.proj_size(1), ...
                        size(self.ss_geometries{n}, 1), self.proj_size(2), 'single');

                    sino_offsets = self.ss_offsets{n}.sino_offsets(:);
                    blob_offsets = self.ss_offsets{n}.blob_offsets(:);
                    proj_offsets = self.ss_offsets{n}.proj_offsets(:);
                    proj_coeffs = self.ss_offsets{n}.proj_coeffs(:);
                    for m = 1:numel(proj_offsets)
                        p = proj_offsets(m);
                        b = blob_offsets(m);
                        c = proj_coeffs(m);
                        s = sino_offsets(m);
                        ss_sino(:, s, :) = ...
                            ss_sino(:, s, :) + c * blobs{b}(:, p, :);
                    end
                end
                self.statistics.toc('bproj_permute_ss_single');

                volume = self.backprojectSingleVolumeSS(ss_sino, n);

                coeffs = self.ss_coeffs(n);
                num_inerp = numel(coeffs.indx);
                for c = 1:num_inerp
                    b = coeffs.indx(c);
                    volumes{b} = volumes{b} + coeffs.coeff(c) * volume;
                end
            end

            num_geoms = numel(self.geometries);
            renorm_factor = num_geoms / (num_geoms + num_ss_geoms);

            volumes = internal_cell_prod_scalar_assign(volumes, renorm_factor);
        end

        function y = project(self, x)
            self.statistics.tic('fproj_pre_weight_sum');
            y = internal_cell_prod_copy(x, self.backwardWeights);
            self.statistics.toc('fproj_pre_weight_sum');

            y = self.projectVolume(y);

            self.statistics.tic('fproj_post_weight_sum');
            y = internal_cell_prod_assign(y, self.forwardWeights);
            self.statistics.toc('fproj_post_weight_sum');
        end

        function y = backproject(self, x)
            self.statistics.tic('bproj_pre_weight_sum');
            y = internal_cell_prod_copy(x, self.forwardWeights);
            self.statistics.toc('bproj_pre_weight_sum');

            y = self.backprojectVolume(y);

            self.statistics.tic('bproj_post_weight_sum');
            y = internal_cell_prod_assign(y, self.backwardWeights);
            self.statistics.toc('bproj_post_weight_sum');
        end
    end
end