Commit 2afce331 authored by Michael Krause's avatar Michael Krause 🎉
Browse files

Import

parents
octave-workspace
# What's this?
This is a very simple pair of functions `pack` and `unpack` that accept a 3D
matrix and a binary mask of the same dimension. It will "compress" the matrix
by cutting away everything the is not part of a cuboid hull of the mask and
saves the original dimension and position in a structure.
It's intended to be used in larger arrays of arrays that contain masked brain
data (hence 3D) to reduce the memory footprint.
# Example
(see [doc/zipmask_example.m](doc/zipmask_example.m) )
First, we create a 20x20x20 cube and a spherical mask in the center:
```
d = 20;
m = rand(d, d, d);
mask = zeros(d, d, d);
c = ceil(d/2);
for x = 1:d
for y = 1:d
for z = 1:d
if (x - c)**2 + (y - c)**2 + (z - c)**2 <= 16
mask(x, y, z) = 1;
end
end
end
end
plot_lattice3D(m, mask, 'original matrix/mesh');
```
![plot1](doc/plot1.png)
We cut out everything outside of the mask with `zipmask_pack()`:
```
s = zipmask_pack(m, mask);
plot_lattice3D(s.data, s.mask, 'compressed matrix/mesh');
```
![plot1](doc/plot1.png)
And re-grow the small data with `zipmask_unpack()`:
```
[um, umask] = zipmask_unpack(s);
plot_lattice3D(um, umask, 'expanded matrix/mesh');
```
![plot1](doc/plot1.png)
# Limitations
Only works in 3D for now.
function zipmask_example()
function plot_lattice3D(matrix, mask, t);
figure;
l = size(matrix, 1); % expect cube
k = 1;
j = 1;
for x = 1:l
for y = 1:l
for z = 1:l
if mask(x, y, z)
mask_xs(j) = x;
mask_ys(j) = y;
mask_zs(j) = z;
j = j + 1;
else
matrix_xs(k) = x;
matrix_ys(k) = y;
matrix_zs(k) = z;
k = k + 1;
end
end
end
end
scatter3(mask_xs, mask_ys, mask_zs, 10, 'filled', '-bo');
hold on;
scatter3(matrix_xs, matrix_ys, matrix_zs, 1, '-ro');
axis equal;
axis square;
title(t);
end
d = 20;
m = rand(d, d, d);
% create spherecial mask around the center of the cube with r=4
mask = zeros(d, d, d);
c = ceil(d/2);
for x = 1:d
for y = 1:d
for z = 1:d
if (x - c)**2 + (y - c)**2 + (z - c)**2 <= 16
mask(x, y, z) = 1;
end
end
end
end
plot_lattice3D(m, mask, 'original matrix/mesh');
% mask and pack the data
s = zipmask_pack(m, mask);
plot_lattice3D(s.data, s.mask, 'compressed matrix/mesh');
% unpack data
[um, umask] = zipmask_unpack(s);
plot_lattice3D(um, umask, 'expanded matrix/mesh');
end
function out = zipmask_pack(matrix, mask);
%
% struct = zipmask_pack(X, Y);
%
% Apply a binary mask Y on a matrix X and reduces the matrix dimensions to
% a simple cuboid hull. The function returns a strcuture containing the
% smaller matrices for matrix and mask and references to the original size and
% its position in the original space.
%
% Use zipmask_unpack(struct) to grow the matrix back into its original size.
% Note that this is not the inverse operation to pack.
%
% Only works with 3D data right now.
%
d_mat = size(matrix);
d_mask = size(mask);
if ( length(d_mat) ~= length(d_mask) || ...
any(d_mat ~= d_mask) )
d_mat
d_mask
error('Matrix and ROI mask dimensions have to match');
end
if length(d_mat) ~= 3
error("Only implemented for 3D arrays, yet");
end
if all(all(all(mask == 0)))
warning("Mask is empty");
out.data = [];
out.mask = [];
out.dimension = size(matrix);
out.position = [1, 1, 1];
out.ratio = 0;
return;
end
% get cuboid around mask
mins(1) = find(sum(squeeze(sum(mask, 3)), 2) ~= 0)(1);
mins(2) = find(sum(squeeze(sum(mask, 3)), 1) ~= 0)(1);
mins(3) = find(sum(squeeze(sum(mask, 2)), 1) ~= 0)(1);
maxs(1) = find(sum(squeeze(sum(mask, 2)), 2) ~= 0)(end);
maxs(2) = find(sum(squeeze(sum(mask, 1)), 2) ~= 0)(end);
maxs(3) = find(sum(squeeze(sum(mask, 1)), 1) ~= 0)(end);
% apply mask
matrix = matrix .* mask;
% set outputs
out.data = matrix(mins(1):maxs(1), mins(2):maxs(2), mins(3):maxs(3));
out.mask = mask(mins(1):maxs(1), mins(2):maxs(2), mins(3):maxs(3));
out.position = [mins(1), mins(2), mins(3)];
out.dimension = size(matrix);
out.ratio = prod(size(out.data)) / prod(size(matrix));
end
function zipmask_test()
d = 10;
m = rand(d, d, d);
zs = zeros(d, d, d);
function test_cube()
mask = zs;
l = ceil(d/4);
mask(1:l, 1:l, 1:l) = 1;
s = zipmask_pack(m, mask);
[um, umask] = zipmask_unpack(s);
assert(umask, mask);
end
function test_plane()
mask = zs;
mask(:, :, 1) = 1;
s = zipmask_pack(m, mask);
[um, umask] = zipmask_unpack(s);
assert(umask, mask);
end
function test_sphere()
mask = zs;
c = ceil(d/2);
for x = 1:d
for y = 1:d
for z = 1:d
if (x - c)**2 + (y - c)**2 + (z - c)**2 <= 4
mask(x, y, z) = 1;
end
end
end
end
s = zipmask_pack(m, mask);
[um, umask] = zipmask_unpack(s);
assert(umask, mask);
assert(um, m .* mask);
clear x y z;
end
function test_scalar()
mask = zs;
p = randi([1,d], [1,3]);
mask(p(1), p(2), p(3)) = 1;
s = zipmask_pack(m, mask);
[um, umask] = zipmask_unpack(s);
assert(umask, mask);
assert(um, m .* mask);
end
function test_empty()
mask = zs;
s = zipmask_pack(m, mask);
[um, umask] = zipmask_unpack(s);
assert(umask, mask);
assert(um, m .* mask);
end
test_cube()
test_plane()
test_sphere()
test_scalar()
test_empty()
end
function [matrix, mask] = zipmask_unpack(s);
%
% [X, Y] = zipmask_unpack(S);
%
% Blows up a structure with a compressed matrix/roi into its original size.
%
% Use zipmask_pack(X, Y) to pack/reduce a matrix based on a mask.
matrix = zeros(s.dimension);
mask = matrix;
x0 = s.position(1);
y0 = s.position(2);
z0 = s.position(3);
[xl, yl, zl] = size(s.data);
matrix(x0:x0+xl-1, y0:y0+yl-1, z0:z0+zl-1) = s.data;
mask(x0:x0+xl-1, y0:y0+yl-1, z0:z0+zl-1) = s.mask;
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment