Code covered by the BSD License  

Highlights from
myLabel 0.04

image thumbnail
from myLabel 0.04 by Nikolay Chumerin
Image labeling tool for Matlab.

myLabel(varargin)
function varargout = myLabel(varargin)
% myLabel - image sequencies labeling tool
% developed by Nick Chumerin in the framework of EU MCCOOP project
% myLabel.support@gmail.com
% version 0.04
% 2008-01-09

% TODO:
% - flexible label description


gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @myLabel_OpeningFcn, ...
                   'gui_OutputFcn',  @myLabel_OutputFcn, ...
                   'gui_LayoutFcn',  [], ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

gui_mainfcn(gui_State, varargin{:});
%  if nargout
%      [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
%  else
%      gui_mainfcn(gui_State, varargin{:});
%  end
%% --------------------------------------------------------------------
function [filenames, pathname] = uigetfiles(directory)
% based on uigetfiles by shanrong.zhang@utsouthwestern.edu
if ~nargin || isempty(directory),
    directory = pwd;
end
filechooser = javax.swing.JFileChooser(directory);
filechooser.setMultiSelectionEnabled(true);
filechooser.setFileSelectionMode(filechooser.FILES_ONLY);
selectionStatus = filechooser.showOpenDialog(com.mathworks.mwswing.MJFrame);

if selectionStatus == filechooser.APPROVE_OPTION
    pathname = [char(filechooser.getCurrentDirectory.getPath), ...
                java.io.File.separatorChar];
    selectedfiles = filechooser.getSelectedFiles;
    for k = 1:size(selectedfiles)
        filenames{k} = char(selectedfiles(k).getName);
    end
else
    pathname = pwd;
    filenames = {};
end
%% --------------------------------------------------------------------
function Init_Picture
global picture
picture.list          = {};
picture.dir           = {};
picture.list_length   = 0;
picture.current       = 0;
picture.previous      = 0;
picture.data          = [];
picture.output_dir    = getpref('myLabel', 'default_output_dir'); %fullfile(pwd, 'output');
picture.rows          = 0;
picture.cols          = 0;
%% --------------------------------------------------------------------
function Set_Labels_Default_Values
global labels
labels.changed      = 0;
labels.max_number   = 10;
labels.std_colors   = ...
       [1, 0 , 0; ... % red      for label1
        0, 1 , 0; ... % green    for label2
        0, 0 , 1; ... % blue     for label3
        1, 1 , 0; ... % yellow   for label4
        0, 1 , 1; ... % ???      for label5
        1, 0 , 1; ... % ???      for label6
        1, 1 , 1; ... % white    for label7
        .5, 0 , 1; ... % ???     for label8
        0, .5 , 1; ... % ???     for label9
       .5, 1, 0];   % ??? for label10
labels.colors       = labels.std_colors;
labels.visible      = zeros(labels.max_number, 1);
if length(labels.values)<labels.max_number,
    labels.values(end:labels.max_number) = uiny16(1);
end
if length(labels.std_values)<labels.max_number
    labels.std_values = uint16([1 2 3 4 21 22 61 62 131 132]);
end
%% --------------------------------------------------------------------

function Init_Labels
global labels
labels.dir              = getpref('myLabel', 'default_labels_dir'); %fullfile(pwd, 'labels');
labels.filename         = [];
labels.short_filename   = [];
labels.filename_ext     = [];
labels.number           = 1;
labels.active           = 1;
labels.data             = uint16([]);
labels.layer            = [];
labels.std_values       = uint16([1 2 3 4 21 22 61 62 131 132]);
labels.values           = labels.std_values;
Set_Labels_Default_Values
labels.colormap     = zeros(256, 3);
labels.alpha        = .4;
labels.alphaData    = 0;
labels.operation    = 1; % 1 - set, 2 - inverse, 3 - clear

%% --------------------------------------------------------------------
function Init_Interpolation
global interpolation
interpolation.first.mask    = [];
interpolation.first.frame_n = 0;
interpolation.last.mask     = [];
interpolation.last.frame_n  = 0;
interpolation.label         = 0;

%% --------------------------------------------------------------------
function Init_Cursor
global cursor
cursor.x            = 0;
cursor.y            = 0;
cursor.prev_x       = 0;
cursor.prev_y       = 0;
cursor.mb_pressed   = 0;
cursor.size         = 2;
cursor.max_size     = 32;
%% --------------------------------------------------------------------
function Init_Undo
global undo
undo                = [];

%% --------------------------------------------------------------------
function Init_Clipboard
global clipboard
clipboard           = [];

%% --------------------------------------------------------------------
function Init_Zoom
global zoom
zoom.picture        = [];
zoom.labels         = [];
zoom.active         = 0;

%% --------------------------------------------------------------------
function Init_Myhandles(handles);
global myhandles labels
myhandles.picture       = imshow(0);
hold on
myhandles.labels        = imshow(0);
myhandles.brush         = rectangle('Position', [0 0 1 1], 'Visible', 'off');
hold off
set(myhandles.picture, 'Visible', 'off');
set(myhandles.labels, 'Visible', 'off');
myhandles.Image_Axes    = handles.Image_Axes;
myhandles.hLA = eval(['[' sprintf('handles.Label%g_Active ', 1:labels.max_number) ']']);
myhandles.hLV = eval(['[' sprintf('handles.Label%g_Visible ', 1:labels.max_number) ']']);

%% --------------------------------------------------------------------
function myLabel_OpeningFcn(hObject, eventdata, handles, varargin)
global picture labels cursor undo zoom clipboard myhandles interpolation info

info.version        = 0;
info.subversion     = 4;
info.release_date   = '2008-01-09';

if ~ispref('myLabel'),
    addpref('myLabel', {'version', 'subversion', 'release_date'}, {0, 0, ''});
    addpref('myLabel', {'default_labels_dir', 'default_pix_dir', 'default_output_dir', 'default_states_dir'}, {'', '', '', ''});
end
setpref('myLabel', {'version', 'subversion', 'release_date'}, {info.version,  info.subversion, info.release_date});
if ~ispref('myLabel', 'default_labels_dir') || isempty(getpref('myLabel', 'default_labels_dir')),
    if isunix,
        setpref('myLabel', 'default_labels_dir', '/huge_sidonia/nick/sequences');
    else
        setpref('myLabel', 'default_labels_dir', './labels');
    end
end

if ~ispref('myLabel', 'default_pix_dir') || isempty(getpref('myLabel', 'default_pix_dir')),
    if isunix,
        setpref('myLabel', 'default_pix_dir', '/huge_sidonia/nick/sequences');
    else
        setpref('myLabel', 'default_pix_dir', '.');
    end
end

if ~ispref('myLabel', 'default_output_dir') || isempty(getpref('myLabel', 'default_output_dir')),
    setpref('myLabel', 'default_output_dir', './output');
end

if ~ispref('myLabel', 'default_states_dir') || isempty(getpref('myLabel', 'default_states_dir')),
    setpref('myLabel', 'default_states_dir', './states');
end


Init_Picture
Init_Labels
Init_Cursor
Init_Clipboard
Init_Undo
Init_Zoom
Init_Myhandles(handles)
Init_Interpolation
if exist('autosave.mat', 'file'),
    load('autosave.mat', 'picture', 'labels', 'undo', 'zoom', 'clipboard');
    Set_Labels_Default_Values
    Init_Myhandles(handles)
    set(handles.Transparency_Slider, 'Enable', 'on', 'Value', labels.alpha);
    Update_Images_List(handles)
    Update_Labels_List(handles)
end

set(handles.myLabel, 'WindowButtonUpFcn', 'Button_Released');
%  set(handles.myLabel, 'KeyPressFcn', 'myLabel(''KeyPressFcn'', gcbo, [], guidata(gcbo))');
set(handles.Labels_Directory_Text, 'String', labels.dir);
set(handles.Output_Dir_Text, 'String', picture.output_dir);
set(handles.Cursor_Size_Text, 'Enable', 'off', 'String', num2str(cursor.size));
set(handles.Cursor_Size_Slider, 'Min', 0);
set(handles.Cursor_Size_Slider, 'Max', cursor.max_size);
set(handles.Cursor_Size_Slider, 'Enable', 'off', 'Value', cursor.size);
set(handles.Transparency_Edit, 'String', num2str(labels.alpha));

fprintf('myLabel %g.%02g (%s)\n', info.version, info.subversion, info.release_date);
%  %  set(hObject, 'NumberTitle', 'off', 'Name', sprintf('myLabel %g.%02g %s\n', version, subversion, release_date));
handles.output = hObject;
guidata(hObject, handles);
Update_Window(handles)
%% --------------------------------------------------------------------
function varargout = myLabel_OutputFcn(hObject, eventdata, handles)
varargout{1} = handles.output;

%% --------------------------------------------------------------------
function CloseMenuItem_Callback(hObject, eventdata, handles)
selection = questdlg('Close myLabel?', 'Close myLabel...', 'Yes', 'No', 'Yes');
if strcmp(selection, 'No')
    return;
end
delete(handles.myLabel)

%% --------------------------------------------------------------------
function Images_List_Callback(hObject, eventdata, handles)
global picture labels
% stat('Images_List_Callback started')
if get(handles.Images_List, 'Value')~=picture.current,
    Update_Window(handles);
else
    Update_Labels_Transparency;
    Update_Labels_Layer;
end
% stat('Images_List_Callback finished')

%% --------------------------------------------------------------------
function Update_Images_List(handles)
global picture
% stat('Update_Images_List started')
picture.list_length = length(picture.list);
if picture.current>picture.list_length,
    picture.current = 1;
end
if picture.current,
    set(handles.Images_List, 'Value', picture.current);
else
    set(handles.Images_List, 'Value', 1);
end
set(handles.Images_List, 'String', picture.list);

if picture.list_length
    set(handles.Images_List, 'Enable', 'on');
    set(handles.Clear_Images_List, 'Enable', 'on');
    set(handles.Del_Image_From_List, 'Enable', 'on');
    set(handles.Save_State, 'Enable', 'on');
    set(handles.Save_For_Training, 'Enable', 'on');
    set(handles.Copy, 'Enable', 'on');
    set(handles.Next_Image, 'Enable', 'on');
    set(handles.Previous_Image, 'Enable', 'on');
    set(handles.First_Image, 'Enable', 'on');
    set(handles.Last_Image, 'Enable', 'on');
    set(handles.Ten_Images_Back, 'Enable', 'on');
    set(handles.Ten_Images_Forward, 'Enable', 'on');
else
    set(handles.Images_List, 'Enable', 'off');
    set(handles.Clear_Images_List, 'Enable', 'off');
    set(handles.Del_Image_From_List, 'Enable', 'off');
    set(handles.Save_State, 'Enable', 'off');
    set(handles.Save_For_Training, 'Enable', 'off');
    set(handles.Copy, 'Enable', 'off');
    set(handles.Next_Image, 'Enable', 'off');
    set(handles.Previous_Image, 'Enable', 'off');
    set(handles.First_Image, 'Enable', 'off');
    set(handles.Last_Image, 'Enable', 'off');
    set(handles.Ten_Images_Back, 'Enable', 'off');
    set(handles.Ten_Images_Forward, 'Enable', 'off');
end
% stat('Update_Images_List finished')

%% --------------------------------------------------------------------
function Change_Labels_Dir_Callback(hObject, eventdata, handles)
global labels
% stat('Change_Labels_Dir_Callback started')

labels.dir = uigetdir(getpref('myLabel', 'default_labels_dir'), 'Pick a new labels directory');
if ~ischar(labels.dir),
    labels.dir = fullfile(pwd, 'labels');
end
set(handles.Labels_Directory_Text, 'String', labels.dir);
% stat('Change_Labels_Dir_Callback finished')

%% --------------------------------------------------------------------
function Change_Output_Dir_Callback(hObject, eventdata, handles)
global picture
% stat('Change_Images_Dir_Callback started')

picture.output_dir = uigetdir(getpref('myLabel', 'default_output_dir'), 'Pick a new output directory');
if ~ischar(picture.output_dir),
    picture.output_dir = fullfile(pwd, 'output');
end
set(handles.Output_Dir_Text, 'String', picture.output_dir);
% stat('Change_Images_Dir_Callback finished')

%% --------------------------------------------------------------------
function Update_Window(handles)
global picture myhandles labels
% stat('Update_Window started')
Load_Image_and_Labels(handles)
Parse_Labels(handles);
Update_Labels_List(handles);
Update_Labels_Transparency;
if picture.current,
    myhandles.picture  = imshow(picture.data); % Show_Image;
    set(myhandles.picture, 'CDataMapping', 'direct')
    Create_Labels_Layer(handles);
    Update_Labels_Layer;
end
picture.previous = picture.current;
if labels.changed,
    Autosave
end
% stat('Update_Window finished')
%% --------------------------------------------------------------------
function Load_Image_and_Labels(handles)
%% Load picture data and (if possible) Labels data
global labels picture undo myhandles info
% stat('Load_Image_and_Labels started')
set(handles.Cursor_Size_Text, 'Enable', 'off');
set(handles.Cursor_Size_Slider, 'Enable', 'off');
% im_list  = get(handles.Images_List, 'String');
im_list = picture.list;
im_index = get(handles.Images_List, 'Value');
if ~isempty(picture.list) && im_index,  % if pix list isn't empty and current pix is selected
    if isa(picture.list, 'cell'),
        picture.filename = fullfile(picture.dir{im_index}, picture.list{im_index});
    else
        picture.filename = im_list;
    end
    set(handles.myLabel, 'Name', sprintf('myLabel %g.%02g - %s', info.version, info.subversion, picture.filename));
    [picture.dir{im_index}, picture.short_filename, picture.filename_ext] = ...
        fileparts(picture.filename);

    if (picture.list_length) & (picture.current~= im_index),
        picture.current = im_index;
        picture.data    = imread(picture.filename);
        picture.rows    = size(picture.data, 1);
        picture.cols    = size(picture.data, 2);

        Save_Current_Labels(handles);

        set(handles.Cropped_Checkbox, 'Enable', 'on');
        set(handles.Masked_Checkbox, 'Enable', 'on');
        Masked_or_Cropped_Checkbox_Callback(handles.Masked_Checkbox, [], handles)

        % trying to read corresponding labels file
        labels.filename = fullfile(labels.dir, [picture.short_filename '_labels.mat']);
        if exist(labels.filename, 'file'),
            data = load(labels.filename);
            labels.data = uint16(data.l);
            % stat('Labels data loaded.');
            set(handles.Transparency_Slider, 'Enable', 'on', 'Value', labels.alpha);
            if get(handles.Autoresize_Checkbox, 'Value') && ...
                    (size(labels.data, 1)~=picture.rows || ...
                     size(labels.data, 2)~=picture.cols),
                stat('Resizing labels')
                labels.data = uint16(imresize(labels.data, [picture.rows picture.cols]));
                labels.changed = 1;
            end
        else
            labels.data = zeros(picture.rows, picture.cols, 'uint16');
            set(handles.Transparency_Slider, 'Enable', 'off');
        end
        labels.colors = labels.std_colors;
    end
    set(handles.Labels_Directory_Text, 'String', labels.dir);
else % there is no picture
    if ishandle(myhandles.picture)
        delete(myhandles.picture)
    end
    picture.data    = 0;
    picture.rows    = size(picture.data, 1);
    picture.cols    = size(picture.data, 2);
    labels.data     = 0;
    labels.alphaData= 0;
    set(handles.Transparency_Slider, 'Enable', 'off');
    set(handles.Cropped_Checkbox, 'Enable', 'off');
    set(handles.Masked_Checkbox, 'Enable', 'off');
    set(handles.Autorewrite_Checkbox, 'Enable', 'off');
    set(handles.Autoresize_Checkbox, 'Enable', 'off');
    Masked_or_Cropped_Checkbox_Callback(handles.Masked_Checkbox, [], handles)
end
labels.alphaData= zeros(picture.rows, picture.cols);

% stat('Load_Image_and_Labels finished')


%% --------------------------------------------------------------------
function Save_Current_Labels(handles)
global labels
if ~isempty(labels.filename) && labels.changed,
    stat(['Labels were changed, saving in ' labels.filename]);
    l = labels.data;
    save_it = 1;
    if ~get(handles.Autorewrite_Checkbox, 'Value') && exist(labels.filename),
        save_it = strcmp(questdlg(['Overwrite ' labels.filename '?']), 'Yes');
    end
    if save_it,
        if ~exist(labels.dir, 'dir'), mkdir(labels.dir); end
        save(labels.filename, 'l');
        % stat('Labels data saved.');
        labels.changed  = 0;
        undo = [];
        set(handles.Undo, 'Enable', 'off');
        set(handles.Undo, 'Enable', 'off');
    end
end

%% --------------------------------------------------------------------
function Create_Labels_Layer(handles)
%% Creat Labels Layer for current picture (not yet show it)
global labels picture cursor myhandles
% stat('Create_Labels_Layer started')
% preparing labels layer
labels.layer = zeros(picture.cols, picture.rows, 3);
if ishandle(myhandles.labels),
   delete(myhandles.labels);
end
if ishandle(myhandles.brush),
   delete(myhandles.brush);
end
hold on
myhandles.labels = imshow(labels.layer);
%  set(myhandles.labels, 'alphaData', labels.alpha*(labels.data==0));
myhandles.brush = rectangle('Position', [cursor.x-.5 cursor.y-.5 2*cursor.size+1 2*cursor.size+1]);
hold off
if labels.operation==3,
    set(myhandles.brush, 'Visible', 'off')
end

set(myhandles.labels, 'ButtonDownFcn', 'myLabel(''Labels_ButtonDown'', gcbo, [], guidata(gcbo))')
set(handles.myLabel, 'WindowButtonMotionFcn', 'Mouse_Moved');
set(handles.Cursor_Size_Text, 'Enable', 'on');
set(handles.Cursor_Size_Slider, 'Enable', 'on');
set(handles.Copy, 'Enable', 'on')
set(handles.Copy, 'Enable', 'on')
set(handles.Autorewrite_Checkbox, 'Enable', 'on');
set(handles.Autoresize_Checkbox, 'Enable', 'on');
% stat('Create_Labels_Layer finished')

%% --------------------------------------------------------------------
function Update_Labels_Transparency
%% Prepare labels layer transparency
global labels
% stat('Update_Labels_Transparency started')
for i = 1:labels.number,
    labels.colormap(labels.values(i)+1, :) = labels.colors(i, :);
%      labels.colormap(labels.values(i)+1, :) = labels.colors(i, :) * labels.visible(i);
    labels.alphaData(labels.data == labels.values(i)) = labels.visible(i);
end
labels.alphaData = labels.alpha * labels.alphaData;
% stat('Update_Labels_Transparency finished')


%% Parsing labels data ------------------------------------------------
function Parse_Labels(handles)
global labels
% stat('Parse_Labels started')
labels.colors = labels.std_colors;
% ul  = unique(labels.data);
% ul  = ul(find(ul~=0));
%  ul  = unique_labels(labels.data);
ul  = unique_labels(uint16(labels.data));

n_ul= length(ul);
if n_ul>labels.max_number,
%      error('Too many labels! Sorry...');
    n_ul = labels.max_number;
end

i = 1;
while (i<=labels.number) && (n_ul<labels.max_number),
%     ul  = unique([ul; labels.values(i)]);
%      ul  = unique_labels([ul; labels.values(i)]);
    ul  = unique_labels(uint16([ul; labels.values(i)]));
    n_ul= length(ul);
    i   = i + 1;
end
labels.number = n_ul;
labels.values(1:n_ul) = ul;
% stat('Parse_Labels finished')

%% Update labels list ------------------------------------------------
function Update_Shifts_and_Saves(handles)
global labels
if any(labels.visible),
    set(handles.Shift_Left, 'Enable', 'On');
    set(handles.Shift_Right, 'Enable', 'On');
    set(handles.Shift_Up, 'Enable', 'On');
    set(handles.Shift_Down, 'Enable', 'On');
    set(handles.Save_For_Training, 'Enable', 'on');
    set(handles.Save_Set, 'Enable', 'on');
    set(handles.Percentage_Edit, 'Enable', 'on');
    set(handles.Set_Size_Edit, 'Enable', 'on');
else
    set(handles.Shift_Left, 'Enable', 'Off');
    set(handles.Shift_Right, 'Enable', 'Off');
    set(handles.Shift_Up, 'Enable', 'Off');
    set(handles.Shift_Down, 'Enable', 'Off');
    set(handles.Save_For_Training, 'Enable', 'off');
    set(handles.Save_Set, 'Enable', 'off');
    set(handles.Percentage_Edit, 'Enable', 'off');
    set(handles.Set_Size_Edit, 'Enable', 'off');
end

%% Update labels list ------------------------------------------------
function Update_Labels_List(handles)
global labels picture myhandles
Update_Shifts_and_Saves(handles)
% stat('Update_Labels_List started')
if picture.list_length,
    set(handles.Add_New_Label, 'Enable', 'On');
    set(handles.Edit_Active_Label, 'Enable', 'On');
else
    set(handles.Add_New_Label, 'Enable', 'Off');
    set(handles.Edit_Active_Label, 'Enable', 'Off');
end

if labels.number>1,
    set(handles.Del_Active_Label, 'Enable', 'On');
else
    set(handles.Del_Active_Label, 'Enable', 'Off');
end
for i = 1:labels.max_number,
    if i<=labels.number,
        set(myhandles.hLA(i), ...
            'Enable', 'On', 'Value', 0, ...
            'BackgroundColor', labels.colors(i, :), ...
            'String', num2str(labels.values(i)));
        set(myhandles.hLV(i), ...
            'Enable', 'On', ...
            'BackgroundColor', labels.colors(i, :), ...
            'Value', labels.visible(i));
    else
        set(myhandles.hLA(i), ...
            'Enable', 'Off', 'Value', 0, ...
            'BackgroundColor', [.7 .7 .7], ...
            'String', '');
        set(myhandles.hLV(i), ...
            'Enable', 'Off', ...
            'BackgroundColor', [.7 .7 .7], ...
            'Value', labels.visible(i));
    end
    set(myhandles.hLA(labels.active), 'Value', 1);
end
% stat('Update_Labels_List finished')

%% --------------------------------------------------------------------
function Next_Image_Callback(hObject, eventdata, handles)
global labels picture
if picture.list_length,
    im_index = get(handles.Images_List, 'Value');
    if im_index==picture.list_length,
        set(handles.Images_List, 'Value', 1);
    else
        set(handles.Images_List, 'Value', im_index+1);
    end
    Update_Window(handles);
end
% stat('Go to next picture')
%% --------------------------------------------------------------------
function Previous_Image_Callback(hObject, eventdata, handles)
global labels picture
if picture.list_length,
    im_index = get(handles.Images_List, 'Value');
    if im_index==1,
        set(handles.Images_List, 'Value', picture.list_length);
    else
        set(handles.Images_List, 'Value', im_index-1);
    end
    Update_Window(handles);
end
% stat('Go to prev picture')


%% --- Executes on button press in First_Image.
function First_Image_Callback(hObject, eventdata, handles)
global picture
if (picture.list_length) && (get(handles.Images_List, 'Value')~=1),
    set(handles.Images_List, 'Value', 1);
    Update_Window(handles);
end
% stat('Go to first picture')

%% --------------------------------------------------------------------
function Last_Image_Callback(hObject, eventdata, handles)
global picture
if (picture.list_length) && (get(handles.Images_List, 'Value')~=picture.list_length),
    set(handles.Images_List, 'Value', picture.list_length);
    Update_Window(handles);
end
% stat('Go to last picture')

%% --------------------------------------------------------------------
function Close_MenuItem_Callback(hObject, eventdata, handles)
selection = questdlg(['Close ' get(handles.myLabel, 'Name') '?'], ...
                     ['Close ' get(handles.myLabel, 'Name') '...'], ...
                     'Yes', 'No', 'Yes');
if strcmp(selection, 'No')
    return;
end
delete(handles.myLabel)

%% Some stupid callbcks
function Change_Active_Label(hObject, eventdata, handles, new_active_label)
global labels myhandles
labels.active = new_active_label;
% stat(['Now ' num2str(new_active_label) ' is active label']);
set(myhandles.hLA(labels.active), 'Value', 1);

%% --------------------------------------------------------------------
function Toggle_Label_Visibility(hObject, eventdata, handles, label_num)
global labels picture myhandles
% stat(['Toggle visibility of label ' num2str(label_num)]);
labels.visible(label_num) = get(myhandles.hLV(label_num), 'Value');
labels.alphaData(labels.data == labels.values(label_num)) = labels.visible(label_num)*labels.alpha;
Update_Shifts_and_Saves(handles);
%  Update_Labels_Transparency;
Update_Labels_Layer;
%  get(myhandles.picture, 'CDataMapping')
%  get(myhandles.labels, 'CDataMapping')
%  get(myhandles.labels, 'AlphaDataMapping')
%% --------------------------------------------------------------------
%% Labels Add/Edit/Del
function Add_New_Label_Callback(hObject, eventdata, handles)
global labels
labels.max_number
if labels.number<labels.max_number,
    labels.number = labels.number + 1;
    labels.colors(labels.number, :)  = labels.std_colors(labels.number, :);
    labels.values(labels.number)    = labels.std_values(labels.number);
    Update_Labels_List(handles);
    % stat('New label added');
else
    stat('Too many labels, sorry...');
end

% -------------
function Edit_Active_Label_Callback(hObject, eventdata, handles)
global labels
al      = labels.active
old_l   = labels.values(al);
notOK   = true;
while notOK,
    dlg_title = ['Value of label ' num2str(al)];
    prompt = {'Enter new value (nonzero uint8):'};
    num_lines = 1;
    def = {num2str(labels.values(al))};
    answer = inputdlg(prompt, dlg_title, num_lines, def);
    if ~isempty(answer),
        try
            new_l = uint8(str2num(answer{1}));
            labels.values(al) = new_l;
            labels.data(find(labels.data==old_l)) = new_l;
            notOK = false;
        catch
            % stat('Check your values...');
        end
    else
        notOK = false;
    end
end
labels.changed = 1;
Update_Labels_List(handles);
guidata(hObject, handles);

% -------------
function Del_Active_Label_Callback(hObject, eventdata, handles)
global labels
if labels.number>1,
%     h = strcmp(questdlg('Are you sure?'), 'Yes')
    if strcmp(questdlg('Are you sure?'), 'Yes'),

        % erase data from labels data
        labels.data(find(labels.data==labels.values(labels.active))) = 0;

        % scroll up labels
        for i = labels.active:(labels.number-1),
            labels.values(i) = labels.values(i+1);
            labels.colors(i, :) = labels.colors(i+1, :);
            labels.visible(i) =  labels.visible(i+1);
        end
        % adjust the last label (now it should be free)
        labels.visible(labels.number) = 0;
        labels.values(labels.number) = labels.std_values(labels.number);
        labels.colors(labels.number, :) = labels.std_colors(labels.number, :);
        labels.number = labels.number - 1;
        if labels.active>labels.number,
            labels.active = labels.number;
        end
        Update_Labels_List(handles);
        Update_Labels_Transparency;
        Update_Labels_Layer;
    end
    labels.changed = 1;
end

%% --- Executes on button press in Ten_Images_Back.
function Ten_Images_Back_Callback(hObject, eventdata, handles)
global labels picture
if picture.list_length,
    im_index = get(handles.Images_List, 'Value');
    if im_index-10 < 1,
        set(handles.Images_List, 'Value', 1);
    else
        set(handles.Images_List, 'Value', im_index-10);
    end
    Update_Window(handles);
end
% stat('Skip by 10 picture backward')

%% --- Executes on button press in Ten_Images_Forward.
function Ten_Images_Forward_Callback(hObject, eventdata, handles)
global labels picture
if picture.list_length,
    im_index = get(handles.Images_List, 'Value');
    if im_index+10 > picture.list_length,
        set(handles.Images_List, 'Value', picture.list_length);
    else
        set(handles.Images_List, 'Value', im_index+10);
    end
    Update_Window(handles);
end
% stat('Skip by 10 picture forward')

%% ---------------------------------------------------------------------
function Add_Images_To_List_Callback(hObject, eventdata, handles)
% --- Executes on button press in Add_Images_To_List.
global labels picture
% stat('Add_Images_To_List_Callback started')
if isempty(picture.dir),
    [filenames, pathname] = uigetfiles(getpref('myLabel', 'default_pix_dir'));
else
    [filenames, pathname] = uigetfiles(picture.dir{end});
end

n_files       = length(filenames);
if n_files>0,
    if isempty(picture.list),
        picture.list = {};
        picture.dir = {};
    end
    p_list        = cell(1, n_files);
    p_dir         = cell(1, n_files);
    p_list_length = 0;
    h = waitbar(0, 'Adding images to list...');

    for i=1:n_files,
        try
            info    = imfinfo(fullfile(pathname, filenames{i}));
            p_list_length = p_list_length + 1;
            p_list{p_list_length} = filenames{i};
            p_dir{p_list_length}  = pathname;
            waitbar(i/n_files, h)
        catch
            fprintf('File %s not recognized as picture, skipping.\n', filenames{i});
        end
    end
    close(h)
    % picture.current     = 0;
    picture.list = [picture.list p_list(1:p_list_length)];
    picture.dir  = [picture.dir  p_dir(1:p_list_length)];

    Update_Images_List(handles);
    Update_Window(handles);
end
% stat('Load_Images_Callback finished')
%% ---------------------------------------------------------------------
function Clear_Images_List_Callback(hObject, eventdata, handles)
% stat('Clear_Images_List_Callback started')
Save_Current_Labels(handles);
Init_Picture
Init_Labels
Init_Cursor
Init_Clipboard
Init_Undo
Init_Zoom
Init_Myhandles(handles)
Update_Window(handles)
Update_Images_List(handles)
% stat('Clear_Images_List_Callback finished')

%% ---------------------------------------------------------------------
% --- Executes on button press in Load_State.
function Load_State_Callback(hObject, eventdata, handles)
global labels picture
default_path = getpref('myLabel', 'default_states_dir');
[filename, pathname] = uigetfile([default_path '/*.mat'], 'Pick state ...');
if ischar(filename) && ~isempty(filename),
    load(fullfile(pathname, filename), 'picture', 'labels', 'undo', 'zoom', 'clipboard');
    Set_Labels_Default_Values
    Init_Myhandles(handles)
    set(handles.Images_List, 'String', picture.list);
    set(handles.Images_List, 'Value', 1);
    picture.current     = 0;
    Update_Window(handles);
    Update_Images_List(handles)
end

%% ---------------------------------------------------------------------
function Save_State_Callback(hObject, eventdata, handles)
% --- Executes on button press in Save_State.
global labels picture undo zoom clipboard
[filename, pathname] = uiputfile('*.mat', 'Save state as...');
if ~isempty(filename),
    save(fullfile(pathname, filename), 'picture', 'labels', 'undo', 'zoom', 'clipboard');
end

%% ---------------------------------------------------------------------
function Autosave
global labels picture undo zoom clipboard
save(fullfile(pwd, 'autosave.mat'), 'picture', 'labels', 'undo', 'zoom', 'clipboard');
%% ---------------------------------------------------------------------
function Image_Axes_CreateFcn(hObject, eventdata, handles)
% --- Executes during object creation, after setting all properties.

%% ---------------------------------------------------------------------
function Labels_ButtonDown(hObject, eventdata, handles)
global labels cursor undo
% stat('Labels_ButtonDown started')
cursor.mb_pressed = 1;
undo = labels.data;
set(handles.Undo, 'Enable', 'on');
Action
% stat('Labels_ButtonDown finished')

%% ---------------------------------------------------------------------
function Change_Operation(hObject, eventdata, handles, new_operation)
global labels myhandles
labels.operation = new_operation;
% new_operation
% stat(['Current operation is ' num2str(new_operation)]);
% set(handles.myLabel, 'WindowButtonMotionFcn', 'Mouse_Moved');
if labels.operation==3,
    set(myhandles.brush, 'Visible', 'off');
else
    set(myhandles.brush, 'Visible', 'on');
end

set(hObject, 'Value', 1);

% --- Executes on slider movement.
function Cursor_Size_Slider_Callback(hObject, eventdata, handles)
global cursor
cursor.size = round(get(hObject, 'Value'));
set(handles.Cursor_Size_Text, 'String', num2str(cursor.size))


% --- Executes during object creation, after setting all properties.
function Cursor_Size_Slider_CreateFcn(hObject, eventdata, handles)
global cursor
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject, 'BackgroundColor'), get(0, 'defaultUicontrolBackgroundColor'))
    set(hObject, 'BackgroundColor', [.9 .9 .9]);
