function [mosaic,pth] = compute_mosaic(movie, dominant_pan_dir, ALLOW_Y) % Compute mosaic from movie using Dijkstra algorithm with video pyramid. % % INPUT % movie - a string (e.g., 'orig/*.jpg' or 'movie.avi') % Note: an avi movie should be readable by Matlab. % For example, in UNIX it should most probably be % uncompressed. % dominant_pan_dir = 1 if camera panning is to the right; % = -1 if camera panning is to the left % /if wrong pan direction is chosen, mosaic will be degenerate (small)/ % ALLOW_Y (default = false) - whether to search for vertical shifts % (in y dimension). % Caution: in the current (very unoptimal) implementation setting % ALLOW_Y=true may enlarge running time considerably. % OUTPUT % mosaic (YxNx3 uint8) - mosaic image % pth (struct) - pth.x, pth.t, pth.y_shift (1xN) are coordinates % of the found optimal path. % % USAGE % [mosaic,pth] = compute_mosaic(movie, dominant_pan_dir, ALLOW_Y=false) % EXAMPLES % [mosaic,pth] = compute_mosaic('movie/*.jpg', 1); % [mosaic,pth] = compute_mosaic('movie.avi', -1); % [mosaic,pth] = compute_mosaic('jitter_movie.avi', -1, true); % This function basically sets default parameters and calls mosaic_main() if ~exist('ALLOW_Y','var'); ALLOW_Y = false; end % =========== Set up default parameters ============== % % Reference points (start and end points of the path) % ref_points = [x0 xend; t0 tend] if dominant_pan_dir==1 % to the right % from the first to the last frame ref_points=[-inf inf;-inf +inf]; else % to the left % from the last to the first frame ref_points=[-inf inf;+inf -inf]; end % 2D mask (in xt dimensions): restrict the path mask=[]; % all points % Parameters: % /see pyramid_level_from_scratch() for more detailed % description of parameter structs/ % Path and cost function comp_params=struct(... 'trange',[-5 -3:3 5], ... % frame shifts to explore 'path_norm_type',10,... % combine arc weights using L_p norm 'xwnd',10,... % size of the window in x dimension 'ysubwnd',10); % size of subwindows in y % How much freedom to give on a finer pyramid level exp_params=struct(... 'xvel_wnd',7,... 'yvel_wnd',0,... 'dilation_diam',[7 7 1]); % Dynamics of the video dyn_params=struct(... 'xvel',7*[-1 1],... % velocity range in x dimension (dx/dt) 'yvel',[0 0],... % velocity range in y dimension (dy/dt) 'max_y_shift',0); % maximal shift in y accounted for if ALLOW_Y exp_params.yvel_wnd = 3; exp_params.dilation_diam = [7 7 7]; dyn_params.yvel = [-3 3]; dyn_params.max_y_shift = 10; end % =========== Run main function ============== % pyr = mosaic_main(movie, ref_points,mask,... comp_params,exp_params,dyn_params); % =========== Return results ============== % mosaic = pyr{1}.mosaic; pth = pyr{1}.pth; % ======================== % % actually_get_frame % % ======================== % function img=actually_get_frame(mov, frame) % Obtain frames of the movie % INPUT % mov (1x1 struct) - movie, see load_movie.m % frame (1xF) - frame numbers % % OUTPUT % img (HxWxCxF uint8) - movie frames. C=1 for grayscale, 3 for color. %disp(['loading frames ' int2str(frame) ]); N=length(frame); img = uint8(0); img(mov.framesize(1), mov.framesize(2), ... mov.framesize(3), length(frame)) = 0; switch mov.input_type case 'avi' if isfield(mov,'framerange'); frame=mov.framerange(frame); end pfx = ''; if isfield(mov,'path_prefix'); pfx = mov.path_prefix; end tmp=aviread([pfx mov.files], frame); for i=1:N img(:,:,:,i)=tmp(i).cdata; end case 'images' pfx = ''; if isfield(mov,'path_prefix'); pfx = mov.path_prefix; end if isfield(mov,'read_func'); rf=mov.read_func; for i=1:N; tmp=feval(rf,[pfx mov.files{frame(i)}]); img(:,:,:,i)=tmp; end else for i=1:N; tmp=imread([pfx mov.files{frame(i)}]); img(:,:,:,i)=tmp; end end case 'array' img(:,:,:,:) = mov.video_data(:,:,:,frame); otherwise error('Unknown movie type %s', mov.input_type); end % ====================== % % best_path_forest % % ====================== % function forest = best_path_forest(start_nodes,stop_nodes, n_nodes, path_norm_type,graph_func,varargin) % forest = best_path_forest(start_nodes,stop_nodes, n_nodes, path_norm_type,graph_func,GRAPH_FUNC_PARAMS); % Computes forest of best paths, starting from any of 'start_nodes'. % The forest is built until any of the 'stop_nodes' is reached, or till the end. % % INPUT: % start_nodes, stop_nodes (1xK,1xL) - lists of nodes. Forest is built for finding % the best path strarting at any of the start_nodes and ending at any of the stop_nodes. % n_nodes (1x1) - total number of nodes (used for displaying progress) % path_norm_type (1x1) = 1 (sum) or Inf (max) - operation to compute path cost from weights of arcs in the path % graph_func (e.g., @graph_func_movie) - handler of the graph function % GRAPH_FUNC_PARAMS - parameters required by the graph_func % % OUTPUT: % (see forest_struct.m for description of the fields) % forest (1x1) - structure with fields: % alias (1xN) % parent (1xN) % path_cost (1xN), arc_weight (1xN) % Initialization for Dijkstra algorithm final=zeros(0,6); pending=zeros(length(start_nodes),6); pending(:,2)=start_nodes(:); pending(:,3)=-1; % Run Dijkstra algorithm START_CLOCK=clock; % fprintf('\t [%s] Started Dijkstra algorithm\n',datestr(START_CLOCK,21)); [final,pending,stop_reason,n_arcs_visited] = dijkstra_online_core(final,pending, inf,inf,stop_nodes, START_CLOCK,n_nodes, path_norm_type, graph_func,varargin{:}); % report % fprintf('\t [%s]',datestr(clock,21)); fprintf('\t Dijkstra algorithm stopped after %s, reason: %s\n\t\t best path computed for %d (%.1f%%) nodes, visited %d arcs per node\n',... seconds2str(etime(clock,START_CLOCK)), stop_reason,size(final,1),100*size(final,1)/n_nodes,round(n_arcs_visited/size(final,1))); % Create forest structure forest=forest_struct(final); % ==================== % % decimate_movie % % ==================== % function mov=decimate_movie(mov,n) % mov=decimate_movie(mov,n) % Take every n's frame from movie. % % mov - movie structure (see load_movie()) switch mov.input_type case 'avi' if isfield(mov,'framerange'); mov.framerange=mov.framerange(1:n:end); else mov.framerange=1:n:mov.nframes; end mov.nframes=length(mov.framerange); case 'images' mov.files=mov.files(1:n:end); mov.nframes=length(mov.files); case 'array' mov.video_data=mov.video_data(:,:,:,1:n:end); mov.nframes=size(mov.video_data,4); end if isfield(mov,'xtslice'); mov.xtslice=mov.xtslice(:,1:n:end,:); end % ========================== % % dijkstra_online_core % % ========================== % function [final,pending,stop_reason,n_arcs_visited] = dijkstra_online_core(final,pending, STOP_ITER,STOP_TIME,STOP_NODES, ... START_CLOCK,N_NODES, path_norm_type, graph_func,varargin) % Dijkstra algorithm with on-line computation of arcs and weights % and a few stopping criterion options. % % Work structures: % final (Nx6) [dist node parent arc_weight path_step path_length] - finished part of tree, % pending (Mx6) [dist node parent arc_weight path_step path_length] - other nodes with non-infinite estimation of distance % parent->node is an arc in the tree with weight arc_weight; % dist = distance from the root (-1) to the node, according to the path_norm_type % for 'final' the distance is globally minimal; for 'pending' - estimated % % Stopping criteria: % STOP_ITER, STOP_TIME, STOP_NODES % Stop: 1) after certain number of iterations; % OR 2) after given time; % OR 3) upon reaching one of the given nodes % % Progress report: % START_CLOCK (from 'clock' function), N_NODES - total number of nodes (to calculate percentage) % % Path computation: % path_norm_type (integer or inf), graph_func and its parameters (varargin) THIS_START_TIME=clock; last_mess_len=0; min_tm_interval=5; TM_last_report=THIS_START_TIME; STOP_NODES=sort(STOP_NODES); % for faster search afterwards % MAIN LOOP (Dijkstra) iter=0; TM_cur=THIS_START_TIME; n_arcs_visited=0; while (iter0; adj_in_pend_pos(adj_in_pend)=pnd_srt_idx(adj_in_pend_pos(adj_in_pend)); % these we need to add: adj_to_add=adj(~adj_in_pend); w_to_add=w(~adj_in_pend); steps_to_add=steps(~adj_in_pend); % these we need to update: adj_to_upd=adj(adj_in_pend); w_to_upd=w(adj_in_pend); steps_to_upd=steps(adj_in_pend); adj_to_upd_i=adj_in_pend_pos(adj_in_pend); % Compute new distances if isinf(path_norm_type) % Linf norm new_dist_to_upd=max(new(1),w_to_upd); new_dist_to_add=max(new(1),w_to_add); else % Lk norm new_dist_to_upd=new(1)+w_to_upd.^path_norm_type; new_dist_to_add=new(1)+w_to_add.^path_norm_type; end % Update % compute and compare distances if ~isempty(adj_to_upd); old_dist=pending(adj_to_upd_i,1); better= new_dist_to_upd=min_tm_interval fprintf(repmat('\b',1,last_mess_len)); last_final=size(final,1); last_pending=size(pending,1); TM_iter=etime(TM_cur,START_CLOCK); TM_iter_str=seconds2str(TM_iter); TM_estim=round((N_NODES/last_final-1)*TM_iter); TM_estim_str=seconds2str(TM_estim); last_mess_len=fprintf(' [Dijkstra: %s] %d (%.1f%%) final, %d (%.1f%%) queued; %s to 100%%',... TM_iter_str,last_final,100*last_final/N_NODES,... last_pending,100*last_pending/N_NODES,... TM_estim_str); TM_last_report=TM_cur; end end % main loop % Clean the report fprintf(repmat('\b',1,last_mess_len)); % Return the reason for stopping if ismembc(final(end,2),STOP_NODES); % found a target node - EXIT stop_reason='OK (found target node)'; elseif isempty(pending); % the forest cannot grow further stop_reason='exhausted the graph'; elseif iter>=STOP_ITER; % exceeded number of iterations stop_reason='max iterations'; elseif etime(TM_cur,THIS_START_TIME) >= STOP_TIME; % exceeded maximum time stop_reason='max time'; else stop_reason='unknown'; end % ======================== % % dynamics_from_path % % ======================== % function [xvel,yvel,yshift]=dynamics_from_path(pth) % [xvel,yvel,yshift]=dynamics_from_path(pth) % Derive parameters of the video dynamics given mosaic path. % % INPUT % pth - structure with fields: % x,t,y_shift (1xN) - coordinates of the path % xstep (1x1) - shift in mosaic (in pixels) corresponding to each arc % in the path % % OUTPUT % xvel,yvel (1x2) [min max] - ranges of velocities % yshift (1x2) [min max] - range of values of y_shift if ~isfield(pth,'xstep'); pth.xstep=1; end dt=diff(pth.t); dx=diff(pth.x)-pth.xstep; % how the point moved (~optical flow) dy=diff(pth.y_shift); jumps = dt~=0; dt=dt(jumps); dx=dx(jumps); dy=dy(jumps); xvel = [min(dx./dt) max(dx./dt)]; yvel = [min(dy./dt) max(dy./dt)]; yshift = [min(pth.y_shift) max(pth.y_shift)]; % =================== % % eval_refpoint % % =================== % function ref_points=eval_refpoint(ref_points,mov,xwnd_rad) % ref_points=eval_refpoint(ref_points,mov,xwnd_rad); % Change all +Inf and -Inf (meaning boundary) to numeric values % % ref_points (2xP) % mov - movie structure (load_movie.m) x_min = 1+xwnd_rad; x_max = mov.framesize(2)-xwnd_rad-1; t_min=1; t_max=mov.nframes; ref_points(1,ref_points(1,:)==-inf) = x_min; ref_points(1,ref_points(1,:)==+inf) = x_max; ref_points(2,ref_points(2,:)==-inf) = t_min; ref_points(2,ref_points(2,:)==+inf) = t_max; % ======================= % % expand_multipoint % % ======================= % function indices = expand_multipoint(mpoint, sz) % indices = expand_multipoint(mpoint, sz); % Return indices of all points described by "multipoint". % "Multipoint", e.g.: [1 nan 2], i.e., 2nd dimension can take any value. % % mpoint (Dx1) - Multipoint % sz (1xD) - Dimensions for indexing % % indices (1xN) exp_sz=sz; isgiven=isfinite(mpoint); exp_sz(isgiven)=1; indices=zeros(1,prod(exp_sz)); for d=length(mpoint):-1:1; % Determine values if isgiven(d); exp_val=mpoint(d); else exp_val=1:exp_sz(d); end % Copy in correct order resh_sz=ones(size(exp_sz)); resh_sz(d)=exp_sz(d); rep_sz=exp_sz; rep_sz(d)=1; exp_val=repmat(reshape(exp_val,resh_sz),rep_sz); exp_val=exp_val(:)'; % Add to indices indices = (exp_val-1) + sz(d)*indices; end indices=1+indices; % =================== % % forest_struct % % =================== % function forest=forest_struct(final) % forest=forest_struct(final) % Converts array to forest structure. % Optimized for tracing paths (path_in_forest.m). % % INPUT % final (NxK, K>=4) [dist node parent arc_weight ....] % - working array of 'dijkstra_online_core.m' % % OUTPUT % % All nodes in the forest are renumbered to 1:N. All the arrays of the 'forest' % have entries corresponding to these numbers. % % forest (1x1) - structure with fields: % alias (1xN) - forest.alias(k) is the "real name" of the node 'k' (its number in the % input 'final' array) % parent (1xN) - forest.parent(k) is the parent node of the node 'k' % parent = -1 if k is one of the start_nodes; % parent = NaN if the node is not in the tree % path_cost (1xN) - total cost of the best path from the source (one of start_nodes) % to the node (depends on the path_norm_type) % arc_weight (1xN) - weight of the arc from the parent to the node % Find aliases for all nodes in the forest [forest.alias,ind_in_f,ind_in_al]=unique(final(:,2:3)); forest.alias=forest.alias(:)'; % Rename nodes final(:,2:3)=reshape(ind_in_al,size(final(:,2:3))); % init with "nan" max_node=length(forest.alias); forest.parent=repmat(nan,1,max_node); forest.path_cost=repmat(nan,1,max_node); forest.arc_weight=repmat(nan,1,max_node); % put values forest.parent(final(:,2))=final(:,3); root_symb=find(forest.alias==-1); forest.parent(root_symb)=root_symb(1); % remove 'nan's from the symbolic "-1" node forest.path_cost(final(:,2))=final(:,1); forest.arc_weight(final(:,2))=final(:,4); % ===================== % % gaussian_window % % ===================== % function [win,sigma] = gaussian_window(rect_length,win_length) % [win,sigma] = gaussian_window(rect_length,win_length = automatic); % Gaussian window function for given window size (not standard deviation). % % Standard deviation of the gaussian (sigma) and size of the window % are chosen automatically. % % If win_length is not defined, or empty, or <=0, it is computed automatically. THROW_OUT = .1; % Values < THROW_OUT*max_value are thrown out sigma = rect_length/2/sqrt(2); if ~exist('win_length','var'); win_length = []; end if isempty(win_length) || (win_length<=0); N = max(3, round(sigma*sqrt(2*log(1/THROW_OUT)))*2+1); else N = win_length; end x = 1:N; middle = 1+(N-1)/2; win = exp(-.5*((x-middle)./sigma).^2); win = win./sum(win); % =============== % % get_frame % % =============== % function img=get_frame(mov, frame,flag) % frame = get_frame(mov, frame[, flag=0]) % % Returns video frame(s) with given index % % IN: % mov - is structure returned by function 'load_movie' % frame - 1xK list of frame numbers to get % flag - 1x1, some options: % 0(default) - use cache % 1 - reset cache % 2 - skip cache use % 3 - test mode % % % OUTPUT: % img - HxWxCxT one or more images if ~exist('flag','var'); flag = 0; end if isequal(mov.input_type,'array') % Bypass cache when movie is in memory flag = 2; end global GET_FRAME_CACHE; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Load into cache and obtain indices % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if flag==0, % Use cache ind = load_frame(mov, frame); elseif flag==1, % Reset cache init_movie_cache(mov); ind = load_frame(mov, frame); elseif flag==2, % Directly read the frames --- bypass cache img=actually_get_frame(mov, frame); return; elseif flag==3 % test this function N=mov.nframes; F_MAX = min(N,20); % how many frames try to read disp 'Testing the cache...' % Make sure cache is initialized, without forcing initialization get_frame(mov, max(1,fix(N*rand(1,5)))); % Get size of the cache K = size(GET_FRAME_CACHE.data, 2); for i=1:30 if rand<.8 % try less than in the cache F = min(K,F_MAX); else F = F_MAX; end n_frames = max(1,round(rand*F)); ind = max(1,round(N*rand(1,n_frames))); % Call load_frame and take directly from cache ind_in_cache = load_frame(mov,ind); fr_1 = GET_FRAME_CACHE.data(:,:,:,ind_in_cache); % Call get_frame with cacheing fr_2 = get_frame(mov, ind); % Call get_frame without cacheing fr_3 = get_frame(mov,ind,2); % Check if ~isequal(fr_1,fr_2,fr_3) GET_FRAME_CACHE error 'Got bad results' end end disp ' okay.' disp 'timing...' tic for i=1:min(N-4, 40); img=get_frame(mov, i:i+4); end; t1=toc; disp '------------------' tic for i=1:min(N-4, 40); img=get_frame(mov, i:i+4,2); end; t2=toc; fprintf('Speedup is %.2f times (%.1f vs %.1f)\n', t2/t1, t1,t2); return; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Return frames from cache % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if isempty(ind); img = []; return; end % Allocate images img_sz=mov.framesize; if length(img_sz)<3; img_sz(3)=1; end; img_sz(4)=length(frame); img=uint8(0); img(img_sz(1),img_sz(2),img_sz(3),img_sz(4))=0; % Copy from cache img = GET_FRAME_CACHE.data(:, :, :, ind); % ====================== % % graph_func_movie % % ====================== % function [adj_nodes,weights,steps]=graph_func_movie(node,nodes_exclude, mov,mask3D,xwnd, xrange,trange,yrange, ysubwnd) % [adj_nodes,weights,steps]=graph_func_movie(node,nodes_exclude, mov,mask3D,xwnd, xrange,trange,yrange, ysubwnd) % Computes adjacent arcs and their weights for the given movie. % /Helper function for dijkstra_online_core()./ % Arcs measure how consistent are pairs of space-time(-yshift) % points to be put into mosaic. % Arcs will be only in the given 3D mask. Points too close to % the boundary will be ignored. % % INPUT: % node (1x1) % nodes_exclude (1xK) - disregard arcs to these nodes (e.g., the ones % that are already in the tree in dijkstra algorithm) % mov - movie structure (see load_movie.m) % mask3D (XxTxYshifts) - 3-dimensional mask of nodes which should be % in the graph. % % xwnd (1x1) - window size in X-dimension % xrange, trange, yrange (1xA,1xB,1xC) - increments to x, t, y-shift % values to generate arcs % ysubwnd (1x1) - size of comparison subwindow in Y-dimension: % each column is divided into subwindows, cost = maximum % over average differences in these subwindows; % E.g.: % ysubwnd==1: strict match over Y % ysubwnd==Inf: average match over Y % % OUTPUT % adj_nodes, weights (1xN) - nodes, adjacent to the given one, and % weights of corresponding arcs % steps (1xN) - distance in mosaic (in pixels) represented by each arc % (equal to 1). % Empty output - in case there are no arcs adj_nodes=[]; weights=[]; steps=[]; max_y_shift=size(mask3D,3)-1; ywnd=mov.framesize(1)-max_y_shift; ysubwnd=max(1,min(ywnd,round(ysubwnd))); ysuboff=floor(rem(ywnd,ysubwnd)/2); xstep=1; sz=size(mask3D); sz=sz(1:2); % [X T] % x,t,y_shift of the given node node_x= 1+mod(node-1,sz(1)); node_t= 1+mod(floor((node-1)/sz(1)),sz(2)); node_ysh= 1+floor((node-1)/prod(sz)); % Check if the node is too close to boundary or not in mask if (node_x-xwnd<1) || (node_x+xwnd>sz(1)) || ~mask3D(node_x,node_t,node_ysh); return; end % Determine if shifts inside frame are required - we can save on computing them i_t_zero=find(trange==0); IN_FRAME_SHIFT=~isempty(i_t_zero); if IN_FRAME_SHIFT; trange(i_t_zero)=[]; end % save on computation % -------------------------------- % ---- Determine to-points ------- % x and t for points in mask and in range poss_ts=node_t+trange; poss_ts=poss_ts(poss_ts>=1 & poss_ts<=sz(2)); xtymask = mask3D(:,poss_ts,:); if ~isempty(xrange); xmask=false; xmask(sz(1),1)=0; poss_xs=node_x+xrange+xstep; % we shift arcs, and xrange is for corresponding points poss_xs=poss_xs(poss_xs-xwnd-xstep>=1 & poss_xs+xwnd-xstep<=sz(1)); xmask(poss_xs)=1; xmask=xmask(:,ones(1,size(xtymask,2)),ones(1,size(xtymask,3))); % Combine x- and t-masks xtymask=xmask & xtymask; end if ~isempty(yrange); ymask=false; ymask(1,1,max_y_shift+1)=0; poss_ysh=node_ysh+yrange; poss_ysh=poss_ysh(poss_ysh>=1 & poss_ysh<=max_y_shift+1); ymask(poss_ysh)=1; ymask=ymask(ones(1,size(xtymask,1)),ones(1,size(xtymask,2)), :); % Combine it with xt-mask xtymask=ymask & xtymask; end inds=find(xtymask); if isempty(inds); return; end xs= 1+mod(inds-1,size(xtymask,1)); ts= 1+mod(floor((inds-1)./size(xtymask,1)),size(xtymask,2)); ysh= 1+floor((inds-1)./(size(xtymask,1)*size(xtymask,2))); ts=poss_ts(ts); % make t first [ts,inds]=sort(ts); xs=xs(inds); ysh=ysh(inds); xs=xs(:); ts=ts(:); ysh=ysh(:); % Transform coordinates into node indices adj_nodes = xs+sz(1)* (ts-1+ sz(2)*(ysh-1)); % Remove unnecessary nodes if ~isempty(nodes_exclude) % "ismembc" is a C routine used in "ismember" function % calling it directly is faster: nodes_exclude=sort(nodes_exclude); to_keep=~ismembc(adj_nodes,nodes_exclude); % instead of: % to_keep=~ismember(adj_nodes,nodes_exclude); adj_nodes=adj_nodes(to_keep); xs=xs(to_keep); ts=ts(to_keep); ysh=ysh(to_keep); end if isempty(adj_nodes); return; end % % Now xs,ts,ysh (Kx1) are coordinates of % all to-points for the given node % !!! ts are sorted!!!! %_____________________________________ % ====== Load all images at once % ts_unique=poss_ts(find(any(xtmask,1))); - may be not good because of nodes_exclude % ts_unique=unique(ts); % A faster version for t_unique (uses the fact that "ts" is sorted): ts_unique=[];last_t=-1; for k=1:length(ts); if ts(k)~=last_t;last_t=ts(k); ts_unique(end+1,1)=last_t; end; end ts_unique=unique([ts_unique; node_t]); % Load images imgs=get_frame(mov,ts_unique); % Delete non-needed x range min_x=max(1,min(node_x,min(xs)-abs(xstep))-xwnd); max_x=min(sz(1),max(node_x,max(xs)+abs(xstep))+xwnd); imgs=imgs(:,min_x:max_x,:,:); imgs=double(imgs); % if size(imgs,3)>1; imgs(:,:,2)=2*imgs(:,:,2); end % Simulate x-values (NOTE: adj_nodes are already computed on correct xs) xs_eff=xs-min_x+1; node_x_eff=node_x-min_x+1; % From-window - shifts from the current node wnd_1=[-(xwnd-1) 0]; wnd_2=[1 xwnd]; wnd_1=sort(sign(xstep)*wnd_1); wnd_2=sort(sign(xstep)*wnd_2); % Cut out the from-image im_from=imgs(node_ysh:node_ysh+end-max_y_shift-1,:,:,find(ts_unique==node_t)); im_from_1=im_from(:,node_x_eff+wnd_1(1):node_x_eff+wnd_1(2),:); im_from_2=im_from(:,node_x_eff+wnd_2(1):node_x_eff+wnd_2(2),:); % To-window wnd_1=wnd_1-xstep; wnd_2=wnd_2-xstep; % -------------------------------------------- % ----- Iterate over all adjacent nodes ------ adj_i=1; n_adj=length(xs); weights=zeros(n_adj,1); while adj_i<=n_adj t=ts(adj_i); % Extract one frame im_to=imgs(:,:,:,find(ts_unique==t)); % Do for all adjacent nodes with current t (ts should be sorted!!!) while (adj_i<=n_adj) && (t==ts(adj_i)); x=xs_eff(adj_i); y_shift=ysh(adj_i); % Cut out the to-image im_to_1=im_to(y_shift:y_shift+end-max_y_shift-1,x+wnd_1(1):x+wnd_1(2),:); im_to_2=im_to(y_shift:y_shift+end-max_y_shift-1,x+wnd_2(1):x+wnd_2(2),:); % Compare diff_1=(im_to_1-im_from_1).^2; diff_2=(im_to_2-im_from_2).^2; % Accumulate diff_1=sum(sum(diff_1,2),3); diff_1=[0;cumsum(diff_1,1)]; diff_1=diff_1(1+ysuboff+ysubwnd:ysubwnd:end)-diff_1(1+ysuboff:ysubwnd:end-ysubwnd); diff_1=max(diff_1); diff_2=sum(sum(diff_2,2),3); diff_2=[0;cumsum(diff_2,1)]; diff_2=diff_2(1+ysuboff+ysubwnd:ysubwnd:end)-diff_2(1+ysuboff:ysubwnd:end-ysubwnd); diff_2=max(diff_2); % Compute the weight weights(adj_i)=min([diff_1,diff_2]); % Go to the next adjacent node adj_i=adj_i+1; end end % for all adjacent nodes % -------------------------------------------- % Add "trivial" shift to the neighboring point if IN_FRAME_SHIFT % add only one arc - to the neighboring point in the frame neighb_x=node_x+xstep; if (neighb_x>=1) && (neighb_x<=sz(1)) && mask3D(neighb_x,node_t,node_ysh) neighb_ind=neighb_x+sz(1)* (node_t-1+ sz(2)*(node_ysh-1)); adj_nodes=[adj_nodes;neighb_ind]; weights=[weights;0]; end end % Normalize weights (make them "per pixel and color channel") n_pixels=xwnd*ysubwnd; if length(mov.framesize)>2; n_pixels=n_pixels*mov.framesize(3); end weights=(1/n_pixels).*weights; % Return trivial 'steps' vector steps=ones(size(adj_nodes)); % ====================== % % init_movie_cache % % ====================== % function init_movie_cache(mov) % init_movie_cache(mov) % Initialize movie cache (used by get_frame.m and load_frame.m). global GET_FRAME_CACHE MAX_AGE = 9e99; % use instead of inf MEM_LIMIT = 50*2^20; % 50 Mb K=min(mov.nframes, fix(MEM_LIMIT/prod(mov.framesize))); % initial cache size % fprintf('\nInitializing movie cache to %d frames\n\n',K); GET_FRAME_CACHE.signature=mov.signature; GET_FRAME_CACHE.data=... repmat(uint8(0), [mov.framesize K]); % the data we have GET_FRAME_CACHE.indices=zeros(1,K); % the indices of the data GET_FRAME_CACHE.age=repmat(MAX_AGE,[1 K]); % age (unused) of each entry % age is the number of calls without access to the frame % age is 0 when the frame have just been accessed % GET_FRAME_CACHE.stat=[0 0]; % [saved loaded] % % Statistics for every frame: % GET_FRAME_CACHE.loaded_from_disk=zeros(1,mov.nframes); % GET_FRAME_CACHE.loaded_from_memory=zeros(1,mov.nframes); % ================ % % load_frame % % ================ % function frame_in_cache = load_frame(mov, frame) % % frame_in_cache = load_frame(mov, frame) % Load video frame(s) to memory cache. % For use with get_frame.m % % Cache is linked to a specific movie. If frames from a different % movie are requested, cache is automatically reset. % % 'frame' array can contain multiple instances of the same frame. % Each frame will be loaded once. % % IN: % mov - 1x1 structure returned by function 'load_movie' % frame - 1xF list of frame numbers to get % % OUTPUT: % frame_in_cache (1xF) - indices of the loaded frames in cache % (requested frames are GET_FRAME_CACHE.data(:,:,:,frame_in_cache)) global GET_FRAME_CACHE % Reset cache in the following cases: % no global variable GET_FRAME_CACHE % for new movie (check signature) if isempty(GET_FRAME_CACHE) ... || ~isequal(GET_FRAME_CACHE.signature, mov.signature) init_movie_cache(mov); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Load all required images into the cache % % replacing old (least accessed) ones % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % ====== Deal with non-unique frames in 'frame' ===== % % find unique frames (~4 times faster than unique()) ts = sort(frame); m = diff(ts)~=0; m(1,end+1) = true; ts = ts(m); % ========================================== % % See which frames we actually need to get % % ========================================== % % SLOW VARIANT (approximately equivalent): % [have_nums have_ind have_loc]=intersect(frame, GET_FRAME_CACHE.indices); % [get_num get_ind]=setdiff(frame, GET_FRAME_CACHE.indices); % FAST VARIANT: [indices_sorted,srt_idx]=sort(GET_FRAME_CACHE.indices); have_loc=ismembc2(ts,indices_sorted); % numbers have_ind=have_loc>0; have_loc=srt_idx(have_loc(have_ind)); % index to the unsorted indices have_nums=ts(have_ind); get_ind=~have_ind; get_num=ts(get_ind); % ===== Now: ===== % have_ind (1xF logical) - ts(have_ind) can be taken from cache % have_nums (1xK) - frame numbers, corresponding to have_ind % have_loc (1xK) - indices in cache, which can be used % get_ind (1xF logical) - ts(get_ind) need to be read from disk % get_num (1xK) - frame numbers, corresponding to get_ind % ========================================== % % Check if we need to enlarge cache (in case too many frames are % requested at once) enlarge_cache = max(0, numel(get_num) - ... (numel(GET_FRAME_CACHE.indices)-numel(have_nums))); if enlarge_cache>0; sz = size(GET_FRAME_CACHE.data); GET_FRAME_CACHE.data(sz(1), sz(2), sz(3), sz(4)+enlarge_cache) = 0; GET_FRAME_CACHE.indices(sz(4)+enlarge_cache) = 0; GET_FRAME_CACHE.age(sz(4)+1:sz(4)+enlarge_cache) = MAX_AGE; end % ========================================== % % Update age of the current cache % % ========================================== % GET_FRAME_CACHE.age = 1 + GET_FRAME_CACHE.age; GET_FRAME_CACHE.age(have_loc) = 0; % /now all frames that in cache which are not requested have age>0/ % Where to find requested frames in cache: ts_cache = zeros(1,numel(ts)); ts_cache(have_ind) = have_loc; % ============================== % % Read missing frames % % ============================== % if ~isempty(get_num) % Find the least used ones that should be replaced: % (with maximal age) [Y I]=sort(GET_FRAME_CACHE.age); replace_these=I(end-numel(get_num)+1:end); % Read new data from disk and fill in the cache GET_FRAME_CACHE.data(:,:,:,replace_these)=actually_get_frame(mov, get_num); GET_FRAME_CACHE.indices(replace_these)=get_num; GET_FRAME_CACHE.age(replace_these)=0; % Where to find loaded frames ts_cache(get_ind) = replace_these; end % ===== Return position in cache for 'frame' ====== % ts_map = 0; ts_map(1, max(ts)) = 0; ts_map(ts) = ts_cache; frame_in_cache = ts_map(frame); % ================ % % load_movie % % ================ % function mov=load_movie(basename, range) % %INPUT: % basename - banana.avi or C:a/adsf/*.jpg % range - which images to load. Only valid for list of images. % % OUTPUT: % mov - structure % .input_type = 'avi' or 'images' (sequence of images) or 'array' (matlab array) % .framesize 1x3 size of the frame, [Y X n_colors] % .nframes % .signature - unique identifier of the movie (used in cacheing) % For mov.input_type = 'avi': % .files - name of avi file % .path_prefix /OPTIONAL/ - if present, will be added before file name % .framerange (1xR) /OPTIONAL/ - frames that are considered (as 'range' argument of this function) % For mov.input_type = 'images': % .files - cell array of frames of the video % .path_prefix /OPTIONAL/ - if present, will be added before file name % For mov.input_type = 'array': % .video_data - Y x X x n_colors x T - video volume REMEMBER_PATH = true; if ischar(basename); % path to the video is given if findstr(basename, '.avi') mov.files=basename; mov.input_type='avi'; img=aviread(basename, 1); img=img.cdata; mov.framesize = [size(img) repmat(1,1,3-length(size(img)))]; info=aviinfo(basename); if ~exist('range','var'); mov.nframes = info.NumFrames; else mov.framerange=range(:)'; mov.nframes = length(mov.framerange); end % Compute unique movie signature mov.signature=cat(2, mov.framesize,mov.nframes,double(cd),... double(basename)); % if REMEMBER_PATH; mov.path_prefix = [cd '/']; end return else % We expect C:/..../*.jpg D=dir(basename); % We need to add the prefix of the path... ind=find(basename=='/' | basename=='\'); if ~isempty(ind), basename=basename(1:ind(end)); else basename=''; end mov.files={D(:).name}; mov.files=sort(mov.files); for i=1:length(mov.files), mov.files{i}=[basename mov.files{i}]; end % clip to the desired range if exist('range','var'), mov.files=mov.files(range); end mov.input_type='images'; img=imread(mov.files{1}); mov.framesize = [size(img) repmat(1,1,3-length(size(img)))]; mov.nframes = length(mov.files); % Determine read-function for this type of images % ALL IMAGE FILES MUST BE IN THE SAME FORMAT !!! imfi=imfinfo(mov.files{1}); imfo = imformats(imfi.Format); mov.read_func=imfo.read; % Compute unique movie signature mov.signature=cat(2, mov.framesize,mov.nframes,double(cd),... double(basename)); if REMEMBER_PATH; mov.path_prefix = [cd '/']; end return; end else % array itself is given if exist('range','var'); mov.video_data = basename(:,:,:,range); else mov.video_data = basename; end mov.input_type = 'array'; sz = size(mov.video_data); sz = [sz ones(1,4-numel(sz))]; mov.framesize = sz(1:3); mov.nframes = sz(4); % Movie signature: make it unique for sure mov.signature = clock; end % ===================== % % mkdir_overwrite % % ===================== % function new_dir = mkdir_overwrite(new_dir) CONFIRMATION = 0; if exist(new_dir,'dir') % need to remove % Ask confirmation if CONFIRMATION conf = input(['Remove "' new_dir '" ? (enter = yes)'],'s'); if ~isempty(conf) warning('User aborted directory removal'); new_dir = []; return; end end % Remove if ~rmdir(new_dir,'s'); % remove the directory with its contents warning('Directory could not be removed'); new_dir = []; return; end end % Make the directory if ~mkdir(new_dir); % error creating directory new_dir = []; end % ============================ % % mosaic_image_from_path % % ============================ % function img = mosaic_image_from_path(pth, mov, BCK_COLOR,SHOW_ARC_COST) % img = mosaic_image_from_path(pth, mov, BCK_COLOR=black/0/,SHOW_ARC_COST=1); % Create mosaic image given path in XT. % Path may include shifts in Y. % Optionally, arc weights ("errors") are depicted at the bottom of the mosaic image % as different intensities of red % % INPUT % pth - structure with fields: % x,t, y_shift (1xN) % mov - movie structure (from load_movie.m) % BCK_COLOR - background color; can be either 1x1 (intensity) or 1x3 (RGB) % SHOW_ARC_COST (1x1) logical - whether to include dislay of path arc weights into the image % OUTPUT % img - mosaic image (uint8) % ---------------------------- % Deal with parameters % ---------------------------- if ~exist('BCK_COLOR','var'); BCK_COLOR=uint8([0 0 0]); end if ~exist('SHOW_ARC_COST','var'); SHOW_ARC_COST=true; end if length(BCK_COLOR)==1; BCK_COLOR=BCK_COLOR([1 1 1]); end BCK_COLOR=reshape(BCK_COLOR,1,1,3); BCK_COLOR=uint8(BCK_COLOR); if mov.framesize(3)==1; % grayscale BCK_COLOR=rgb2gray(BCK_COLOR); end COST_COLOR=[255 0 0]; % red if norm(double(COST_COLOR(:))-double(BCK_COLOR(:)))<5; % too similar color COST_COLOR=[0 255 0]; % green end % ---------------------------- % Prepare data % ---------------------------- x=pth.x; t=pth.t; if isfield(pth,'y_shift'); y_shift=pth.y_shift; else y_shift=zeros(size(x)); end y_shift=y_shift-min(y_shift); % ensure it starts from zero xstep=1; n_points = length(x); Y = mov.framesize(1); mos_sz = mov.framesize; mos_sz(1)=mos_sz(1)+max(y_shift); mos_sz(2)=1+xstep*(n_points-1); % each point except the first corresponds to xstep points img = repmat(BCK_COLOR,mos_sz(1:2)); % --------------------------------- % Put columns from video frames % into mosaic % --------------------------------- tt = unique(t); for tti = 1:length(tt); this_t = tt(tti); this_inds = find(t==this_t); orig_frame = get_frame(mov,this_t); for k=1:length(this_inds); this_i=this_inds(k); this_ysh=y_shift(this_i); this_x=x(this_i); this_col=1+xstep*(this_i-1); if this_i==1; img(end-this_ysh-Y+1:end-this_ysh, this_col,:)... = orig_frame(:,this_x,:); else img(end-this_ysh-Y+1:end-this_ysh, this_col-xstep+sign(xstep):sign(xstep):this_col,:)... = orig_frame(:,this_x-abs(xstep)+1:1:this_x,:); end end; end % --------------------------------- % Add arc cost (weight) if needed % --------------------------------- if SHOW_ARC_COST && isfield(pth,'arc_weight'); cost_sz=mos_sz; cost_sz(1)=max(10,ceil(.05*mos_sz(1))); cost_im=repmat(BCK_COLOR,ceil(cost_sz(1:2).*[.1 1])); % normalize cost cost=pth.arc_weight; if max(cost)~=min(cost); cost=(cost-min(cost))./(max(cost)-min(cost)); else cost(:)=0; end cost=[0 cost]; % put into image cost=repmat(cost(:)',[1 1 length(COST_COLOR)]).*repmat(reshape(COST_COLOR,1,1,[]),[1,length(cost),1]); cost=repmat(cost,[ceil(cost_sz(1)*.9) 1]); cost_im=uint8([cost_im;round(cost)]); % add to the mosaic img=[img;cost_im]; end % ================= % % mosaic_main % % ================= % function pyr = mosaic_main(movie,... ref_points,mask,comp_params,exp_params,dyn_params) % Compute mosaic from movie using Dijkstra algorithm with video pyramid. % % INPUT % movie - a string (e.g., 'orig/*.jpg' or 'movie.avi') % ref_points (2xP, P>=2) - path will go through these points. % ref_points [x0 x1 ... xP; t0 t1 ... tP] % Can contain +-Inf and NaN values, treated specially % (see mov_best_path_thru_points() for description). % mask (XxT logical or empty) - mask in xt-coordinates, where % the path is constrained to be located. Empty is treated % as no constraints (full mask of true's). % comp_params, exp_params, dyn_params (struct) - algrorithm parameters. % See pyramid_level_from_scratch() for description. % OUTPUT % pyr (cell 1xL) - intermediate results for each level of the pyramid % (see pyramid_level_from_scratch() for description.) % % USAGE % pyr = mosaic_main(movie, dominant_pan_dir, ... % ref_points,mask,comp_params,exp_params,dyn_params) % Load original movie movies=load_movie(movie); % =========== Prepare movie pyramid if needed ============== % fprintf('*** Creating pyramid of subsampled movies ***\n'); movies=movie_pyramid_create(movies); fprintf('\n'); % ================================================================ % % === Run the pyramid iterations == % % ================================================================ % pyr=pyramid_init(ref_points,movies,mask,comp_params,exp_params,dyn_params); pyr=pyramid_run(pyr); % ========================== % % mov_best_path_params % % ========================== % function params=mov_best_path_params(comp_params,dyn_params,exp_params) % params - see mov_best_path_thru_points.m % Bounds for x and y ranges xbnd = dyn_params.xvel + exp_params.xvel_wnd*[-1 1]; ybnd = dyn_params.yvel + exp_params.yvel_wnd*[-1 1]; xbnd(1)=floor(xbnd(1)); xbnd(2)=ceil(xbnd(2)); ybnd(1)=floor(ybnd(1)); ybnd(2)=ceil(ybnd(2)); % Parameters params=comp_params; params.xrange=xbnd(1):xbnd(2); params.yrange=ybnd(1):ybnd(2); % =============================== % % mov_best_path_thru_points % % =============================== % function [pth,forest]=mov_best_path_thru_points(ref_points, mov,mask3D, params) % [pth,forest]=mov_best_path_thru_points(ref_points, mov,mask3D, params) % Compute best path for building mosaic from the given movie. % The path should pass through the given reference points. % The path is restricted to go only in the given mask. % % INPUT % ref_points (2xN) [x;t] - path should pass through these x-t points. % Can contain +Inf/-Inf values (meaning boundaries); % NaN - meaning any value (inside boundaries). % E.g.: [nan nan;-inf inf] means "from the first to the last frame". % /Shifts in y are determined automatically./ % mov - movie structure (as in load_movie.m) % mask3D (X x T x Yshifts) logical - determines which points % are allowed in the path. Can be used to remove unwanted % regions or to constrain search when refining a known path. % % params - struct with fields: % path_norm_type (1x1) - k (sum w^k, k integer) or Inf (max w) % - operation used to compute cost of the path % from weights 'w' of constituting arcs % xwnd (1x1) - window size in x-dimension; % ysubwnd (1x1) - size of subwindow in y-dimension % (cost function requires averages over these subwindows to match), % Inf means the whole column; the smaller ysubwnd, % the more strict is matching. % xrange,trange,yrange (1xK,1xL,1xM) - allowed shifts in each dimension % (xrange and yrange can be empty, meaning no restrictions). % % OUTPUT % pth - structure with fields % x, y, y_shift (1xP) - coordinates of the path nodes % arc_weight (1 x P-1) - weights of arcs in the path % forest (1 x N-1 cell) - best-path forest for each segment, % structures with fields: parent, arc_weight, path_cost n_segments=size(ref_points,2)-1; m_tmp=mask3D(1+params.xwnd:end-params.xwnd, :,:); n_nodes=sum(sum(sum(m_tmp))); % % Uncomment for additional info % fprintf('\t Computing best path through %d reference points, ',n_segments+1); % fprintf(' mask contains %.1f%% points\n',100*n_nodes/numel(m_tmp)); % xr=length(params.xrange); tr=length(params.trange); yr=length(params.yrange); % fprintf('\t\t'); % if xr*tr*yr>0; % fprintf('search range %dx%dx%d (expected %d arcs per node); ',yr,xr,tr,xr*tr*yr); % end % fprintf('window size (x) = %d; path norm = %d\n',params.xwnd,params.path_norm_type) % Remove '+-Inf's from ref_points ref_points=eval_refpoint(ref_points,mov,params.xwnd); % Parameters for computing forest of best path (best_path_forest.m) forest_params={n_nodes, params.path_norm_type, ... @graph_func_movie, mov,mask3D,... params.xwnd, params.xrange,params.trange,params.yrange, params.ysubwnd}; % ==================================== % == Find best path for every segment pth=[]; % expand all 'NaN's from_nodes=expand_multipoint([ref_points(:,1);nan],[size(mask3D) 1]); forest = cell(1, n_segments); for r=1:n_segments; if n_segments>1; fprintf('\t *** Segment %d/%d ***\n',r,n_segments); end % expand all 'NaN's to_nodes=expand_multipoint([ref_points(:,r+1);nan],[size(mask3D) 1]); % Find best path forest forest{r} = best_path_forest(from_nodes,to_nodes, forest_params{:}); % Extract best path (closest to the to_nodes) [best_to_node,to_dist]... =nearest_node_in_forest(forest{r},to_nodes,size(mask3D)); if to_dist>0; fprintf('\t *** Warning: have not found path to the given nodes, taking nearest path\n'); end pth_segm=path_in_forest(forest{r},best_to_node); % Add computed segment to previous ones if isempty(pth); % 1st segment pth=pth_segm; pth=rmfield(pth,'path_cost'); fprintf('\t *** Added %d nodes to the path ***\n',... length(pth_segm.nodes)); else % add a new segment pth.nodes=[pth.nodes pth_segm.nodes(2:end)]; pth.arc_weight=[pth.arc_weight pth_segm.arc_weight]; fprintf('\t *** Added %d nodes to the path ***\n',... length(pth_segm.nodes)-1); end % Prepare for the next segment from_nodes=pth.nodes(end); % for the next segment end % == Create path structure [pth.x,pth.t, y_shift] = ind2sub(size(mask3D),pth.nodes); pth.y_shift=y_shift-1; % ************************************************** function [node,min_dist]=nearest_node_in_forest(forest,to_nodes,dims) % list all nodes in the forest if isfield(forest,'alias') nodes_in_forest=forest.alias; nodes_in_forest(nodes_in_forest==-1)=[]; else nodes_in_forest=find(~isnan(forest.parent)); end [x,t,ysh]=ind2sub(dims,nodes_in_forest); [x_to,t_to,ysh_to]=ind2sub(dims,to_nodes); [k,dist]=dsearchn([x(:) t(:) ysh(:)], [x_to(:) t_to(:) ysh_to(:)]); [min_dist,kk]=min(dist); node=nodes_in_forest(k(kk)); % ========================== % % movie_pyramid_create % % ========================== % function movies=movie_pyramid_create(mov,min_frame_dim) % movies=movie_pyramid_create(mov,min_frame_dim=30) % % INPUT % mov (1x1) - movies struct (load_movie.m) % min_frame_dim (1x1) - frame dimensions in the smalles level of the pyramid will be not less than this value % OUTPUT % movies (cell 1xL) - movies for pyramid levels if ~exist('min_frame_dim', 'var'); min_frame_dim = 30; end % Create pyramid movies=spatial_pyramid_create(mov,'.',min_frame_dim); movies=movie_pyramid_from_spatial(movies); % Report fprintf('Pyramid levels:'); for level=1:length(movies); fprintf(' %d) %dx%dx%d',level,movies{level}.framesize(1:2),movies{level}.nframes); end fprintf('\n'); % ******************************************************* function movs=spatial_pyramid_create(orig_mov,target_dir, MIN_SIZE) % % movs=spatial_pyramid_create(orig_mov,target_dir='.', MIN_SIZE) % E.g.: spat_pyr=spatial_pyramid_create(load_movie('orig/*.jpg'),'.'); % Create pyramid of movies, frames at each level are half the size (in x and y) % of the size at the previous level. % % INPUT % orig_mov - movie structure (e.g., from load_movie.m) % target_dir (string), e.g., 'my_subdir/' % MIN_SIZE (1x1) - pyramid creation stop before image size goes below MIN_SIZE fprintf('Original video: %03dx%03d, %d frames\n',... orig_mov.framesize(1:2),orig_mov.nframes); if ~exist('target_dir','var'); target_dir='.'; end target_dir=standardize_path(target_dir); if ~exist(target_dir,'dir') if ~mkdir(target_dir) error(['Error creating output directory ' target_dir]); end end n_levels = 1+floor(log2(min(orig_mov.framesize(1:2))/MIN_SIZE)); filt=gaussian_window(3); % Create output directories if iscell(orig_mov.files) orig_pth=fileparts(orig_mov.files{1}); else orig_pth=fileparts(orig_mov.files); end orig_pth=standardize_path(orig_pth); sz=orig_mov.framesize(1:2); for l=2:n_levels; sz=ceil(sz./2); level_dir{l}=[orig_pth(1:end-1) sprintf('_%03dx%03d/',sz)]; if isempty(mkdir_overwrite(level_dir{l})); error('Failed to create directory %s',level_dir{l}); end end % Resize and save images START_CLOCK=clock; T=orig_mov.nframes; xt_sz=[orig_mov.framesize(2) T]; if length(orig_mov.framesize)>2; xt_sz=[xt_sz orig_mov.framesize(3)]; end xtslice=repmat(uint8(0),xt_sz); last_mess_len=0; for t=1:T; im=get_frame(orig_mov,t); xtslice(:,t,:)=reshape(uint8(median(double(im),1)),xt_sz([1 3])); for l=2:n_levels; im=imfilter(im,filt); im=im(1:2:end,1:2:end,:); out_fname=[target_dir level_dir{l} sprintf('%04d.jpg',t)]; imwrite(im,out_fname,'Quality',100); end fprintf(repmat('\b',1,last_mess_len)); last_mess_len=fprintf('Frame %d/%d, finishing in %s',t,T,seconds2str((T/t-1)*etime(clock,START_CLOCK))); end fprintf(repmat('\b',1,last_mess_len)); fprintf('Movie pyramid: finished in %s\n',seconds2str(etime(clock,START_CLOCK))); movs=cell(1,n_levels); movs{1}=orig_mov; movs{1}.xtslice=xtslice; for l=2:n_levels; movs{l}=load_movie([target_dir level_dir{l} '*.jpg']); movs{l}.xtslice=imresize(xtslice,[movs{l}.framesize(2) movs{l}.nframes]); end % ******************************************************* function pyr=movie_pyramid_from_spatial(spatial_pyr) % pyr=movie_pyramid_from_spatial(spatial_pyr) % Returns pyramid of movies, for given spatial movie pyramid % (i.e. full-length movies subsampled in width and height only). % % INPUT % Cell array of movie structures (see load_movie.m). % % OUTPUT % Cell array of movie structures (see load_movie.m), % constituting the pyramid level=1; x_level=1; t_level=1; pyr{1}=spatial_pyr{1}; % Equal subsampling in X and T while x_level0); % maximal number of nodes in path % Track path from end_node till the root (marked by -1) curr_node = end_node; if DIRECT; reached_root= (curr_node<0); else reached_root= (alias(curr_node)<0); end pth_nodes = zeros(1,n_nodes); cnt = 0; while (~reached_root)&&(cnt=sz(1)-xwnd_fc+1; rp=rp(:,rel_rp); % consider separately left and right border left_border=isnan(rp(1,:)) | rp(1,:)<=xwnd_fc; brd_ts=rp(2,left_border); % t-coords of the points if any(isnan(brd_ts)); brd_ts=1:sz(2); end % all points mask3D(1:max(1,xwnd_fc-lev.exp_params.dilation_diam(1)+1),unique(brd_ts))=1; % compensate for dilation right_border=isnan(rp(1,:)) | rp(1,:)>=sz(1)-xwnd_fc+1; brd_ts=rp(2,right_border); % t-coords of the points if any(isnan(brd_ts)); brd_ts=1:sz(2); end % all points mask3D(min(end,end-xwnd_fc+lev.exp_params.dilation_diam(1)):end,unique(brd_ts))=1; % compensate for dilation % ***** Dilate ***** mask3D=imdilate(mask3D,ones(lev.exp_params.dilation_diam)); % put in to the lev structure lev.mask3D = mask3D; % Create parameters params=mov_best_path_params(lev.comp_params,lev.dyn_params,lev.exp_params); % Compute best path [lev.pth,lev.forest]=mov_best_path_thru_points(lev.ref_points, ... lev.mov,lev.mask3D, params); % Generate mosaic image lev.mosaic = mosaic_image_from_path(lev.pth, lev.mov); % ******************************************** function coords_new = no_jumps_path(coords) jumps = find(any(abs(diff(coords))>1,2)); coords_new=coords(1:jumps(1),:); for jm = 1:length(jumps); i_from = jumps(jm); pnt_from = coords(i_from,:); pnt_to = coords(i_from+1,:); n_steps = max(abs(pnt_to-pnt_from))-1; coeff = (1:n_steps)./(n_steps+1); coeff=coeff(:); extra_coords=pnt_from(ones(n_steps,1),:).*(1-coeff(:,ones(length(pnt_from),1))) + pnt_to(ones(n_steps,1),:).*coeff(:,ones(length(pnt_from),1)); coords_new=[coords_new;extra_coords]; if jm0; % include days timestr=sprintf('%dd %d:%02d:%02d',dhms); else % don't include days timestr=sprintf('%d:%02d:%02d',dhms(2:end)); end % ====================== % % standardize_path % % ====================== % function output_path = standardize_path(input_path) % output_path = standardize_path(input_path) % % Makes path of the form: 'c:/dir/subdir/subsubdir/' % (with '/' as separator, and ending on '/') % Replace '\' by '/' output_path = strrep(input_path,'\','/'); % Remove all '/' at the end if isempty(output_path); output_path = './'; return; end while output_path(end)=='/' output_path(end) = []; if isempty(output_path); output_path = './'; return; end end % Add exactly one '/' at the end output_path(end+1) = '/';