Home > bml > sync > bml_timewarp.m

bml_timewarp

PURPOSE ^

BML_TIMEWARP aligns and linearly warps slave to master

SYNOPSIS ^

function warpedcoords = bml_timewarp(cfg, master, slave)

DESCRIPTION ^

 BML_TIMEWARP aligns and linearly warps slave to master 

 Use as
   warpedcoords = bml_timealign(master, slave)
   warpedcoords = bml_timealign(cfg, master, slave)
   
 This function first aligns slave to master by calling BML_TIMEALIGN and
 then applies a constrained linear time-warp to slave, in order to
 maximize the correlation with master. The time-warping is done with the
 raws pre-processed by BML_TIMEALIGN.

 The time-warping is done by optimizing the cost function below using
 the 'simplex' method

 w(t) = wt0 + pivot_time + (t - pivot_time) ws1
 s = f(w(t))
 cost = -s*p/sqrt(s*s p*p) + (wt0/penalty_wt0)^2 + ((ws1-1)/penalty_ws1)^4

 where pivot_time is the midpoint of the intersection of master and
 slave after initial alignment, f(t) is a interpolation function
 constructed from the slave's data and time points (after alingment) and 
 '*' is the dot product .
 wt0 and ws1 are the time shifting and stretching paramenters,
 respectiveley. penalty_wt0 and penalty_ws1 are used to constrain their
 value by adding a penalty cost. penalty_wt0 is set to the max between
 penalty_wt0_min and abs(ws1-1)*duration to avoid the optimization 
 algorithm to missalign the signals in the first iterations.

 Parameters
 ----------
 cfg is a configuration structure
 see BML_TIMEALIGN parameters
 cfg.penalty_wt0_min - double: see details above. Defaults to 1e-6. 
 cfg.penalty_ws1 - double: see details above. Defaults to 1e-3. 
 cfg.timewarp - logical: defaults to true. If false no warping is done

 master - FT_DATATYPE_RAW continuous with single channel and trial
 slave - FT_DATATYPE_RAW continuous with single channel and trial

 Returns
 -------
 warpedcoords structure with fields
 warpedcoords.s1 - integer: first coordinate sample
 warpedcoords.t1 - double: first coordinate time in seconds
 warpedcoords.s2 - integer: second coordinate sample
 warpedcoords.t2 - double: second coordinate time in seconds
 warpedcoords.wt0 - double: fitted parameter
 warpedcoords.ws1 - double: fitted parameter

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function warpedcoords = bml_timewarp(cfg, master, slave)
0002 
0003   % BML_TIMEWARP aligns and linearly warps slave to master
0004   %
0005   % Use as
0006   %   warpedcoords = bml_timealign(master, slave)
0007   %   warpedcoords = bml_timealign(cfg, master, slave)
0008   %
0009   % This function first aligns slave to master by calling BML_TIMEALIGN and
0010   % then applies a constrained linear time-warp to slave, in order to
0011   % maximize the correlation with master. The time-warping is done with the
0012   % raws pre-processed by BML_TIMEALIGN.
0013   %
0014   % The time-warping is done by optimizing the cost function below using
0015   % the 'simplex' method
0016   %
0017   % w(t) = wt0 + pivot_time + (t - pivot_time) ws1
0018   % s = f(w(t))
0019   % cost = -s*p/sqrt(s*s p*p) + (wt0/penalty_wt0)^2 + ((ws1-1)/penalty_ws1)^4
0020   %
0021   % where pivot_time is the midpoint of the intersection of master and
0022   % slave after initial alignment, f(t) is a interpolation function
0023   % constructed from the slave's data and time points (after alingment) and
0024   % '*' is the dot product .
0025   % wt0 and ws1 are the time shifting and stretching paramenters,
0026   % respectiveley. penalty_wt0 and penalty_ws1 are used to constrain their
0027   % value by adding a penalty cost. penalty_wt0 is set to the max between
0028   % penalty_wt0_min and abs(ws1-1)*duration to avoid the optimization
0029   % algorithm to missalign the signals in the first iterations.
0030   %
0031   % Parameters
0032   % ----------
0033   % cfg is a configuration structure
0034   % see BML_TIMEALIGN parameters
0035   % cfg.penalty_wt0_min - double: see details above. Defaults to 1e-6.
0036   % cfg.penalty_ws1 - double: see details above. Defaults to 1e-3.
0037   % cfg.timewarp - logical: defaults to true. If false no warping is done
0038   %
0039   % master - FT_DATATYPE_RAW continuous with single channel and trial
0040   % slave - FT_DATATYPE_RAW continuous with single channel and trial
0041   %
0042   % Returns
0043   % -------
0044   % warpedcoords structure with fields
0045   % warpedcoords.s1 - integer: first coordinate sample
0046   % warpedcoords.t1 - double: first coordinate time in seconds
0047   % warpedcoords.s2 - integer: second coordinate sample
0048   % warpedcoords.t2 - double: second coordinate time in seconds
0049   % warpedcoords.wt0 - double: fitted parameter
0050   % warpedcoords.ws1 - double: fitted parameter
0051   
0052   if nargin == 2
0053     slave = master;
0054     master = cfg;
0055     cfg = [];
0056   elseif nargin ~=3
0057     error('incorrect number of arguments');
0058   end
0059   
0060   penalty_wt0_min   = bml_getopt(cfg,'penalty_wt0_min', 60);
0061   penalty_ws1       = bml_getopt(cfg,'penalty_ws1', 1e-3); 
0062   timewarp          = bml_getopt(cfg,'timewarp', true); 
0063 
0064   mc=[]; sc=[]; %original master and slave time coordinates
0065   mc.s1=1; mc.s2=length(master.time{1});
0066   mc.t1=master.time{1}(1); mc.t2=master.time{1}(end);
0067   sc.s1=1; sc.s2=length(slave.time{1});
0068   sc.t1=slave.time{1}(1); sc.t2=slave.time{1}(end);  
0069   
0070   %aligning and transforming raws
0071   [slave_delta_t, ~, master, slave] = bml_timealign(cfg, master, slave);
0072   sc.t1 = sc.t1 + slave_delta_t; sc.t2 = sc.t2 + slave_delta_t;
0073   
0074   %calculating pivot time
0075   ovlp = [max(mc.t1,sc.t1),min(sc.t2,sc.t2)];
0076   pivot_time = mean(ovlp);
0077 
0078   %saving cropped start and end indices of original slave raw
0079   crop_sc=[]; %cropped slave coordinates
0080   [crop_sc.s1, crop_sc.s2]=bml_crop_idx(sc,ovlp(1),ovlp(2));
0081   crop_sc.t1=bml_idx2time(sc,crop_sc.s1);
0082   crop_sc.t2=bml_idx2time(sc,crop_sc.s2);
0083   
0084   %cropping raws to overlap region
0085   master = bml_crop(master, ovlp(1), ovlp(2));
0086   slave = bml_crop(slave, ovlp(1), ovlp(2));
0087   ovlp_duration = ovlp(2) - ovlp(1);
0088   
0089   %linear warping - wt0, ws1
0090   f = @(t) interp1(slave.time{1}(1,:),slave.trial{1}(1,:),t,'PCHIP',0);
0091   p = master.trial{1}(1,:);
0092   t = master.time{1}(1,:);
0093   s0 = f(t);
0094   dot0 = sqrt(dot(s0,s0) * dot(p,p)); 
0095   
0096   function cost = costfun(param) % [wt0, ws1]
0097     s = f(param(1) + pivot_time + (t - pivot_time) .* param(2));
0098     penalty_wt0_dur = max([penalty_wt0_min, ovlp_duration*abs(param(2)-1)]);
0099     cost = -dot(s,p)/dot0 + (param(1)/penalty_wt0_dur)^2 + ((param(2)-1)/penalty_ws1)^4;
0100   end
0101 
0102   wt0=0; ws1=1;
0103   if timewarp
0104     fited=fminsearch(@costfun,[wt0,ws1]);
0105     wt0=fited(1); ws1=fited(2);
0106   end
0107   
0108   warpedcoords=[];
0109   warpedcoords.t1 = pivot_time - wt0 - (1/ws1)*(crop_sc.t2 - crop_sc.t1)/2;
0110   warpedcoords.t2 = pivot_time - wt0 + (1/ws1)*(crop_sc.t2 - crop_sc.t1)/2;
0111   warpedcoords.s1 = crop_sc.s1;
0112   warpedcoords.s2 = crop_sc.s2;
0113   warpedcoords.wt0 = wt0;
0114   warpedcoords.ws1 = ws1;
0115 
0116 end

Generated on Tue 25-Sep-2018 10:08:19 by m2html © 2005