% Returns cropped images of both pre-processed stack and GP maps 
% Would be more efficient to return coordinates of the bounding box.

function objectSeparatorGUI(img, GPimg)
close all

% Take nanmean of input images
M = cat(3,img{:}); % Convert to a 3-dimensional matrix
q = nanmean(M,3);
q2 = q;
q2(isnan(q2)) = 0;
q2(q2>0) = 1;
L = bwlabel(q2,8);
[croppedObjects, croppedObjectsThresh, croppedObjectsTemp, croppedObjectsThreshTemp] = deal(cell(1,1));

% Initial segmentation
ii = 1;
for i = 1:max(L(:))
    if numel(L(L==i))>20
        [r, c] = find(L==i);
        rcInd = sub2ind(size(q), r, c);
        mask = zeros(size(q));
        mask(rcInd) = 1;
        if numel(rcInd)>200 % Exclude small regions, user should have some control over this
            croppedObjects{ii,1} = GPimg;
            croppedObjects{ii,1}(~mask) = nan; % apply mask
            objectExtents = regionprops(mask,'BoundingBox');
            croppedObjects{ii,1} = imcrop(croppedObjects{ii,1}, objectExtents.BoundingBox);
            
            % NaN padding to prevent object from touching border
            croppedObjects{ii,1} = padarray(croppedObjects{ii,1}, [1,1]);
            croppedObjects{ii,1}(1,:) = nan;
            croppedObjects{ii,1}(end,:) = nan;
            croppedObjects{ii,1}(:,1) = nan;
            croppedObjects{ii,1}(:,end) = nan;
            
            for c=1:length(img) % for each wavelength in c-stack
                croppedObjectsThresh{ii,1}{c} = img{c};
                croppedObjectsThresh{ii,1}{c}(~mask) = nan; % apply mask
                croppedObjectsThresh{ii,1}{c} = imcrop(croppedObjectsThresh{ii,1}{c}, objectExtents.BoundingBox);
                croppedObjectsThresh{ii,1}{c} = padarray(croppedObjectsThresh{ii,1}{c}, [2,2]);
            end
            ii = ii+1;
        end
    end
end

% For figure sizing
screenDim = get(0,'screensize');
screenDim(screenDim == 1) = [];
figWidth = floor((screenDim(1)-150)/3);

% Image set preview
g.f = figure('toolbar','none','menu','none','units','pixels',...
    'name','Select image for separating objects with arrow keys');
imdisp(croppedObjects,'Size',1);
set(g.f, 'position',[0,0,figWidth,figWidth])
movegui(g.f,'center')
setappdata(g.f,'croppedObjects',croppedObjects)
setappdata(g.f,'croppedObjectsThresh',croppedObjectsThresh)

hh.f = [];
if(length(croppedObjects)>1)
    % Image set preview montage
    hh.f = figure('toolbar','none','menu','none','units','pixels', 'name','Overview');
    imdisp(croppedObjects);
    set(hh.f, 'position',[0,0,figWidth,figWidth])
    movegui(hh.f,'east')
end

% Create figure
h.f = figure('toolbar','none','menu','none','units','pixels',...
    'position',[0,0,550,600], 'name','Spectral Imaging Tool - Object Separator');
movegui(h.f,'west')

% Introduction
h.t(1) = uicontrol('style','text','units','pixels',...
    'position',[15,260,510,300], 'HorizontalAlignment','Left');
intro = sprintf('The Spectral Imaging Toolbox segments/crops connected-components (objects) from images automatically. The “Object Separator” is used to verify this automatic segmentation and to further segment as necessary.\nThe overview panel (right window) displays a gallery of the segmented/cropped objects. The current image to be segmented is shown in the center. Change the current image using the left and right arrows on the keyboard. Use this input GUI panel to perform segmentation on the current image as necessary.\nIf two or more objects are touching they will not be automatically separated. To separate them, enter the integer number of objects in the current image to be separated. Then click “automatically separate” to perform Watershed-based segmentation on the current image. You can then preview the results and accept or cancel the Watershed-segmentation attempt.\nIf Watershed-based segmentation does not yield the desired result, a manual alternative is lasso segmentation. Click “manually separate” to initialize lasso segmentation on the current image.\nTo remove the current image from the analysis click “remove current image”. The image will be removed and this change will be reflected in the overview panel on the right.\nWhen satisfied with the images in the overview panel, click “submit”.');
set(h.t(1),'String',intro)

% Instructions for manual cell separation 
h.t2(1) = uicontrol('style','text','units','pixels',...
    'position',[15,260,510,200], 'HorizontalAlignment','Left');
instructions = sprintf('Use the mouse to draw a border around the object to be segmented in the center panel. When you let go of the mouse the region drawn will be closed. It is OK to include background pixels in your outline but do not include pixels from adjacent objects. The center panel will then display a preview of the results. Accept or cancel the resulting segmented images by clicking “accept” or “cancel”, both of which return to the Object Separator.');
set(h.t2(1),'String',instructions, 'Visible','off')