end

% --------------------------------------------------------------------
function Cursor_Size_Text_Callback(hObject, eventdata, handles)
global cursor
% Hints: get(hObject, 'String') returns contents of Cursor_Size_Text as text
%        str2double(get(hObject, 'String')) returns contents of Cursor_Size_Text as a double
new_pointer_size = str2double(get(hObject, 'String'));
if isfinite(new_pointer_size),
    new_pointer_size = min(new_pointer_size, cursor.max_size);
    new_pointer_size = max(new_pointer_size, 0);
    cursor.size = round(new_pointer_size);
end
set(hObject, 'String', num2str(cursor.size))
set(handles.Cursor_Size_Slider, 'String', num2str(cursor.size))


% --- Executes during object creation, after setting all properties.
function Cursor_Size_Text_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject, 'BackgroundColor'), get(0, 'defaultUicontrolBackgroundColor'))
    set(hObject, 'BackgroundColor', 'white');
end

% --------------------------------------------------------------------
function Copy_Callback(hObject, eventdata, handles)
global clipboard labels
% stat('Copy_Callback started')
clipboard = labels.data;
set(handles.Paste, 'Enable', 'on')
set(handles.Overlay, 'Enable', 'on')
set(handles.Overlay, 'Enable', 'on')
set(handles.Paste, 'Enable', 'on')
for i = 1:labels.number,
    if ~labels.visible(i),
        clipboard(find(clipboard==labels.values(i)))=0;
    end
