function varargout = FTrack(varargin)
% FTRACK 
% For all your fly-tracking needs! . See documentation for usage details.

% Last Modified by GUIDE v2.5 26-Nov-2007 18:07:29

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @FTrack_OpeningFcn, ...
                   'gui_OutputFcn',  @FTrack_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Executes just before FTrack is made visible.
function FTrack_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to FTrack (see VARARGIN)

% Choose default command line output for FTrack
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);
disp('Welcome to FTrack!')
warning off all

% UIWAIT makes FTrack wait for user response (see UIRESUME)
% uiwait(handles.figure1);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Outputs from this function are returned to the command line.
function varargout = FTrack_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% --- Executes on button press in load_video.
function load_video_Callback(hObject, eventdata, handles)
% hObject    handle to load_video (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA


%The following allows the user to select multiple videos to be tracked.  
%The videos will be tracked sequentially, not in parallel. The filenames
%will be displayed in the message center.

[filename, pathname] = uigetfile({'*.avi;*.mpg;*.mp2','Video Files (*.avi,*.mpg,*.mp2)'}, 'Pick a video', 'MultiSelect','on');
if iscell(filename)
    NFiles = length(filename);
else
    NFiles = 1;
    filename = {filename};
end

if isequal(filename,0) || isequal(pathname,0)
    disp('File select canceled')
else
    for i = 1:NFiles
        disp(['Video selected: ', fullfile(pathname, filename{i})])
        handles.filename{i} = fullfile(pathname, filename{i}); 
    end
end

guidata(gcbo,handles);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Executes on button press in viewframe.
function viewframe_Callback(hObject, eventdata, handles)
% hObject    handle to viewframe (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%This is the single frame viewer.  It simply gives the user some info about
%the video (displayed in the message center), and also shows the user a
%single frame of the movie so that they can make sure it looks correct and
%they can get some parameters off of the frame if need be (such as fly size
%or a pixel/cm calilbration).

filename = handles.filename;
if iscell(filename)
    NFiles = length(filename);
else
    NFiles = 1;
    filename = {filename};
end

for i = 1:NFiles
    video = videoReader(filename{i});
    seek(video,1);
    info = getinfo(video);
    img = getframe(video);
    figure
    imagesc(img)
    title(filename{i})
    disp(['Video name:',filename{i}])
    disp(['Video dimensions [Width, Height]:  [', num2str(info.width),' , ' num2str(info.height),']'])
    disp(['Number of frames:  ',num2str(info.numFrames)])
    disp(['Frame rate:  ',num2str(info.fps),' frames/s'])
    
    handles.VideoInfo(i) = info;
end

disp('Frame Viewer Finished')
guidata(gcbo, handles);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Executes on button press in arena_dims.
function arena_dims_Callback(hObject, eventdata, handles)
% hObject    handle to arena_dims (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

filename = handles.filename;
if iscell(filename)
    NFiles = length(filename);
else
    NFiles = 1;
    filename = {filename};
end

p = [];
q = [];
for i = 1:NFiles
    video = videoReader(filename{i});
    info = getinfo(video);
    seek(video,1);
    img = getframe(video);
    sz = size(img);
    figure
    colormap gray
    imagesc(img)
    axis image
    title({filename{i}; 'Left click to select points on boundary of arena. Press return when done.'})
 
    % Choose points on arena boundary
    [p q] = ginput;

    %Find center and radius of arena

    ellipse = fit_ellipse(p',q','y');
    semiminor = min(ellipse.a,ellipse.b);
    semimajor = max(ellipse.a,ellipse.b);
    ellipse.epsilon = sqrt(1-semiminor^2/semimajor^2);
    ellipse.psi = asin(ellipse.epsilon);
    ellipse.semiminor = semiminor;
    ellipse.semimajor = semimajor;

    rotated_ellipse = ellipse.rotated_ellipse;
    ellipse.points_selected = [p q];
    title('Arena w/ Boundary')
    ellipse.boundaries = [ rotated_ellipse(1,:)', info.height-rotated_ellipse(2,:)'];
       
    info = handles.VideoInfo(i);
    figure
    title('Masking arena.  Please wait...')
    drawnow
    disp('Masking arena...')
    mask = zeros(info.height, info.width);
    for k=1:info.height
        for j=1:info.width
            rot = inv(ellipse.R)*[j;k];
            testpoint = (rot(1)-ellipse.X0).^2/(ellipse.a).^2+(rot(2)-ellipse.Y0).^2/(ellipse.b).^2;
            if (testpoint <= 1.01) %giving a little lee-way near boundary
                mask(k,j) = 1;
            end
        end
    end
    ellipse.mask =  double(mask);
    handles.arena{i} = ellipse;
    
    imagesc(double(img(:,:,1)).*double(mask))
    axis image
    colormap gray
    title('Portion to be tracked')
    
end

%setting things up to only consider pixels within selected arena...

disp('Arena Dimensions Calculated')
disp(ellipse)
guidata(gcbo, handles);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% --- Executes on button press in input_params.
function input_params_Callback(hObject, eventdata, handles)
% hObject    handle to input_params (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


%Opens up a dialog box so that the user can input relevant parameters for
%each video that is being tracked.  A separate box will open for each
%video. 

filename = handles.filename;
if iscell(filename)
    NFiles = length(filename);
else
    Nfiles = 1;
    filename = {filename};
end
UserIn = [];

for i = 1:NFiles
    first = strvcat(filename{i}, '          ','Output directory');
    prompt = {first,'Start Frame (Remember that first frame is indexed 0)','End Frame:', 'Initial background size:', 'Arena radius (cm)', 'Bounding box half-size (in pixels)','Background weight (0.9 < a < 1)'};
    dlg_title = ['Input Paramters'];
    num_lines = 1;
    defaults = {'C:\Documents and Settings\liam\My Documents\LabVIEW Data\FTrack output','0','999', '100','7.5','10','0.9'};
    options.Resize='on';
    options.WindowStyle='normal';
    answer = inputdlg(prompt,dlg_title,num_lines,defaults, options);
    UserIn = [UserIn answer];
    disp(['Input parameters have been entered for ', filename{i}])
    
    InputData(i).OutputPath = UserIn{1,i};
    InputData(i).StartFrame = str2double(UserIn(2,i));
    InputData(i).EndFrame = str2double(UserIn(3,i));
    InputData(i).NBackFrames = str2double(UserIn(4,i));
    InputData(i).ArenaRadius = str2double(UserIn(5,i));
    InputData(i).sqrsize = str2double(UserIn(6,i));
    InputData(i).alpha = str2double(UserIn(7,i));

end

handles.InputData = InputData;
 
guidata(gcbo, handles);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Radio Buttons to select fly finding option

% --- Executes during object creation, after setting all properties.
function find_opt_CreateFcn(hObject, eventdata, handles)
% hObject    handle to find_opt (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called


% --------------------------------------------------------------------
function find_opt_SelectionChangeFcn(hObject, eventdata, handles)
% hObject    handle to find_opt (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Radio buttons to select whether fly is white on black or black on white

% --- Executes during object creation, after setting all properties.
function neg_opt_CreateFcn(hObject, eventdata, handles)
% hObject    handle to neg_opt (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% --------------------------------------------------------------------
function neg_opt_SelectionChangeFcn(hObject, eventdata, handles)
% hObject    handle to neg_opt (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% --- Executes on button press in TrackStart.
function TrackStart_Callback(hObject, eventdata, handles)
% hObject    handle to TrackStart (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%This part of the calls FlyTracker to do the actual tracking.

%grab the filename and user input parameters 
filename = handles.filename;
InputData = handles.InputData;

if iscell(filename)
    NVideos = length(filename);
else
    NVideos = 1;
    filename = {filename};
end

neg_opt = get(get(handles.neg_opt,'SelectedObject'), 'Tag');

% Call FlyTracker
for i=1:NVideos
     
    %video parameters, just because...
    info = handles.VideoInfo(i);
    FrameRate = info.fps;
    FrameRange = [InputData(i).StartFrame:InputData(i).EndFrame];
    
    %Track the fly!
    [x, y, orientation] = FlyTracker(filename{i}, FrameRange,...
                       InputData(i).NBackFrames, neg_opt, InputData(i).sqrsize,...
                        InputData(i).alpha, handles.arena{i});
                    
    %Time vector
    if (info.numFrames == InputData(i).EndFrame)
        InputData(i).EndFrame = InputData(i).EndFrame-1;
    elseif (info.numFrames < InputData(i).EndFrame)
        InputData(i).EndFrame = info.numFrames-1;
    end
    
    t = [InputData(i).StartFrame:InputData.EndFrame(i)]/FrameRate;
        
    %Save data to handles so the rest of the GUI can access it.  
    handles.x = x;
    handles.y = y;
    handles.orientation = orientation;
    handles.t = t;

    guidata(gcbo, handles);  
    %Also, save variables as a .mat file so that the user will be able to
    %load in the tracked data later on.
    
    out = SaveFiles(i,handles);
    
end
disp('Tracking Complete.')


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Executes on button press in view_traj.
function view_traj_Callback(hObject, eventdata, handles)
% hObject    handle to view_traj (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

[filename, pathname] = ...
     uigetfile({'*.mat'},'Select Raw Trajectory Data');

if isequal(filename,0) || isequal(pathname,0)
    disp('File select canceled')
    return;
else
    fullname = fullfile(pathname, filename);
end


load(fullname)
figure
set(gcf,'Name',fullname)
subplot(2,2,1)
plot(t,x)
xlim([0 t(end)])
xlabel('Time (s)')
ylabel('cm')
title('Raw x data')
subplot(2,2,3)
plot(t,y)
xlim([0 t(end)])
xlabel('Time (s)')
ylabel('cm')
title('Raw y data')
subplot(2,2,[2 4])
plot(x,y)
xlabel('x position (cm)')
ylabel('y position (cm)')
title('Raw Trajectory')
axis equal

handles.filename = filename;
handles.x = x;
handles.y = y;
handles.orientation = orientation;
handles.t = t;
handles.VideoInfo = VideoInfo;
handles.InputData = InputData;
handles.arena{1} = arena;

guidata(gcbo, handles);  
     



% --- Executes on button press in clean_x.
function clean_x_Callback(hObject, eventdata, handles)
% hObject    handle to clean_x (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

x = handles.x;
filename = handles.filename;

%open up plot to examine data
figure
plot(x)
title('Zoom in if necessary.  Press any key to continue.')
xlabel('Frame')
ylabel('cm')
pause

% This loop runs until the user hits return to exit. We wait for the user
% to click four points on the graph, and then run the CleanData function on
% the region defined by those four points.

while(1)
title('Define region of data to clean: left, right, baseline, threshold. Hit Return to exit.')
[p q] = ginput(4);
if isempty(p)
    close;
    disp('X data has been cleaned.')
    out = SaveFiles(1,handles);
    return;
else
    rnge = floor(p(1)):floor(p(2));
end

if (q(3) > q(4))
    choice = 'below';
elseif (q(3) < q(4));
    choice = 'above';
end
epsilon = q(4);

x = CleanData(x, rnge, choice, epsilon);
handles.x = x;
guidata(gcbo, handles)
ax = gca;
xlim_temp = get(ax, 'XLim');
ylim_temp = get(ax, 'YLim');

%Show plot of clean data
figure
plot(x)
xlim(xlim_temp);
ylim(ylim_temp);
xlabel('Frame')
ylabel('cm')
title('Zoom in if necessary.  Press any key to continue.')
pause
end



% --- Executes on button press in clean_y.
function clean_y_Callback(hObject, eventdata, handles)
% hObject    handle to clean_y (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

y = handles.y;
filename = handles.filename;
%open up plot to examine data
figure(2)
plot(y)
xlabel('Frame')
ylabel('cm')
title('Zoom in if necessary.  Press any key to continue.')
pause

% This loop runs until the user hits return to exit. We wait for the user
% to click four points on the graph, and then run the CleanData function on
% the region defined by those four points.

while(1)
title('Define region of data to clean: left, right, baseline, threshold. Hit Return to exit.')
[p q] = ginput(4);
if isempty(p)
    close;
    disp('Y data has been cleaned.')
    out = SaveFiles(1,handles);
    return;
else
    rnge = floor(p(1)):floor(p(2));
end

if (q(3) > q(4))
    choice = 'below';
elseif (q(3) < q(4));
    choice = 'above';
end
epsilon = q(4);

y = CleanData(y, rnge, choice, epsilon);
handles.y = y;
guidata(gcbo, handles)
ax = gca;
xlim_temp = get(ax, 'XLim');
ylim_temp = get(ax, 'YLim');

%Show plot of clean data
figure(2)
plot(y)
xlim(xlim_temp);
ylim(ylim_temp);
xlabel('Frame')
ylabel('cm')
title('Zoom in if necessary.  Press any key to continue.')
pause
end

return;

function out = SaveFiles(i,handles)
    
    if iscell(handles.filename)
        filename = handles.filename{i};
    else
        filename = handles.filename;
    end
    
    OutputPath = handles.InputData(i).OutputPath; 
    x = handles.x;
    y = handles.y;
    t = handles.t;
    orientation = handles.orientation;
    InputData = handles.InputData(i);
    VideoInfo = handles.VideoInfo(i);
    arena =  handles.arena{i};
    
    [temp, name, ext, versn] = fileparts(filename);
    
    name_mat = strcat(name,'.mat');
    name_xy=strcat(name,'.xy');
    name_ori=strcat(name,'.ori');
    save_filename = fullfile(OutputPath,name);
    save_filename_xy = fullfile(OutputPath,name_xy);
    save_filename_ori = fullfile(OutputPath,name_ori);
    ori=handles.orientation(1,:)';
    xy=[handles.x' handles.y'];
    save(save_filename,'x','y','t','orientation','InputData', 'VideoInfo','arena')
    disp(['Saved ',save_filename])
    save(save_filename_xy,'xy','-ascii','-tabs');
    disp(['Saved ',save_filename_xy])
    save(save_filename_ori,'ori','-ascii','-tabs');   
    disp(['Saved ',save_filename_ori])
    out = 1;
    
return;


% --- Executes on button press in tilt_correct.
function tilt_correct_Callback(hObject, eventdata, handles)
% hObject    handle to tilt_correct (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


%Note.  This is a slightly different version of tilt correction than
%described in the PLoS paper.  I think this is more robust and general.

x = handles.x;
y = handles.y;
t = handles.t;
ellipse = handles.arena{1};
radius_in_cm = handles.InputData.ArenaRadius;
video = videoReader(handles.VideoInfo.url);
info = getinfo(video);
height = info.height;

psi = ellipse.psi;
phi = ellipse.phi;

%redefining coordinate system so center of arena is at (0,0)
rotated_ellipse(:,1) = ellipse.boundaries(:,1)-ellipse.X0_in;
rotated_ellipse(:,2) = ellipse.boundaries(:,2)-(height-ellipse.Y0_in);

M = [cos(phi) sin(phi);-sin(phi) cos(phi)];  %rotation matrix.

%stretch ellipse only in shortest dimension to length of longest.  
if (ellipse.a > ellipse.b)
    L = [1 0; 0 ellipse.a/ellipse.b]; 
else
    L = [ellipse.b/ellipse.a 0; 0 1];
end

T = L*M;  %transformation matrix.  A rotation and a stretch to turn an ellipse into a circle.

disp('Correcting for camera tilt.  This may take a few minutes, so be patient!')

z = zeros(1,length(rotated_ellipse(:,1)));

%Transform boundary line
for j = 1:length(rotated_ellipse(:,1))
    temp = T*[rotated_ellipse(j,1); rotated_ellipse(j,2)];
    xe(j) = temp(1);
    ye(j) = temp(2);      
end

%fit new ellipse 
z = zeros(1,length(x));
new_ellipse = fit_ellipse(xe',ye','n');

semiminor = min(new_ellipse.a,new_ellipse.b);
semimajor = max(new_ellipse.a,new_ellipse.b);
new_ellipse.epsilon = sqrt(1-semiminor^2/semimajor^2);
new_ellipse.psi = asin(new_ellipse.epsilon);
new_ellipse.semiminor = semiminor;
new_ellipse.semimajor = semimajor;
new_ellipse.boundaries = [xe' ye'];

%Now transform trajectory
for j = 1:length(x)   
        temp = T*[x(j)-ellipse.X0_in; y(j)-(height-ellipse.Y0_in)];
        xp(j) = temp(1);
        yp(j) = temp(2);              
end


if (abs(new_ellipse.b-new_ellipse.a) <= 2) %2 pixel accuracy seems adequate
    disp('Tilt corrected')
    pixels_in_cm = semimajor/radius_in_cm;

    x = (xp)/pixels_in_cm;
    y = (yp)/pixels_in_cm;
    
    figure
    plot(x,y,new_ellipse.boundaries(:,1)/pixels_in_cm,new_ellipse.boundaries(:,2)/pixels_in_cm,'r')
    title('Transformed trajectory with boundary')

    handles.arena{1}=new_ellipse;
    handles.x = x;
    handles.y = y;
    handles.InputData.pixels_in_cm =  pixels_in_cm;
    out = SaveFiles(1,handles);
else
    disp('Something went wrong. Arena is still an ellipse. ')
end


guidata(gcbo, handles)


 
 
 
 

