diff --git a/.github/badges/code_issues.svg b/.github/badges/code_issues.svg
index ef1396f28..cd05e9181 100644
--- a/.github/badges/code_issues.svg
+++ b/.github/badges/code_issues.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/code/+nansen/+tools/+image/+resize/imsplit.m b/code/+nansen/+tools/+image/+resize/imsplit.m
deleted file mode 100644
index dbf9cddf4..000000000
--- a/code/+nansen/+tools/+image/+resize/imsplit.m
+++ /dev/null
@@ -1,120 +0,0 @@
-function imOut = getChunkedData(IM, chunk, sz, varargin)
-
-param = struct('numRows', 4, 'numCols', 4);
-param = utility.parsenvpairs(param, [], varargin);
-
-if nargin < 2; chunk = true; end
-
- if ~chunk
- d1 = sz(1); d2 = sz(2); d3 = sz(3);
- else
- [d1,d2,d3] = size(IM);
- end
-
- gridSize = [d1/param.numRows, d2/param.numCols, d3];
- gridSize = ceil(gridSize);
-
- [xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,xx_us,xx_uf,yy_us,yy_uf,zz_us,zz_uf] = ...
- construct_grid(gridSize, [1,1,1] , d1,d2,d3, [32,32,16] );
-
- if chunk
- imOut = mat2cell_ov(IM,xx_s,xx_f,yy_s,yy_f,zz_s,zz_f, [1,1,0],[d1,d2,d3]);
- else %unchunk
- imOut = cell2mat_ov(IM,xx_us,xx_uf,yy_us,yy_uf,zz_us,zz_uf, [1,1,0], [d1,d2,d3]);
- end
-end
-
-function I = mat2cell_ov(X,xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,overlap,sz)
-
-% converts a matrix into a cell array with overlapping elements
-% INPUTS:
-% X: Input matrix
-% grid_size: size of each element without overlap
-% overlap: amount of overlap
-% sz: spatial size of X
-
-% OUTPUT:
-% I: output cell array
-
-% Written by Eftychios A. Pnevmatikakis, Simons Foundation, 2016
-
-I = cell(length(xx_s),length(yy_s),length(zz_s));
-nd = length(sz);
-if nd == 2; sz(3) = 1; end
-for i = 1:length(xx_s)
- for j = 1:length(yy_s)
- for k = 1:length(zz_s)
- extended_grid = [max(xx_s(i)-overlap(1),1),min(xx_f(i)+overlap(1),sz(1)),max(yy_s(j)-overlap(2),1),min(yy_f(j)+overlap(2),sz(2)),max(zz_s(k)-overlap(3),1),min(zz_f(k)+overlap(3),sz(3))];
- if nd == 2
- I{i,j} = X(extended_grid(1):extended_grid(2),extended_grid(3):extended_grid(4),:);
- else
- I{i,j,k} = X(extended_grid(1):extended_grid(2),extended_grid(3):extended_grid(4),extended_grid(5):extended_grid(6),:);
- end
- end
- end
-end
-end
-
-function X = cell2mat_ov(I,xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,overlap,sz)
-
-% converts a cell array to a matrix when the cell elements overlap
-% INPUTS:
-% I: cell array
-% grid_size: true size of each element
-% overlap: amount of overlap in each direction
-% d1: number of rows of matrix
-% d2: number of columns of matrix
-
-% OUTPUT:
-% X: output matrix
-
-% Written by Eftychios A. Pnevmatikakis, Simons Foundation, 2016
-
-X = NaN([sz,size(I{1,1},length(sz)+1)]);
-if length(sz) == 2; sz(3) = 1; end
-
-for i = 1:length(xx_f)
- for j = 1:length(yy_f)
- for k = 1:length(zz_f)
- extended_grid = [max(xx_s(i)-overlap(1),1),min(xx_f(i)+overlap(1),sz(1)),max(yy_s(j)-overlap(2),1),min(yy_f(j)+overlap(2),sz(2)),max(zz_s(k)-overlap(3),1),min(zz_f(k)+overlap(3),sz(3))];
- X(xx_s(i):xx_f(i),yy_s(j):yy_f(j),zz_s(k):zz_f(k)) = ...
- I{i,j,k}(1+(xx_s(i)-extended_grid(1)):end-(extended_grid(2)-xx_f(i)),1+(yy_s(j)-extended_grid(3)):end-(extended_grid(4)-yy_f(j)),1+(zz_s(k)-extended_grid(5)):end-(extended_grid(6)-zz_f(k)));
- end
- end
-end
-end
-
-function [xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,xx_us,xx_uf,yy_us,yy_uf,zz_us,zz_uf] = construct_grid(grid_size,mot_uf,d1,d2,d3,min_patch_size)
-
-xx_s = 1:grid_size(1):d1;
-yy_s = 1:grid_size(2):d2;
-zz_s = 1:grid_size(3):d3;
-
-xx_f = [xx_s(2:end)-1,d1];
-yy_f = [yy_s(2:end)-1,d2];
-zz_f = [zz_s(2:end)-1,d3];
-
-if xx_f(end)-xx_s(end) + 1 < min_patch_size(1) && length(xx_s) > 1; xx_s(end) = []; xx_f(end-1) = []; end
-if yy_f(end)-yy_s(end) + 1 < min_patch_size(2) && length(yy_s) > 1; yy_s(end) = []; yy_f(end-1) = []; end
-if zz_f(end)-zz_s(end) + 1 < min_patch_size(3) && length(zz_s) > 1; zz_s(end) = []; zz_f(end-1) = []; end
-
-grid_size_us = floor(grid_size./mot_uf);
-if mot_uf(1) > 1
- xx_us = 1:grid_size_us(1):d1;
- xx_uf = [xx_us(2:end)-1,d1];
-else
- xx_us = xx_s; xx_uf = xx_f;
-end
-if mot_uf(2) > 1
- yy_us = 1:grid_size_us(2):d2;
- yy_uf = [yy_us(2:end)-1,d2];
-else
- yy_us = yy_s; yy_uf = yy_f;
-end
-if mot_uf(3) > 1
- zz_us = 1:grid_size_us(3):d3;
- zz_uf = [zz_us(2:end)-1,d3];
-else
- zz_us = zz_s; zz_uf = zz_f;
-end
-end
diff --git a/code/algorithms/+stack/+reshape/imsplit.m b/code/algorithms/+stack/+reshape/imsplit.m
deleted file mode 100644
index dbf9cddf4..000000000
--- a/code/algorithms/+stack/+reshape/imsplit.m
+++ /dev/null
@@ -1,120 +0,0 @@
-function imOut = getChunkedData(IM, chunk, sz, varargin)
-
-param = struct('numRows', 4, 'numCols', 4);
-param = utility.parsenvpairs(param, [], varargin);
-
-if nargin < 2; chunk = true; end
-
- if ~chunk
- d1 = sz(1); d2 = sz(2); d3 = sz(3);
- else
- [d1,d2,d3] = size(IM);
- end
-
- gridSize = [d1/param.numRows, d2/param.numCols, d3];
- gridSize = ceil(gridSize);
-
- [xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,xx_us,xx_uf,yy_us,yy_uf,zz_us,zz_uf] = ...
- construct_grid(gridSize, [1,1,1] , d1,d2,d3, [32,32,16] );
-
- if chunk
- imOut = mat2cell_ov(IM,xx_s,xx_f,yy_s,yy_f,zz_s,zz_f, [1,1,0],[d1,d2,d3]);
- else %unchunk
- imOut = cell2mat_ov(IM,xx_us,xx_uf,yy_us,yy_uf,zz_us,zz_uf, [1,1,0], [d1,d2,d3]);
- end
-end
-
-function I = mat2cell_ov(X,xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,overlap,sz)
-
-% converts a matrix into a cell array with overlapping elements
-% INPUTS:
-% X: Input matrix
-% grid_size: size of each element without overlap
-% overlap: amount of overlap
-% sz: spatial size of X
-
-% OUTPUT:
-% I: output cell array
-
-% Written by Eftychios A. Pnevmatikakis, Simons Foundation, 2016
-
-I = cell(length(xx_s),length(yy_s),length(zz_s));
-nd = length(sz);
-if nd == 2; sz(3) = 1; end
-for i = 1:length(xx_s)
- for j = 1:length(yy_s)
- for k = 1:length(zz_s)
- extended_grid = [max(xx_s(i)-overlap(1),1),min(xx_f(i)+overlap(1),sz(1)),max(yy_s(j)-overlap(2),1),min(yy_f(j)+overlap(2),sz(2)),max(zz_s(k)-overlap(3),1),min(zz_f(k)+overlap(3),sz(3))];
- if nd == 2
- I{i,j} = X(extended_grid(1):extended_grid(2),extended_grid(3):extended_grid(4),:);
- else
- I{i,j,k} = X(extended_grid(1):extended_grid(2),extended_grid(3):extended_grid(4),extended_grid(5):extended_grid(6),:);
- end
- end
- end
-end
-end
-
-function X = cell2mat_ov(I,xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,overlap,sz)
-
-% converts a cell array to a matrix when the cell elements overlap
-% INPUTS:
-% I: cell array
-% grid_size: true size of each element
-% overlap: amount of overlap in each direction
-% d1: number of rows of matrix
-% d2: number of columns of matrix
-
-% OUTPUT:
-% X: output matrix
-
-% Written by Eftychios A. Pnevmatikakis, Simons Foundation, 2016
-
-X = NaN([sz,size(I{1,1},length(sz)+1)]);
-if length(sz) == 2; sz(3) = 1; end
-
-for i = 1:length(xx_f)
- for j = 1:length(yy_f)
- for k = 1:length(zz_f)
- extended_grid = [max(xx_s(i)-overlap(1),1),min(xx_f(i)+overlap(1),sz(1)),max(yy_s(j)-overlap(2),1),min(yy_f(j)+overlap(2),sz(2)),max(zz_s(k)-overlap(3),1),min(zz_f(k)+overlap(3),sz(3))];
- X(xx_s(i):xx_f(i),yy_s(j):yy_f(j),zz_s(k):zz_f(k)) = ...
- I{i,j,k}(1+(xx_s(i)-extended_grid(1)):end-(extended_grid(2)-xx_f(i)),1+(yy_s(j)-extended_grid(3)):end-(extended_grid(4)-yy_f(j)),1+(zz_s(k)-extended_grid(5)):end-(extended_grid(6)-zz_f(k)));
- end
- end
-end
-end
-
-function [xx_s,xx_f,yy_s,yy_f,zz_s,zz_f,xx_us,xx_uf,yy_us,yy_uf,zz_us,zz_uf] = construct_grid(grid_size,mot_uf,d1,d2,d3,min_patch_size)
-
-xx_s = 1:grid_size(1):d1;
-yy_s = 1:grid_size(2):d2;
-zz_s = 1:grid_size(3):d3;
-
-xx_f = [xx_s(2:end)-1,d1];
-yy_f = [yy_s(2:end)-1,d2];
-zz_f = [zz_s(2:end)-1,d3];
-
-if xx_f(end)-xx_s(end) + 1 < min_patch_size(1) && length(xx_s) > 1; xx_s(end) = []; xx_f(end-1) = []; end
-if yy_f(end)-yy_s(end) + 1 < min_patch_size(2) && length(yy_s) > 1; yy_s(end) = []; yy_f(end-1) = []; end
-if zz_f(end)-zz_s(end) + 1 < min_patch_size(3) && length(zz_s) > 1; zz_s(end) = []; zz_f(end-1) = []; end
-
-grid_size_us = floor(grid_size./mot_uf);
-if mot_uf(1) > 1
- xx_us = 1:grid_size_us(1):d1;
- xx_uf = [xx_us(2:end)-1,d1];
-else
- xx_us = xx_s; xx_uf = xx_f;
-end
-if mot_uf(2) > 1
- yy_us = 1:grid_size_us(2):d2;
- yy_uf = [yy_us(2:end)-1,d2];
-else
- yy_us = yy_s; yy_uf = yy_f;
-end
-if mot_uf(3) > 1
- zz_us = 1:grid_size_us(3):d3;
- zz_uf = [zz_us(2:end)-1,d3];
-else
- zz_us = zz_s; zz_uf = zz_f;
-end
-end
diff --git a/code/algorithms/+stack/+reshape/mergeImageBlocks.m b/code/algorithms/+stack/+reshape/mergeImageBlocks.m
new file mode 100644
index 000000000..a8f42fd27
--- /dev/null
+++ b/code/algorithms/+stack/+reshape/mergeImageBlocks.m
@@ -0,0 +1,41 @@
+function IM = mergeImageBlocks(blocks, outputSize, overlap)
+%MERGEIMAGEBLOCKS Reassemble a cell grid of overlapping 2-D blocks.
+%
+% IM = stack.reshape.mergeImageBlocks(blocks, outputSize) merges the
+% (numRows x numCols) cell array of overlapping 2-D image blocks
+% produced by stack.reshape.splitImage into a single outputSize image,
+% trimming the 1-pixel overlap on shared edges back to a clean tiling.
+%
+% IM = stack.reshape.mergeImageBlocks(blocks, outputSize, overlap)
+% uses a custom overlap (in pixels; default 1). Must match the value
+% used when the blocks were created.
+%
+% Syntax:
+% IM = stack.reshape.mergeImageBlocks(blocks, outputSize)
+% IM = stack.reshape.mergeImageBlocks(blocks, outputSize, overlap)
+
+ arguments
+ blocks cell
+ outputSize (1,2) double {mustBePositive, mustBeInteger}
+ overlap (1,1) double {mustBeNonnegative, mustBeInteger} = 1
+ end
+
+ [numRows, numCols] = size(blocks);
+ h = outputSize(1);
+ w = outputSize(2);
+ rowEdges = floor(linspace(0, h, numRows + 1));
+ colEdges = floor(linspace(0, w, numCols + 1));
+
+ IM = zeros(h, w, 'like', blocks{1,1});
+ for i = 1:numRows
+ rowDst = (rowEdges(i)+1):rowEdges(i+1);
+ srcR0 = rowEdges(i) + 1 - max(rowEdges(i) + 1 - overlap, 1) + 1;
+ for j = 1:numCols
+ colDst = (colEdges(j)+1):colEdges(j+1);
+ srcC0 = colEdges(j) + 1 - max(colEdges(j) + 1 - overlap, 1) + 1;
+ IM(rowDst, colDst) = blocks{i,j}( ...
+ srcR0 : srcR0 + numel(rowDst) - 1, ...
+ srcC0 : srcC0 + numel(colDst) - 1);
+ end
+ end
+end
diff --git a/code/algorithms/+stack/+reshape/splitImage.m b/code/algorithms/+stack/+reshape/splitImage.m
new file mode 100644
index 000000000..71b09566c
--- /dev/null
+++ b/code/algorithms/+stack/+reshape/splitImage.m
@@ -0,0 +1,42 @@
+function blocks = splitImage(IM, numRows, numCols, overlap)
+%SPLITIMAGE Split an H-by-W(-by-T) array into a (numRows x numCols) cell grid.
+%
+% blocks = stack.reshape.splitImage(IM, numRows, numCols) splits the
+% first two dimensions of IM into a numRows-by-numCols cell array of
+% sub-arrays. Each block extends one pixel into its neighbours on each
+% side (clamped at the image borders) so that per-pixel computations
+% needing a 1-pixel neighbourhood can be performed block-wise. The third
+% dimension (e.g. time) is preserved in full inside each block.
+%
+% blocks = stack.reshape.splitImage(IM, numRows, numCols, overlap) uses
+% a custom overlap (in pixels; default 1).
+%
+% Use stack.reshape.mergeImageBlocks to reassemble the blocks; the
+% overlap region is trimmed during reassembly.
+%
+% Syntax:
+% blocks = stack.reshape.splitImage(IM, numRows, numCols)
+% blocks = stack.reshape.splitImage(IM, numRows, numCols, overlap)
+
+ arguments
+ IM
+ numRows (1,1) double {mustBePositive, mustBeInteger}
+ numCols (1,1) double {mustBePositive, mustBeInteger}
+ overlap (1,1) double {mustBeNonnegative, mustBeInteger} = 1
+ end
+
+ [h, w, ~] = size(IM);
+ rowEdges = floor(linspace(0, h, numRows + 1));
+ colEdges = floor(linspace(0, w, numCols + 1));
+
+ blocks = cell(numRows, numCols);
+ for i = 1:numRows
+ r0 = max(rowEdges(i) + 1 - overlap, 1);
+ r1 = min(rowEdges(i+1) + overlap, h);
+ for j = 1:numCols
+ c0 = max(colEdges(j) + 1 - overlap, 1);
+ c1 = min(colEdges(j+1) + overlap, w);
+ blocks{i,j} = IM(r0:r1, c0:c1, :);
+ end
+ end
+end
diff --git a/code/algorithms/+stack/+zproject/globalCorrelation.m b/code/algorithms/+stack/+zproject/globalCorrelation.m
index 4c01c314f..d97bf1a50 100644
--- a/code/algorithms/+stack/+zproject/globalCorrelation.m
+++ b/code/algorithms/+stack/+zproject/globalCorrelation.m
@@ -24,7 +24,7 @@
imageSignal = single( squeeze(imageSignal) );
% Divide data into chunks to use less memory during calculation
-tmpIm = stack.reshape.imsplit(IM, true, [], 'numRows', numRows, 'numCols', numCols);
+tmpIm = stack.reshape.splitImage(IM, numRows, numCols);
tmpC = cell(size(tmpIm));
for i = 1:size(tmpIm,1)
@@ -43,9 +43,9 @@
end
end
-% Unchunk data
+% Reassemble blocks into a single image
[d1,d2,~] = size(IM);
-cIm = stack.reshape.imsplit(tmpC, false, [d1,d2,1], 'numRows', numRows, 'numCols', numCols);
+cIm = stack.reshape.mergeImageBlocks(tmpC, [d1,d2]);
% cast...
cIm = cIm .* nansen.util.range(P) + P(1);
diff --git a/code/algorithms/+stack/+zproject/localCorrelation.m b/code/algorithms/+stack/+zproject/localCorrelation.m
index e3ba04efc..f00e9589b 100644
--- a/code/algorithms/+stack/+zproject/localCorrelation.m
+++ b/code/algorithms/+stack/+zproject/localCorrelation.m
@@ -16,8 +16,7 @@
showWaitbar = false;
- getChunkedData = @stack.reshape.imsplit;
- tmpIm = getChunkedData(imArray, true, [], 'numRows', numRows, 'numCols', numCols);
+ tmpIm = stack.reshape.splitImage(imArray, numRows, numCols);
tmpC = cell(size(tmpIm));
@@ -45,7 +44,7 @@
end
[d1,d2,~] = size(imArray);
- cIm = getChunkedData(tmpC, false, [d1,d2,1], 'numRows', numRows, 'numCols', numCols);
+ cIm = stack.reshape.mergeImageBlocks(tmpC, [d1,d2]);
%cIm = stack.makeuint8(cIm);
cIm = cIm .* nansen.util.range(P) + P(1);