end
% stat('Copy_Callback finished')

% --------------------------------------------------------------------
function Paste_Callback(hObject, eventdata, handles)
global clipboard labels
% stat('Paste_Callback started')
temp            = clipboard;
if size(temp, 1)<size(labels.data, 1),
    temp(size(labels.data, 1), 1) = 0;        % add rows
else
    temp = temp(1:size(labels.data, 1), :);   % del extra rows
end
if size(temp, 2)<size(labels.data, 2),
    temp(1, size(labels.data, 1)) = 0;        % add cols
else
    temp = temp(:, 1:size(labels.data, 2));   % del extra cols
end
labels.data = temp;       % paste
labels.changed  = 1;
Update_Window(handles);
% stat('Paste_Callback finished')

% --- Executes on button press in Undo.
function Undo_Callback(hObject, eventdata, handles)
global undo labels
if ~isempty(undo),
    labels.data = undo;
    set(handles.Undo, 'Enable', 'off');
    set(handles.Undo, 'Enable', 'off');
    labels.changed  = 1;
    Update_Window(handles);
end
% --------------------------------------------------------------------
function Overlay_Callback(hObject, eventdata, handles)
global clipboard labels
% stat('Overlay_Callbackstarted')
temp            = clipboard;
if size(temp, 1)<size(labels.data, 1),
    temp(size(labels.data, 1), 1) = 0;        % add rows