h.t3(1) = uicontrol('style','text','units','pixels',...
    'position',[15,260,510,200], 'HorizontalAlignment','Left');
instructions = sprintf('Accept or cancel automatic object separation segmentation attempt');
set(h.t3(1),'String',instructions, 'Visible','off')

h.c(1) = uicontrol('style','edit','units','pixels',...
    'position',[300,225,20,20], 'String','2','HorizontalAlignment', 'left');

h.t(2) = uicontrol('style','text','units','pixels',...
    'position',[190,205,110,40], 'string','Objects in current image:','HorizontalAlignment', 'left');

h.p(1) = uicontrol('style','pushbutton','units','pixels',...
    'position',[50,150,130,20],'string','Manually separate', 'callback',@p_manuallySeparateObjects);

h.p(2) = uicontrol('style','pushbutton','units','pixels',...
    'position',[50,225,130,20],'string','Automatically separate', 'callback',@p_separateObjects);

h.p(3) = uicontrol('style','pushbutton','units','pixels',...
    'position',[50,25,130,20],'string','Submit', 'callback',@p_submit);

h.p(4) = uicontrol('style','pushbutton','units','pixels',...
    'position',[50,90,130,20],'string','Remove current image', 'callback',@p_removeImage);

h.p2(1) = uicontrol('style','pushbutton','units','pixels',...
    'position',[100,125,300,20],'string','Cancel', 'Visible', 'off', 'callback',@p_back);

h.p2(2) = uicontrol('style','pushbutton','units','pixels',...
    'position',[100,75,300,20],'string','Accept', 'Visible', 'off', 'callback',@p_accept);

h.p3(1) = uicontrol('style','pushbutton','units','pixels',...
    'position',[100,175,300,20],'string','Accept', 'Visible', 'off', 'callback',@p_accept);

h.p3(2) = uicontrol('style','pushbutton','units','pixels',...
    'position',[100,125,300,20],'string','Cancel', 'Visible', 'off', 'callback',@p_back);

function p_back(varargin)
    % Update GUI
    croppedObjects = getappdata(g.f,'croppedObjects');
    figure(g.f)
    clf(g.f, 'reset')
    imdisp(croppedObjects, 'Size',1);
    set(g.f, 'position',[0,0,figWidth,figWidth], 'name', 'Select image for separating objects with arrow keys')
    movegui(g.f,'center')
    
    set(h.t(:),'Visible','on')
    set(h.c(:),'Visible','on')
    set(h.p(:),'Visible','on')
    set(h.t2(:),'Visible','off')
    set(h.p2(:),'Visible','off')
    set(h.t3(:),'Visible','off')
    set(h.p3(:),'Visible','off')
end

function p_accept(varargin)
    imgInd = getappdata(g.f, 'imgInd');
    
    % Integrate newly segmented GP maps with existing images in stack for processing
    croppedObjectsTemp = getappdata(g.f, 'croppedObjectsTemp');
    croppedObjects = getappdata(g.f,'croppedObjects');
    croppedObjects(imgInd) = []; % remove uncropped image
    if imgInd == 1
        croppedObjects = [croppedObjectsTemp(:); croppedObjects(:)]; % insert at beginning
    else
        croppedObjects = [croppedObjects(1:imgInd-1); croppedObjectsTemp(:); croppedObjects(imgInd:end)]; % insert at correct index
    end
    
    setappdata(g.f,'croppedObjects',croppedObjects);

    % Integrate newly segmented images with existing images in stack for processing
    croppedObjectsThreshTemp = getappdata(g.f,'croppedObjectsThreshTemp');
    croppedObjectsThresh = getappdata(g.f,'croppedObjectsThresh');
    croppedObjectsThresh(imgInd) = []; % remove uncropped image
    if imgInd == 1
        croppedObjectsThresh = [croppedObjectsThreshTemp(:); croppedObjectsThresh(:)]; % insert at beginning
    else
        croppedObjectsThresh = [croppedObjectsThresh(1:imgInd-1); croppedObjectsThreshTemp; croppedObjectsThresh(imgInd:end)]; % insert at correct index
    end
    setappdata(g.f,'croppedObjectsThresh',croppedObjectsThresh);

    if isempty(hh.f)
        hh.f = figure('toolbar','none','menu','none','units','pixels', 'name','Preview: montage');
    end
    
    figure(hh.f)
    clf(hh.f, 'reset')
    imdisp(croppedObjects);
    set(hh.f, 'position',[0,0,figWidth,figWidth])
    movegui(hh.f,'east')
    
    p_back(); 
end

