diff --git a/miss_hit.cfg b/miss_hit.cfg index cd8e82be..727d20b4 100644 --- a/miss_hit.cfg +++ b/miss_hit.cfg @@ -36,7 +36,7 @@ copyright_entity: "Wellcome Trust Centre for Neuroimaging" tab_width: 2 # metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html) -metric "cnest": limit 5 +metric "cnest": limit 4 metric "file_length": limit 1000 -metric "cyc": limit 22 -metric "parameters": limit 7 +metric "cyc": limit 20 +metric "parameters": limit 6 diff --git a/src/plotting/createMontage.m b/src/plotting/createMontage.m new file mode 100644 index 00000000..550bdef4 --- /dev/null +++ b/src/plotting/createMontage.m @@ -0,0 +1,135 @@ +function montage = createMontage(varargin) + % + % USAGE + % + % montage = createMontage(img, ... + % 'columns', 9, ... + % 'rotate', true, ... + % 'cmap', 'gray', ... + % 'visibility', 'on', ... + % 'shape', 'max', ... + % 'cxs', [0 255]) + % + % + % Simple function to create a montage / + % mosaic of multiple slices from a single 3D + % image matrix. + % + % INPUT: + % img - 3D (x,y,z) image matrix + % columns - number of columns in montage + % (rows are calculated accordingly) + % rotate - rotate images 90 deg clockwise? yes = 1; no = 0. + % cmap - figure colormap + % visibility - show figure? + % + % OUTPUT: + % output - structure with montage data + + % (C) Copyright 2022 bidspm developers + + % TODO: Needs improvement i.t.o RAS/LAS orientation specification + % and image layout... + + args = inputParser; + + addRequired(args, 'img'); + addParameter(args, 'columns', 9, @isnumeric); + addParameter(args, 'rotate', true, @islogical); + addParameter(args, 'cmap', 'gray', @ischar); + addParameter(args, 'visibility', 'on', @ischar); + addParameter(args, 'shape', 'max', @ischar); % max or square + addParameter(args, 'cxs', 'auto'); + + parse(args, varargin{:}); + + img = args.Results.img; + columns = args.Results.columns; + rotate = args.Results.rotate; + cmap = args.Results.cmap; + visibility = args.Results.visibility; + shape = args.Results.shape; + cxs = args.Results.cxs; + + montage = struct; + [Ni, Nj, Nk] = size(img); + + % Rotate image slices if required + if rotate + img_orig = img; + clear img; + for p = 1:Nk + img(:, :, p) = rot90(img_orig(:, :, p)); + end + end + + % Determine amount of rows and filler slices + rows = floor(Nk / columns); + if rows == 0 + rows = 1; + end + fill = mod(Nk, columns); + if fill == 0 + N_fill = 0; + else + N_fill = columns - mod(Nk, columns); + end + if rotate + filler = zeros(Nj, Ni); + else + filler = zeros(Ni, Nj); + end + + montage.rows = rows; + montage.columns = columns; + montage.N_fill = N_fill; + + parts = {}; + % 1 - Concatenate slices together horizontally, per row (except last). + % 2 - Concatenate rows together vertically + for i = 1:rows + for j = 1:columns + if j == 1 + parts{i} = img(:, :, columns * (i - 1) + j); + else + parts{i} = cat(2, parts{i}, img(:, :, columns * (i - 1) + j)); + end + end + if i == 1 + whole = parts{i}; + else + whole = cat(1, whole, parts{i}); + end + end + + % 1 - Concatenate filler slices to last row, if required. + % 2 - Concatenate last row to whole matrix, if required. + if N_fill ~= 0 + % last row + last_parts = img(:, :, rows * columns + 1); + for k = (rows * columns + 2):Nk + last_parts = cat(2, last_parts, img(:, :, k)); + end + for m = 1:N_fill + last_parts = cat(2, last_parts, filler); + end + montage.whole_img = cat(1, whole, last_parts); + else + montage.whole_img = whole; + end + + f = initMontageFigure(shape, visibility); + + ax = subplot(1, 1, 1); + im = imagesc(ax, montage.whole_img); + + colormap(cmap); + if ~isempty(cxs) + caxis(ax, cxs); + end + + montage.im = im; + montage.f = f; + montage.ax = ax; + +end diff --git a/src/plotting/createOverlayMontage.m b/src/plotting/createOverlayMontage.m new file mode 100644 index 00000000..2897ef27 --- /dev/null +++ b/src/plotting/createOverlayMontage.m @@ -0,0 +1,133 @@ +function output = createOverlayMontage(varargin) + + % (C) Copyright 2024 bidspm developers + + % fmrwhy_util_createOverlayMontage(tsnr_img{i}, overlayImg, 9, 1, '', ... + % 'hot', 'off', 'max', [0 250], [33, 168, 10], tsnr_saveAss{i}); + + % Function to create montages of images/rois overlaid on a template image + + rgbcolors = [255, 255, 191; ... + 215, 25, 28; ... + 253, 174, 97; ... + 171, 217, 233; ... + 44, 123, 182]; + + args = inputParser; + + addRequired(args, 'templateImg'); + addRequired(args, 'overlayImg'); + addParameter(args, 'columns', 9, @isnumeric); + addParameter(args, 'rotate', true, @islogical); + addParameter(args, 'cmap', 'gray', @ischar); + addParameter(args, 'visibility', 'on', @ischar); + addParameter(args, 'shape', 'max', @ischar); % max or square + addParameter(args, 'cxs', 'auto'); + addParameter(args, 'rgbcolors', rgbcolors); + addParameter(args, 'saveAs', ''); + + parse(args, varargin{:}); + + templateImg = args.Results.templateImg; + overlayImg = args.Results.overlayImg; + columns = args.Results.columns; + rotate = args.Results.rotate; + cmap = args.Results.cmap; + visibility = args.Results.visibility; + shape = args.Results.shape; + cxs = args.Results.cxs; + rgbcolors = args.Results.rgbcolors; + saveAs = args.Results.saveAs; + + % Structure to save output + output = struct; + alpha = 0.2; + plot_contour = 1; + rgbcolors = rgbcolors / 255; + + % Create background montage + montage_template = createMontage(templateImg, ... + 'columns', columns, ... + 'rotate', rotate, ... + 'cmap', cmap, ... + 'visibility', 'off', ... + 'shape', shape, ... + 'cxs', cxs); + + % Create figures with background montage and overlaid masks + f = initMontageFigure(shape, visibility); + imagesc(montage_template.whole_img); + colormap(cmap); + if ~isempty(cxs) + caxis(cxs); + end + ax = gca; + outerpos = ax.OuterPosition; + ti = ax.TightInset; + left = outerpos(1) + ti(1); + bottom = outerpos(2) + ti(2); + ax_width = outerpos(3) - ti(1) - ti(3); + ax_height = outerpos(4) - ti(2) - ti(4); + ax.Position = [left bottom ax_width ax_height]; + hold(ax, 'on'); + [Nimx, Nimy] = size(montage_template.whole_img); + oo = ones(Nimx, Nimy); + + if iscell(overlayImg) + for i = 1:numel(overlayImg) + montage_overlay{i} = createMontage(overlayImg{i}, ... + 'columns', columns, ... + 'rotate', rotate, ... + 'cmap', cmap, ... + 'visibility', 'off', ... + 'shape', shape, ... + 'cxs', 'auto'); + end + else + montage_overlay = {}; + montage_overlay{1} = createMontage(overlayImg, ... + 'columns', columns, ... + 'rotate', rotate, ... + 'cmap', cmap, ... + 'visibility', 'off', ... + 'shape', shape, ... + 'cxs', 'auto'); + end + + for i = 1:numel(montage_overlay) + rbgclr = rgbcolors(i, :); + clr = cat(3, rbgclr(1) * oo, rbgclr(2) * oo, rbgclr(3) * oo); + imC = imagesc(ax, clr); + set(imC, 'AlphaData', alpha * montage_overlay{i}.whole_img); + if plot_contour + bound_whole_bin = bwboundaries(montage_overlay{i}.whole_img); + Nblobs_bin = numel(bound_whole_bin); + for b = 1:Nblobs_bin + p = plot(ax, bound_whole_bin{b, 1}(:, 2), bound_whole_bin{b, 1}(:, 1), ... + 'color', rbgclr, 'LineWidth', 1); + end + end + end + + hold(ax, 'off'); + set(ax, 'xtick', []); + set(ax, 'xticklabel', []); + set(ax, 'ytick', []); + set(ax, 'yticklabel', []); + set(ax, 'ztick', []); + set(ax, 'zticklabel', []); + + output.ax = ax; + output.f = f; + + if saveAs ~= 0 + print(f, saveAs, '-dpng', '-r0'); + end + % Close necessary figure handles + close(montage_template.f); + for i = 1:numel(montage_overlay) + close(montage_overlay{i}.f); + end + if strcmp(visibility, 'off') + close(f); + end diff --git a/src/plotting/initMontageFigure.m b/src/plotting/initMontageFigure.m new file mode 100644 index 00000000..38a43655 --- /dev/null +++ b/src/plotting/initMontageFigure.m @@ -0,0 +1,24 @@ +function f = initMontageFigure(shape, visibility) + % (C) Copyright 2024 bidspm developers + + scr_size = get(0, 'ScreenSize'); + dist = scr_size(4); + if scr_size(3) < dist + dist = scr_size(3); + end + % Create figure - outerposition = [left bottom width height] + + if strcmp(shape, 'max') + f = figure('visible', visibility, ... + 'units', 'normalized', ... + 'outerposition', [0 0 1 1]); + elseif strcmp(shape, 'square') + f = figure('visible', visibility, ... + 'units', 'pixels', ... + 'outerposition', [0 0 dist dist]); + else + f = figure('visible', visibility, ... + 'units', 'pixels', ... + 'outerposition', [0 0 dist dist]); + end +end diff --git a/untitled.m b/untitled.m new file mode 100644 index 00000000..5c2c1a8a --- /dev/null +++ b/untitled.m @@ -0,0 +1,24 @@ +% (C) Copyright 2022 bidspm developers +close all; + +bidspm_dir = '/home/remi/github/cpp-lln-lab/bidspm'; + +demos_dir = fullfile(bidspm_dir, 'demos/MoAE/outputs/derivatives/'); + +overlay_img = fullfile(demos_dir, ... + 'bidspm-stats/sub-01/task-auditory_space-MNI152NLin6Asym_FWHM-8/mask.nii'); + +template_img = fullfile( ... + demos_dir, ... + 'bidspm-preproc', 'sub-01', 'func', ... + 'sub-01_task-auditory_space-MNI152NLin6Asym_desc-smth8_bold.nii'); + +template_hdr = spm_vol(template_img); +template = spm_read_vols(template_hdr(1)); + +mask_hdr = spm_vol(overlay_img); +mask = spm_read_vols(mask_hdr); + +% createMontage(template, 'shape', 'square'); +createOverlayMontage(template, mask, 'rgbcolors', [255, 0, 0], ... + 'shape', 'square');