else
    temp = temp(1:size(labels.data, 1), :);   % del extra rows
end
if size(temp, 2)<size(labels.data, 2),
    temp(1, size(labels.data, 1)) = 0;        % add cols
else
    temp = temp(:, 1:size(labels.data, 2));   % del extra cols
end
labels.data = labels.data .* uint16(~temp);       % clear
labels.data = labels.data + uint16(temp);           % overlay

labels.changed  = 1;
Update_Window(handles);
% stat('Overlay_Callback finished')


% --- Executes on slider movement.
function Transparency_Slider_Callback(hObject, eventdata, handles)
global labels
labels.alpha = get(hObject, 'Value');
set(handles.Transparency_Edit, 'String', sprintf('%04.2f', labels.alpha));
%  labels.changed = 1;
Update_Labels_Transparency;
Update_Labels_Layer;

% --- Executes during object creation, after setting all properties.
function Transparency_Slider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject, 'BackgroundColor'), get(0, 'defaultUicontrolBackgroundColor'))
    set(hObject, 'BackgroundColor', [.9 .9 .9]);
end

% --- Executes during object creation, after setting all properties.
function Transparency_Edit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject, 'BackgroundColor'), get(0, 'defaultUicontrolBackgroundColor'))
    set(hObject, 'BackgroundColor', 'white');
