mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-18 01:49:09 +09:00
initial commit
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
% DISTMAT Compute a Distance Matrix for One or Two Sets of Points
|
||||
%
|
||||
%
|
||||
%
|
||||
% Copyright (c) 2015, Joseph Kirk
|
||||
% All rights reserved.
|
||||
%
|
||||
% Redistribution and use in source and binary forms, with or without
|
||||
% modification, are permitted provided that the following conditions are
|
||||
% met:
|
||||
%
|
||||
% * Redistributions of source code must retain the above copyright
|
||||
% notice, this list of conditions and the following disclaimer.
|
||||
% * Redistributions in binary form must reproduce the above copyright
|
||||
% notice, this list of conditions and the following disclaimer in
|
||||
% the documentation and/or other materials provided with the distribution
|
||||
%
|
||||
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
% POSSIBILITY OF SUCH DAMAGE.
|
||||
%
|
||||
|
||||
% Filename: distmat.m
|
||||
%
|
||||
% Description: Computes a matrix of pair-wise distances between points in
|
||||
% A and B, using one of {euclidean,cityblock,chessboard} methods
|
||||
%
|
||||
% Author:
|
||||
% Joseph Kirk
|
||||
% jdkirk630@gmail.com
|
||||
%
|
||||
% Date: 02/27/15
|
||||
%
|
||||
% Release: 2.0
|
||||
%
|
||||
% Inputs:
|
||||
% A - (required) MxD matrix where M is the number of points in D dimensions
|
||||
% B - (optional) NxD matrix where N is the number of points in D dimensions
|
||||
% if not provided, B is set to A by default
|
||||
% METHOD - (optional) string specifying one of the following distance methods:
|
||||
% 'euclidean' Euclidean distance (default)
|
||||
% 'taxicab','manhattan','cityblock' Manhattan distance
|
||||
% 'chebyshev','chessboard','chess' Chebyshev distance
|
||||
% 'grid','diag' Diagonal grid distance
|
||||
%
|
||||
% Outputs:
|
||||
% DMAT - MxN matrix of pair-wise distances between points in A and B
|
||||
%
|
||||
% Usage:
|
||||
% dmat = distmat(a)
|
||||
% -or-
|
||||
% dmat = distmat(a,b)
|
||||
% -or-
|
||||
% dmat = distmat(a,method)
|
||||
% -or-
|
||||
% dmat = distmat(a,b,method)
|
||||
%
|
||||
% Example:
|
||||
% % Pairwise Euclidean distances within a single set of 2D points
|
||||
% xy = 10*rand(25,2); % 25 points in 2D
|
||||
% dmat = distmat(xy);
|
||||
% figure; plot(xy(:,1),xy(:,2),'.');
|
||||
% for i=1:25, text(xy(i,1),xy(i,2),[' ' num2str(i)]); end
|
||||
% figure; imagesc(dmat); colorbar
|
||||
%
|
||||
% Example:
|
||||
% % Pairwise Manhattan distances within a single set of 2D points
|
||||
% xy = 10*rand(25,2); % 25 points in 2D
|
||||
% dmat = distmat(xy,'cityblock');
|
||||
% figure; plot(xy(:,1),xy(:,2),'.');
|
||||
% for i=1:25, text(xy(i,1),xy(i,2),[' ' num2str(i)]); end
|
||||
% figure; imagesc(dmat); colorbar
|
||||
%
|
||||
% Example:
|
||||
% % Pairwise Chebyshev distances within a single set of 2D points
|
||||
% xy = 10*rand(25,2); % 25 points in 2D
|
||||
% dmat = distmat(xy,'chebyshev');
|
||||
% figure; plot(xy(:,1),xy(:,2),'.');
|
||||
% for i=1:25, text(xy(i,1),xy(i,2),[' ' num2str(i)]); end
|
||||
% figure; imagesc(dmat); colorbar
|
||||
%
|
||||
% Example:
|
||||
% % Inter-point Euclidean distances for 2D points
|
||||
% xy = 10*rand(15,2); % 15 points in 2D
|
||||
% uv = 10*rand(25,2); % 25 points in 2D
|
||||
% dmat = distmat(xy,uv);
|
||||
% figure; plot(xy(:,1),xy(:,2),'.');
|
||||
% for i=1:15, text(xy(i,1),xy(i,2),[' ' num2str(i)]); end
|
||||
% figure; plot(uv(:,1),uv(:,2),'.');
|
||||
% for i=1:25, text(uv(i,1),uv(i,2),[' ' num2str(i)]); end
|
||||
% figure; imagesc(dmat); colorbar
|
||||
%
|
||||
% See also:
|
||||
%
|
||||
function dmat = distmat(a,varargin)
|
||||
|
||||
|
||||
% Set defaults
|
||||
method = 'euclidean';
|
||||
b = a;
|
||||
|
||||
% Error check primary input
|
||||
if ~isnumeric(a)
|
||||
error('Expecting a matrix of floating point values for A input.');
|
||||
end
|
||||
|
||||
% Process optional inputs
|
||||
for var = varargin
|
||||
arg = var{1};
|
||||
if ischar(arg)
|
||||
method = arg;
|
||||
elseif ~isempty(arg)
|
||||
b = arg;
|
||||
end
|
||||
end
|
||||
|
||||
% Check input dimensionality
|
||||
[na,aDims] = size(a);
|
||||
[nb,bDims] = size(b);
|
||||
if (aDims ~= bDims)
|
||||
error('Input matrices must have the same dimensionality.');
|
||||
end
|
||||
|
||||
% Create index matrices
|
||||
[j,i] = meshgrid(1:nb,1:na);
|
||||
|
||||
% Compute array of inter-point differences
|
||||
delta = a(i,:) - b(j,:);
|
||||
|
||||
% Compute distance by specified method
|
||||
dmat = zeros(na,nb);
|
||||
switch lower(method)
|
||||
case {'euclidean','euclid'}
|
||||
% Euclidean distance
|
||||
dmat(:) = sqrt(sum(delta.^2,2));
|
||||
case {'cityblock','city','block','manhattan','taxicab','taxi'}
|
||||
% Cityblock distance
|
||||
dmat(:) = sum(abs(delta),2);
|
||||
case {'chebyshev','cheby','chessboard','chess'}
|
||||
% Chebyshev distance
|
||||
dmat(:) = max(abs(delta),[],2);
|
||||
case {'grid','diag'}
|
||||
dmat(:) = max(abs(delta),[],2) + (sqrt(2) - 1)*min(abs(delta),[],2);
|
||||
otherwise
|
||||
error('Unrecognized distance method %s',method);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
% GET_CLOSE_INDICES simple based method to select indices for DM
|
||||
% !! GPU needs the sets to be with similar , ideally the same sizes !!!
|
||||
%
|
||||
% [indices_out, scan_ids_out] = get_close_indices(self, cache, par )
|
||||
%
|
||||
%
|
||||
% ** self structure containing inputs: e.g. current reconstruction results, data, mask, positions, pixel size, ..
|
||||
% ** par structure containing parameters for the engines
|
||||
% ** cache structure with precalculated values to avoid unnecessary overhead
|
||||
%
|
||||
% returns:
|
||||
% ++ indices_out cell of arrays, contain indices of positions processed in parallel
|
||||
% ++ scan_ids_out cell of arrays, contain scan numbers for each position
|
||||
|
||||
% Academic License Agreement
|
||||
%
|
||||
% Source Code
|
||||
%
|
||||
% Introduction
|
||||
% • This license agreement sets forth the terms and conditions under which the PAUL SCHERRER INSTITUT (PSI), CH-5232 Villigen-PSI, Switzerland (hereafter "LICENSOR")
|
||||
% will grant you (hereafter "LICENSEE") a royalty-free, non-exclusive license for academic, non-commercial purposes only (hereafter "LICENSE") to use the cSAXS
|
||||
% ptychography MATLAB package computer software program and associated documentation furnished hereunder (hereafter "PROGRAM").
|
||||
%
|
||||
% Terms and Conditions of the LICENSE
|
||||
% 1. LICENSOR grants to LICENSEE a royalty-free, non-exclusive license to use the PROGRAM for academic, non-commercial purposes, upon the terms and conditions
|
||||
% hereinafter set out and until termination of this license as set forth below.
|
||||
% 2. LICENSEE acknowledges that the PROGRAM is a research tool still in the development stage. The PROGRAM is provided without any related services, improvements
|
||||
% or warranties from LICENSOR and that the LICENSE is entered into in order to enable others to utilize the PROGRAM in their academic activities. It is the
|
||||
% LICENSEE’s responsibility to ensure its proper use and the correctness of the results.”
|
||||
% 3. THE PROGRAM IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
|
||||
% A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. IN NO EVENT SHALL THE LICENSOR, THE AUTHORS OR THE COPYRIGHT
|
||||
% HOLDERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROGRAM OR THE USE
|
||||
% OF THE PROGRAM OR OTHER DEALINGS IN THE PROGRAM.
|
||||
% 4. LICENSEE agrees that it will use the PROGRAM and any modifications, improvements, or derivatives of PROGRAM that LICENSEE may create (collectively,
|
||||
% "IMPROVEMENTS") solely for academic, non-commercial purposes and that any copy of PROGRAM or derivatives thereof shall be distributed only under the same
|
||||
% license as PROGRAM. The terms "academic, non-commercial", as used in this Agreement, mean academic or other scholarly research which (a) is not undertaken for
|
||||
% profit, or (b) is not intended to produce works, services, or data for commercial use, or (c) is neither conducted, nor funded, by a person or an entity engaged
|
||||
% in the commercial use, application or exploitation of works similar to the PROGRAM.
|
||||
% 5. LICENSEE agrees that it shall make the following acknowledgement in any publication resulting from the use of the PROGRAM or any translation of the code into
|
||||
% another computing language:
|
||||
% "Data processing was carried out using the cSAXS ptychography MATLAB package developed by the Science IT and the coherent X-ray scattering (CXS) groups, Paul
|
||||
% Scherrer Institut, Switzerland."
|
||||
%
|
||||
% Additionally, any publication using the package, or any translation of the code into another computing language should cite for difference map:
|
||||
% P. Thibault, M. Dierolf, A. Menzel, O. Bunk, C. David, F. Pfeiffer, High-resolution scanning X-ray diffraction microscopy, Science 321, 379–382 (2008).
|
||||
% (doi: 10.1126/science.1158573),
|
||||
% for mixed coherent modes:
|
||||
% P. Thibault and A. Menzel, Reconstructing state mixtures from diffraction measurements, Nature 494, 68–71 (2013). (doi: 10.1038/nature11806),
|
||||
% for LSQ-ML method
|
||||
% M. Odstrcil, A. Menzel, M.G. Sicairos, Iterative least-squares solver for generalized maximum-likelihood ptychography, Optics Express, 2018
|
||||
% for OPRP method
|
||||
% M. Odstrcil, P. Baksh, S. A. Boden, R. Card, J. E. Chad, J. G. Frey, W. S. Brocklesby, "Ptychographic coherent diffractive imaging with orthogonal probe relaxation." Optics express 24.8 (2016): 8360-8369
|
||||
% and/or for multislice:
|
||||
% E. H. R. Tsai, I. Usov, A. Diaz, A. Menzel, and M. Guizar-Sicairos, X-ray ptychography with extended depth of field, Opt. Express 24, 29089–29108 (2016).
|
||||
% 6. Except for the above-mentioned acknowledgment, LICENSEE shall not use the PROGRAM title or the names or logos of LICENSOR, nor any adaptation thereof, nor the
|
||||
% names of any of its employees or laboratories, in any advertising, promotional or sales material without prior written consent obtained from LICENSOR in each case.
|
||||
% 7. Ownership of all rights, including copyright in the PROGRAM and in any material associated therewith, shall at all times remain with LICENSOR, and LICENSEE
|
||||
% agrees to preserve same. LICENSEE agrees not to use any portion of the PROGRAM or of any IMPROVEMENTS in any machine-readable form outside the PROGRAM, nor to
|
||||
% make any copies except for its internal use, without prior written consent of LICENSOR. LICENSEE agrees to place the following copyright notice on any such copies:
|
||||
% © All rights reserved. PAUL SCHERRER INSTITUT, Switzerland, Laboratory for Macromolecules and Bioimaging, 2017.
|
||||
% 8. The LICENSE shall not be construed to confer any rights upon LICENSEE by implication or otherwise except as specifically set forth herein.
|
||||
% 9. DISCLAIMER: LICENSEE shall be aware that Phase Focus Limited of Sheffield, UK has an international portfolio of patents and pending applications which relate
|
||||
% to ptychography and that the PROGRAM may be capable of being used in circumstances which may fall within the claims of one or more of the Phase Focus patents,
|
||||
% in particular of patent with international application number PCT/GB2005/001464. The LICENSOR explicitly declares not to indemnify the users of the software
|
||||
% in case Phase Focus or any other third party will open a legal action against the LICENSEE due to the use of the program.
|
||||
% 10. This Agreement shall be governed by the material laws of Switzerland and any dispute arising out of this Agreement or use of the PROGRAM shall be brought before
|
||||
% the courts of Zürich, Switzerland.
|
||||
|
||||
|
||||
|
||||
function [indices_out, scan_ids_out] = get_close_indices(self, cache, par )
|
||||
|
||||
import math.*
|
||||
import utils.*
|
||||
|
||||
grouping = par.grouping;
|
||||
|
||||
% in case of a shared scan join together all positions to find the optimal groups
|
||||
group_across_scans = true;
|
||||
|
||||
|
||||
if par.share_object && group_across_scans
|
||||
Nsets = 1;
|
||||
else
|
||||
Nsets = par.Nscans;
|
||||
end
|
||||
cluster_refinement_time = 0;
|
||||
cluster_time = 0;
|
||||
|
||||
% in simplest case process all positions together
|
||||
if Nsets == 1 && grouping >= self.Npos
|
||||
indices_out = {[self.reconstruct_ind{:}]};
|
||||
scan_ids_out{1} = [];
|
||||
for ii = 1:length(self.reconstruct_ind)
|
||||
scan_ids_out{1} = [scan_ids_out{1}; ii*ones(length(self.reconstruct_ind{ii}),1)];
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
%rng default
|
||||
|
||||
for kk = 1:Nsets
|
||||
% take them sequentially but with random offset
|
||||
if par.share_object && group_across_scans
|
||||
% join all indices into one large set if the object is shared
|
||||
indices_0 = [self.reconstruct_ind{:}];
|
||||
for ii = 1:length(self.reconstruct_ind)
|
||||
scans_0(self.reconstruct_ind{ii}) = ii;
|
||||
end
|
||||
else
|
||||
indices_0 = self.reconstruct_ind{kk};
|
||||
scans_0 = kk * ones(size(indices_0)); % scan number
|
||||
end
|
||||
N = length(indices_0);
|
||||
Ngroups=ceil(N/grouping);
|
||||
positions = self.probe_positions_0(indices_0,:);
|
||||
Npos = length(positions);
|
||||
|
||||
% get initial set distribution
|
||||
[groups, C, sum_D, D] = get_best_kmeans(positions, Ngroups);
|
||||
|
||||
iter = 0;
|
||||
t0 = tic;
|
||||
|
||||
while true
|
||||
iter= iter +1;
|
||||
[nbins,bins] = hist(groups, unique(groups));
|
||||
% if less than 2 types of groups are present, finish
|
||||
Ngroups_sizes = length(unique(nbins));
|
||||
% try to find distribution with most similar sets sizes, if not
|
||||
% easy, end with suboptimal distribution after 50 iterations
|
||||
if ( Ngroups_sizes <= max(2, ceil(iter/1e3)) && (Ngroups*grouping ~= N || iter > 1e3 )) ...
|
||||
|| Ngroups_sizes == 1 % choose suboptimal solution if better is not found soon
|
||||
break
|
||||
end
|
||||
|
||||
% find group with lowest number of members , add new points into
|
||||
% this group
|
||||
min_group = bins(argmin(nbins));
|
||||
large_groups = bins(nbins>grouping);
|
||||
if isempty(large_groups) || any(ismember(min_group, large_groups)) ; break; end
|
||||
% choose closest position from the largest group to be moved to the
|
||||
% smallest group
|
||||
ind_large = (D(:,min_group) == min(D(ismember(groups, large_groups), min_group)));
|
||||
|
||||
groups(ind_large) = min_group;
|
||||
|
||||
end
|
||||
|
||||
% remove empty groups
|
||||
ugroups = unique(groups);
|
||||
Ngroups = length(ugroups);
|
||||
groups = sum((1:Ngroups) .*(groups == ugroups'),2);
|
||||
|
||||
|
||||
|
||||
cluster_time = cluster_time + toc(t0);
|
||||
|
||||
|
||||
for ii = 1:Ngroups
|
||||
C(ii,:) = median(positions(groups == ii,:));
|
||||
end
|
||||
for ii = 1:Ngroups
|
||||
D(:,ii) = (sum((positions - C(ii,:)).^2,2));
|
||||
end
|
||||
|
||||
|
||||
t0 = tic;
|
||||
%% find more compact refinement
|
||||
% find the most distanced points
|
||||
[~,sind] = sort(D,2);
|
||||
% positions to be improved -> find the best matching group
|
||||
optimal_group = sind(:,1);
|
||||
|
||||
nonoptimal_ratio_0 = 1;
|
||||
|
||||
for iter = 1:10
|
||||
ind_switch = (groups ~= optimal_group);
|
||||
nonoptimal_ratio = sum(ind_switch) / numel(ind_switch);
|
||||
if nonoptimal_ratio > 0
|
||||
verbose(0, 'Indexes to be switched: %3.2g%% positions', nonoptimal_ratio * 100)
|
||||
end
|
||||
|
||||
if nonoptimal_ratio >= nonoptimal_ratio_0
|
||||
break
|
||||
end
|
||||
nonoptimal_ratio_0 = nonoptimal_ratio;
|
||||
|
||||
max_dist_0 = inf;
|
||||
for i = 1:sum(ind_switch)
|
||||
% calculate distance for each point from its group center
|
||||
center_dist = (D(sub2ind(size(D), (1:Npos)', groups)));
|
||||
max_dist_0 = max(center_dist(ind_switch));
|
||||
% start from the worst case
|
||||
ind_worse = find(max(center_dist(ind_switch)) == center_dist, 1, 'first');
|
||||
% initial group
|
||||
group_old = groups(ind_worse);
|
||||
% better fitting group
|
||||
group_new = optimal_group(ind_worse);
|
||||
% position to be switched in the new group
|
||||
ind_new = find(D(:,group_old) == min(D(groups == group_new, group_old)), 1, 'first');
|
||||
% switch the group members
|
||||
groups(ind_worse) = group_new;
|
||||
groups(ind_new) = group_old;
|
||||
ind_switch([ind_worse, ind_new]) = 0;
|
||||
if all(ind_switch == 0)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
% ind_switch = (groups ~= sind(:,1));
|
||||
% for ii = Ngroups
|
||||
% clf
|
||||
% hold all;
|
||||
% ind = groups == ii;
|
||||
% ax = plot(self.probe_positions_0(ind & ind_switch, 1), self.probe_positions_0(ind & ind_switch, 2), 'o');
|
||||
% ax2 = plot(self.probe_positions_0(ind & ~ind_switch, 1), self.probe_positions_0(ind & ~ind_switch, 2), 'x');
|
||||
% try; ax2.Color = ax.Color; end
|
||||
% plot(C(ii,1),C(ii,2),'x','Linewidth', 2)
|
||||
% % drawnow
|
||||
% % pause(1)
|
||||
% end
|
||||
% title(num2str(iter))
|
||||
% axis tight equal
|
||||
% pause(1)
|
||||
%
|
||||
end
|
||||
|
||||
cluster_refinement_time = cluster_refinement_time + toc(t0);
|
||||
|
||||
|
||||
%% optimally sort the indices to help GPU
|
||||
[nbins,bins] = hist(groups, unique(groups));
|
||||
[~,ind] = sort(nbins,2,'descend');
|
||||
for ii = 1:length(bins)
|
||||
indices{kk}{ii} = indices_0((groups == bins(ind(ii))));
|
||||
scan_ids{kk}{ii} = scans_0((groups == bins(ind(ii))));
|
||||
end
|
||||
verbose(2,'=== Number of cluster sizes %i', length(unique(nbins)))
|
||||
end
|
||||
|
||||
verbose(0,'=== Position clusters found in %i iterations in %3.2gs', iter, cluster_time)
|
||||
verbose(0,'=== Position clusters refined in %i iterations in %3.2gs', iter, cluster_refinement_time)
|
||||
|
||||
|
||||
%rng shuffle
|
||||
|
||||
if verbose() > 1 && Ngroups_sizes > 1
|
||||
warning('Unequal group sizes, it may cause slower calculation')
|
||||
end
|
||||
|
||||
|
||||
indices_out = horzcat(indices{:});
|
||||
scan_ids_out = horzcat(scan_ids{:});
|
||||
|
||||
|
||||
if Ngroups == 1 && Nsets == 1
|
||||
%% merge groups from multiple scans into larger chunks if grouping is too large
|
||||
indices_out = {horzcat(indices_out{:})};
|
||||
scan_ids_out = {horzcat(scan_ids_out{:})};
|
||||
end
|
||||
|
||||
|
||||
|
||||
%% sort them to minimize allocation of new projection matrices
|
||||
Nitems = cellfun(@length, indices_out);
|
||||
|
||||
if all(max(Nitems) - min(Nitems) <= 1) && all(Nitems > 100)
|
||||
% just neglect one scanning position to keep the bunches with the same
|
||||
% size -> faster run on GPU
|
||||
for i = 1:length(indices_out)
|
||||
indices_out{i} = indices_out{i}(1:min(Nitems));
|
||||
scan_ids_out{i} = scan_ids_out{i}(1:min(Nitems));
|
||||
end
|
||||
else
|
||||
[~,ind] = sort(Nitems(:),1,'descend' );
|
||||
indices_out = indices_out(ind);
|
||||
scan_ids_out = scan_ids_out(ind);
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
function [groups, C, sum_D, D] = get_best_kmeans(positions, Ngroups)
|
||||
% make several guesses to get better Kmean distribution
|
||||
warning('off','stats:kmeans:FailedToConverge')
|
||||
for i = 1:10
|
||||
[groups{i}, C{i}, sum_D{i}, D{i}] = kmeans(positions, Ngroups);
|
||||
nbins = hist(groups{i}, unique(groups{i}));
|
||||
score(i) = std(nbins);
|
||||
end
|
||||
best = math.argmin(score);
|
||||
groups = groups{best};
|
||||
C = C{best};
|
||||
sum_D = sum_D{best};
|
||||
D = D{best};
|
||||
end
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
% GET_NONOVERLAPPING_INDICES a heuristic based method to select pseudorandom indices of non overlapping regions
|
||||
% Note: It can be slow for large number of scanning positions
|
||||
%
|
||||
% [indices_out, scan_ids_out] = get_nonoverlapping_indices(self, cache, par )
|
||||
%
|
||||
% ** self structure containing inputs: e.g. current reconstruction results, data, mask, positions, pixel size, ..
|
||||
% ** par structure containing parameters for the engines
|
||||
% ** cache structure with precalculated values to avoid unnecessary overhead
|
||||
%
|
||||
% returns:
|
||||
% ++ indices_out cell of arrays, contain indices of positions processed in parallel
|
||||
% ++ scan_ids_out cell of arrays, contain scan numbers for each position
|
||||
|
||||
|
||||
% Academic License Agreement
|
||||
%
|
||||
% Source Code
|
||||
%
|
||||
% Introduction
|
||||
% • This license agreement sets forth the terms and conditions under which the PAUL SCHERRER INSTITUT (PSI), CH-5232 Villigen-PSI, Switzerland (hereafter "LICENSOR")
|
||||
% will grant you (hereafter "LICENSEE") a royalty-free, non-exclusive license for academic, non-commercial purposes only (hereafter "LICENSE") to use the cSAXS
|
||||
% ptychography MATLAB package computer software program and associated documentation furnished hereunder (hereafter "PROGRAM").
|
||||
%
|
||||
% Terms and Conditions of the LICENSE
|
||||
% 1. LICENSOR grants to LICENSEE a royalty-free, non-exclusive license to use the PROGRAM for academic, non-commercial purposes, upon the terms and conditions
|
||||
% hereinafter set out and until termination of this license as set forth below.
|
||||
% 2. LICENSEE acknowledges that the PROGRAM is a research tool still in the development stage. The PROGRAM is provided without any related services, improvements
|
||||
% or warranties from LICENSOR and that the LICENSE is entered into in order to enable others to utilize the PROGRAM in their academic activities. It is the
|
||||
% LICENSEE’s responsibility to ensure its proper use and the correctness of the results.”
|
||||
% 3. THE PROGRAM IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
|
||||
% A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. IN NO EVENT SHALL THE LICENSOR, THE AUTHORS OR THE COPYRIGHT
|
||||
% HOLDERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROGRAM OR THE USE
|
||||
% OF THE PROGRAM OR OTHER DEALINGS IN THE PROGRAM.
|
||||
% 4. LICENSEE agrees that it will use the PROGRAM and any modifications, improvements, or derivatives of PROGRAM that LICENSEE may create (collectively,
|
||||
% "IMPROVEMENTS") solely for academic, non-commercial purposes and that any copy of PROGRAM or derivatives thereof shall be distributed only under the same
|
||||
% license as PROGRAM. The terms "academic, non-commercial", as used in this Agreement, mean academic or other scholarly research which (a) is not undertaken for
|
||||
% profit, or (b) is not intended to produce works, services, or data for commercial use, or (c) is neither conducted, nor funded, by a person or an entity engaged
|
||||
% in the commercial use, application or exploitation of works similar to the PROGRAM.
|
||||
% 5. LICENSEE agrees that it shall make the following acknowledgement in any publication resulting from the use of the PROGRAM or any translation of the code into
|
||||
% another computing language:
|
||||
% "Data processing was carried out using the cSAXS ptychography MATLAB package developed by the Science IT and the coherent X-ray scattering (CXS) groups, Paul
|
||||
% Scherrer Institut, Switzerland."
|
||||
%
|
||||
% Additionally, any publication using the package, or any translation of the code into another computing language should cite for difference map:
|
||||
% P. Thibault, M. Dierolf, A. Menzel, O. Bunk, C. David, F. Pfeiffer, High-resolution scanning X-ray diffraction microscopy, Science 321, 379–382 (2008).
|
||||
% (doi: 10.1126/science.1158573),
|
||||
% for mixed coherent modes:
|
||||
% P. Thibault and A. Menzel, Reconstructing state mixtures from diffraction measurements, Nature 494, 68–71 (2013). (doi: 10.1038/nature11806),
|
||||
% for LSQ-ML method
|
||||
% M. Odstrcil, A. Menzel, M.G. Sicairos, Iterative least-squares solver for generalized maximum-likelihood ptychography, Optics Express, 2018
|
||||
% for OPRP method
|
||||
% M. Odstrcil, P. Baksh, S. A. Boden, R. Card, J. E. Chad, J. G. Frey, W. S. Brocklesby, "Ptychographic coherent diffractive imaging with orthogonal probe relaxation." Optics express 24.8 (2016): 8360-8369
|
||||
% and/or for multislice:
|
||||
% E. H. R. Tsai, I. Usov, A. Diaz, A. Menzel, and M. Guizar-Sicairos, X-ray ptychography with extended depth of field, Opt. Express 24, 29089–29108 (2016).
|
||||
% 6. Except for the above-mentioned acknowledgment, LICENSEE shall not use the PROGRAM title or the names or logos of LICENSOR, nor any adaptation thereof, nor the
|
||||
% names of any of its employees or laboratories, in any advertising, promotional or sales material without prior written consent obtained from LICENSOR in each case.
|
||||
% 7. Ownership of all rights, including copyright in the PROGRAM and in any material associated therewith, shall at all times remain with LICENSOR, and LICENSEE
|
||||
% agrees to preserve same. LICENSEE agrees not to use any portion of the PROGRAM or of any IMPROVEMENTS in any machine-readable form outside the PROGRAM, nor to
|
||||
% make any copies except for its internal use, without prior written consent of LICENSOR. LICENSEE agrees to place the following copyright notice on any such copies:
|
||||
% © All rights reserved. PAUL SCHERRER INSTITUT, Switzerland, Laboratory for Macromolecules and Bioimaging, 2017.
|
||||
% 8. The LICENSE shall not be construed to confer any rights upon LICENSEE by implication or otherwise except as specifically set forth herein.
|
||||
% 9. DISCLAIMER: LICENSEE shall be aware that Phase Focus Limited of Sheffield, UK has an international portfolio of patents and pending applications which relate
|
||||
% to ptychography and that the PROGRAM may be capable of being used in circumstances which may fall within the claims of one or more of the Phase Focus patents,
|
||||
% in particular of patent with international application number PCT/GB2005/001464. The LICENSOR explicitly declares not to indemnify the users of the software
|
||||
% in case Phase Focus or any other third party will open a legal action against the LICENSEE due to the use of the program.
|
||||
% 10. This Agreement shall be governed by the material laws of Switzerland and any dispute arising out of this Agreement or use of the PROGRAM shall be brought before
|
||||
% the courts of Zürich, Switzerland.
|
||||
|
||||
|
||||
function [indices_out, scan_ids_out] = get_nonoverlapping_indices(self, cache, par )
|
||||
|
||||
% find groups accross the scans in order to further minimize overlap
|
||||
group_across_scans = true; %need to be true for sharing object amongs scans
|
||||
|
||||
if group_across_scans
|
||||
% divide the grouping equally over all the scans
|
||||
grouping = ceil(par.grouping/par.Nscans);
|
||||
else
|
||||
grouping = par.grouping;
|
||||
end
|
||||
max_groups = 0;
|
||||
|
||||
for kk = 1:par.Nscans
|
||||
|
||||
% setdiff sort the indices by size
|
||||
indices_0 = self.reconstruct_ind{kk}; % remove unwanted from the decision process
|
||||
ind_start(kk) = min(indices_0)-1;
|
||||
indices_0 = indices_0 - ind_start(kk); %
|
||||
Npos_tmp=length(indices_0);
|
||||
% randomly permutate the indices
|
||||
indices_0 = indices_0(randperm(Npos_tmp));
|
||||
max_groups = max(max_groups, ceil(Npos_tmp/grouping));
|
||||
% fill it with some initial random guess
|
||||
for ii = 1:ceil(Npos_tmp/grouping)
|
||||
indices{kk}{ii} = indices_0(1+(ii-1)*grouping : min(Npos_tmp,ii*grouping));
|
||||
end
|
||||
|
||||
% no need for this method ot it calculation would be too long -> use
|
||||
% just the random initial guess
|
||||
if (self.Npos/par.Nscans > 1e3 ) || (grouping == 1) || ~isfield(cache, 'distances_matrix')
|
||||
%%%for ii = 1:length(indices{1}) %why length(indices{1})? Bug?
|
||||
for ii = 1:length(indices{kk}) %modified by YJ to prevent error when different scans have differernt number of positions
|
||||
scan_ids{kk}{ii} = ones(1,length(indices{kk}{ii}))*kk; % note their scan origin
|
||||
end
|
||||
continue
|
||||
end % hope that for large number of positions the random statistics will be enough
|
||||
|
||||
try
|
||||
|
||||
update_score = 0;
|
||||
for i = 1:ceil(Npos_tmp/grouping)-1
|
||||
id = indices{kk}{i};
|
||||
dist_mat_small = cache.distances_matrix{kk}(id,id);
|
||||
for ii = 0:2*length(indices{kk}{i+1}) % go twice through all positions
|
||||
j = 1+mod(ii, length(indices{kk}{i+1}));
|
||||
min_dist = 1./sum(1./dist_mat_small.^2); % find the shortest distance between the probes
|
||||
if all(isinf(min_dist)) % all(isnan(min_dist))
|
||||
break
|
||||
end
|
||||
[~,min_dist_ind] = min(min_dist);
|
||||
% make a swap with the j position in i+1 index array
|
||||
tmp = indices{kk}{i+1}(j);
|
||||
indices{kk}{i+1}(j) = indices{kk}{i}(min_dist_ind);
|
||||
indices{kk}{i}(min_dist_ind) = tmp;
|
||||
|
||||
|
||||
% update distance matrix
|
||||
dist_mat_small_update = cache.distances_matrix{kk}(tmp,indices{kk}{i});
|
||||
dist_mat_small(min_dist_ind,:) = dist_mat_small_update';
|
||||
dist_mat_small(:,min_dist_ind) = dist_mat_small_update;
|
||||
end
|
||||
update_score = update_score +j;
|
||||
end
|
||||
catch
|
||||
keyboard
|
||||
end
|
||||
|
||||
|
||||
% fill the last group by the skip indieces but do not expand it
|
||||
skip_ind = cache.skip_ind(randperm(length(cache.skip_ind)));
|
||||
indices{kk}{end} = [indices{kk}{end}, skip_ind(1:min(end, grouping-length(indices{kk}{end})))]; % join skip_ind back to the last (smallest) set
|
||||
|
||||
for ii = 1:length(indices{kk})
|
||||
scan_ids{kk}{ii} = ones(1,length(indices{kk}{ii}))*kk; % note their scan origin
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if group_across_scans
|
||||
indices_out = cell(max_groups,1);
|
||||
scan_ids_out = cell(max_groups,1);
|
||||
%% merge groups from difference scans into larger chunks if required
|
||||
for ii = 1:max_groups
|
||||
indices_out{ii} = [];
|
||||
scan_ids_out{ii} = [];
|
||||
% from each scan add one group
|
||||
for kk = 1:par.Nscans
|
||||
if ii <= length(indices{kk})
|
||||
indices_out{ii} = [indices_out{ii}, indices{kk}{ii}+ind_start(kk)];
|
||||
scan_ids_out{ii} = [scan_ids_out{ii}, scan_ids{kk}{ii}];
|
||||
end
|
||||
end
|
||||
if length(scan_ids_out) > 1 && length(scan_ids_out{end}) < grouping / 10
|
||||
% if the a group is too small, merge it with the previous to
|
||||
% reduce the overhead
|
||||
indices_out{end-1} = [indices_out{end-1}, indices_out{end}];
|
||||
scan_ids_out{end-1} = [scan_ids_out{end-1}, scan_ids_out{end}];
|
||||
scan_ids_out(end) = []; indices_out(end) = [];
|
||||
end
|
||||
end
|
||||
else
|
||||
indices_out = {};
|
||||
for ii = 1:par.Nscans
|
||||
for kk = 1:length(indices{ii})
|
||||
indices_out = [indices_out, indices{ii}{kk}+ind_start(ii)];
|
||||
end
|
||||
end
|
||||
scan_ids_out = [scan_ids{:}]';
|
||||
|
||||
end
|
||||
|
||||
%% sort them to minimize allocation of new projection matrices
|
||||
Nitems = cellfun(@length, indices_out);
|
||||
|
||||
if all(max(Nitems) - min(Nitems) <= 1) && all(Nitems > 100)
|
||||
% just neglect one scanning position to keep the bunches with the same
|
||||
% size -> faster run on GPU
|
||||
for i = 1:length(indices_out)
|
||||
indices_out{i} = indices_out{i}(1:min(Nitems));
|
||||
scan_ids_out{i} = scan_ids_out{i}(1:min(Nitems));
|
||||
end
|
||||
else
|
||||
[~,ind] = sort(Nitems(:),1,'descend' );
|
||||
indices_out = indices_out(ind);
|
||||
scan_ids_out = scan_ids_out(ind);
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
% GET_SCANNING_INDICES simple based method to select indices for DM
|
||||
%
|
||||
% [indices_out, scan_ids_out] = get_scanning_indices(self, cache, par )
|
||||
%
|
||||
% ** self structure containing inputs: e.g. current reconstruction results, data, mask, positions, pixel size, ..
|
||||
% ** par structure containing parameters for the engines
|
||||
% ** cache structure with precalculated values to avoid unnecessary overhead
|
||||
%
|
||||
% returns:
|
||||
% ++ indices_out cell of arrays, contain indices of positions processed in parallel
|
||||
% ++ scan_ids_out cell of arrays, contain scan numbers for each position
|
||||
|
||||
|
||||
% Academic License Agreement
|
||||
%
|
||||
% Source Code
|
||||
%
|
||||
% Introduction
|
||||
% • This license agreement sets forth the terms and conditions under which the PAUL SCHERRER INSTITUT (PSI), CH-5232 Villigen-PSI, Switzerland (hereafter "LICENSOR")
|
||||
% will grant you (hereafter "LICENSEE") a royalty-free, non-exclusive license for academic, non-commercial purposes only (hereafter "LICENSE") to use the cSAXS
|
||||
% ptychography MATLAB package computer software program and associated documentation furnished hereunder (hereafter "PROGRAM").
|
||||
%
|
||||
% Terms and Conditions of the LICENSE
|
||||
% 1. LICENSOR grants to LICENSEE a royalty-free, non-exclusive license to use the PROGRAM for academic, non-commercial purposes, upon the terms and conditions
|
||||
% hereinafter set out and until termination of this license as set forth below.
|
||||
% 2. LICENSEE acknowledges that the PROGRAM is a research tool still in the development stage. The PROGRAM is provided without any related services, improvements
|
||||
% or warranties from LICENSOR and that the LICENSE is entered into in order to enable others to utilize the PROGRAM in their academic activities. It is the
|
||||
% LICENSEE’s responsibility to ensure its proper use and the correctness of the results.”
|
||||
% 3. THE PROGRAM IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
|
||||
% A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. IN NO EVENT SHALL THE LICENSOR, THE AUTHORS OR THE COPYRIGHT
|
||||
% HOLDERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROGRAM OR THE USE
|
||||
% OF THE PROGRAM OR OTHER DEALINGS IN THE PROGRAM.
|
||||
% 4. LICENSEE agrees that it will use the PROGRAM and any modifications, improvements, or derivatives of PROGRAM that LICENSEE may create (collectively,
|
||||
% "IMPROVEMENTS") solely for academic, non-commercial purposes and that any copy of PROGRAM or derivatives thereof shall be distributed only under the same
|
||||
% license as PROGRAM. The terms "academic, non-commercial", as used in this Agreement, mean academic or other scholarly research which (a) is not undertaken for
|
||||
% profit, or (b) is not intended to produce works, services, or data for commercial use, or (c) is neither conducted, nor funded, by a person or an entity engaged
|
||||
% in the commercial use, application or exploitation of works similar to the PROGRAM.
|
||||
% 5. LICENSEE agrees that it shall make the following acknowledgement in any publication resulting from the use of the PROGRAM or any translation of the code into
|
||||
% another computing language:
|
||||
% "Data processing was carried out using the cSAXS ptychography MATLAB package developed by the Science IT and the coherent X-ray scattering (CXS) groups, Paul
|
||||
% Scherrer Institut, Switzerland."
|
||||
%
|
||||
% Additionally, any publication using the package, or any translation of the code into another computing language should cite for difference map:
|
||||
% P. Thibault, M. Dierolf, A. Menzel, O. Bunk, C. David, F. Pfeiffer, High-resolution scanning X-ray diffraction microscopy, Science 321, 379–382 (2008).
|
||||
% (doi: 10.1126/science.1158573),
|
||||
% for mixed coherent modes:
|
||||
% P. Thibault and A. Menzel, Reconstructing state mixtures from diffraction measurements, Nature 494, 68–71 (2013). (doi: 10.1038/nature11806),
|
||||
% for LSQ-ML method
|
||||
% M. Odstrcil, A. Menzel, M.G. Sicairos, Iterative least-squares solver for generalized maximum-likelihood ptychography, Optics Express, 2018
|
||||
% for OPRP method
|
||||
% M. Odstrcil, P. Baksh, S. A. Boden, R. Card, J. E. Chad, J. G. Frey, W. S. Brocklesby, "Ptychographic coherent diffractive imaging with orthogonal probe relaxation." Optics express 24.8 (2016): 8360-8369
|
||||
% and/or for multislice:
|
||||
% E. H. R. Tsai, I. Usov, A. Diaz, A. Menzel, and M. Guizar-Sicairos, X-ray ptychography with extended depth of field, Opt. Express 24, 29089–29108 (2016).
|
||||
% 6. Except for the above-mentioned acknowledgment, LICENSEE shall not use the PROGRAM title or the names or logos of LICENSOR, nor any adaptation thereof, nor the
|
||||
% names of any of its employees or laboratories, in any advertising, promotional or sales material without prior written consent obtained from LICENSOR in each case.
|
||||
% 7. Ownership of all rights, including copyright in the PROGRAM and in any material associated therewith, shall at all times remain with LICENSOR, and LICENSEE
|
||||
% agrees to preserve same. LICENSEE agrees not to use any portion of the PROGRAM or of any IMPROVEMENTS in any machine-readable form outside the PROGRAM, nor to
|
||||
% make any copies except for its internal use, without prior written consent of LICENSOR. LICENSEE agrees to place the following copyright notice on any such copies:
|
||||
% © All rights reserved. PAUL SCHERRER INSTITUT, Switzerland, Laboratory for Macromolecules and Bioimaging, 2017.
|
||||
% 8. The LICENSE shall not be construed to confer any rights upon LICENSEE by implication or otherwise except as specifically set forth herein.
|
||||
% 9. DISCLAIMER: LICENSEE shall be aware that Phase Focus Limited of Sheffield, UK has an international portfolio of patents and pending applications which relate
|
||||
% to ptychography and that the PROGRAM may be capable of being used in circumstances which may fall within the claims of one or more of the Phase Focus patents,
|
||||
% in particular of patent with international application number PCT/GB2005/001464. The LICENSOR explicitly declares not to indemnify the users of the software
|
||||
% in case Phase Focus or any other third party will open a legal action against the LICENSEE due to the use of the program.
|
||||
% 10. This Agreement shall be governed by the material laws of Switzerland and any dispute arising out of this Agreement or use of the PROGRAM shall be brought before
|
||||
% the courts of Zürich, Switzerland.
|
||||
%
|
||||
%
|
||||
|
||||
function [indices_out, scan_ids_out] = get_scanning_indices(self, cache, par )
|
||||
|
||||
import engines.GPU.GPU_wrapper.*
|
||||
import engines.GPU.shared.*
|
||||
|
||||
grouping = par.grouping;
|
||||
max_groups = 0;
|
||||
|
||||
if self.Npos == grouping && par.Nscans == 1
|
||||
indices_out = self.reconstruct_ind;
|
||||
scan_ids_out = {ones(self.Npos,1)};
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
for kk = 1:par.Nscans
|
||||
N = length(self.reconstruct_ind{kk});
|
||||
|
||||
% !! indices ordering has to be always the same for DM !!!
|
||||
|
||||
indices_0 = self.reconstruct_ind{kk};
|
||||
|
||||
max_groups = max(max_groups, ceil(N/grouping));
|
||||
|
||||
for ii = 1:ceil(N/grouping)
|
||||
indices{kk}{ii} = indices_0(1+(ii-1)*grouping : min(end,ii*grouping));
|
||||
end
|
||||
% fill the last group by the skip indices but do not expand it
|
||||
skip_ind = cache.skip_ind(randperm(length(cache.skip_ind)));
|
||||
indices{kk}{end} = [indices{kk}{end}, skip_ind(1:min(end, grouping-length(indices{kk}{end})))]; % join skip_ind back to the last (smallest) set
|
||||
for ii = 1:length(indices{kk})
|
||||
scan_ids{kk}{ii} = kk * ones(1,length(indices{kk}{ii})); % note their scan origin
|
||||
end
|
||||
end
|
||||
|
||||
% how many scans should be merged to reach the desired grouping
|
||||
Njoin = ceil(par.grouping / (self.Npos/par.Nscans));
|
||||
|
||||
|
||||
if Njoin > 1 && par.Nscans > 1 && is_method(par, {'PIE', 'ML'})
|
||||
% join several scan to improve performance
|
||||
indices_out = cell(ceil(par.Nscans/Njoin),1);
|
||||
scan_ids_out = cell(ceil(par.Nscans/Njoin),1);
|
||||
%% merge groups from difference scans into larger chunks
|
||||
for kk = 1:ceil(par.Nscans/Njoin)
|
||||
indices_out{kk} = [];
|
||||
scan_ids_out{kk} = [];
|
||||
for ii = 1:Njoin
|
||||
if kk+(ii-1)*ceil(par.Nscans/Njoin) <= par.Nscans
|
||||
indices_out{kk} = [indices_out{kk}, indices{kk+(ii-1)*ceil(par.Nscans/Njoin)}{1}];
|
||||
scan_ids_out{kk} = [scan_ids_out{kk}, scan_ids{kk+(ii-1)*ceil(par.Nscans/Njoin)}{1}];
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
indices_out = horzcat(indices{:});
|
||||
scan_ids_out = horzcat(scan_ids{:});
|
||||
end
|
||||
|
||||
|
||||
%% sort them to minimize allocation of new projection matrices
|
||||
Nitems = cellfun(@length, indices_out);
|
||||
|
||||
if all(max(Nitems) - min(Nitems) <= 1) && all(Nitems > 100)
|
||||
% just neglect one scanning position to keep the bunches with the same
|
||||
% size -> faster run on GPU
|
||||
for i = 1:length(indices_out)
|
||||
indices_out{i} = indices_out{i}(1:min(Nitems));
|
||||
scan_ids_out{i} = scan_ids_out{i}(1:min(Nitems));
|
||||
end
|
||||
else
|
||||
[~,ind] = sort(Nitems(:),1,'descend' );
|
||||
indices_out = indices_out(ind);
|
||||
scan_ids_out = scan_ids_out(ind);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user