mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 20:39:08 +09:00
initial commit
This commit is contained in:
@@ -0,0 +1,523 @@
|
||||
%IMAGESC3D 3D wrapper for imagesc
|
||||
% imagesc3D supports the same parameters as Matlab's imagesc. In addition, the following
|
||||
% parameters can be set
|
||||
%
|
||||
% init_frame... starting frame number (default 1)
|
||||
% slider_axis... axis along which you want to use imagesc (default 3)
|
||||
% fps... frames per second (default 25); will be adjusted by a factor of 1.2 to account for internal overhead
|
||||
% title_list... individual title for each frame (default {})
|
||||
% loop... run in a loop (default false)
|
||||
% reset_frame... stop resets frame to init_frame (default false)
|
||||
% autoplay... stark movie automatically (default false)
|
||||
% slider_position... slider position [left bottom width height] (default center of axis)
|
||||
% play_position... play button position [left bottom width height]
|
||||
% edit_position... edit box position [left bottom width height]
|
||||
% show_play_button... show/hide button; needs to be visible if loop=true; (default true)
|
||||
% show_edit_box... show/hide box
|
||||
% fnct... data processing function
|
||||
% order... change slice order in stack
|
||||
% save_movie... specify filename if a movie shall be written
|
||||
% movie_quality... image quality of the saved movie
|
||||
%
|
||||
% Complex images will be converted to RGB using c2image.
|
||||
%
|
||||
% If you are not using 'autplay', you can also set a global title instead
|
||||
% of a title list (similar to imagesc) and use '%d' to get the slice number
|
||||
% title('Random block - slice %d')
|
||||
%
|
||||
%
|
||||
% EXAMPLES:
|
||||
% imagesc3D(rand(256, 256, 100), 'fps', 10, 'loop', true)
|
||||
% imagesc3D(rand(256, 256)*1j)
|
||||
% imagesc3D(rand(20, 256, 256), 'slider_axis', 1);
|
||||
%
|
||||
%
|
||||
% Additionally, you can use imagesc/imagesc3D routines and trigger the movie by
|
||||
% calling the play method of a specified axis:
|
||||
%
|
||||
% figure(1);
|
||||
% imagesc3D(rand(256, 256, 100), 'fps', 20);
|
||||
% title('Random block - slice %d');
|
||||
% colorbar();
|
||||
% ax = gca;
|
||||
% ax.play();
|
||||
%
|
||||
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function imagesc3D(varargin)
|
||||
import math.isint
|
||||
import plotting.c2image
|
||||
|
||||
ax_img = {};
|
||||
if nargin == 1
|
||||
img = varargin{1};
|
||||
vararg = {};
|
||||
elseif nargin == 3 && isnumeric(varargin{1}) && isnumeric(varargin{2}) && (islogical(varargin{3}) || isnumeric(varargin{3}))
|
||||
img = varargin{3};
|
||||
ax_img = varargin(1:2);
|
||||
vararg = {};
|
||||
elseif (islogical(varargin{1}) || isnumeric(varargin{1})) && ischar(varargin{2})
|
||||
% assume that first argument is images, and next are
|
||||
% string+arguments
|
||||
img = varargin{1};
|
||||
vararg = varargin(2:end);
|
||||
elseif isnumeric(varargin{1}) && isnumeric(varargin{2}) && (islogical(varargin{3}) || isnumeric(varargin{3}))
|
||||
% assume that first two arguments are axis,and third is images, and next are
|
||||
% string+arguments
|
||||
ax_img = varargin(1:2);
|
||||
img = varargin{3};
|
||||
vararg = varargin(4:end);
|
||||
else
|
||||
error('Unknown combination of parameters')
|
||||
end
|
||||
|
||||
ax = gca;
|
||||
pos = ax.Position;
|
||||
slider_default = [pos(1)+pos(3)/2-0.06 pos(2)-0.1 0.14 0.05];
|
||||
play_default = [slider_default(1)-0.1 pos(2)-0.1 0.08 0.05];
|
||||
edit_default = [slider_default(1)+slider_default(3)+0.01 slider_default(2) 0.08 0.05];
|
||||
|
||||
par = inputParser;
|
||||
par.addParameter('fps', 25, @isnumeric) % maximal frame rate
|
||||
par.addParameter('init_frame', 1, @isnumeric) % starting frame number
|
||||
par.addParameter('title_list', {}, @iscell) % list of titles for each frame
|
||||
par.addParameter('slider_axis',3, @isnumeric) % array axis
|
||||
par.addParameter('loop', false, @islogical) % loop
|
||||
par.addParameter('reset_frame', false, @islogical) % stop resets frame to init_frame
|
||||
par.addParameter('autoplay', false, @islogical) % start loop automatically
|
||||
par.addParameter('slider_position',slider_default, @isnumeric) % slider position; [left bottom width height]
|
||||
par.addParameter('play_position',play_default, @isnumeric) % slider position; [left bottom width height]
|
||||
par.addParameter('edit_position', edit_default, @isnumeric) % edit position; [left bottom width height]
|
||||
par.addParameter('show_play_button',true, @islogical) % array axis
|
||||
par.addParameter('show_edit_box', true, @islogical) % edit box
|
||||
par.addParameter('fnct', @(x)x) % data processing function
|
||||
par.addParameter('order', 1:size(img,3), @isnumeric) % change slice order in stack
|
||||
par.addParameter('plot_residua', false, @islogical) % plot residua in the image
|
||||
par.addParameter('save_movie', '', @ischar) % specify filename if a movie shall be written
|
||||
par.addParameter('movie_quality', 80, @isnumeric) % movie quality
|
||||
|
||||
|
||||
par.parse(vararg{:})
|
||||
vars = par.Results;
|
||||
|
||||
vars.fps = vars.fps *1.2; % correct for overhead
|
||||
|
||||
% permute the array to slide along diferent axis
|
||||
switch vars.slider_axis
|
||||
case 1
|
||||
img = rot90(permute(img,[2,3,1]));
|
||||
case 2
|
||||
img = rot90(permute(img,[1,3,2]));
|
||||
end
|
||||
if any(cellfun(@(x)(strcmpi(x, 'order')), par.UsingDefaults))
|
||||
% redefine the order just in case that the axis were swapped, but only
|
||||
% if there is not use preference
|
||||
vars.order = 1:size(img,3);
|
||||
end
|
||||
|
||||
|
||||
if ~isempty(vars.title_list)
|
||||
assert(length(vars.title_list) == size(img,3), 'Number of titles has to correspond to number of frames')
|
||||
end
|
||||
|
||||
sz = size(img,3);
|
||||
|
||||
im = imhandles(gcf);
|
||||
ax = gca;
|
||||
if ~isprop(ax, 'index')
|
||||
ax.addprop('index');
|
||||
ax.index = length(im)+1;
|
||||
else
|
||||
if isprop(ax, 'play_handle')
|
||||
delete(ax.play_handle);
|
||||
end
|
||||
if isprop(ax, 'slider_handle')
|
||||
delete(ax.slider_handle);
|
||||
end
|
||||
if isprop(ax, 'edit_handle')
|
||||
delete(ax.edit_handle);
|
||||
end
|
||||
if isprop(ax, 'vars')
|
||||
ax.vars = [];
|
||||
end
|
||||
end
|
||||
|
||||
if sz>1
|
||||
% checks
|
||||
vars.init_frame = round(vars.init_frame);
|
||||
|
||||
if vars.init_frame > sz || vars.init_frame < 1
|
||||
warning('Initial frame exceeds stack size.')
|
||||
vars.init_frame = 1;
|
||||
end
|
||||
|
||||
vars.vargin = ax_img;
|
||||
|
||||
if ~ax.isprop('img')
|
||||
ax.addprop('img');
|
||||
end
|
||||
|
||||
ax.img = img;
|
||||
|
||||
if ~ax.isprop('play')
|
||||
ax.addprop('play');
|
||||
end
|
||||
ax.play = @(x)play(x);
|
||||
|
||||
if ~ax.isprop('stop')
|
||||
ax.addprop('stop');
|
||||
end
|
||||
ax.stop = @(x)stop(x);
|
||||
|
||||
if ~ax.isprop('update_fig')
|
||||
ax.addprop('update_fig');
|
||||
end
|
||||
ax.update_fig = @(x)update_fig(x);
|
||||
|
||||
|
||||
%%% set handles
|
||||
|
||||
% slider
|
||||
slider_handle=uicontrol(gcf,'Style','slider','Max',sz,'Min',1,...
|
||||
'Value',vars.init_frame,'SliderStep',[1/(sz-1) 10/(sz-1)],...
|
||||
'Units','normalized','Position',vars.slider_position);
|
||||
if ~isprop(slider_handle, 'ax_index')
|
||||
slider_handle.addprop('ax_index');
|
||||
slider_handle.ax_index = ax.index;
|
||||
end
|
||||
if ~ax.isprop('slider_handle')
|
||||
ax.addprop('slider_handle');
|
||||
ax.slider_handle = slider_handle;
|
||||
elseif ax.isprop('slider_handle') && ~ax.slider_handle.isvalid
|
||||
ax.slider_handle = slider_handle;
|
||||
end
|
||||
|
||||
% play button
|
||||
if vars.show_play_button
|
||||
visible_button = 'on';
|
||||
else
|
||||
visible_button = 'off';
|
||||
if vars.loop
|
||||
warning('Loop can not be aborted without buttons. Setting ''loop'' back to ''false''.');
|
||||
vars.loop = false;
|
||||
end
|
||||
end
|
||||
|
||||
play_handle=uicontrol(gcf,'Style','pushbutton','string','Play',...
|
||||
'Units','normalized','Position',vars.play_position, 'Visible', visible_button);
|
||||
if ~isprop(play_handle, 'ax_index')
|
||||
play_handle.addprop('ax_index');
|
||||
play_handle.ax_index = ax.index;
|
||||
end
|
||||
if ~ax.isprop('play_handle')
|
||||
ax.addprop('play_handle');
|
||||
ax.play_handle = play_handle;
|
||||
elseif ax.isprop('play_handle') && ~ax.play_handle.isvalid
|
||||
ax.play_handle = play_handle;
|
||||
end
|
||||
if ~ax.isprop('vars')
|
||||
ax.addprop('vars');
|
||||
ax.vars = vars;
|
||||
else
|
||||
ax.vars = vars;
|
||||
end
|
||||
set(play_handle,'Callback',{@play_callback,ax});
|
||||
|
||||
|
||||
% text edit
|
||||
if vars.show_edit_box
|
||||
visible_box = 'on';
|
||||
else
|
||||
visible_box = 'off';
|
||||
end
|
||||
edit_handle = uicontrol('style','edit','units','normalized', 'Position', vars.edit_position, 'Visible', visible_box);
|
||||
set(edit_handle, 'Callback', {@edit_callback, ax});
|
||||
if ~ax.isprop('edit_handle')
|
||||
ax.addprop('edit_handle');
|
||||
ax.edit_handle = edit_handle;
|
||||
elseif ax.isprop('edit_handle') && ~ax.edit_handle.isvalid
|
||||
ax.edit_handle = edit_handle;
|
||||
end
|
||||
if ~isprop(edit_handle, 'ax_index')
|
||||
edit_handle.addprop('ax_index');
|
||||
edit_handle.ax_index = ax.index;
|
||||
end
|
||||
|
||||
|
||||
% set callback functions
|
||||
set(slider_handle,'Callback',{@slider_callback,ax});
|
||||
set(edit_handle, 'String', num2str(get(ax.slider_handle,'Value')));
|
||||
|
||||
|
||||
if vars.autoplay
|
||||
play_callback(ax, ax, ax);
|
||||
end
|
||||
|
||||
update_fig(ax)
|
||||
|
||||
else
|
||||
% standard imagesc should be enough
|
||||
if ax.isprop('update_title') || ax.isprop('play_handle') || ax.isprop('vars')
|
||||
if ax.isprop('vars') && isfield(ax.vars, 'slider_handle')
|
||||
ax.vars = rmfield(ax.vars, 'slider_handle');
|
||||
end
|
||||
if ax.isprop('play_handle')
|
||||
delete(ax.play_handle);
|
||||
end
|
||||
cla(ax);
|
||||
end
|
||||
|
||||
img = gather(vars.fnct(img));
|
||||
|
||||
if ~isreal(img)
|
||||
img = c2image(img);
|
||||
end
|
||||
if ~isempty(ax_img)
|
||||
imagesc(ax_img{:}, img);
|
||||
else
|
||||
imagesc(img);
|
||||
end
|
||||
if ~isempty(vars.title_list)
|
||||
title(ax, vars.title_list{1}, 'Interpreter', 'none')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
% plotting function
|
||||
function update_fig(ax)
|
||||
import math.isint
|
||||
import plotting.c2image
|
||||
|
||||
% im = imhandles(gcf);
|
||||
vars = ax.vars;
|
||||
slice = round(get(ax.slider_handle,'Value'));
|
||||
slice = max(1, min(length(vars.order), slice));
|
||||
|
||||
% FIXME: everything works better without following lines
|
||||
% sl = gcbo();
|
||||
% if ~isempty(sl)
|
||||
% ax = findobj('index', sl.ax_index);
|
||||
% end
|
||||
|
||||
img = gather(vars.fnct(squeeze(ax.img(:,:,vars.order(slice),:))));
|
||||
|
||||
if ~isreal(img)
|
||||
img = c2image(img);
|
||||
end
|
||||
if vars.plot_residua
|
||||
[residua{2},residua{1}] = find(abs(utils.findresidues(img))>0.1);
|
||||
|
||||
end
|
||||
|
||||
% if the current axis is empty, use imagesc with remaining arguments
|
||||
if ~ax.isprop('update_title')
|
||||
ax.addprop('update_title');
|
||||
ax.addprop('user_title');
|
||||
ax.update_title = true;
|
||||
if ~isempty(vars.vargin)
|
||||
imagesc(vars.vargin{:}, img);
|
||||
else
|
||||
imagesc(img);
|
||||
end
|
||||
hold all
|
||||
if vars.plot_residua && ~isempty(residua{1})
|
||||
plot(residua{:},'or')
|
||||
elseif vars.plot_residua
|
||||
plot(0,0,'or')
|
||||
end
|
||||
hold off
|
||||
addlistener(ax.Title, 'String', 'PostSet', @(gt, event)callback_title_post(ax, ax));
|
||||
else
|
||||
% if we just need to update the figure, only update the data
|
||||
ax_data = ax.findobj('Type', 'Image');
|
||||
ax_data.CData = img;
|
||||
if vars.plot_residua
|
||||
ax_data = ax.findobj('Type', 'Line');
|
||||
ax_data(1).XData = residua{1};
|
||||
ax_data(1).YData = residua{2};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
% write title
|
||||
if isempty(vars.title_list)
|
||||
ax.update_title = false;
|
||||
if ~isempty(ax.user_title)
|
||||
title_text = sprintf(ax.user_title, vars.order(slice));
|
||||
title(ax, title_text, 'Interpreter', 'none');
|
||||
end
|
||||
ax.update_title = true;
|
||||
else
|
||||
if ~isempty(vars.title_list)
|
||||
title(ax, vars.title_list{vars.order(slice)}, 'Interpreter', 'none')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%% Callback subfunctions %%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
function slider_callback(~,~,ax)
|
||||
% ob = gco;
|
||||
% vars.ax_index = ob.ax_index;
|
||||
if ax.isprop('edit_handle')
|
||||
set(ax.edit_handle, 'string', num2str(round(get(ax.slider_handle,'Value'))));
|
||||
end
|
||||
update_fig(ax)
|
||||
drawnow()
|
||||
|
||||
end
|
||||
|
||||
|
||||
function callback_title_post(ax, ~, ~)
|
||||
if ax.update_title
|
||||
ax.user_title = ax.Title.String;
|
||||
try
|
||||
ax.Title.String = sprintf(ax.user_title, get(ax.slider_handle,'Value'));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function play_callback(~,~,ax)
|
||||
% ax = findobj('index', ax.slider_handle.ax_index);
|
||||
vars = ax.vars;
|
||||
if ax.isprop('slider_handle') && ax.slider_handle.isvalid
|
||||
update_slider = true;
|
||||
else
|
||||
update_slider = false;
|
||||
end
|
||||
if ax.isprop('edit_handle') && ax.edit_handle.isvalid
|
||||
update_edit = true;
|
||||
else
|
||||
update_edit = false;
|
||||
end
|
||||
try
|
||||
switch get(ax.play_handle,'string')
|
||||
case 'Play'
|
||||
if ~isempty(vars.save_movie)
|
||||
disp(['Saving movie to ' vars.save_movie]);
|
||||
writeobj = VideoWriter(vars.save_movie);
|
||||
writeobj.Quality=vars.movie_quality;
|
||||
writeobj.FrameRate=vars.fps;
|
||||
|
||||
open(writeobj);
|
||||
vars.writeobj = writeobj;
|
||||
end
|
||||
set(ax.play_handle,'string','Stop')
|
||||
sz = size(ax.img,3);
|
||||
pos = round(get(ax.slider_handle,'Value'));
|
||||
if pos == sz
|
||||
set(ax.slider_handle,'Value',1);
|
||||
pos = 1;
|
||||
end
|
||||
while pos <=sz
|
||||
|
||||
if strcmp(get(ax.play_handle,'string'), 'Play')
|
||||
break
|
||||
end
|
||||
if update_slider
|
||||
set(ax.slider_handle,'Value',pos)
|
||||
end
|
||||
if update_edit
|
||||
set(ax.edit_handle, 'String', num2str(pos));
|
||||
end
|
||||
|
||||
update_fig(ax)
|
||||
pause(1/vars.fps)
|
||||
if vars.loop && pos == sz
|
||||
pos = 1;
|
||||
else
|
||||
pos = pos+1;
|
||||
end
|
||||
if vars.save_movie
|
||||
currFrame = getframe;
|
||||
writeVideo(vars.writeobj,currFrame);
|
||||
end
|
||||
|
||||
end
|
||||
set(ax.play_handle,'string','Play')
|
||||
if vars.reset_frame
|
||||
set(ax.slider_handle,'Value',vars.init_frame)
|
||||
end
|
||||
if vars.save_movie
|
||||
close(vars.writeobj);
|
||||
end
|
||||
case 'Stop'
|
||||
set(ax.play_handle,'string','Play')
|
||||
if vars.save_movie
|
||||
close(vars.writeobj);
|
||||
end
|
||||
end
|
||||
catch
|
||||
if ~ax.isprop('play_handle')
|
||||
fprintf('Lost connection to figure instance.\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function edit_callback(~,~, ax)
|
||||
str=get(ax.edit_handle,'String');
|
||||
|
||||
if isempty(str2num(str))
|
||||
warndlg('Input must be numerical');
|
||||
set(ax.edit_handle, 'string', num2str(round(get(ax.slider_handle,'Value'))));
|
||||
else
|
||||
set(ax.slider_handle,'Value',str2num(str))
|
||||
update_fig(ax)
|
||||
drawnow()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function play(ax)
|
||||
play_callback(ax.vars, ax.vars, ax);
|
||||
end
|
||||
|
||||
function stop(ax)
|
||||
set(ax.play_handle,'string','Play')
|
||||
set(ax.slider_handle,'Value',ax.vars.init_frame)
|
||||
end
|
||||
Reference in New Issue
Block a user