end

% -------------------------------------------------------------------------
function Save_Image_Pushbutton_Callback(hObject, eventdata, handles)
% --- Executes on button press in Save_Image_Pushbutton.
global labels picture
% prepare temp mask
[mask vl]= Get_Mask;

filename = fullfile(picture.output_dir, picture.short_filename);
data_2_save = picture.data;
if get(handles.Masked_Checkbox, 'Value'),
    filename = [filename '_masked'];
    data_2_save = data_2_save .* mask;
end
if get(handles.Cropped_Checkbox, 'Value'),
    filename = [filename '_cropped'];
    cols = find(sum(mask(:, :, 1)));
    rows = find(sum(mask(:, :, 1), 2));
    data_2_save = data_2_save(min(rows):max(rows), min(cols):max(cols), :);
end
filename = [filename vl picture.filename_ext];

if ~exist(picture.output_dir, 'dir'), mkdir(picture.output_dir); end
imwrite(data_2_save, filename);
stat(['Image saved in ' filename]);

% --- Executes on button press in Masked_Checkbox.
function Masked_or_Cropped_Checkbox_Callback(hObject, eventdata, handles)
if ~get(handles.Zoom_Togglebutton, 'Value') && ...
        (get(handles.Masked_Checkbox, 'Value')+get(handles.Cropped_Checkbox, 'Value')),
    set(handles.Save_Image_Pushbutton, 'Enable', 'on');
else
    set(handles.Save_Image_Pushbutton, 'Enable', 'off');
end