function p_separateObjects(varargin)
    % Update GUI
    set(h.t(:),'Visible','off')
    set(h.c(:),'Visible','off')
    set(h.p(:),'Visible','off')
    set(h.t3(:),'Visible','on')
    set(h.p3(:),'Visible','on')
    
    % Get inputs
    croppedObjects = getappdata(g.f,'croppedObjects');
    croppedObjectsThresh = getappdata(g.f,'croppedObjectsThresh');
    imData = get(g.f, 'UserData');
    if isempty(imData) || length(croppedObjects) == 1
        imgInd = 1;
    else
        imgInd = imData.index;
    end
    
    nObjs = str2double(get(h.c(1),'String'));
    
    % Segmentation
    [croppedObjectsTemp, croppedObjectsThreshTemp] = watershedObjectSplitter(croppedObjects{imgInd}, croppedObjectsThresh{imgInd}, nObjs);
    
    % Store results
    setappdata(g.f, 'croppedObjectsTemp', croppedObjectsTemp)
    setappdata(g.f, 'croppedObjectsThreshTemp', croppedObjectsThreshTemp) 
    setappdata(g.f, 'imgInd', imgInd)
    
    % Preview results
    figure(g.f)
    clf(g.f, 'reset')
    set(g.f, 'name', 'Automatically separated objects - accept or cancel in GUI');
    imdisp(croppedObjectsTemp)
    set(g.f, 'position',[0,0,figWidth,figWidth]);
    movegui(g.f,'center')
end

function p_manuallySeparateObjects(varargin)
    % Update GUI
    set(h.t(:),'Visible','off')
    set(h.c(:),'Visible','off')
    set(h.p(:),'Visible','off')
    set(h.t2(:),'Visible','on')
    set(h.p2(:),'Visible','on')
    
    % Get inputs
    croppedObjects = getappdata(g.f,'croppedObjects');
    croppedObjectsThresh = getappdata(g.f,'croppedObjectsThresh');
    imData = get(g.f(1), 'UserData');
    
    if isempty(imData) || length(croppedObjects) == 1
        imgInd = 1;
    else
        imgInd = imData.index;
    end
    
    % Segmentation 
    figure(g.f)
    clf(g.f, 'reset')
    set(g.f, 'name', 'Manually separated objects - accept or cancel in GUI');
    sc(croppedObjects{imgInd}, 'jet', 'k')
    ax = gca;
    [croppedObjectsTemp, croppedObjectsThreshTemp] = manualObjectSplitter(croppedObjects{imgInd}, croppedObjectsThresh{imgInd}, ax);
    
    % Store results 
    setappdata(g.f, 'croppedObjectsTemp', croppedObjectsTemp) 
    setappdata(g.f, 'croppedObjectsThreshTemp', croppedObjectsThreshTemp) 
    setappdata(g.f, 'imgInd', imgInd) 
    
    % Preview results 
    figure(g.f)
    clf(g.f, 'reset')
    set(g.f, 'name', 'Automatically separated objects - accept or cancel in GUI');
    imdisp(croppedObjectsTemp)
    set(g.f, 'position',[0,0,figWidth,figWidth])
    movegui(g.f,'center')
end

function p_removeImage(varargin)     
    % Get current state
    croppedObjects = getappdata(g.f,'croppedObjects');
    croppedObjectsThresh = getappdata(g.f,'croppedObjectsThresh');
    imData = get(g.f, 'UserData');
    if ~isempty(imData)
        imgInd = imData.index;
    else
        imgInd = 1;
    end
    
    % Remove images
    croppedObjects(imgInd) = []; % remove poor image
    croppedObjects = croppedObjects(~cellfun(@isempty, croppedObjects));
    croppedObjectsThresh(imgInd) = []; % remove poor image
    croppedObjectsThresh = croppedObjectsThresh(~cellfun(@isempty, croppedObjectsThresh));
    setappdata(g.f,'croppedObjects', croppedObjects);
    setappdata(g.f,'croppedObjectsThresh', croppedObjectsThresh);
    
    % Update selection window
    figure(g.f)
    clf(g.f, 'reset')
    imdisp(croppedObjects, 'Size',1);
    set(g.f, 'position',[0,0,figWidth,figWidth])
    movegui(g.f,'center')
    
    % Update preview montage
    figure(hh.f)
    clf(hh.f)
    imdisp(croppedObjects);
    set(hh.f, 'position',[0,0,figWidth,figWidth])
    movegui(hh.f,'east')

    % Store results 
    setappdata(g.f, 'croppedObjects', croppedObjects) 
    setappdata(g.f, 'croppedObjectsThresh', croppedObjectsThresh) 
    setappdata(g.f, 'imgInd', imgInd)     
end

function p_submit(varargin)
    % Get segmented images
    croppedObjects = getappdata(g.f,'croppedObjects');
    croppedObjectsThresh = getappdata(g.f,'croppedObjectsThresh');
   
    % Assign results to workspace
    assignin('base','croppedObjectsGP',croppedObjects)
    assignin('base','croppedObjectsThresh',croppedObjectsThresh)
    close all
end
end