% --- Executes on button press in Zoom_Togglebutton.
function Zoom_Togglebutton_Callback(hObject, eventdata, handles)
global picture labels zoom
zoom.active = get(handles.Zoom_Togglebutton, 'Value');
if zoom.active,
    % Store main data
    zoom.picture = picture;
    zoom.labels = labels;

    % Store some controls states
    zoom.events.WindowButtonMotionFcn = get(handles.myLabel, 'WindowButtonMotionFcn');
    set(handles.myLabel, 'WindowButtonMotionFcn', ''); % forget about mousemove

    % Disable some controls
    set(handles.Save_Image_Pushbutton, 'Enable', 'off');
    set(handles.Images_List, 'Enable', 'off');
    set(handles.First_Image, 'Enable', 'off');
    set(handles.Last_Image, 'Enable', 'off');
    set(handles.Ten_Images_Back, 'Enable', 'off');
    set(handles.Ten_Images_Forward, 'Enable', 'off');
    set(handles.Next_Image, 'Enable', 'off');
    set(handles.Previous_Image, 'Enable', 'off');
    Masked_or_Cropped_Checkbox_Callback(hObject, eventdata, handles);

    region = round(getrect(handles.Image_Axes))
    zoom.col1 = region(1);
    zoom.row1 = region(2);
    zoom.col2 = region(1) + region(3);
    zoom.row2 = region(2) + region(4);

    % check boundaries
    if zoom.col1<1, zoom.col1 = 1; end
    if zoom.col1>picture.cols, zoom.col1 = picture.cols; end
    if zoom.col2<1, zoom.col2 = 1; end
    if zoom.col2>picture.cols, zoom.col2 = picture.cols; end

    if zoom.row1<1, zoom.row1 = 1; end
    if zoom.row1>picture.rows, zoom.row1 = picture.rows; end
    if zoom.row2<1, zoom.row2 = 1; end
    if zoom.row2>picture.rows, zoom.row2 = picture.rows; end


    % cut data
    labels.data     = labels.data(zoom.row1:zoom.row2, zoom.col1:zoom.col2);
    labels.alphaData= labels.alphaData(zoom.row1:zoom.row2, zoom.col1:zoom.col2);
    picture.data    = picture.data(zoom.row1:zoom.row2, zoom.col1:zoom.col2);

    % restore events
    set(handles.myLabel, 'WindowButtonMotionFcn', zoom.events.WindowButtonMotionFcn);

    % let the show begins...
%     Update_Window(handles);
    myhandles.picture  = imshow(picture.data); % Show_Image;
    Create_Labels_Layer(handles);
    Update_Labels_Layer;
    Autosave
else
    % Paste new data
    zoom.labels.changed = labels.changed;
    zoom.picture.data(zoom.row1:zoom.row2, zoom.col1:zoom.col2) = picture.data;
    zoom.labels.data(zoom.row1:zoom.row2, zoom.col1:zoom.col2)  = labels.data;
    zoom.labels.alphaData(zoom.row1:zoom.row2, zoom.col1:zoom.col2)  = labels.alphaData;

    % Retore main data
    picture = zoom.picture;
    labels = zoom.labels;

    % Restore some controls states
    set(handles.myLabel, 'WindowButtonMotionFcn', zoom.events.WindowButtonMotionFcn);
    Update_Images_List(handles);
    Masked_or_Cropped_Checkbox_Callback(hObject, eventdata, handles);
    Update_Window(handles);
end

% --------------------------------------------------------------------
function Del_Image_From_List_Callback(hObject, eventdata, handles)
global picture
if picture.list_length <= 1,
    Init_Picture;
else
    list = {};
    dir = {};
    j = 0;
    for i = 1:picture.list_length,
        if i~=picture.current,
            j = j + 1;
            list{j} = picture.list{i};
            dir{j} = picture.dir{i};
        end
    end
    picture.list = list;
    picture.dir = dir;
    picture.list_length = picture.list_length - 1;
    if picture.current>picture.list_length,
        picture.current = picture.list_length;
    end
end
Update_Images_List(handles);
Update_Window(handles);

% --- Executes on key press over myLabel with no controls selected.
function KeyPressFcn(hObject, eventdata, handles)
global myhandles labels zoom
key = get(handles.myLabel, 'CurrentCharacter');
%  fprintf('KeyPressFcn called by %s\n', get(hObject, 'Tag'));
%  stat('KeyPressFcn')
if ~isempty(key),
    uikey = uint16(key);
%      fprintf(' uikey=%g\n', uikey);
    switch uikey
        case {29, 31}  % right down
            if ~zoom.active && hObject~=handles.Images_List,
                Next_Image_Callback(hObject, eventdata, handles);
            end
        case {28, 30}  % left up
            if ~zoom.active && hObject~=handles.Images_List,
                Previous_Image_Callback(hObject, eventdata, handles);
            end
        case 32  % space key
            if strcmp(get(myhandles.hLV(labels.active), 'Enable'), 'on'),
                labels.visible(labels.active) = 1 - labels.visible(labels.active);
                Update_Labels_List(handles);
                Update_Labels_Transparency;
                Update_Labels_Layer;
            end
        case {48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, % '0' .. '9'
            numkey = uikey - 48;
            if numkey==0, numkey = 10; end
            if labels.number >= numkey,
                labels.visible(numkey) = 1 - labels.visible(numkey);
                Update_Labels_List(handles);
                Update_Labels_Transparency;
                Update_Labels_Layer;
                Update_Labels_List(handles);
            end
        case {33, 35, 36, 37}, % Shift+'1', '3'..'5'
            numkey = uikey - 32;
            if labels.number >= numkey,
               labels.active = numkey;
               Update_Labels_List(handles);
            end
        case 64, % Shift+'2'
            if labels.number >= 2,
               labels.active = 2;
               Update_Labels_List(handles);
            end
        case 94, % Shift+'6'
            if labels.number >= 6,
               labels.active = 6;
               Update_Labels_List(handles);
            end
        case 38, % Shift+'7'
            if labels.number >= 7,
               labels.active = 7;
               Update_Labels_List(handles);
            end
        case 42, % Shift+'8'
            if labels.number >= 8,
               labels.active = 8;
               Update_Labels_List(handles);
            end
        case {83, 115} % 'S' 's' - operation set
            labels.operation = 1;
            set(myhandles.brush, 'Visible', 'on');
            set(handles.Operation_Set, 'Value', 1);
            set(handles.Operation_Clear, 'Value', 0);
            set(handles.Operation_Fill, 'Value', 0);
        case {67, 99} % 'C' 'c' - operation clear
            labels.operation = 2;
            set(myhandles.brush, 'Visible', 'on');
            set(handles.Operation_Set, 'Value', 0);
            set(handles.Operation_Clear, 'Value', 1);
            set(handles.Operation_Fill, 'Value', 0);
        case {70, 102} % 'F' 'f' - operation fill
            labels.operation = 3;
            set(myhandles.brush, 'Visible', 'off');
            set(handles.Operation_Set, 'Value', 0);
            set(handles.Operation_Clear, 'Value', 0);
            set(handles.Operation_Fill, 'Value', 1);
        case {90, 122} % 'Z' 'z' - zoom
            set(handles.Zoom_Togglebutton, 'Value', 1-get(handles.Zoom_Togglebutton, 'Value'));
            Zoom_Togglebutton_Callback(handles.Zoom_Togglebutton, eventdata, handles)
        case 65  % 'A' -  shift left by 10 pixels
            Shift_Visible_Labels(hObject, eventdata, handles, 1, 10);
        case 97  % 'a' - shift left by 1 pixel
            Shift_Visible_Labels(hObject, eventdata, handles, 1);
        case 68  % 'D' - shift right by 10 pixels
            Shift_Visible_Labels(hObject, eventdata, handles, 2, 10);
        case 100 % 'd' - shift right by 1 pixel
            Shift_Visible_Labels(hObject, eventdata, handles, 2);
        case 87  % 'W' - shift up by 10 pixels
            Shift_Visible_Labels(hObject, eventdata, handles, 3, 10);
        case 119 % 'w' - shift up by 1 pixel
            Shift_Visible_Labels(hObject, eventdata, handles, 3);
        case 88  % 'X' - shift down by 10 pixels
            Shift_Visible_Labels(hObject, eventdata, handles, 4, 10);
        case 120 % 'X' 'x' - shift down by 1 pixel
            Shift_Visible_Labels(hObject, eventdata, handles, 4);
        case {84, 116} % 'T' 't' - Save_For_Training
            Save_For_Training_Callback(hObject, eventdata, handles);
    end
end

% --- Executes on button press in Shift_Left.
function Shift_Visible_Labels(hObject, eventdata, handles, direction, value)
global labels
if nargin<5,
    value = 1;
end
if any(labels.visible),
    mask = false(size(labels.data));
    for i = 1:labels.number,
        if labels.visible(i),
            mask = mask | labels.data==labels.values(i);
        end
    end
    temp  = labels.data .* uint16(mask);
    labels.data = labels.data .* uint16(~mask);  % clear visible labels
    switch direction,
        case 1, % left
            mask(:, 1:(end-value)) = mask(:, (1+value):end); mask(:, (end-value+1):end) = 0;
            temp(:, 1:(end-value)) = temp(:, (1+value):end); temp(:, (end-value+1):end) = 0;
        case 2, % right
            mask(:, (1+value):end) = mask(:, 1:(end-value));  mask(:, 1:value) = 0;
            temp(:, (1+value):end) = temp(:, 1:(end-value));  temp(:, 1:value) = 0;
        case 3, % up
            mask(1:(end-value), :) = mask((1+value):end, :); mask((end-value+1):end, :) = 0;
            temp(1:(end-value), :) = temp((1+value):end, :); temp((end-value+1):end, :) = 0;
        case 4, % down
            mask((1+value):end, :) = mask(1:(end-value), :);  mask(1:value, :) = 0;
            temp((1+value):end, :) = temp(1:(end-value), :);  temp(1:value, :) = 0;
    end
    labels.data = labels.data .* uint16(~mask);  % clear again
    labels.data = labels.data + temp;
    labels.changed = 1;
    Update_Labels_Transparency;
    Update_Labels_Layer
end

function [mask vl] = Get_Mask
global labels picture
% prepare temp mask
mask = uint8(labels.data>0);
vl = '';
for i = 1:labels.number,
    if ~labels.visible(i),
        mask(find(labels.data==labels.values(i)))=0;
    else
        vl = [vl '_' num2str(labels.values(i))];
    end
end

% transform it to needed format
if ndims(picture.data)==3,
    mask = repmat(mask, [1 1 3]);
end


% --- Executes on button press in Save_For_Training.
function Save_For_Training_Callback(hObject, eventdata, handles)
global labels picture
[mask vl]= Get_Mask;
filename = picture.short_filename;
im = picture.data;
if get(handles.Masked_Checkbox, 'Value'),
    filename = [filename '_masked'];
    im = im .* mask;
end
cols    = find(sum(mask(:, :, 1)));
rows    = find(sum(mask(:, :, 1), 2));
y1      = min(rows);
y2      = max(rows);
x1      = min(cols);
x2      = max(cols);

if get(handles.Cropped_Checkbox, 'Value'),
    filename = [filename '_cropped'];
    im = im(y1:y2, x1:x2, :);
end
stat(['Saving for training in: ' filename]);
cut_object(im, [x1 y1 x2 y2], vl, picture.output_dir, filename);
stat('done');


% --- Executes on button press in Save_Set.
function Save_Set_Callback(hObject, eventdata, handles)
global labels picture
stat(['Saving set of images:']);

percentage  = str2double(get(handles.Percentage_Edit, 'String'));
images2save = str2double(get(handles.Set_Size_Edit, 'String'));

[mask vl]   = Get_Mask;
filename    = picture.short_filename;
min_size_ratio  = 1/5;
%  im          = picture.data;
cut_slices(picture.data, mask, [], picture.output_dir, picture.short_filename, [], min_size_ratio, percentage, images2save, [], [], 2, 0, 4)
%  stat('done');

% --- Executes when user edit Percentage edit control
function Percentage_Edit_Callback(hObject, eventdata, handles)
Percentage = str2double(get(hObject, 'String'));
if ~isfinite(Percentage) || Percentage<0 || Percentage>=100,
    set(hObject, 'String', '75')
else
    set(hObject, 'String', num2str(round(Percentage)));
end

% --- Executes during object creation, after setting all properties.
function Percentage_Edit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject, 'BackgroundColor'), get(0, 'defaultUicontrolBackgroundColor'))
    set(hObject, 'BackgroundColor', 'white');
end


% --- Executes when user edit Set_Size_Edit edit control.
function Set_Size_Edit_Callback(hObject, eventdata, handles)
SetSize = str2double(get(hObject, 'String'));
if ~isfinite(SetSize) || SetSize <=0 || SetSize>=100,
    set(hObject, 'String', '10')
else
    set(hObject, 'String', num2str(ceil(SetSize)));
end

% --- Executes during object creation, after setting all properties.
function Set_Size_Edit_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject, 'BackgroundColor'), get(0, 'defaultUicontrolBackgroundColor'))
    set(hObject, 'BackgroundColor', 'white');
end

function Update_Interpolate_and_Track_Buttons(handles)
if get(handles.Toggle_Starting_Mask, 'Value') && get(handles.Toggle_Ending_Mask, 'Value'),
    set(handles.Interpolate, 'Enable', 'on')
    set(handles.Track, 'Enable', 'on')
else
    set(handles.Interpolate, 'Enable', 'off')
    set(handles.Track, 'Enable', 'off')
end


% --- Executes on button press in Toggle_Starting_Mask.
function Toggle_Starting_Mask_Callback(hObject, eventdata, handles)
global labels picture interpolation
Update_Interpolate_and_Track_Buttons(handles)
if get(hObject, 'Value'),
    interpolation.label         = labels.values(labels.active);
    mask                        = labels.data==interpolation.label;
    cols                        = find(sum(mask));
    rows                        = find(sum(mask, 2));
    interpolation.first.row     = min(rows);
    interpolation.first.height  = max(rows) - min(rows) + 1;
    interpolation.first.col     = min(cols);
    interpolation.first.width   = max(cols) - min(cols) + 1;
    interpolation.first.mask    = mask(min(rows):max(rows), min(cols):max(cols));
    interpolation.first.frame_n = picture.current;

    object  = picture.data( min(rows):max(rows), min(cols):max(cols) );
    if ndims(object)==3,
        interpolation.first.object = rgb2gray(object);
    else
        interpolation.first.object = object;
    end
    interpolation.first.object = double(interpolation.first.object)/255 .* double(interpolation.first.mask);

%      stat(sprintf('first_frame:%g, label:%g, row:%g, col:%g, mask size:%gx%g\n', interpolation.first.frame_n, interpolation.label, interpolation.first.row, interpolation.first.col, size(interpolation.first.mask)));
end

% --- Executes on button press in Toggle_Ending_Mask.
function Toggle_Ending_Mask_Callback(hObject, eventdata, handles)
global labels picture interpolation
Update_Interpolate_and_Track_Buttons(handles);
if get(hObject, 'Value'),
    %  interpolation.label        = labels.values(labels.active);
    interpolation.label         = labels.values(labels.active);
    mask                        = labels.data==interpolation.label;
    cols                        = find(sum(mask));
    rows                        = find(sum(mask, 2));
    interpolation.last.row      = min(rows);
    interpolation.last.height   = max(rows) - min(rows) + 1;
    interpolation.last.col      = min(cols);
    interpolation.last.width    = max(cols) - min(cols) + 1;
    interpolation.last.mask     = mask(min(rows):max(rows), min(cols):max(cols));
    interpolation.last.frame_n  = picture.current;

    object  = picture.data( min(rows):max(rows), min(cols):max(cols) );
    if ndims(object)==3,
        interpolation.last.object = rgb2gray(object);
    else
        interpolation.last.object = object;
    end
    interpolation.last.object = double(interpolation.last.object)/255 .* double(interpolation.last.mask);


    stat(sprintf('last_frame:%g, label:%g, row:%g, col:%g, mask size:%gx%g\n', interpolation.last.frame_n, interpolation.label, interpolation.last.row, interpolation.last.col, size(interpolation.last.mask)));
end
% --- Executes on button press in Interpolate.
function Interpolate_or_Track(hObject, eventdata, handles, track)
global labels picture interpolation
search_radius = 6;
momentum      = 8;
if ~exist('track', 'var') || isempty(track),
    track = 0;
end
if interpolation.first.frame_n<interpolation.last.frame_n,
    start_frame = interpolation.first.frame_n;
    start_mask  = double(interpolation.first.mask);
    end_frame   = interpolation.last.frame_n;
    end_mask    = double(interpolation.last.mask);
    prev_obj    = interpolation.first.object;
else
    start_frame = interpolation.last.frame_n;
    start_mask  = double(interpolation.last.mask);
    end_frame   = interpolation.first.frame_n;
    end_mask    = double(interpolation.first.mask);
    prev_obj    = interpolation.last.object;
end

frames2interpolate = end_frame - start_frame - 1;

fprintf('start_frame: %g, end_frame: %g, frames2interpolate: %g, label: %g\n', start_frame, end_frame, frames2interpolate, interpolation.label);
%  figure(2);
%  subplot(1, frames2interpolate+2, 1); imagesc(start_mask)
%  subplot(1, frames2interpolate+2, frames2interpolate+2); imagesc(end_mask)


first_x = (1:interpolation.first.width)-1;
first_x = first_x / max(first_x);
first_y = (1:interpolation.first.height)-1;
first_y = first_y / max(first_y);
[first_x, first_y] = meshgrid(first_x, first_y);

last_x  = (1:interpolation.last.width)-1;
last_x  = last_x / max(last_x);
last_y  = (1:interpolation.last.height)-1;
last_y  = last_y / max(last_y);
[last_x, last_y] = meshgrid(last_x, last_y);

prev_row = interpolation.first.row;
prev_col = interpolation.first.col;
picture.data(prev_row + (1:interpolation.first.height) - 1, prev_col + (1:interpolation.first.width) - 1);

if frames2interpolate,
    for frame_i = 1:frames2interpolate,
        fprintf('processing frame #%g\n', start_frame+frame_i);
        lambda  = frame_i / (frames2interpolate+1);
        if 0 && track,
            row = prev_row;
            col = prev_col;
        else
            row = round((1-lambda)*interpolation.first.row + lambda*interpolation.last.row);
            col = round((1-lambda)*interpolation.first.col + lambda*interpolation.last.col);
        end
        width   = round((1-lambda)*interpolation.first.width + lambda*interpolation.last.width);
        height  = round((1-lambda)*interpolation.first.height + lambda*interpolation.last.height);
        if track,
            prev_obj= imresize(prev_obj, [height width], 'bicubic');
            if frame_i<frames2interpolate/2,
                warped_obj = imresize(interpolation.first.object, [height width], 'bicubic');
            else
                warped_obj = imresize(interpolation.last.object, [height width], 'bicubic');
            end
            figure(2);
            subplot(231); imshow(interpolation.first.object);
            subplot(232); imshow(warped_obj);
            subplot(233); imshow(interpolation.last.object);
            drawnow;
        end
        curr_x  = (1:width)-1;
        curr_x  = curr_x / max(curr_x);
        curr_y  = (1:height)-1;
        curr_y  = curr_y / max(curr_y);
        [curr_x, curr_y] = meshgrid(curr_x, curr_y);

        first_warped_mask = interp2(first_x, first_y, interpolation.first.mask, curr_x, curr_y);
        last_warped_mask  = interp2(last_x, last_y, interpolation.last.mask, curr_x, curr_y);

        mask = ((1-lambda) * first_warped_mask + lambda * last_warped_mask);
        mask    = mask>=.5;
        if track,
            figure(2);
            subplot(234); imshow(prev_obj); drawnow
            best_SSD = inf;
            best_object = [];
            best_row = 0;
            best_col = 0;
%              fprintf('min_col=%g, max_col=%g\n', max(1, col-search_radius), min(col+search_radius, picture.cols-width+1))
            for r = max(1, row-search_radius):min(row+search_radius, picture.rows-height+1),
                for c = max(1, col-search_radius):min(col+search_radius, picture.cols-width+1),
                    cutted_object = picture.data(r-1+(1:height), c-1+(1:width));
                    if ndims(cutted_object)==3,
                        cutted_object = rgb2gray(cutted_object);
                    end
                    cutted_object = double(cutted_object)/255 .* double(mask);
                    SSD = sum(sum((cutted_object - prev_obj).^2)) + momentum*sum(sum((warped_obj - cutted_object).^2));
                    figure(2);
                    if SSD<best_SSD,
                        best_SSD = SSD;
                        best_object = cutted_object;
                        best_row = r;
                        best_col = c;
                        subplot(235); imshow(best_object)
                    end
                    subplot(236); imshow(cutted_object);
                    drawnow
                end
            end
            row = best_row;
            col = best_col;
            prev_obj = best_object;
        end

        set(handles.Images_List, 'Value', start_frame+frame_i);
        Load_Image_and_Labels(handles);

        fullmask= zeros(size(labels.data), 'uint16');
        fullmask(row-1+(1:height), col-1+(1:width)) = mask;

        labels.data = labels.data .* uint16(1-fullmask);
        labels.data = labels.data + uint16(fullmask)*uint16(interpolation.label);
        labels.changed  = 1;
    end
%      masks = masks * interpolation.label;
    Load_Image_and_Labels(handles);
end
set(handles.Toggle_Starting_Mask, 'Value', 0);
set(handles.Toggle_Ending_Mask, 'Value', 0);
set(handles.Interpolate, 'Enable', 'off')
set(handles.Track, 'Enable', 'off')


% --------------------------------------------------------------------
function Set_Default_Labels_Dir_Callback(hObject, eventdata, handles)
default_dir = uigetdir(getpref('myLabel', 'default_labels_dir'), 'Pick a default labels directory');
if ischar(default_dir),
    setpref('myLabel', 'default_labels_dir', default_dir);
end

% --------------------------------------------------------------------
function Set_Default_images_dir_Callback(hObject, eventdata, handles)
default_dir = uigetdir(getpref('myLabel', 'default_pix_dir'), 'Pick a default images directory');
if ischar(default_dir),
    setpref('myLabel', 'default_pix_dir', default_dir);
end


% --------------------------------------------------------------------
function Set_Default_Output_Dir_Callback(hObject, eventdata, handles)
default_dir = uigetdir(getpref('myLabel', 'default_output_dir'), 'Pick a default output directory');
if ischar(default_dir),
    setpref('myLabel', 'default_output_dir', default_dir);
end

% --------------------------------------------------------------------
function About_Callback(hObject, eventdata, handles)
global info
%  helpdlg
text = {'myLabel - images labeling tool for Matlab', ...
        'by Nikolay Chumerin (myLabel.support@gmail.com)', ...
        sprintf('\nVersion: %g.%02g\nLast update %s', info.version, info.subversion, info.release_date)};
msgbox(text, 'About myLabel', 'Help', 'modal');
% myLabel - image sequencies labeling tool

Contact us at files@mathworks.com