From cb29d467377c3ad5667f4f161b4a67bee7867e17 Mon Sep 17 00:00:00 2001 From: xiuyechen Date: Wed, 24 Jun 2015 16:19:38 -0400 Subject: [PATCH] v1.1 050415 first version shared with Janelia, with example data provided through dropbox --- BasicPlotMaps.m | 9 + DrawClusters.m | 515 ++- DrawClustersOnMap_LSh.m | 6 +- DrawTiledPics.m | 91 + GUI_FishExplorer.m | 357 +- GUIpreload_step1_cleanup.m | 268 ++ GUIpreload_step2_detrend.m | 66 + GUIpreload_step3.5_PoolClustersFromAllFish.m | 113 + GUIpreload_step3_align.m | 298 ++ GUIpreload_step4_loadGUI.m | 30 + PartitionForFastSaveLoad.m | 85 + README.txt | 20 + StimulusKeyPreprocessing.m | 401 ++ arrows.jpg | Bin 0 -> 28083 bytes arrows_wide.jpg | Bin 0 -> 25217 bytes first version/AccessLocalFunctions.m | 12 + first version/BasicPlotMaps.m | 261 ++ Conv_test.m => first version/Conv_test.m | 0 .../DA_2_Solution.m | 0 first version/DrawClusters.m | 210 + first version/DrawClustersOnMap_LSh.m | 137 + FFT_test.m => first version/FFT_test.m | 0 first version/GUI_FishExplorer.m | 3446 +++++++++++++++++ first version/GetMotorRegressor.m | 86 + .../GetPhotoTrans.m | 0 first version/GetStimBar.m | 25 + first version/GetStimRegressor.m | 186 + .../Load_LSh_step1_cleanup.m | 0 .../Load_LSh_step2_detrend.m | 0 .../Load_LSh_step3_align.m | 0 .../Load_LSh_step4_loadGUI.m | 0 Modeling001.m => first version/Modeling001.m | 0 .../PlotBy16StimsFromGUI.m | 0 Plotting.m => first version/Plotting.m | 0 .../Plotting_withinM.m | 0 .../PoolClustersFromAllFish.m | 0 SortMbystim.m => first version/SortMbystim.m | 0 first version/imNormalize99.m | 11 + first version/push_cIX_gIX.m | 10 + first version/read_LSstack_fast_float.m | 15 + fishpic.jpg | Bin 0 -> 25805 bytes read_LSstack_fast_float_mex64.mexw64 | Bin 0 -> 7680 bytes 42 files changed, 6383 insertions(+), 275 deletions(-) create mode 100644 DrawTiledPics.m create mode 100644 GUIpreload_step1_cleanup.m create mode 100644 GUIpreload_step2_detrend.m create mode 100644 GUIpreload_step3.5_PoolClustersFromAllFish.m create mode 100644 GUIpreload_step3_align.m create mode 100644 GUIpreload_step4_loadGUI.m create mode 100644 PartitionForFastSaveLoad.m create mode 100644 README.txt create mode 100644 StimulusKeyPreprocessing.m create mode 100644 arrows.jpg create mode 100644 arrows_wide.jpg create mode 100644 first version/AccessLocalFunctions.m create mode 100644 first version/BasicPlotMaps.m rename Conv_test.m => first version/Conv_test.m (100%) rename DA_2_Solution.m => first version/DA_2_Solution.m (100%) create mode 100644 first version/DrawClusters.m create mode 100644 first version/DrawClustersOnMap_LSh.m rename FFT_test.m => first version/FFT_test.m (100%) create mode 100644 first version/GUI_FishExplorer.m create mode 100644 first version/GetMotorRegressor.m rename GetPhotoTrans.m => first version/GetPhotoTrans.m (100%) create mode 100644 first version/GetStimBar.m create mode 100644 first version/GetStimRegressor.m rename Load_LSh_step1_cleanup.m => first version/Load_LSh_step1_cleanup.m (100%) rename Load_LSh_step2_detrend.m => first version/Load_LSh_step2_detrend.m (100%) rename Load_LSh_step3_align.m => first version/Load_LSh_step3_align.m (100%) rename Load_LSh_step4_loadGUI.m => first version/Load_LSh_step4_loadGUI.m (100%) rename Modeling001.m => first version/Modeling001.m (100%) rename PlotBy16StimsFromGUI.m => first version/PlotBy16StimsFromGUI.m (100%) rename Plotting.m => first version/Plotting.m (100%) rename Plotting_withinM.m => first version/Plotting_withinM.m (100%) rename PoolClustersFromAllFish.m => first version/PoolClustersFromAllFish.m (100%) rename SortMbystim.m => first version/SortMbystim.m (100%) create mode 100644 first version/imNormalize99.m create mode 100644 first version/push_cIX_gIX.m create mode 100644 first version/read_LSstack_fast_float.m create mode 100644 fishpic.jpg create mode 100644 read_LSstack_fast_float_mex64.mexw64 diff --git a/BasicPlotMaps.m b/BasicPlotMaps.m index 9196801..f8e5c37 100644 --- a/BasicPlotMaps.m +++ b/BasicPlotMaps.m @@ -18,6 +18,15 @@ function BasicDrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,is barratio = 0.02; numK = double(max(double(numK),double(max(gIX)))); +% down-sample +displaymax = 1000; +numcell = size(M,1); +if numcell > displaymax, + skip = round(numcell/displaymax); + M = M(1:skip:end,:); + gIX = gIX(1:skip:end,:); +end + % sort traces by index im = M; [nLines,nFrames] = size(im); diff --git a/DrawClusters.m b/DrawClusters.m index 964da88..524ce77 100644 --- a/DrawClusters.m +++ b/DrawClusters.m @@ -1,201 +1,293 @@ -function DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite) -pos = get(gca,'Position'); -barratio = 0.03; +function DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite,isPopout,isPlotLines,isPlotFictive) +axis off; +pos = get(h1,'Position'); % [left, bottom, width, height] -%% Prepare cluster data -% down-sample -displaymax = 1000; -numcell = size(M,1); -if numcell > displaymax, - skip = round(numcell/displaymax); - M = M(1:skip:end,:); - gIX = gIX(1:skip:end,:); -end - -numK = double(max(double(numK),double(max(gIX)))); +if isPlotLines, + if 1, % (just to collapse isPlotLines = true) + %% settings + len = pos(3); + fps = 1.97; + + % set position grid + nLines = length(unique(gIX)); + if isPlotFictive, + nExtraRows = 3; % number of extra rows + else + nExtraRows = 2; + end + nRows = max(8,nLines)+nExtraRows; + lineH = pos(4)/nRows; +% Lpos = [pos(1),pos(2)+pos(4)-lineH*nRows,pos(3),lineH*nRows]; +% % Lpos = [pos(1),pos(2)+pos(4)-lineH*(nLines+nExtraRows),pos(3),lineH*(nLines+nExtraRows)]; +% set(h1,'Position',Lpos); -% sort traces by index -im = M; -[nLines,nFrames] = size(im); -[~,I] = sort(gIX); -im = im(I,:); + % figure('Position',[500,900-(min(nLines,15)+nDiv)*50,600,(min(nLines,15)+nDiv)*50],'color',[1 1 1]); + xv = 1:size(M,2); + + %% Set colormap + if strcmp(clrmap,'jet'), + clr = flipud(jet(double(numK)))*0.8; % make darker by *0.8 + else % if strcmp(clrmap,'hsv'), + clr = hsv(round(double(numK)*1.1))*0.7; % make darker by *0.7 + end + % clr = lines(nLines); + % clr = ones(nLines,3)*0.3; % uniform charcoal + + %% draw stimulus bar + stimbar = GetStimBar(1,stim); + rpos = [pos(1),pos(2)+pos(4)-3/4*lineH,len,lineH/2]; + h = axes('Position',rpos); + + image(stimbar); + set(h,'Xtick',[],'Ytick',[]) + + % draw fish icon + rpos = [pos(1)*0.2,pos(2)+pos(4)-3/4*lineH,pos(1)*0.7,lineH/2]; + axes('Position',rpos); + DrawFishIcon; + + %% draw cluster means + Ymeans = zeros(nLines,size(M,2)); + U = unique(gIX); + for j = 1:nLines, + k = U(j); + % ymean = Cz(k,:); + Ys = M(gIX==k,:); + ymean = mean(Ys,1); + Ymeans(k,:) = ymean; + rpos = [pos(1),pos(2)+pos(4)-lineH*(j+1),len,lineH]; + axes('Position',rpos); hold on; + + ySTD=std(Ys); % /sqrt(size(Ys,1)) + ySTD_upper=ymean+ySTD; + ySTD_lower=ymean-ySTD; + h = fill([xv fliplr(xv)], [ySTD_upper fliplr(ySTD_lower)],0.5+0.3*clr(k,:)); + set(h, 'EdgeColor','none') + + plot(xv,ymean,'-','Linewidth',1,'color',clr(k,:)) + axis tight;axis off + end + + %% plot scale bar + axes('Position',[pos(1),pos(2)+pos(4)-lineH*(nLines+2),len,lineH]); hold on; + if size(M,2)<1200, % <~10 min, plot 1min scale bar + plot([1,60*fps],[0.75,0.75],'k-'); + text(pos(1)+60*fps/2,0.5,'1 min','HorizontalAlignment','center') + else % >~10 min, plot 10min scale bar + plot([1,600*fps],[0.75,0.75],'k-'); + text(pos(1)+600*fps/2,0.5,'10 min','HorizontalAlignment','center') + end + xlim([xv(1),xv(end)]);ylim([0,1]); + axis off + + %% draw fictive behavior bar + if isPlotFictive, + h = axes('Position',[pos(1),pos(2)+pos(4)-lineH*(nLines+3),len,0.7/(nLines+nExtraRows)]); + DrawFictiveBar(h,fictive); + + axes('Position',[0.02,pos(2)+pos(4)-lineH*(nLines+3),pos(1)-0.02,0.7/(nLines+nExtraRows)]); + DrawArrowsIcon(isPopout); + end -%% convert imagesc effect into rgb matrix 'RGB' -if dataFR, % is drawing cell-response fluorescence - cmap = gray(64); - im = AutoScaleImage0to1(im); % scaling to min/max 0/1 - minlim = 0; - maxlim = 1; -else % create blue-white-red map, for regression results - cmap = zeros(64,3); - cmap(:,1) = [linspace(0,1,32), linspace(1,1,32)]; - cmap(:,2) = [linspace(0,1,32), linspace(1,0,32)]; - cmap(:,3) = [linspace(1,1,32), linspace(1,0,32)]; - minlim = -1; %min(min(im)); - maxlim = 1; %max(max(im)); -end -RGB = ImageToRGB(im,cmap,minlim,maxlim); % map image matrix to range of colormap + end + +else -%% add vertical color code -if exist('clrmap','var'), - if strcmp(clrmap,'jet'), - temp = flipud(jet(numK)); + if ~isPopout, + barratio = 0.025; + else + barratio = 0.05; + end + %% Prepare cluster data + % down-sample + displaymax = 1000; + numcell = size(M,1); + if numcell > displaymax, + skip = round(numcell/displaymax); + M = M(1:skip:end,:); + gIX = gIX(1:skip:end,:); + end + +% numK = double(max(double(numK),double(max(gIX)))); + + % sort traces by index + im = M; + [nLines,nFrames] = size(im); + [~,I] = sort(gIX); + im = im(I,:); + + %% convert imagesc effect into rgb matrix 'RGB' + if dataFR, % is drawing cell-response fluorescence + cmap = gray(64); + im = AutoScaleImage0to1(im); % scaling to min/max 0/1 + minlim = 0; + maxlim = 1; + else % create blue-white-red map, for regression results + cmap = zeros(64,3); + cmap(:,1) = [linspace(0,1,32), linspace(1,1,32)]; + cmap(:,2) = [linspace(0,1,32), linspace(1,0,32)]; + cmap(:,3) = [linspace(1,1,32), linspace(1,0,32)]; + minlim = -1; %min(min(im)); + maxlim = 1; %max(max(im)); + end + RGB = ImageToRGB(im,cmap,minlim,maxlim); % map image matrix to range of colormap + + %% add vertical color code + if exist('clrmap','var'), + if strcmp(clrmap,'jet'), + temp = flipud(jet(numK)); + else % 'hsv' + temp = hsv(round(numK*1.1)); + end else % 'hsv' temp = hsv(round(numK*1.1)); end -else % 'hsv' - temp = hsv(round(numK*1.1)); -end -cmap2 = vertcat(temp(1:numK,:),[0,0,0]); % extend colormap to include black -bwidth = max(round(nFrames/30),1); - -idx = gIX(I); -ix_div = [find(diff(idx));length(idx)]; - -bars = ones(nLines,bwidth); -if numK>1, - bars(1:ix_div(1),:) = idx(ix_div(1)); - for i = 2:length(ix_div), - % paint color - bars(ix_div(i-1)+1:ix_div(i),:) = idx(ix_div(i)); - end -end -im_bars = reshape(cmap2(bars,:),[size(bars),3]); -% add a white division margin -div = ones(nLines,round(bwidth/2),3); -% put 3 parts together -im = horzcat(RGB,div,im_bars); - -%% plot figure - -[s1,s2,s3] = size(im); - -hold off; -set(h1,'Position',[pos(1),pos(2)+pos(4)*barratio*3,pos(3),pos(4)*(1-4*barratio)]); -if s1<30, - temp = im; - im = ones(30,s2,s3); - im(1:s1,:,:) = temp; -end -image(im); -set(gca, 'box', 'off') - -hold on; - -% plot cluster division lines -plot([0.5,s2+0.5],[0.5,0.5],'k','Linewidth',0.5); -if numK>1, - for i = 1:length(ix_div),% = numK-1, - y = ix_div(i)+0.5; - plot([0.5,s2+0.5],[y,y],'k','Linewidth',0.5); + cmap2 = vertcat(temp(1:numK,:),[0,0,0]); % extend colormap to include black + bwidth = max(round(nFrames/30),1); + + idx = gIX(I); + ix_div = [find(diff(idx));length(idx)]; + + bars = ones(nLines,bwidth); + if numK>1, + bars(1:ix_div(1),:) = idx(ix_div(1)); + for i = 2:length(ix_div), + % paint color + bars(ix_div(i-1)+1:ix_div(i),:) = idx(ix_div(i)); + end end -end - -ylabel(['Number of cells: ' num2str(numcell)]); -set(gca,'YTick',[],'XTick',[]); -set(gcf,'color',[1 1 1]); - -colormap gray; - -% write in number label -x = s2*1.003; -y_last = 0; -for i = 1:length(ix_div), - % avoid label crowding - margin = 0.015*s1; - y0 = ix_div(i)+0.5; - y = max(y_last+margin,y0); - if i1, + for i = 1:length(ix_div),% = numK-1, + y = ix_div(i)+0.5; + plot([0.5,s2+0.5],[y,y],'k','Linewidth',0.5); + end + end + + ylabel(['Number of cells: ' num2str(numcell)]); + set(gca,'YTick',[],'XTick',[]); + set(gcf,'color',[1 1 1]); + + colormap gray; + + % write in number label + x = s2*1.003; + y_last = 0; + for i = 1:length(ix_div), + % avoid label crowding + margin = 0.015*s1; + y0 = ix_div(i)+0.5; + y = max(y_last+margin,y0); + if i0 & (cinds+circle_inds)<=dimv_yxz(1)*dimv_yxz(2)); + zinds=dimv_yxz(1)*dimv_yxz(2)*(CInfo(cIX(j)).slice-1); + ix = find(U==gIX(j)); + ixs = cinds+circle_inds(labelinds)+zinds; + ave_stack2(ixs)=cmap(ix,1)*weight + ave_stack2(ixs)*(1-weight); + ixs = cinds+circle_inds(labelinds)+zinds+stacklen; + ave_stack2(ixs)=cmap(ix,2)*weight + ave_stack2(ixs)*(1-weight); + ixs = cinds+circle_inds(labelinds)+zinds+stacklen*2; + ave_stack2(ixs)=cmap(ix,3)*weight + ave_stack2(ixs)*(1-weight); +end + +%% view stack sequentially +% figure; +% for i_plane = 1:nPlanes, +% im = squeeze(ave_stack2(:,:,i_plane,:)); +% image(im); +% axis image; axis off +% pause(0.1) +% end + +% lay out + +h = figure('Position',[10,20,round(1136*1.4),round(683*1.4)],'color','k'); +hold on; + +axes('Position',[0,0,1,1]); % BS fix to get the figure background to stay when saving pics +image(zeros(3,3,3));axis off + +numRow = 3; +numP = nPlanes - mod(nPlanes,numRow); +numCol = numP/numRow; + +for i_plane = 1:numP, + im = squeeze(ave_stack2(:,:,i_plane,:)); + im = imresize(im,0.25); + + [col, row] = ind2sub([numCol,numRow],i_plane); % this is intensionally inverted... + posVec = [(col-1)*1/numCol,1-row*1/numRow,1/numCol,1/numRow]; + axes('Position',posVec); + image(im); + axis image; axis off + % pause(0.1) +end + +end \ No newline at end of file diff --git a/GUI_FishExplorer.m b/GUI_FishExplorer.m index a417875..04405e0 100644 --- a/GUI_FishExplorer.m +++ b/GUI_FishExplorer.m @@ -21,9 +21,10 @@ % global VAR; % load('VAR_current.mat','VAR'); % load('CONSTs_current.mat','CONSTs'); -% hfig = GUI_FishExplorer(CONSTs,VAR); +% data_dir = [pwd '\example data']; +% [hfig,fcns] = GUI_FishExplorer(data_dir,CONSTs,VAR); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Full datasets are stored as 'CONST_F?.mat' (? = fish number), to load from the GUI +% Full datasets are stored as 'CONST_F?_fast.mat' (? = fish number), to load from the GUI %% User Interface: % function hfig = GUI_FishExplorer(CONSTs,VAR,CONST,i_fish) @@ -57,7 +58,11 @@ %% Pass external variables into appdata (stored with main figure handle) setappdata(hfig,'CONSTs',CONSTs); setappdata(hfig,'VAR',VAR); -nFish = length(CONSTs); +nFish = length(CONSTs) + 1; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% fish protocol sets (different sets have different parameters) +M_fish_set = [1, 1, 1, 1, 1, 1, 1, 2, 3, 3]; % M = Matrix +setappdata(hfig,'M_fish_set',M_fish_set); % parameters / constants setappdata(hfig,'z_res',19.7); % resoltion in z, um per slice @@ -81,9 +86,6 @@ QuickUpdateFish(hfig,i_fish,'init'); %% Initialize internal params into appdata -% fish protocol sets (different sets have different parameters) -M_fish_set = [1, 1, 1, 1, 1, 1, 1, 2, 1]; % M = Matrix -setappdata(hfig,'M_fish_set',M_fish_set); % thresholds thres_merge = 0.9; @@ -100,6 +102,8 @@ setappdata(hfig,'clrmap','hsv'); setappdata(hfig,'opID',0); setappdata(hfig,'rankID',0); +setappdata(hfig,'isPlotLines',0); +setappdata(hfig,'isPlotFictive',1); setappdata(hfig,'rankscore',[]); setappdata(hfig,'isCentroid',0); setappdata(hfig,'isWkmeans',1); % in autoclustering, with/without kmeans @@ -148,7 +152,7 @@ % various UI element handles ('global' easier than passing around..) global hback hfwd hclassname hclassmenu hclusgroupmenu hclusmenu hclusname... - hdatamenu hopID hcentroid hdataFR hloadfish hfishnum hstimreg hmotorreg... + hdatamenu hopID hdataFR hloadfish hfishnum hstimreg hmotorreg... hcentroidreg; %% UI ----- tab one ----- (General) @@ -176,12 +180,30 @@ 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... 'Callback',@pushbutton_writeZstack_Callback); +i=i+n; +n=2; % plots selected cells on anatomy z-stack, tiled display +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Tile Zstack',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_tileZstack_Callback); + i=i+n; n=2; % make new figure without the GUI components, can save manually from default menu uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Popup plot',... 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... 'Callback',@pushbutton_popupplot_Callback); +i=i+n; +n=2; % popupplot option: whether to plot cluster mean lines instead of all raw traces +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Plot lines',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@checkbox_isPlotLines_Callback); + +i=i+n; +n=2; % popupplot option: whether to plot fictive behavior bar +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Plot fictive','Value',1,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@checkbox_isPlotFictive_Callback); + i=i+n; n=3; % export main working variables to workspace, can customize! uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Export to workspace',... @@ -233,7 +255,7 @@ i=i+n; n=4; % only centroids (~mean) of clusters shown on left-side plot, the rest is unchanged -hcentroid = uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Display centroids of clusters',... +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Display centroids of clusters',... 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... 'Callback',@checkbox_showcentroids_Callback); @@ -413,7 +435,7 @@ i=i+n; n=2; % (unlike stim regressors, names hardcoded, not importet from regressor...) -menu = {'(choose)','right swims','left swims','forward swims','raw right','raw left','raw average'}; +menu = {'(choose)','left swims','right swims','forward swims','raw left','raw right','raw average'}; hmotorreg = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',menu,'Value',1,... 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... 'Callback',@popup_getmotorreg_Callback); @@ -493,19 +515,19 @@ 'Callback',{@pushbutton_IterCentroidRegression_Callback}); %% UI row 3: (TBD) -i_row = 3; % old idea: display correlation values of multiple regressors (instead of fluo. trace) -i = 1;n = 0; % and then can cluster that and further manipulate... - -i=i+n; -n=4; % did not implement combination of regressors in this version... but display option is coded (dataFR==0) -uicontrol('Parent',tab{i_tab},'Style','text','String','regressor combos??(TBD)',... - 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); - -i=i+n; -n=4; % also data storage is coded, 'M' could be loaded from M_0_fluo or M_0_reg (storing reg corr values) -hdataFR = uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Display fluo./regression',... - 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... - 'Callback',@checkbox_dataFR_Callback); +% i_row = 3; % old idea: display correlation values of multiple regressors (instead of fluo. trace) +% i = 1;n = 0; % and then can cluster that and further manipulate... +% +% i=i+n; +% n=4; % did not implement combination of regressors in this version... but display option is coded (dataFR==0) +% uicontrol('Parent',tab{i_tab},'Style','text','String','regressor combos??(TBD)',... +% 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); +% +% i=i+n; +% n=4; % also data storage is coded, 'M' could be loaded from M_0_fluo or M_0_reg (storing reg corr values) +% hdataFR = uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Display fluo./regression',... +% 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... +% 'Callback',@checkbox_dataFR_Callback); %% UI ----- tab four ----- (Clustering etc.) i_tab = 4; @@ -977,6 +999,17 @@ function pushbutton_writeZstack_Callback(hObject,~) close(h) end +function pushbutton_tileZstack_Callback(hObject,~) +hfig = getParentFigure(hObject); +isfullfish = getappdata(hfig,'isfullfish'); +if isfullfish, + disp('Rendering...') + DrawTiledPics(hfig); +else + errordlg('Load full fish first!'); +end +end + function out = makeDisk2(radius, dim) center=floor(dim/2)+1; out=zeros(dim); @@ -997,7 +1030,7 @@ function pushbutton_popupplot_Callback(hObject,~) % figure('Position',[50,100,853,512]); % right plot size: 2048x1706 % h1 = axes('Position',[0, 0, 0.5, 1]); % left ~subplot % h2 = axes('Position',[0.50, 0, 0.5, 1]); % right ~subplot -figure('Position',[50,100,1600,900]); +figure('Position',[50,100,1700,900],'color',[1 1 1]); h1 = axes('Position',[0.05, 0.03, 0.53, 0.94]); % left ~subplot h2 = axes('Position',[0.61, 0.03, 0.35, 0.94]); % right ~subplot @@ -1017,22 +1050,39 @@ function pushbutton_popupplot_Callback(hObject,~) clrmap = getappdata(hfig,'clrmap'); rankscore = getappdata(hfig,'rankscore'); rankID = getappdata(hfig,'rankID'); - iswrite = (rankID>=2); +isPlotLines = getappdata(hfig,'isPlotLines'); +isPlotFictive = getappdata(hfig,'isPlotFictive'); + +isPopout = 1; % left subplot axes(h1); if isCentroid, [C,~] = FindCentroid(gIX,M); - DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); + DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,... + iswrite,isPopout,isPlotLines,isPlotFictive); +% DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite,ispopout); else - DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); + DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,... + iswrite,isPopout,isPlotLines,isPlotFictive); +% DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite,ispopout); end % right subplot axes(h2); -DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,clrmap); +DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,clrmap,'full'); + +end + +function checkbox_isPlotLines_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isPlotLines',get(hObject,'Value')); +end +function checkbox_isPlotFictive_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isPlotFictive',get(hObject,'Value')); end function pushbutton_exporttoworkspace_Callback(hObject,~) @@ -1069,12 +1119,12 @@ function QuickUpdateFish(hfig,new_i_fish,init) %#ok numcell = CONSTs{new_i_fish}.numcell; temp = CONSTs{new_i_fish}.CRAZ; -CRAZ = zeros(numcell,size(temp,2)); -CRAZ(CIX,:) = temp; +CellRespAvr = zeros(numcell,size(temp,2)); +CellRespAvr(CIX,:) = temp; temp = CONSTs{new_i_fish}.CRZt; -CRZt = zeros(numcell,size(temp,2)); -CRZt(CIX,:) = temp; +CellResp = zeros(numcell,size(temp,2)); +CellResp(CIX,:) = temp; temp = CONSTs{new_i_fish}.CInfo; CInfo(numcell).center = ''; @@ -1085,17 +1135,21 @@ function QuickUpdateFish(hfig,new_i_fish,init) %#ok CInfo(numcell).x_minmax = ''; CInfo(CIX) = temp; -setappdata(hfig,'CRAZ',CRAZ); % Matrix array, misc collection -setappdata(hfig,'CRZt',CRZt); % Matrix array, misc collection -setappdata(hfig,'CInfo',CInfo); % Matrix array, misc collection +setappdata(hfig,'CellRespAvr',CellRespAvr); +setappdata(hfig,'CellResp',CellResp); +setappdata(hfig,'CInfo',CInfo); UpdateFishData(hfig,new_i_fish); if ~exist('init','var'), - UpdateFishDisplay(hfig,new_i_fish); + UpdateFishDisplay(hfig); end end -function UpdateFishData(hfig,new_i_fish) +function UpdateFishData(hfig,new_i_fish) % loading steps shared by both quick-load and full-load +M_fish_set = getappdata(hfig,'M_fish_set'); +fishset = M_fish_set(new_i_fish); +setappdata(hfig,'fishset',fishset); + UpdateDatamenu_Direct(hfig,1); % save ClusGroup before updating, if applicable @@ -1122,12 +1176,12 @@ function UpdateFishData(hfig,new_i_fish) setappdata(hfig,'Cluster',Cluster); end -function UpdateFishDisplay(hfig,new_i_fish) +function UpdateFishDisplay(hfig) % loading steps that are not performed at very first initialization stim = getappdata(hfig,'stim'); -M_fish_set = getappdata(hfig,'M_fish_set'); +fishset = getappdata(hfig,'fishset'); -fishset = M_fish_set(new_i_fish); [~, names] = GetStimRegressor(stim,fishset); + global hstimreg; set(hstimreg,'String',['(choose)',(names)]); @@ -1148,25 +1202,57 @@ function popup_loadfullfishmenu_Callback(hObject,~) set(hfishnum,'Value',new_i_fish); end -function LoadFullFish(hfig,new_i_fish,CONST) +function LoadFullFish(hfig,new_i_fish) data_dir=getappdata(hfig,'data_dir'); -if ~exist('CONST','var'), - disp(['loading fish #' num2str(new_i_fish) '...']); - tic - load(fullfile(data_dir,['CONST_F' num2str(new_i_fish) '.mat']),'CONST'); - toc -end -setappdata(hfig,'isfullfish',1); +disp(['loading fish #' num2str(new_i_fish) '...']); + +%% +tic +fishdir = fullfile(data_dir,['CONST_F' num2str(new_i_fish) '_fast.mat']); +load(fishdir,'const'); +load(fishdir,'dimCR'); +CellResp = zeros(dimCR); +num = 0; +nParts = round(dimCR(1)*dimCR(2)/42000000); +for i = 1:nParts, + load(fishdir,['CRZt_' num2str(i)']); + eval(['len = size(CRZt_' num2str(i) ',1);']); + eval(['CellResp(num+1:num+len,:) = CRZt_' num2str(i) ';']); + eval(['CellResp(num+1:num+len,:) = CRZt_' num2str(i) ';']); + num = num+len; +end +% for i = 1:nParts, +% load(fishdir,['CellResp_' num2str(i)']); +% eval(['len = size(CellResp_' num2str(i) ',1);']); +% eval(['CellResp(num+1:num+len,:) = CellResp_' num2str(i) ';']); +% num = num+len; +% end +toc +setappdata(hfig,'CellResp',CellResp); -% load all fields from CONST, with names preserved -names = fieldnames(CONST); % cell of strings +%% load all fields from CONST, with names preserved +names = fieldnames(const); % cell of strings for i = 1:length(names), - setappdata(hfig,names{i},eval(['CONST.',names{i}])); -end -setappdata(hfig,'numcell',length(CONST.CInfo)); + + + % renaming exception!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if strcmp(names{i},'CRAZ'), + setappdata(hfig,'CellRespAvr',const.CRAZ); + elseif strcmp(names{i},'photostate'), + setappdata(hfig,'stim_full',const.photostate); + + + + else + setappdata(hfig,names{i},eval(['const.',names{i}])); + end +end +setappdata(hfig,'numcell',length(const.CInfo)); + UpdateFishData(hfig,new_i_fish); -UpdateFishDisplay(hfig,new_i_fish); +UpdateFishDisplay(hfig); +setappdata(hfig,'isfullfish',1); end function popup_datamenu_Callback(hObject,~) @@ -1178,29 +1264,49 @@ function popup_datamenu_Callback(hObject,~) end function UpdateDatamenu_Direct(hfig,ID) -CRAZ = getappdata(hfig,'CRAZ'); -CRZt = getappdata(hfig,'CRZt'); -FcAvr = getappdata(hfig,'FcAvr'); +CellResp = getappdata(hfig,'CellResp'); Fc = getappdata(hfig,'Fc'); datanames = getappdata(hfig,'datanames'); tlists = getappdata(hfig,'tlists'); -photostate = getappdata(hfig,'photostate'); +stim_full = getappdata(hfig,'stim_full'); +periods = getappdata(hfig,'periods'); +shift = getappdata(hfig,'shift'); +numcell = getappdata(hfig,'numcell'); +stimtypelist = 1:length(tlists);% getappdata(hfig,'stimtypelist'); + +fishset = getappdata(hfig,'fishset'); + +if fishset == 1, + CellRespAvr = getappdata(hfig,'CellRespAvr'); + FcAvr = getappdata(hfig,'FcAvr'); +else + % find averages of different stimuli, and splice together + % in future should allow user to chose stimtypelist to compile average + % of selected stimuli groups + CRavr = []; + for i = 1:stimtypelist, + M = circshift(CellResp(:,tlists{i}),shift,2); + CRavr = horzcat(CRavr,mean(reshape(M,numcell,periods(i),[]),3)); + end + setappdata(hfig,'CellRespAvr',CRavr); +end + %% SWITCH LEFT/RIGHT DIRECTION AGAIN -FcAvr = vertcat(FcAvr(2,:),FcAvr(1,:),FcAvr(3:end,:)); +% % FcAvr = vertcat(FcAvr(2,:),FcAvr(1,:),FcAvr(3:end,:)); Fc = vertcat(Fc(2,:),Fc(1,:),Fc(3:end,:)); % now 1=left, 2=right, 3=forward, 4 = raw left, 5 = raw right %% if ID == 1, - M_0 = CRAZ; + M_0 = CellRespAvr; fictive = FcAvr; elseif ID == 2, - M_0 = CRZt; + M_0 = CellResp; fictive = Fc; else IX = tlists{ID}; - M_0 = CRZt(:,IX); + M_0 = CellResp(:,IX); temp = Fc(:,IX); % normalize fictive channels, in 2 sets for k = 1:3,%size(F,1), @@ -1211,8 +1317,8 @@ function UpdateDatamenu_Direct(hfig,ID) temp(4:5,:) = (m-min(min(m)))/(max(max(m))-min(min(m))); fictive = temp; end -% stim from photostate -stim = photostate(tlists{ID}); +% stim from stim_full +stim = stim_full(tlists{ID}); setappdata(hfig,'M_0_fluo',M_0); setappdata(hfig,'fictive',fictive); @@ -1411,11 +1517,12 @@ function edit_t_range_Callback(hObject,~) end end -function UpdateTRange(hfig,range) +function UpdateTRange(hfig,range) % this function is incomplete... delete? +% what about stimulus??? + M_0 = GetM_0(hfig); cIX = getappdata(hfig,'cIX'); fictive_0 = getappdata(hfig,'fictive_0'); -% photostate_0 = getappdata(hfig,'photostate_0'); % set M M = M_0(cIX,range); @@ -1423,10 +1530,6 @@ function UpdateTRange(hfig,range) % set fictive fictive = fictive_0(:,range); setappdata(hfig,'fictive',fictive); -% % set photostate -% photostate = photostate_0(:,range); -% setappdata(hfig,'photostate',photostate); - end %% row 2: Operations @@ -1504,17 +1607,17 @@ function popup_ranking_Callback(hObject,~) if length(periods)==1, period = periods{1}; [C,~] = FindCentroid(gIX,M); -else % i_fish>=8, +else %if i_fish==8, tlists = getappdata(hfig,'tlists'); - CRZt = getappdata(hfig,'CRZt'); + CellResp = getappdata(hfig,'CellResp'); IX = tlists{6}; % ptomr_circ - M_ = CRZt(cIX,IX); + M_ = CellResp(cIX,IX); [C,~] = FindCentroid(gIX,M_); periods = getappdata(hfig,'periods'); period = periods{1}+periods{2}; +% else ? + end -% C2 = zscore(C,0,2); -% C_3D = reshape(C2,size(C2,1),period,[]); C_3D_0 = reshape(C,size(C,1),period,[]); C_3D = zscore(C_3D_0,0,2); @@ -1814,9 +1917,57 @@ function PlotRegWithStimMotor(hfig) reg = imNormalize99(regressor); regbar = repmat(reg,[1,1,3]); -subplot(3,1,1);image(stimbar); axis off; title('stimulus'); -subplot(3,1,2);image(regbar); axis off; title('regressor'); -subplot(3,1,3);imagesc(fictive);colormap hot; axis off; title('motor'); +h = subplot(3,1,1);image(stimbar);set(h,'box','on','xtick',[],'ytick',[]); title('stimulus'); +h = subplot(3,1,2);image(regbar); set(h,'box','on','xtick',[],'ytick',[]); title('regressor'); +subplot(3,1,3); + +temp = fictive; +if 1, % plot all 5 lines + fc = vertcat(temp(1,:),temp(3,:),temp(2,:),temp(4,:),temp(5,:)); + + imagesc(fc);colormap gray + set(gca,'YTick',[],'XTick',[]); + set(gcf,'color',[1 1 1]); + set(gca, 'box', 'off') + hold on;axis ij; + + % plot division lines +% for i = 0:3, +% y = i+0.5; +% plot([0.5,length(fictive)+0.5],[y,y],'w','Linewidth',0.5); +% end +% % labels +% names = {'Left','Right','Forward','Raw L','Raw R'}; +% x = -s2*0.05; +% for i = 1:5, +% y = i; +% text(x,y,names{i},'Fontsize',7); +% end + +else % only plot top 3 lines + fc = vertcat(temp(1,:),temp(3,:),temp(2,:)); + imagesc(fc);colormap gray + set(gca,'YTick',[],'XTick',[]); + set(gcf,'color',[1 1 1]); + set(gca, 'box', 'off') + hold on;axis ij; + + % plot division lines + for i = 0:2, + y = i+0.5; + plot([0.5,length(fictive)+0.5],[y,y],'w','Linewidth',0.5); + end + % labels + names = {'Left','Forward','Right'}; + x = -s2*0.07; + for i = 1:3, + y = i; + text(x,y,names{i},'Fontsize',10); + end + +end + +imagesc(fictive);colormap hot; axis off; title('motor'); end function out=imNormalize99(im) @@ -1881,9 +2032,7 @@ function checkbox_flipstim_Callback(hObject,~) stim = getappdata(hfig,'stim'); if regchoice{1}==1, % stim Regressor - i_fish = getappdata(hfig,'i_fish'); - M_fish_set = getappdata(hfig,'M_fish_set'); - fishset = M_fish_set(i_fish); + fishset = getappdata(hfig,'fishset'); [regressors, names] = GetStimRegressor(stim,fishset); regressor = regressors(regchoice{2}).im; @@ -1928,8 +2077,8 @@ function checkbox_flipstim_Callback(hObject,~) fliptrans = GetPhotoTrans(flipstim); % transition e.g.: - % photostate: 2 3 1 3 3 0 0 1 1 0 3 2 0 2 2 1 - % phototrans: 6 11 13 7 15 12 0 1 5 4 3 14 8 2 10 9 + % stimulus : 2 3 1 3 3 0 0 1 1 0 3 2 0 2 2 1 + % transitionID: 6 11 13 7 15 12 0 1 5 4 3 14 8 2 10 9 IX = []; targetstates = unique(fliptrans,'stable'); @@ -2129,11 +2278,11 @@ function pushbutton_IterCentroidRegression_Callback(hObject,~) %% row 3: (TBD) -function checkbox_dataFR_Callback(hObject,~) -hfig = getParentFigure(hObject); -SetDataFR(hfig,get(hObject,'Value')); -RefreshFigure(hfig); -end +% function checkbox_dataFR_Callback(hObject,~) % obsolete !!! +% hfig = getParentFigure(hObject); +% SetDataFR(hfig,get(hObject,'Value')); +% RefreshFigure(hfig); +% end %% ----- tab four ----- (Clustering etc.) @@ -2728,6 +2877,7 @@ function pushbutton_corrplot_Callback(hObject,~) M = getappdata(hfig,'M'); [C,~] = FindCentroid(gIX,M); coeffs = corr(C');%corr(C(1,:)',C(2,:)') +figure('Position',[1000,200,500,500]); CorrPlot(coeffs); end @@ -2744,8 +2894,8 @@ function CorrPlot(coeffs) RGB = ImageToRGB(im,cmap,minlim,maxlim); % map image matrix to range of colormap -figure('Position',[1000,200,500,500]); image(RGB); axis equal; axis tight; +set(gca,'XTick',1:length(im),'YTick',1:length(im)); for i = 1:size(im,2), % horizontal.. because of image axis for j = 1:size(im,1), @@ -3033,7 +3183,7 @@ function SetDataFR(hfig,dataFR) M_0_fluo = getappdata(hfig,'M_0_fluo'); setappdata(hfig,'M',M_0_fluo(cIX,:)); else % Unchecked - M_0_reg = getappdata(hfig,'M_0_reg'); + M_0_reg = getappdata(hfig,'M_0_reg'); % obsolete !!! setappdata(hfig,'M',M_0_reg(cIX,:)); end global hdataFR; @@ -3201,17 +3351,19 @@ function UpdateClusID(hfig,clusID) figure('Position',[100 400 1300 400]); subplot(1,3,1); CORR = corr(C'); - imagesc(CORR);axis equal;axis tight + CorrPlot(CORR); subplot(1,3,2); dendrogram(tree,numU,'orientation','right','reorder',leafOrder); set(gca,'YDir','reverse'); - colormap(jet); + set(gca,'XTick',[]); +% colormap(jet); subplot(1,3,3); C2 = C(leafOrder,:); CORR2 = corr(C2'); - imagesc(CORR2);axis equal;axis tight + CorrPlot(CORR2); +% imagesc(CORR2);axis equal;axis tight end % sort for uniform colorscale temp = zeros(size(gIX)); @@ -3331,7 +3483,7 @@ function UpdateIndices(hfig,cIX,gIX,numK) setappdata(hfig,'cIX',cIX); setappdata(hfig,'gIX',gIX); if exist('numK','var'), - setappdata(hfig,'numK',numK); + setappdata(hfig,'numK',double(numK)); end else errordlg('dataset too large!') @@ -3368,8 +3520,11 @@ function RefreshFigure(hfig) clrmap = getappdata(hfig,'clrmap'); rankscore = getappdata(hfig,'rankscore'); rankID = getappdata(hfig,'rankID'); - iswrite = (rankID>=2); +isPlotLines = 0; %getappdata(hfig,'isPlotLines'); +isPlotFictive = 1; %getappdata(hfig,'isPlotFictive'); + +isPopout = 0; if isempty(cIX), errordlg('empty set! go back!'); @@ -3384,11 +3539,21 @@ function RefreshFigure(hfig) figure(hfig); % left subplot h1 = axes('Position',[0.05, 0.04, 0.55, 0.83]); +% if isCentroid, +% [C,~] = FindCentroid(gIX,M); +% DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); +% else +% DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); +% end if isCentroid, [C,~] = FindCentroid(gIX,M); - DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); + DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,... + iswrite,isPopout,isPlotLines,isPlotFictive); +% DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite,ispopout); else - DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); + DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,... + iswrite,isPopout,isPlotLines,isPlotFictive); +% DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite,ispopout); end % right subplot diff --git a/GUIpreload_step1_cleanup.m b/GUIpreload_step1_cleanup.m new file mode 100644 index 0000000..f87c089 --- /dev/null +++ b/GUIpreload_step1_cleanup.m @@ -0,0 +1,268 @@ +%%% Loading - Step 1. +% Summary: +% load data from 'cell_resp.stackf' etc, and saved into +% 'FishX_direct_load.mat' (X = number) in the same directory. +% Stimulus parsing is done with function 'StimulusKeyPreprocessing', but +% one is advised to also inspect the results manually. + +% Validation: +% There is an option 'isDiscard50' to discard the noisier 50%, disabled now. +% There is also a section to manually select (draw polygons) regions of +% cells to discard that are anatomical outliers. + +% Format: +% Cell-responses are converted into z-scores and kept that way through out. +% CInfo (cell info for the valid cells) stores the anatomical positions. +% For the rest of parameters saved, see the end of this file. + +% Next: +% For the following Loading steps, only this 'FishX_direct_load.mat' is +% needed, the rest will be cleared from working memory. + +%% +clear all;close all;clc + +%% Set Manaully! + +i_fish = 10; + +M_dir = {'F:\Janelia2014\Fish1_16states_30frames'; + 'F:\Janelia2014\Fish2_20140714_2_4_16states_10frames'; + 'F:\Janelia2014\Fish3_20140715_1_1_16_states_10frames'; + 'F:\Janelia2014\Fish4_20140721_1_8_16states_20frames'; + 'F:\Janelia2014\Fish5_20140722_1_2_16states_30frames'; + 'F:\Janelia2014\Fish6_20140722_1_1_3states_30,40frames'; + 'F:\Janelia2014\Fish7_20140722_2_3_3states_30,50frames'; + 'F:\Janelia2014\Fish8_20141222_2_2_7d_PT_3OMR_shock_lowcut'; + 'F:\Janelia2014\Fish9_20150120_2_1_photo_OMR_prey_blob_blue_cy74_6d_20150120_220356'; + 'F:\Janelia2014\Fish10_20150120_2_2_photo_OMR_prey_blob_blue_cy74_6d_20150120_231917'}; + +fpsec = 1.97; % Hz + +%% load data +datadir = M_dir{i_fish}; +load(fullfile(datadir,'cell_resp_dim.mat')); % 'cell_resp_dim_lowcut.mat' +load(fullfile(datadir,'cell_info.mat')); +cell_resp = read_LSstack_fast_float(fullfile(datadir,'cell_resp.stackf'),cell_resp_dim); +load(fullfile(datadir,'frame_turn.mat')); + +% parse stimulus - should check manually to be sure! +[stimset,stim] = StimulusKeyPreprocessing(frame_turn); + +%% load anatomy +tiffname = fullfile(datadir,'ave.tif'); +info = imfinfo(tiffname,'tiff'); +nPlanes = length(info); +s1 = info(1).Height; +s2 = info(1).Width; +ave_stack = zeros(s1,s2,nPlanes); +for i=1:nPlanes, + ave_stack(:,:,i) = imread(fullfile(datadir,'ave.tif'),i); +end + +% x-y view +im = max(ave_stack,[],3); +out=imNormalize99(im); +anat_yx = repmat(out,[1 1 3]); + +% y-z view +im = squeeze(max(ave_stack,[],2)); +out=imNormalize99(im); +anat_yz = repmat(out,[1 1 3]); + +% x-z view +im = squeeze(max(ave_stack,[],1)); +out=imNormalize99(im); +out = flipud(out'); %%%% empirically necessary... +anat_zx = repmat(out,[1 1 3]); + +dimv_yx = size(anat_yx); +dimv_yz = size(anat_yz); +dimv_zx = size(anat_zx); + +%% compute z-score +cell_resp_z = zscore(cell_resp')'; +nCells = cell_resp_dim(1); + +%% Validating all cells + +isDiscard50 = false; % option to pre-screen +if isDiscard50, + %% Round 1: discard 50% noisy cells based on std of zscore of baseline + + % Compute std of dimest 10% of frames for each cell. + prc = prctile(cell_resp_z,10,2); + STD_full = zeros(nCells,1); + for i = 1:nCells, + ix = find(cell_resp_z(i,:)thr); + [~,I] = sort(STD_full(temp)); + cIX = temp(I); + + % visualize cells to discard + % gIX = (round((1:length(cIX))/1000)+1)'; % option: view sorted index + gIX = round(cIX/1000)+1; % option: view anatomical z index + M = cell_resp_ave_z(cIX,:); + BasicPlotMaps(cIX,gIX,M,cell_info,stim,anat_yx,anat_yz,anat_zx); + + % I_v: index of valid cells + I_v_Holder = ones(1,nCells); + I_v_Holder(cIX) = 0; + I_v = find(I_v_Holder); + + CRAZ = cell_resp_ave_z(I_v,:); + STD = STD_full(I_v); + nCells = length(I_v); + CInfo_0 = cell_info(I_v); + + %% visualize valid cells, sorted by noise + [~,I] = sort(STD); + cIX = I; + gIX = (round((1:length(cIX))/1000)+1)'; % sorted index + M = CRAZ(I,:); + BasicPlotMaps(cIX,gIX,M,CInfo_0,stim,anat_yx,anat_yz,anat_zx); + +else + %% Round 1 alternative: full set + I_v_Holder = ones(1,nCells); + I_v = (1:nCells)'; % valid Index. here not actually screened, I_v is fullset + + cIX = I_v; % 'cell-Index' + gIX = (round((1:length(cIX))/1000)+1)'; % sorted 'group-Index' + M = cell_resp_z(cIX,1:1000); + CInfo_0 = cell_info; + BasicPlotMaps(cIX,gIX,M,CInfo_0,stim,anat_yx,anat_yz,anat_zx); +end + +%% %%%% Round 2: delete anatomically out-of-bound cells by hand +% only execute once (manual choice 1): +cHolder_Anat = []; % collect cIX of all out-of-bound cells + + +% draw cells first +% (choose start and stop index to draw; low numbers ~ ventral) + +%% (optional) step 1: set limit to only plot ventral layer, helps to find outliers within that layer +I_start = 1; +I_stop = round(length(I_v)/10); + +% plot those cells +cIX = I_start:I_stop; +gIX = round(cIX/1000)+1; +figure('Position',[100 0 1300 900]); +numK = round(cIX(end)/1000)+1; +[tot_image, dim_totimage] = DrawClustersOnMap_LSh(CInfo_0,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full'); + +% Here manually draw polygons around cells to discard. +% Double click to connect last vertex to first vertex, then double click again within polygon to fix. +% then click again to start drawing the next one +% and when finished, break loop with Ctrl+C... +MaskArray = zeros(dim_totimage(1), dim_totimage(2)); +k_zres = 20; +% draw polygon around outliers on Y-X view +for i = 1:100, % NOMINAL LOOP, break manually (with Ctrl+C, somehow the design didn't work) + h_poly_yx = impoly; + wait(h_poly_yx); % double click to finalize position! + % update finalized polygon in bright color + setColor(h_poly_yx,[0 1 1]); + + IJs = reshape([CInfo_0(I_start:I_stop).center],2,[])'; + A = sub2ind(dimv_yx(1:2),IJs(:,1),IJs(:,2)); + MaskArray = createMask(h_poly_yx); + MaskArray(1:dimv_zx*k_zres+10,:) = []; + B = find(MaskArray); % find indices of pixels within ROI + cIX2 = find(ismember(A,B)); + cHolder_Anat = union(cHolder_Anat,cIX2); + w = waitforbuttonpress; + if w == 1, + break; % this doesn't work ?! + end +end + +%% step 2: plot all cells (including the ones in the ventral layer, don't +I_start = 1; +I_stop = length(I_v); + +% ...here until the end of the cell is the exact dupliate of the last cell... +cIX = I_start:I_stop; +gIX = round(cIX/1000)+1; +figure('Position',[100 0 1300 900]); +numK = round(cIX(end)/1000)+1; +[tot_image, dim_totimage] = DrawClustersOnMap_LSh(CInfo_0,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full'); + +% Here manually draw polygons around cells to discard. +% Double click to connect last vertex to first vertex, then double click again within polygon to fix. +% then click again to start drawing the next one +% and when finished, break loop with Ctrl+C... +MaskArray = zeros(dim_totimage(1), dim_totimage(2)); +k_zres = 20; +% draw polygon around outliers on Y-X view +for i = 1:100, % NOMINAL LOOP, break manually (with Ctrl+C, somehow the design didn't work) + h_poly_yx = impoly; + wait(h_poly_yx); % double click to finalize position! + % update finalized polygon in bright color + setColor(h_poly_yx,[0 1 1]); + + IJs = reshape([CInfo_0(I_start:I_stop).center],2,[])'; + A = sub2ind(dimv_yx(1:2),IJs(:,1),IJs(:,2)); + MaskArray = createMask(h_poly_yx); + MaskArray(1:dimv_zx*k_zres+10,:) = []; + B = find(MaskArray); % find indices of pixels within ROI + cIX2 = find(ismember(A,B)); + cHolder_Anat = union(cHolder_Anat,cIX2); + w = waitforbuttonpress; + if w == 1, + break; % this doesn't work ?! + end +end + +%% find extra outliers on top/bottom border of image (can't always get with polygon) +temp = [CInfo_0(:).center]; +XY = reshape(temp',[],nCells)'; +IX = find(XY(:,1)<8 | XY(:,1)> 2040); + +cHolder_Anat = union(cHolder_Anat,IX); + +%% test Plot: all antomy outliers +cIX = cHolder_Anat; +gIX = (1:length(cIX))'; +figure; +DrawClustersOnMap_LSh(CInfo_0,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full'); + +%% Clean up anatomy outliers +cIX_Invalid_Anat = I_v(cHolder_Anat); % convert back to index for original full set +I_v_Holder(cIX_Invalid_Anat) = 0; +I_v2 = find(I_v_Holder); + +CR_raw = cell_resp_z(I_v2,:); +nCells = length(I_v2); +CInfo = cell_info(I_v2); + +%% Plot all valid cells to save +cIX = (1:nCells)'; +gIX = round(cIX/1000)+1; +M = CR_raw(cIX,1:1000); +figure; +DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv','full'); + +%% Save mat files +CInfo_full = cell_info; % save in next round! not saved in current mats!!!!!! + +temp = fullfile(datadir,['Fish' num2str(i_fish) '_full_extrainfo.mat']); +save(temp,'cHolder_Anat','cIX_Invalid_Anat','I_v','I_v2','CInfo_full','-v7.3'); % CInfo_full not saved in mats before 4/24/15!! %% 'STD_full' saved before + +temp = fullfile(datadir,['Fish' num2str(i_fish) '_direct_load.mat']); +varList = {'CR_raw','nCells','CInfo','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn'}; % used to have 'periods' handcoded +save(temp,varList{:},'-v7.3'); + +%% (if needed) +% save(temp,'somethingelse','-append'); + +%% next run step 2: detrend + + diff --git a/GUIpreload_step2_detrend.m b/GUIpreload_step2_detrend.m new file mode 100644 index 0000000..e17bdc8 --- /dev/null +++ b/GUIpreload_step2_detrend.m @@ -0,0 +1,66 @@ +clear all;close all;clc + +%% manually set directory, number of cores, range of fish (ID) to process + +M_dir = {'F:\Janelia2014\Fish1_16states_30frames'; + 'F:\Janelia2014\Fish2_20140714_2_4_16states_10frames'; + 'F:\Janelia2014\Fish3_20140715_1_1_16_states_10frames'; + 'F:\Janelia2014\Fish4_20140721_1_8_16states_20frames'; + 'F:\Janelia2014\Fish5_20140722_1_2_16states_30frames'; + 'F:\Janelia2014\Fish6_20140722_1_1_3states_30,40frames'; + 'F:\Janelia2014\Fish7_20140722_2_3_3states_30,50frames'; + 'F:\Janelia2014\Fish8_20141222_2_2_7d_PT_3OMR_shock_lowcut'; + 'F:\Janelia2014\Fish9_20150120_2_1_photo_OMR_prey_blob_blue_cy74_6d_20150120_220356'; + 'F:\Janelia2014\Fish10_20150120_2_2_photo_OMR_prey_blob_blue_cy74_6d_20150120_231917'}; + +poolobj=parpool(4); + +range_fish = 10; %1:8 + +%% +for i_fish = range_fish, + disp(['i_fish = ', num2str(i_fish)]); + + datadir = M_dir{i_fish}; + file = fullfile(datadir,['Fish' num2str(i_fish) '_direct_load.mat']); + load(file,'CR_raw'); + % varList = {'CR_raw','nCells','CInfo','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn','periods'}; + + %% detrend + CR_dtr = zeros(size(CR_raw)); + tmax=size(CR_raw,2); + nCells=size(CR_raw,1); + + tic + parfor i=1:nCells, + cr = CR_raw(i,:); + crd = 0*cr; + for j=1:100:tmax, + if j<=150, + tlim1 = 1; + tlim2 = 300; + elseif j>tmax-150, + tlim1 = tmax-300; + tlim2 = tmax; + else + tlim1 = j-150; + tlim2 = j+150; + end + crr = cr(tlim1:tlim2); + crd(max(1,j-50):min(tmax,j+50)) = prctile(crr,15); + end + if mod(i,100)==0, + disp(num2str(i)); + end + CR_dtr(i,:) = zscore(cr-crd); + end + toc + + CR_dtr = single(CR_dtr); + + %% save into .mat + save(file,'CR_dtr','-append'); +end + +%% +delete(poolobj); diff --git a/GUIpreload_step3.5_PoolClustersFromAllFish.m b/GUIpreload_step3.5_PoolClustersFromAllFish.m new file mode 100644 index 0000000..6ddcbc9 --- /dev/null +++ b/GUIpreload_step3.5_PoolClustersFromAllFish.m @@ -0,0 +1,113 @@ +% Pool from all fish +clear all;close all;clc + +data_dir = 'F:\Janelia2014'; + +global VAR; +load('VAR_current.mat','VAR'); +%% +numfish = 8; +CONSTs = cell(1,numfish); +for i_fish = 1:numfish, + disp(['i_fish = ' num2str(i_fish)]); + + % for new partitioned data, consistant with GUI_050415, but not tested + fishdir = fullfile(data_dir,['CONST_F' num2str(new_i_fish) '_fast.mat']); + load(fishdir,'const'); + load(fishdir,'dimCR'); + CellResp = zeros(dimCR); + num = 0; + nParts = round(dimCR(1)*dimCR(2)/42000000); + for i = 1:nParts, + load(fishdir,['CRZt_' num2str(i)']); + eval(['len = size(CRZt_' num2str(i) ',1);']); + eval(['CellResp(num+1:num+len,:) = CRZt_' num2str(i) ';']); + eval(['CellResp(num+1:num+len,:) = CRZt_' num2str(i) ';']); + num = num+len; + end + names = fieldnames(const); % cell of strings + for i = 1:length(names), + % renaming exception!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +% if strcmp(names{i},'CRAZ'), +% setappdata(hfig,'CellRespAvr',const.CRAZ); +% elseif strcmp(names{i},'photostate'), +% setappdata(hfig,'stim_full',const.photostate); + else + setappdata(hfig,names{i},eval(['const.',names{i}])); + end + end + % add one variable + CONSTs{i_fish}.numcell = length(const.CInfo); + +%% + % load selected clusters + CONSTs{i_fish}.clus = []; + CIX = []; + for i = 1, % just one for now... + if length(VAR(i_fish).ClusGroup{1})-i+1>0, + % copy from VAR + clus = VAR(i_fish).ClusGroup{1}(i); + CONSTs{i_fish}.clus(i).cIX = clus.cIX; + CONSTs{i_fish}.clus(i).gIX = clus.gIX; + CONSTs{i_fish}.clus(i).name = clus.name; + CONSTs{i_fish}.clus(i).numK = clus.numK; + CIX = [CIX; clus.cIX]; + end + end + % copy from CONST + CIX = unique(CIX); + CONSTs{i_fish}.CIX = CIX; + CONSTs{i_fish}.CInfo = const.CInfo(CIX); % CIF + CONSTs{i_fish}.CRAZ = const.CRAZ(CIX,:); + CONSTs{i_fish}.CRZt = CellResp(CIX,:); + + %% OLD FORMAT without partitioning +% % load general info +% load(['CONST_F' num2str(i_fish) '.mat'],'CONST'); +% % CONST: {'ave_stack','anat_yx','anat_yz','CInfo','periods','shift',... +% % 'CRAZ','CRZt','dshift_fc','FcAvr','Fc','photostate','tlists','datanames'}; +% %% +% % load fields from CONST, with names preserved +% names = {'anat_yx','anat_yz','anat_zx','periods','shift','dshift','FcAvr','Fc','photostate','tlists','datanames'}; +% +% for i = 1:length(names), +% eval(['CONSTs{i_fish}.',names{i},' = CONST.',names{i},';']); +% end +% +% % add one variable +% CONSTs{i_fish}.numcell = length(CONST.CInfo); +% %% +% % load selected clusters +% CONSTs{i_fish}.clus = []; +% CIX = []; +% for i = 1, % just one for now... +% if length(VAR(i_fish).ClusGroup{1})-i+1>0, +% % copy from VAR +% clus = VAR(i_fish).ClusGroup{1}(i); +% CONSTs{i_fish}.clus(i).cIX = clus.cIX; +% CONSTs{i_fish}.clus(i).gIX = clus.gIX; +% CONSTs{i_fish}.clus(i).name = clus.name; +% CONSTs{i_fish}.clus(i).numK = clus.numK; +% CIX = [CIX; clus.cIX]; +% end +% end +% % copy from CONST +% CIX = unique(CIX); +% CONSTs{i_fish}.CIX = CIX; +% CONSTs{i_fish}.CInfo = CONST.CInfo(CIX); % CIF +% CONSTs{i_fish}.CRAZ = CONST.CRAZ(CIX,:); +% CONSTs{i_fish}.CRZt = CONST.CRZt(CIX,:); +end +%% +clear CONST; +temp = whos('CONSTs'); +if [temp.bytes]>2*10^9, + save('CONSTs_current.mat','CONSTs','-v7.3'); +else + save('CONSTs_current.mat','CONSTs'); +end + +%% optional +timestamp = datestr(now,'mmddyy_HHMM'); +currentfolder = pwd; +save([currentfolder '\arc mat\CONSTs_' timestamp '.mat'],'CONSTs'); diff --git a/GUIpreload_step3_align.m b/GUIpreload_step3_align.m new file mode 100644 index 0000000..864e55d --- /dev/null +++ b/GUIpreload_step3_align.m @@ -0,0 +1,298 @@ +%%%%%%%%%%%%%%% +%% +% clear all;close all; +% varList = {'CR_dtr','nCells','CInfo','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn','periods'}; + +% % or +varList = {'nCells','CInfo','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn'}; + +%% +M_dir = {'F:\Janelia2014\Fish1_16states_30frames'; + 'F:\Janelia2014\Fish2_20140714_2_4_16states_10frames'; + 'F:\Janelia2014\Fish3_20140715_1_1_16_states_10frames'; + 'F:\Janelia2014\Fish4_20140721_1_8_16states_20frames'; + 'F:\Janelia2014\Fish5_20140722_1_2_16states_30frames'; + 'F:\Janelia2014\Fish6_20140722_1_1_3states_30,40frames'; + 'F:\Janelia2014\Fish7_20140722_2_3_3states_30,50frames'; + 'F:\Janelia2014\Fish8_20141222_2_2_7d_PT_3OMR_shock_lowcut'; + 'F:\Janelia2014\Fish9_20150120_2_1_photo_OMR_prey_blob_blue_cy74_6d_20150120_220356'; + 'F:\Janelia2014\Fish10_20150120_2_2_photo_OMR_prey_blob_blue_cy74_6d_20150120_231917'}; + +M_stimset = [1, 1, 1, 1, 1, 1, 1, 2, 2, 2]; + +save_dir = 'F:\Janelia2014'; + +%% MANUAL +for i_fish = 10, %:8, + disp(num2str(i_fish)); + %% load data + datadir = M_dir{i_fish}; + load(fullfile(datadir,['Fish' num2str(i_fish) '_direct_load.mat']),varList{:}); + + %% ONLY RUN ONCE!!!!!!!!! + % add 1 frame at start: + CR_dtr = horzcat(CR_dtr(:,1),CR_dtr); + + frame_turn = vertcat(frame_turn(1,:),frame_turn); + + % correction of an error in Fish #1 + if i_fish == 1, + CR_dtr = horzcat(CR_dtr(:,1:20),CR_dtr); + end + + %% index processing + if M_stimset(i_fish)==1, % fish 1-7 + M_period = {480,160,160,320,480,280,300}; %,{120,150,360}}; + periods = M_period{i_fish}; + period = periods; + + nrep = floor(size(CR_dtr,2)/period)-1; + dshift = 2; + shift = period+dshift; + + IX_all = 1+shift:period*nrep+shift; + CellResp = CR_dtr(:,IX_all); % old name: CRZt, 'Cell Responses Zscore trimmed' + + IX_avr = 1+shift:period+shift; + nCells = size(CR_dtr,1); + CRAZ = mean(reshape(CR_dtr(:,IX_all),nCells,period,[]),3); % 'Cell Response Average Zscore' + + datanames = {'rep average','all reps','rep #1','rep #2','last rep'}; + ix_avr = 1:period; + ix_all = 1:period*nrep; + tlists = {ix_avr, ix_all}; + for i = [1,2,nrep], + ix = 1+period*(i-1):period*i; + tlists = [tlists, ix]; + end + + stim_full = frame_turn(17,IX_all); + stim_full = round(stim_full); + + else % multiple protocols + % process raw stimulus code + [stimset,stim_full] = StimulusKeyPreprocessing(frame_turn); + + % find time-lists = list of time-frames numbers for a certain stimulus + nTypes = length(stimset); + tlists_raw = cell(1,nTypes+1); % time-lists, i.e. frame indices + IX_all = []; + for i_type = 1:nTypes, + nSets = length(stimset(i_type).starts); + IX = []; + for i_set = 1:nSets, + IX = horzcat(IX,stimset(i_type).starts(i_set):stimset(i_type).stops(i_set)); + end + tlists_raw{i_type} = IX; + IX_all = horzcat(IX_all,IX); + end + tlists_raw{nTypes+1} = IX_all; + + % This is the main data to store and load to GUI + CellResp = CR_dtr(:,IX_all); % old name: CRZt, 'Cell Responses Zscore trimmed' + + %% get tlists corresponding to IX_all (tlists_raw ~ 1:totalframe#) + tlists = cell(1,nTypes+1); % time-lists, i.e. frame indices + for i_type = 1:nTypes, + [~,tlists{i_type}] = intersect(IX_all,tlists_raw{i_type}); + end + + %% find average + CRAZ = []; % 'Cell Response Average Zscore' + stimtypelist = 1:nTypes; + numcell = size(CR_dtr,1); + for i = stimtypelist, + M = circshift(CR_dtr(:,tlists_raw{i}),shift,2); + CRAZ = horzcat(CRAZ,mean(reshape(M,numcell,periods(i),[]),3)); + end + + %% variables to save later in struct + periods = [stimset.period]; + datanames = {'rep average','all reps',stimset.name}; + + dshift = 2; + shift = -dshift; % circshift fluo left by 2 frames + + end + + %% prepare fictive data + rows = [7,8,9,13,14]; + F = frame_turn(:,rows)'; + + F(2,:) = -F(2,:); + Fc = F(:,IX_all); + + % find averages + if M_stimset(i_fish)==1, % fish 1-7 + FcAvr = mean(reshape(Fc,length(rows),period,[]),3); + else + FcAvr = []; % 'Cell Response Average Zscore' + stimtypelist = 1:nTypes; + numcell = size(CR_dtr,1); + for i = stimtypelist, + avr = mean(reshape(F(:,tlists_raw{i}),length(rows),periods(i),[]),3); + FcAvr = horzcat(FcAvr,avr); + end + end + + % normalizations + for i = 1:3, + m = FcAvr(i,:); + FcAvr(i,:) = (m-min(m))/(max(m)-min(m)); + m = Fc(i,:); + Fc(i,:) = (m-min(m))/(max(m)-min(m)); + end + m = FcAvr(4:5,:); + FcAvr(4:5,:) = (m-min(min(m)))/(max(max(m))-min(min(m))); + m = Fc(4:5,:); + Fc(4:5,:) = (m-min(min(m)))/(max(max(m))-min(min(m))); + + + %% compile CONST + CONST = []; + names = {'ave_stack','anat_yx','anat_yz','anat_zx','CInfo','periods','shift','CellResp','CRAZ','dshift','Fc','FcAvr','stim_full','stimset','tlists','datanames'}; + for i = 1:length(names), % use loop to save variables into fields of CONST + eval(['CONST.',names{i},'=', names{i},';']); + end + + %% and save + %%% old method: + % temp = whos('CONST'); + % if [temp.bytes]<2*10^9, + % save(['CONST_F' num2str(i_fish) '.mat'],'CONST'); + % beep; + % else + % save(['CONST_F' num2str(i_fish) '.mat'],'CONST','-v7.3'); + % beep; + % end + + %%% new method with partitioning of main data + newfishdir = fullfile(save_dir,['CONST_F' num2str(i_fish) '_fast.mat']); + const = CONST; + const = rmfield(const,'CellResp'); + dimCR = size(CONST.CellResp); + save(newfishdir,'const','-v6'); + save(newfishdir,'dimCR','-v6','-append'); + + nParts = round(numel(CONST.CellResp)/42000000); + t = zeros(1,nParts); + ix = round(linspace(1,size(CONST.CellResp,1),nParts+1)); + for i = 1:nParts, + disp(num2str(i)); + eval(['CellResp_' num2str(i) '= CONST.CellResp(ix(i):ix(i+1),:);']); + save(newfishdir,['CellResp_' num2str(i)],'-v6','-append'); + end + + + %% OR + +end + + %% initialize VAR (once) + + +%% Initialize VARS % outdated? Clusgroup? + +nCells = length(CONST.CInfo); + + cIX = (1:nCells)'; + i = 1; + VAR(i_fish).Class(i).name = 'all processed'; + VAR(i_fish).Class(i).cIX = cIX; + VAR(i_fish).Class(i).gIX = ones(length(cIX),1); + VAR(i_fish).Class(i).numel = nCells; + VAR(i_fish).Class(i).numK = 1; + VAR(i_fish).Class(i).datatype = 'std'; + + cIX = (1:100:nCells)'; + VAR(i_fish).ClusGroup{1,1}.name = 'test'; + VAR(i_fish).ClusGroup{1,1}.cIX = cIX; + VAR(i_fish).ClusGroup{1,1}.gIX = ones(length(cIX),1); + VAR(i_fish).ClusGroup{1,1}.numel = length(cIX); + VAR(i_fish).ClusGroup{1,1}.numK = 1; + + %% + cIX = (1:10:nCells)'; + VAR(i_fish).ClusGroup{1,1}(12).name = '1/10 of all'; + VAR(i_fish).ClusGroup{1,1}(12).cIX = cIX; + VAR(i_fish).ClusGroup{1,1}(12).gIX = ones(length(cIX),1); + VAR(i_fish).ClusGroup{1,1}(12).numel = length(cIX); + VAR(i_fish).ClusGroup{1,1}(12).numK = 1; + + %% varience/std for reps for each cell +% if i_fish==2 || i_fish==3 || i_fish==6, +% period_real = period/2; +% else +% period_real = period; +% end +% nrep_real = floor((size(CR,2)-shift)/period_real); +% while period_real*nrep_real+shift>size(CR,2), +% nrep_real = nrep_real-1; +% end +% CRZ_3D = reshape(CRZ(:,1+shift:period_real*nrep_real+shift),nCells,period_real,[]); +% %% updated method, weighing both std between each rep and (summed with) std of 1st half & 2nd half of experiment - 1/8/15 +% % CRZ = CONST.M_array.CellResp; +% % if i_fish==2 || i_fish==3 || i_fish==6, +% % period_real = CONST.M_array.period/2; +% % else +% % period_real = CONST.M_array.period; +% % end +% % CRZ_3D = reshape(CRZ,size(CRZ,1),period_real,[]); +% % divide = round(size(CRZ_3D,3)/2); +% % CRZ_std1 = std(CRZ_3D(:,:,1:divide),0,3); +% % CRZ_std2 = std(CRZ_3D(:,:,divide+1:end),0,3); +% % temp1 = mean(CRZ_std1,2); +% % temp2 = mean(CRZ_std2,2); +% % +% % temp12 = horzcat(temp1,temp2); +% % temp = mean(temp12,2)+std(temp12,0,2); +% % [~,I] = sort(temp); +% % M = temp(I); +% % figure;plot(M) +% % +% % figure;imagesc(CRZ(I,:)) +% % +% % nCells = size(CRZ,1); +% +% %% find low variance / stimulus-locked cells +% CRZ_std = std(CRZ_3D,0,3); +% temp = mean(CRZ_std,2); +% +% % find mean-std thres: 0.5 +% [~,I] = sort(temp); +% M = temp(I); +% figure;plot(M) +% %% +% i_last = length(VAR(i_fish).Class); +% M_perc = [0.025,0.1,0.3]; +% for j = 1:length(M_perc); +% thres = M(round(nCells*M_perc(j))); +% cIX = find(tempthr) = thr+1; % assign cap value to be thr+1 + +xv = 1:thr+1; % x-vector for histogram bins + +% Manual inspection +% figure; +% hist(st,xv); + +% (continue by default) find bins with significantly high counts, i.e. +% whole range/set of raw stimulus keys, store in 'M_range_raw' +% use to manually define standardized 'M_range' +counts = hist(st,xv); +thr_counts = round(length(st)/500); +M_range_raw = xv(counts>thr_counts); +disp(['M_range_raw = ' num2str(M_range_raw)]); % use this to adjust the substitution array, M_range, manually after this inspection!%%%%%%%%%%%%%%%%%%%%%% + +%% Standardized Stimulus Key ----------------------------------------------------------------------------- MANUAL INPUT! +% Define normalized code, e.g. same stimulus under different raw stim keys +% can be unified +M_range = [3,1,3,2, 9,10,11,12, 0,13, 3,14,15, 4,16]; % corresponding to +% raw = [2,3,4,5,12,13,14,15,22,23,30,31,32,99,100] % for Fish 10 + +% 0 = all black; 1 = black/white; 2 = white/black; 3 = all white; 4 = all gray; +% 5 = gray/black; 6 = white/gray; 7 = black/gray; 8 = gray/white. +% OMR baseline = not moving?? grey?? +% 10 = forward grating (very slow, more for calibration) +% 11 = rightward grating +% 12 = leftward grating + + +% half-fields can be displayed directly. other L/R directional cues can be +% displayed by gradient. If not-directional or unspecified, will assign a +% color-code randomly. + +%% correct transient frames +% short method: st_rounded = interp1(M_range,M_range,st,'nearest'); +% but this doesn't work if a frame is different from both the one +% before and the one after. Use loop below to force a manual 'round' +% to the closer one of the 2 neighbors. +temp = diff([0,st]); +switches = find(temp); +trans = switches(diff(switches)==1); + +for i = 1:length(trans), + j = trans(i); + if j>1, + st(j) = st(j-1) + round((st(j)-st(j-1))/abs(st(j+1)-st(j-1))); % round to nearest of st(j-1) and st(j+1) + end +end + +% (loop until all transient ones are corrected) +temp = diff([0,st]); +switches = find(temp); +trans = switches(diff(switches)==1); +count = 0; +while ~isempty(trans), + for i = 1:length(trans), + j = trans(i); + st(j) = st(j-1) + round((st(j)-st(j-1))/abs(st(j+1)-st(j-1))); % round to nearest of st(j-1) and st(j+1) + end + temp = diff([0,st]); + switches = find(temp); + trans = switches(diff(switches)==1); + count = count+1; + if count>5, + disp('count>5??'); + break; + end +end + +%% Standardize stimulus keys for both 'stim' and 'block', based on 'M_range_raw'->'M_range' +% (tricky for cell arrays) custom method: +% make a replacement matrix 'M_replace' to substitude raw stimulus key in 'block_raw' +% to normalized key, stored in new cell array 'block' +M_replace = zeros(1,max(M_range_raw)); +for i = 1:max(M_range_raw), + ix = find(i==M_range_raw); + if ~isempty(ix), + M_replace(i) = M_range(ix); + end +end + +stim = zeros(size(st)); +block = cell(nBlocks,1); +for i = 1:length(M_range_raw), + stim(st==M_range_raw(i)) = M_range(i); + for j = 1:nBlocks, + block{j} = cellfun(@(x) M_replace(x),block_raw{j},'UniformOutput',0); + end +end + +%% plot stimulus-key! + + +%% Stimulus set segmentation, based on 'block' (manual input of protocol sequence) +% reduce 'stim' to a sequence of keys without consecutive repeats +ix_singles = find(diff([0,stim])); % index array to map back to raw indices (frame-number) +singles = stim(ix_singles); % sequence of keys without consecutive repeats. +% '_sg' below stands for 'singles'. Manipulations below in both raw and singles, in parallel. + +jump = 1; % searching for 'jumps' in 'singles' +% Based on the known protocol sequence for each set, look for first +% mismatch in 'singles', i.e. position where the next set begins, stored +% as 'block_change(_sg)' and 'set_start(_sg)'. +block_change = cell(nBlocks,1); +block_change_sg = cell(nBlocks,1); +for I = 1:nBlocks, + nSets = length(block{I}); + block_change{I} = zeros(1,nSets); + block_change_sg{I} = zeros(1,nSets); + for J = 1:nSets, + jump_last = jump; + pattern = block{I}{J}; % protocol sequence for this set + + % this is sort of awkward. Instead of padding the 'already-found' sets with zeros, + % one could also search the cropped array and adjusted the index. + % Now 'comparison' is aligned with stim, works. + textile = repmat(pattern,1,ceil(length(singles)/length(pattern))); % tile, for use below + + template = zeros(size(singles)); + ixs_fill = jump_last:length(singles); + template(ixs_fill) = textile(1:length(ixs_fill)); % crop the 'textile' to fill the space after jump_last + + singles_temp = singles; + if jump_last>1, + singles_temp(1:jump_last-1) = 0; + end + comparison = (singles_temp-template); + % find the position where the next set begins + jump = find((comparison),1,'first'); + + %% contingency plan: if start of the new set is different from + % expected protocol ('block') + count_stuck = 0; + count_stuck2 = 0; + while jump-jump_lastlength(pattern), + count_stuck = 0; + jump_last = jump_last+1; + count_stuck2 = count_stuck2+1; + disp(['count_stuck2=' num2str(count_stuck2)]); + if count_stuck2>10, % arb. thres + disp('count_stuck2>10'); + break; + end + end + + pattern_ = circshift(pattern,-count_stuck,2); + textile = repmat(pattern_,1,ceil(length(singles)/length(pattern_))); + + template = zeros(size(singles)); + ixs_fill = jump_last:length(singles); + template(ixs_fill) = textile(1:length(ixs_fill)); + + singles_temp = singles; + if jump_last>1, + singles_temp(1:jump_last-1) = 0; + end + comparison = (singles_temp-template); + + jump = find((comparison),1,'first'); + + end + %% save + if isempty(jump), % reached end of experiment + %% + % NOTE END OF + % EXPERIMENT???????????????????????????????????????????????????????????????????????? + break; + end + block_change_sg{I}(J) = jump; + block_change{I}(J) = ix_singles(jump); + end +end + +% eliminate empty sets +for I = 1:nBlocks, + nSets = length(block_change{I}); + for J = 1:nSets, + if block_change{I}(J) == 0, + block_change{I}(J:end) = []; + block_change_sg{I}(J:end) = []; + if length(block{I})>= J, + block{I}(J:end) = []; + end + break; + end + end +end + +% block_change is basically start of next set. shift by one set to get 'set_start'. +set_start = cell(nBlocks,1); +set_start_sg = cell(nBlocks,1); +set_stop = cell(nBlocks,1); +set_stop_sg = cell(nBlocks,1); +for I = 1:nBlocks, + nSets = length(block{I}); + set_start{I} = zeros(1,nSets); + set_start_sg{I} = zeros(1,nSets); + if I == 1, + set_start{I}(1) = 1; + set_start_sg{I}(1) = 1; + else + set_start{I}(1) = block_change{I-1}(end); + set_start_sg{I}(1) = block_change_sg{I-1}(end); + end + set_start{I}(2:end) = block_change{I}(1:end-1); + set_start_sg{I}(2:end) = block_change_sg{I}(1:end-1); + set_stop{I} = block_change{I}-1; + set_stop_sg{I} = block_change_sg{I}-1; +end + +%% Visualize current segmentation of blocks +if isPlotting, + figure; + hold on; + plot(stim) + for I = 1:nBlocks, + for J = 1:length(block_change{I}), + x = block_change{I}(J); + plot([x,x],[0,20],'r--') + end + end +end +%% Find shift-corrections for each stim-set, used for averaging later + + +%% Find periods for each set-stype, and organize info by type into 'stimset'. +for i_ss = 1:length(stimset), + % find empty sets (for this particular experiment) and delete ij's in 'stimset' + for i_SetRep = 1:size(stimset(i_ss).ij,1), + I = stimset(i_ss).ij(i_SetRep,1); + J = stimset(i_ss).ij(i_SetRep,2); + if I>length(block) || J>length(block{I}), + stimset(i_ss).ij(i_SetRep:end,:) = []; + break; + end + end + nSetReps = length(stimset(i_ss).ij); + + %% get period from first set of block + I = stimset(i_ss).ij(1,1); + J = stimset(i_ss).ij(1,2); + pattern = block{I}{J}; % protocol sequence (singles) in normalized code + stimset(i_ss).pattern = pattern; + + % find periodicity + if length(pattern)==1, % exception, e.g. 'Spontaneous' set, single stimlus key held for full duration of set + if J < length(block{I}), + next_start = set_start{I}(J+1); + elseif J == length(block{I}), + next_start = set_start{I+1}(1); + else + next_start = length(stim); + end + stimset(i_ss).period = next_start - set_start{I}(J); + else % find periodicity based on stimlus key repetition + for i = 1:length(pattern), + if length(find(pattern==pattern(i)))==1, % use a stimulus that appears only once per period + % find period + start_s = set_start_sg{I}(J) + i -1; + stop_s = start_s + find(singles(1,start_s+1:end)==pattern(i),1,'first'); + stimset(i_ss).period = ix_singles(stop_s)-ix_singles(start_s); + break; + end + end + end + + %% + % stimset(i_ss).period % assigned above + + stimset(i_ss).rawstarts = zeros(1,nSetReps); + stimset(i_ss).rawstops = zeros(1,nSetReps); + stimset(i_ss).starts = zeros(1,nSetReps); + stimset(i_ss).stops = zeros(1,nSetReps); + stimset(i_ss).nReps = zeros(1,nSetReps); + for i_SetRep = 1:nSetReps, + I = stimset(i_ss).ij(i_SetRep,1); + J = stimset(i_ss).ij(i_SetRep,2); + stimset(i_ss).rawstarts(i_SetRep) = set_start{I}(J); + stimset(i_ss).rawstops(i_SetRep) = block_change{I}(J)-1; + + if length(pattern)==1, % exception, e.g. 'Spontaneous' set, single stimlus key held for full duration of set + stimset(i_ss).starts(i_SetRep) = stimset(i_ss).rawstarts(i_SetRep); + stimset(i_ss).stops(i_SetRep) = stimset(i_ss).rawstops(i_SetRep); + stimset(i_ss).nReps(i_SetRep) = 1; + else + % circshift to find intact periods + pattern = stimset(i_ss).pattern; + + ix_start = set_start_sg{I}(J); + ix_stop = set_stop_sg{I}(J); + + % find 'shift' (shift = 1 means starting from 2nd in 'singles') + segment = singles(ix_start:ix_start+length(pattern)-1); + shift = 0; + while ~isequal(segment,pattern), + shift = shift+1; + segment = singles(ix_start+shift:ix_start+length(pattern)-1+shift); + end + stimset(i_ss).starts(i_SetRep) = ix_singles(ix_start+shift); + % round to period within this set + nReps = floor((ix_stop-ix_start-shift+1)/length(pattern)); + stimset(i_ss).nReps(i_SetRep) = nReps; + + ix = stimset(i_ss).starts(i_SetRep) + nReps*stimset(i_ss).period - 1; + if ix>stimset(i_ss).rawstops(i_SetRep), + nReps = nReps-1; + end + stimset(i_ss).stops(i_SetRep) = stimset(i_ss).starts(i_SetRep) + nReps*stimset(i_ss).period - 1; + end + end +end + +% discard very first period because of potential artifacts at beginning of +% entire experiment +if stimset(1).nReps(1)>1, + stimset(1).nReps(1) = stimset(1).nReps(1) - 1; + stimset(1).starts(1) = stimset(1).starts(1) + stimset(1).period; +end + + +end \ No newline at end of file diff --git a/arrows.jpg b/arrows.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d1147b6ebf57757aaa9a7a7a99b1aa4dac768d1 GIT binary patch literal 28083 zcmeFZcf8v~)<5if(<|w;03n3{CXppuwwwl%Wx3muZCSRdvSrz_WXYCoxloq{cIh?r zvIG(!S;EpnS$gkF?f)LI{^y-DXGp^0nH(Cj zgvzd5LGrfsT&x|AXWMetDa_n=$>>GR1E*#%z!!kuVn=8SHV zn!I=>-udc`WQd;O()8R6biQR?)?&BJu*36VtIgqX>@x$gz!u1YKrk{7b}X-16OSNpYpNZEOd+L#iTm zIIuwB)%@a_&3QsjAbmt@3mJoXSiZ$kBBb7K(e18#0&LOM&%`?x>X>S#`x*Q>dB ztDIXrGn%Vvbv>IKtlL{vbU<7S8j?^eEJ09vm#&Tk2cou8_47=HoRlxrfTXakxd!5X zE89S=^3!h5)_enzzm>0B4VOU9B4opHCMKu-(L7GzdAEzh{V5zLAqRt3Sx>n2oPlF5f5dRXpHH+Cp+)+p)?$gbpZ!QrhbwLfY`dl2iY=K0?mSskoPkA zfG1S;k!~~+&*x*Fgf-(ySOgc$bOC{M^;FU?``lq8lOzfm6_nc(Rk8@sxPi;9cBP%~ zNIqP2qv2wz7Lshu&F=^9JqojMSD*Y*HiU%YG7^?7tzfaI5z;CV(rp3E*g;aV2!l9M-UgJANIk`E5X5v5xfU z0{E#_opL%EE^Yw?aUcVz%WNpmd!v>tsTo15U=>+Xei^3|q7Q zq0r{aF_^-7x9*auW+a|Mf^lCx6eHYeE?p3~ro~5f1y{@wfUNFi)K7$e%O zEVwp*_5WAE%^&IAf1-y$%CDqLiB_6Ji|H7_@m#{nbEw+Y-BPqHcoZWe5#=t0An@Bd z=xWA5(Uoi|0ffWm6P+ckPd|dFXCm| zhEo()2ep�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 literal 0 HcmV?d00001 diff --git a/arrows_wide.jpg b/arrows_wide.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9438967d7ffda7b2f291069aec782feb1deda3fe GIT binary patch literal 25217 zcmeIacf1ot_XnJNFTHmJ0k3q3u-PP=a;dx7YC(=nA4Bq7*?C5gYPO63RtEpWpNQz3=D!sYCi>G+EnL}|do({zmwW7fSo($6%YEJP=N1GA_uL}HK`n`Bq zadU{{5_J&7rU!ciaUV6SBtr6^(X(G2)j8dX5<0Y-qkNA4|k2&Rd(0McH&+ept^kcpXTWvZ0=} zWfCqjtS4Hkwbo@Y*DB&Esume67~~g71ho=)Ff0;FrPAAKg#uUr34{#R6&sXA~qtmmb6<#M^fInm&FiiTjhTwa0^3I_p-LFod^dGmwV zbgv4?WgMJJ`%_GUW8!RW3D@h3XSiWKdzLD?a#Y$C@L#DXkx50%wFLYS6{TVn%cX$~ zVdx4tC_6OZuM5UgF)ufqiFs)X3MVLfD0DUVY8@p~j^UzIxq(;eEw>bna@XdTpi8Vq znUVzRqF$CB)-yjSKn1;-DA#kiImNKtP^dz3ZP7~ZwTNYouBp62RZlXPLCV$E%l{{d z0lT;&vg?}M^~4McsWdUrq`$LO8e(GfwbBOV>I}f#0$$FGdpT-2FeGGAR zcsx41(rYx)XeQ02yj(n0tFR#zCdvSp$o_<24`pIL*2_fGm-SaMtbeqslI0}4sWfFT zB&cCMtyDUmN%^Vzwtdw^FR808#{{_0a2OGm`6_AfQr1rG6$Xa|#UQ@M;8aF&3oN&g5xL4n z<_SOuil7FYGN16pWC|s?@Hr8IKdMz*7|iW-#A!?hSfWvF1rT+k1`CS1oyMYH3EQnv zZ~+b*>A*E=qs=PNl&`NjnSFqX5;Tem3b2M$061`50xTu}qr>VJVTC}csH0rISO)Z{ z97T5yWVlP5Vd;R#nky3p%S5C*##OHAGUuc}8uBi_qKDC4MmXc7VW}nj$ z@&h6aEfqo|0hJ@{1qlo8Zuo5wb-JF3D=P%#C^jqQ350~vpYb6argFPZNGJ=9WTrr7175K-?iVW+M z5kO0Qw|=6c587506{}iZL;uC{9d*a$)Z^ zpbA?_d*%>0acOzP<1&V?0D?SF0Q6CV+EMDa!f3`*0KG?uM7%C*!Y35dUZ^q6!){qnHI%Sx^d9 z7%DwQ2Mis>HK;L%N=shdYQc1ds+tR3$+DG$umTIh3|2RH!D6Mo@(V z)y7dar--1s2%4f5g$jpyKbl2x3^lmXLJqf}MiLd~a2hp{s3ZrRo!O1@b6}vd_%Ym~ zw4>Gtrp_s&s4a~dXr&mni!rN(FrW?%Rt9uh2pVMoj`*cj8AKDl+Gy#R1-iMw?d!Oh$|dMlnPr;OQKPM!jseH zQ8um!p*j9<(XqMc4CZQWy}HDL?bFM?U7>foT?1STD(~-4fJU7g|RHD#&UiSR!FPOmVg-(D%Am$ zQej9$okA%HlWWw7B`8*)X^lEZ7Zggj#z}`V3XMh+rI}#~!L&|kiL;8ZK<7XcNkv?#i=oMkB2DT(gjfqu?v`d`r%TE8+%AF7Lw-w_nwP{Pnio29%(pm;o=D{B+v-+$+b>6tOU{%MJwBM!;X$fV_ zVhdrqurh71iSq_rSuofwc`%TQ4fX_PCX{@V#6lL9P)0~^$X01Gp%akBkUd9OH4Y=@ za1t)yq4G{SL7`4yYcV1car!W?nMi4zaL6Yi@&cE|8i*3&h$|He+6c(+R^>yeN=3TM z95Nv$t1^;qekek#NYdlY$E+%!-y^cdl`1CgC9MgODk1jft*NvsZ}FM(Tv#QJ`_iGT zSB+}?x=`MxR;T^(e9=$_RGZ0wIs~iLE;JCcif}cRqlgfKs$+2~ib?qDOeCnx%lK+x zBpAi`keVN-2^)rKR5?1Pz?B*^8d56=jmAZW%7Ag3+N24l8Erx9(8M)Ns-O>Oa`CW9 zVT@@IOE|}vb6QLmvD>UNtu7rAGNe{(r=wnl)1~$Mqw<2A)dGJWDtJX@Ky8L*Rf>RC zD~hwpLeQ^61#xpBl-KDraaa*i>FjjETZjd80W^s+aab3lld(d|tjlRqMuto15J5^{ z%Mp4!k}d=Km;$3W`Z-(yV|tgHOBO^SeaM}$Ds#R4Ht+Kr?D z4i#MrV^AP87Ci-HQY@6%{Sl)O6|s2GWx_0C5@!r11C5k9#OzUp$t6RwIE$FVZb<-7 z7EBqLRBz`}CP`c>!E>xxMamL*F>JPJX(~(5d;>}~yh$d%YwF-{Zi!8+i9wg1E6*VfY0-MfiK!6=P99B<)5JlXK zH5O1M1-_iM=unpdDM1jm0sn0iFjku}pjDI6u+1CQ%9L@DjfHjjQa6E(`dB2(*cAzb zKT?p`En1^3BDC8Bd6SkzGWJx!j4I`od5Rq|nnezjCDnu_11nXe1GC0pb&L!XwjiNb zlX;`v1)Gv2rvA5et}{c}&r$+3B=-s6fo@^x8a`5Nmc)b`L)q zHwzi3*OX6KgfXu-NK^oAs$|fc4W#VC4C{sDX@{_o^;trkTO~w%A#KJhlq!9BBul9v zt6#z8LM%@E?TLKUrOx>yTp__4Fh2|zGinQ2ZgN1435sem9&kHgG2knZ;Dmg2z!{Jl z#Y$HwLmBdjj*V$4Dj>14$tabxO5LKYBB&r_w5u2jl7u|&5@W%bmR|JwQZ>q`ddB%zDvbrDh08%wHnNpmu%O9i~yM2aY+1v*%n@-dk*hkCCh zl*)Uvj1)!FYG+Q8BItC$Td+y>igb}Frc#zDr!BxrsZ+<%Dk0_b2y|V2;b;PJa2{G7xcwS*E6MKNFf~7ak8|qAoUrH{sLUIyNsM)EG(vc zW}}QR7T~NU!zpC~pVVfS>oI}AZqIX8Eo{g-?3~LAM_o>#G2kyZ8Rp||k1-q)aD3eaw-v8 z6e|NpGazo3uyI3%kx58jLSRbxBpFxAZ^}xg8Xu>mgnlU#&Tu9Xa*)SFLOw+4_!uirnj>PqKPYiPDV8q^$}p(lhHR`nlST9p zSA={)2!ipaTF@v4JEOuo+=Tqg=esx|JEH}B7DF5e_rBKd@qXIXm z!L16BR>B&u+`CNk55Ly})+2^wV4254gUPqTjpmeA6xCQ!i@}NpAthRwhO5z1!z}e@ z{uZJPh~_jZFnLic&`}MJ8p)?Nu|Hn2r$_fT8mh5ya+* zCMb8zlk~A#A;luPa6;#dB_vEDnare9UP(4yk}qh!2FEOo!J8 ztEBS*GLoaqP1c4zgo+770t6|NB~8U#fldhc=3+=|i*N=ziI}a?0GO*Ld@cf}T(A{P z%>!|-Gm-LDrWJ6;C%i0!8#W)0YQYeTFrHW<<7YjRU?gdZB)ncWsf#6(k^+}X^0Sb) zqGdD(%mh^^OLm6KP&p}1N(q|D5DL)s<vdArRC<#3akjroc$ zqzD;Bat-A+mN~R%j20@F_QjB}%_^~4qfA6C&1flu*Jlr=!4saKgVL#Q#BjMKPBx&l zvH`j>(CZXMLq3jT8Jo@Tpj`YwP=>OaqAX+9yOI{CTo~}dm@%6avJsn=h7dnuPT(SO zlriIk(Ttf*EM&5~5QcJ_D~3%A3f7=rP78CkqSlymKshvtc*734I2Y4-DZ7ivoBejP z*>5u%%^*gLq{9U33B!7WH^LGQGvN}H%7_D!KxGXU_7(L>t6HANd`<}qg$>e-Hz;GQ z44q9t1fF&TnA8nVWW;OJ2>eV)EW#Xu$~?t^VM2|@q|35qnK%oYt8$uxusk4R@(PnL zAZ0Bfiw5#@-iTBe%5CG9s2sAbLt1_Npj?xfHY;m?#)!j4DUes8HZA zmbWF!b_0i?B7-MH3ChNKU^Hb-rO2QalR4;+H6)ZcV&))$Q8F=jz;dMpWi4O5yppnH z5|UIR78Qm?v4qHPjC!q*FQaFy?s#4#0!vCvDxWQa*2oYCWjAV+Q4aBkg055RZuYV zgUzydKo`-(l&^j9f|n8My)1iB?xB#EX|tzEaB8i zV7||ccqKX&WK+jXc`M9^2|}l5$jTsPGU2cq!U-1}%a*#6lZ~hfxHdsVDG8@Dr?Yt{ zn3%}WbV64M$|5pSAme1kAP^rE(yYvhSSN7{98$)LI0>pCW4yRfrE~fydk~D_5Nar74M;fZvWF|{hYnhf z%Yh3>qhdCbcHlbLDu{EyCVW9lHll`5Z9XYuD5qT=kbrm;WpIa5q~ruNkqLnw!ibUu zNTzbilkPZImLZ#E@q!+4%R~}gKn-V#PA`l?f&k1{II$#&=Q8HBGU%s5m`bQq=cRlv zglK>TXdED@vT_z#)@HPcV3RSJu0*h=f=dzROMN1KLF5a%^*RyE@f|8Q7>8t>Sx=z`a2ZJz>-tM3BiH%P7Nw zpgxeKr6C|X2nHjUAPVQ9ViZC6ZjzI01xbA>BXKEXB45G)?nXE%xLXD!_&!HYq48w| zX^9-iz>-4@HfEi27a~q1!m&h<4e))cIL(M!0uQuo^m<3UqL)77fIGO`%jR;G) z;wm~=b|wf2x*c9Am-5*(We)AxLRJfVl2pM$rQx6_=}!3s(t@bu<3fCw&!5mEAvh+6 zu}}uidNmxUcIEU&Nh)oQ=JQq`oGNg9XE0(eaFw3ovv35Lat5fVz+vF>*>q4ONKkf( z$*EJ>FM0li%aQWA0-W9}B5ih|pb*u@Jp>+?q;-UogfT4d2w*r-iEVH-m^|5ZT1&*M zPZJKS1uS2>li9pOsLPa2SDqbq&6F#ph7|- zQjn`@F76hlY`_;K6?P_Msy{rGl~T{)Uh%O1X;p@fT(#KIa8lS0)5S-fJVWm~;`DsyhFJ-{6kZ@4u7Zt#;fO=BEv5LcBC^xfgA&X>W1kNuOi|HtkUO$b!m?!Zq~<&a+6BuV)-RsSDDe5 zG?ztkR3NAkSSS`iw4{49mD|Q7Vg4fvhcE)bg1? zASezBJOP7Q6s9UODP=a7(#yCQus1dfOd$+UPMC>vx`>6#lF0-E`dh}EfI&1s1$1(p}H(I6By2EeL^(w(l%q(WjGG0Q?AN;9eAih@9qi)2KY zI%N-fLJTG?*f^OEN|}9xUP3{7UnyQ>WGTL@NCf$wnBB!WonZwQ1xBUKmSyNQa!8QX za!^DZ=cHsZ$%K@s!$AYxAmGCz?3$i64-4ijFiGqMv<*FS)eA_ zj6LIbSTmK_;~L+6J%4`VymFKO*C_P>%7Fh1hU!68P|ASUAHQzgtiPaK0mxR|mmZ4y zePouOaD^a=O$4F9@#(z4zGX-xRUmB?2_D^fJpoHH7{|B}KTCM+3L!8Itp}5y20{4pyh6tQl*yZFZ%Pdi$Q0{_3_?ZK*vJ?QB-38_ST22V!;! z9#=!AG9XxUNqI?!Q=D}hogo*PQb;hHHz<*rZ3HfpQ^0#1t9j5=W}MtlMvlaMM@E*jy} zN_X7js6>IuFy-RFi$@U{q3l|pGp$hBD9S-I8m97GFX4$9!Fvl1hbG}h+>vP5ZB}dI z9+ldXw3149@&-LA5p_htOAz4stR;}M_#N(I%7v7h><&heR8S*f5i0FTsDxgR+rdQ5 zV0{4&i-8uyOsZHcVqncB$ae*xLeS=s7OjX~9#Td?0l?k~SiX<4YS|_J~XZ4u%a1q!=*6w5NiDX2hXN>V2 zY?2tPm8qGVlCXqc50+Ov{Gbbo@Eu__fhFzUuv-iKBxQ;_SbJEkLo`tfg*uXyP$%Hi zkj4zw(1MX*Q7Le+DPvI2Ph@2kv*AY!Do*8tv>Ba_fDj30m9ju%7PHC4LIn=72<&pb z1g7+AUqQj82$e;~)ccBW|!D&mzgnOp0|NF&QMXh9cUu5oNsKJ$cfu zOnTKG1em-PipNbkNhGCXg*e}ti9-p!EDI**(gI>OCb*0J?@GoXYFS3-3+f>dvD=fP2v{WLl(5YSCXjJjg$UDyLek+&kc!F^ z@HpsR+5i~i^R@_hBpI+L2({55OIq|f+A7pz%A`GSQ9=q`jKi2@lu1b`2+0>yQ`3>O z!3w4iXn_L_@TdQw0cAkWW=&|q)`CtQ_7g5^h*YZGG%Zfb(xv&7SOi7$NCYVgxwJ4O zp<%m39@~n&lZz;(Q*DSJLp2uEiR8fKD_A(&ir)*iHKx=o&b)gJGnF)Ow7f8V) zWpPoPr)Zf@03M4|IKQ;0Zv}6XzOA$erj z)z`r*?|T3FZSeoewusV3fWN*&|9`nT!eIFOT@JArbiExCY0gSr-ScwYjTPzAtNYO9 zH}?T~uj>OTvtYB!-)*$G7D$-|J6*2spegM{0a7v;>NISc^Rj+QgAWH8gBd1Jr&K7w z6K5f;5aUv%7=|&S98*X{A~}pp6hcL1pTM<6uWIWOEFSk~N}Fpg!3O>Zu=R@m1=a*S z6vKL>^=L8~fQ{fnflMwG%S9N0V=yd-#o)1lT!4#UL?Bbh%GL!~zE1hS)M2IA08`pl zq=EVe~JPvkM1cH|_{&fE@<#8&-WT^m=ipR>^Z2oye#?Z1|)G=38QErr%0?m+7 z!9gN{KrqM)i^YTF6eSuI2>3(@BBfxtgc=I{rR-IPN?NYm4*XReGL!&oQ*wnsDHKR# zFf5ZPBq%IYD8-mqf{O9W$n}a|4>|rHYox;2b;@6_8W8H-kNpz*%G&U-EZSLh8U=P>-Xtzz813k9R>n|DB@U#~gNJ+r;@~*NB zs;C@5c_o5+cJc3b(Z3YEF@7#Yv46McssdG}x-m#P9^`V~6ot~D*WYO0Qw8Z-(i=ni zLtuj(6&Ma(mel2pKNtDCvEf<;UbcomDg6IzXkf1H^t!%xu18Sbx>uj7i z#bpxaR6NLl-G!I72L27jC87MEH3fLQ>o)=10IZ9y-NOem>j1BTjGszf(mr^cUK?e6 zDQ~J!D<3QeoA<6E`dfZ0tpHm^!7f$k$~M)%^u)^@<^S~e&(`&S3Vs>vzm;5;#J~0X zUvd3gV!BN9UvXWQ#J~0XUvd3gV!BN9UvXWQ#J~0XUvd3gV!BN9cj9Vx^+Ok6J}|6j z4t&Pq;+HL4;JN!9W~+(N=#{*xDju(eTFNY%HjO9{R6;|tVdukhh$gAcx*XTidVwRzDAiW!;dq*=d2Bd!m=?*cgbWC@tCN(KoB1My!u{`Te-s=rT^bX%b~Yboojs?+wXc~{F` z&5JMO@nq+KY#zOu=X;#TTX#Q?chml>dA*sToUM& z;~xpGP~JFEufD;h`mV^X7O#9Qq_!kfe>@64@KBouAAg~02mRTI|Ko!7*s90JZD28q z0`qK8l>=BA!_q+AmuBU)40|ID|A)ou@lZ-u>>4E6Kl2B#!ySWpZ9eM2t2wou*PwlK zUd`wy!JVordeb*`@Jc<;dCNByy9a4-zkI&tRQ(9JtWGm@Z7Ew}wb%MHscb0?`b6o} zkk^dYn%9BXh1Y}Ei`Snwh$rMpc|&+3c}kv^XX4p;Zr<%YKQF|K@lw1TZyfI)-bCI* zyhnL6d2@MB@t)x=<*nka=B?ws%6p6V9&a1(W8NO#0p20rQQk@38QxF4->Ryr8dtTf zYG2i*>XxcLRs1Spm8@!bm9k1-Wvy~o-C0Ffu~nI>aaH$KJybQVYHrnns-;ygR=ra7 zR@K(39aW!J9j-cF^+VOK)iu>EsykG7ukKq7Rm-Z;YF)LxdQ3H4ovbcaPpF<+J*RqM z^@{4X)o)dAtKL(6sQP5}xf)(g^O{aIx7G}*k<}<_Of{aGU`?v#uA0d;vuYOBtg6{i zv$QgNGZ;ZLp-l+6M15*wx^0gEI~O zXxOr0w}$+NbqzHQT@C4mxrP%P&T6>0;hKi;Hr&(jXv3cxHEPtUQNKpgM(RfHM&U+d z8$I0UiAK*idb81vMn@W*YuvbTm&W|Y!y20!dmD3&CpLb(@ruT8G~U_xXyc!oG;eZC z6JZmgiKhwM6`}G_7lDY8q%-X!=OgXPRzk`cc!PO@C|F zwpqVs!X*-T;AN$oN4~A=5w0A(0oht!_9wb(XPe7 z7K#?5TVz^HZSh=-H(Pw#;%v*7E&H|{+0xUJYdN*$(w1+x{JiDQt=hL5+)C3b(CY41 z^IENKwX@af)=gXYX^pnNy>+qmtky5J{;2i0ZJM^}+eXpG+vct|^V+=9W?!3gZQHkn z+nU?cZ&Gv_qo~ z{W@qmggZRcVP%I8JDlm*uA{i4qhr3~ypFGRJk+VCQ{PUSPSH+}bXwhMZ>L{7ckhgL zraDjRysGog&gXBs`KFOKQ8!J#>G_-X-1KXg+Ahj2kuHyQS=Z%Y*9Kh&cC~cPb$zny zyIsG%xx>vvZuZ|i`R3I(@9$RKZD2QRx3S$8b=%(UT=yQ`RoxTaAMgHl_iuZ2>M^oM zxW|khulG29OZ!`f-@@E7_4J^vj4*Vy9YEHAR7=F@WgYZ4b~67cksHwr=i}E2YL+J4E-TM1W~~= zf&*|nSPS0^Z-9Rg4ix%@^Mt!aEk%UrZqa(t_hP=75Y;J^av!TSs_DEFAIW$X+7@BNvbS1|5Kg(G}=vOn{}ZHQ0GY zonpM=O}qwI;gj(XmF<+Ia<1}oq9;KU%ZW28L^W3Rrn;e8r+!SmPt#rF*DTeX(Mq-B zweRR!=xn;Vx-azu^(p-ZL$yI`m~QyY*vH5k*O_=GjcK~+pt+wpY2IjQWHDRjS-!Ty z)^XO&w)VC=Y|Csv*)jVg_5);pGDB{1w04YkEOnfB;?8N#LoUcQ-nGr$)y=rqdm4Kj zp2ePXqm-j&jygIT89j0I-ZA~gON|#!ku1b$duje{| zU}}4&f9C$o;q372yzI|8NA9J3+dP}!UKm)IRQRSy6rUa2V61=arg689yJy_t@#y#k zcU9fxyKB?kz3!fH_tAf;{af@jmB$8zyw0@UIC+?pNKvd}6DK zsfqg@81leV4>ov^dGNzYqDhZW`eSln^7{`79-8&g?+^PQet!x)WzLj~Q-f1Kd_?ld zlaDrflznvXV7 zAAj`m-{*$r?wNAcx$3FzPd)V1uTL{if3iTeVC}-b3ui5A zv?#mi>t`I#Y<^bu?9#3?oUwh>BX0K0p{o)(NH_pA8c=Ox0=(mn+x?|IU zw_R`Ve#i38hwtj%-SQss-rJke&2MZOw&m5Wbz5I~U-tgGZIW$kwG%7y z`+qy|ug@BOHu+$igR?&G_W8mu27K}SA<3cF4&#Tne`)*jz!CqElV7F3`u*sHuUmdS z>ziA?S$a%(?A7DM@sCf8I&t)5;^c4NPCV82)cn)^Prv-#$nUnFah*B(ed_y*KRkT4 z%h~6C6#uyCocY}6KgE9f?fm4QyZpT57uheHe|7%)&2PEi8~;A%!hj1K{?PsL*~Qq! zix*GxT9rTG#S6R%Vu7lQ2Y3^BEt)iG(zHp7rcGP4ZQiVT+m5YUv}oP&ruOYSwr_t^ z=XM>y(XMmno4fVsc5~<6z5Dg+-5U{!M97dUM^($_&0Dr=*``&iHXT~EZq=bfo3?G+ zbm-opL&pvsy7%eY85~{v)b{GpqerjW{{8#(>EB-{1RD~rlpk{OOQT6 zRky9GX~J6ODry||;N(7LmiZ3fv1^QG=xxLTx`{!&Lt=eIWb)4T99 z_bqx`Y)9Ju1s^c9pVtELQr)(uEpG&G&B%InSCUoFz83ubjettBcIomVcOOum?6diW zmGSB4U#NAiTse9fvhbxh_sh<=V+7O4Q;Wt;{NWyY@4g+Q4!cj%Q|I>nH6`ylzvH#Xzf9qz-K=)7H;hm-x<{e1c>ezTMJXEtS)ta?)Wxbygyqp34}hdk)Ne-i&C z;?C{s!gY1~pX=QbX{*|^@LclF^J~`gE#qQeJhSZfhV36c{_A_}+Lu~n0`3vP&6*24 ze`@;lUpNPLaCUB<`I6hU<&%Xz-!DCu>|L1M!`-6L%@wZc6j-P&K2Xp&BEsf@$TXS>6H#`G7WZvg?ocP@}{SV*5Bj+vZjweI%Y2U5v`qA#=2Nx|^{O+9R z_TE)@^5C2M?)c&8&bORhT+!^juJfL-KKJOBh05Q;=b^8#kFMJSUgP(ct(rBpbaTh) zt$PgKy=2}OZh6!3XTy6Z4-2idYh8d z^2_9RYWLem@ai`R-Etj1_k* zFup?{8AJb3tK4NF4Z5x$ko?+7(!gU|-h1xmnQf+wk<6UA1ew?2^s1S|?;n!X-@QG% zeC*_B5u1~A_S{B_XSN%B@@VR{gZl<9{l0no<*W8SPu6W;`&Renos*}}tt_n%bt?V~r;yHA@}@15mpwRX@~SV}-Atg{&yR7cv@d_W@!X!_E7y;o z`Tf30PpkCXs3*V4KK1lE+wjGP3;6e=rv5hnb$)*LOW$tn|FV@&ZP1?h#n$lHuA9Cz zc2MrkgtcITQ*bM4cOS^v|J5B=+AED;fk4X$NvcP!jv*8UhJ2N?!A z2e*tac~bk+`Oc3H{Pg|Z7e);_aI5{TQMz`o41a0P)ZD;%wr60;O^>%9I%ogG@fur$ zAz+qWI-KzX9h-S~9H4TvhgkROzV3Ig;J#>n_o?X@gfCP%bZfVqGqwBp!PD>W_C4|8 z{VfWTw^O5weD1byBf~!YElcL& zG=pB6Jp8x0M|-vKf4*Tw_?59u<}7`@<>d1tjtx3zJ@U1p*TxTO1}>5XC(kdAdRE)> z^rGIbU9a?5y5h-I%bwl8N2C#L6v{`xG0FbfTaSq{s$-|7EgrsO)Ggn2+eI5zuh=6! zZhdmz?_1#uyKZkF>ojceW_om|)nD(;j_RQO?wxO>ud<<^OtTKZ74E%c*_a>GJEoi_ zcHeFuaCYG2eW~ZOD_h>ShyO?WZokViHngyVYVo4t-dzXoR7sN`#kF7kw)5nGbrH|r zMV<%uF8;Fbgpnl7emap^@zyR?_x7Fd$}L`dOK@1`^dW6*W6w2j-*=i!?HTgvJCoYW zI*y~ETSruNJrErIq9^gkEy@n9@wcCO+hf{4qH}6qZCT>o98 zYrnboz^%Q=$n?F^@lD?I>|ecj$%p2=@&PUw z$>Vif^P&H-56H;e^=q~+u-L}8YQ3gUcQU3Ju(ap6vs>0nWIIM5JzXW-Hp=|UsB;5W z9b5C`(gl4t?^`2tDt`X)r>!5IYds)*GPC>b1;>A%?LQj+G4GxF`UusF&m|r{x^hMV zao>O8<%N^Jc=fT@zCN$nye->ujOp-?zrNPmx#!q{2b3$HU;g2)O^vpIPgVc*sOq>s z{=<0Zfd|e{8GxOZ&C{(Mo6EQ}!j3m%MR!UGj-PK55fp%}e|>yBrITPI-L_w`lZ}uN>d+`(^0h z)19WwhW1Cf3k^3kdiRmp1CKv;krzAp?AZ6-DX-rBl{X$wb@qeRYfsMZyI{d1&n}sz zUNdTP-w9($lKiCa1o0~oYVV1bo_X8mjj_)eh|T%2giuCfr zvh_FD#TEsB9oPPOZl!6_ZOM(Zoug)axri=oYC*$34o z@UFoRFWb|xqs7+Huv=FB{lfh{tSjDX_eyQ?LYMu=&iwZN-1&=tox6JK>fzI-YMQQtVc*PCQ`Rqx$Sv(V4{kE&dCK1E(4X`KpIG^0$4SebhK^XakN@_kCUw}k zH+qrRf6tez)~)R~_{f6vqPI`KxaD&U=RJQiJ0d#0$<+Iu3h_*@I(C-qf$DDfT{_)**Z=KrpBi|zDJ%4DwA26Y4`9Rfk z&6dfN=L{VD<$?vTd=-5@{q8RF>H*GkFBG5iZTzEe%iG1~HTb*;5q{&-J?$p0Ch(`0 z7Ef;8epmnb_@+~g8jY1~fbkZ|bh@k6)&5Sa4*zMQ%3u`}eSVfAj!#pX zkk_WZzv|wnU;k#*n$w3n>Q5hAy7ZaoqgHqvvE12#Jp04}@%iRcVjr$NH*;5AaZ=~^ z#%}CB!SD`n8}Pla_21WP?$q#?ljkh_X~tLkj-HtMl6RdwzE#(J_WiEU3Wk4;{F6@0 zmnVJ_68s*!$99_|U!}L$)=tjy@c2COwtZr5#)?rH?6_*=lPjGkm9Kp8<|~`$+Mgv~2U*14A&pI~E{@i%{tnbmq%broc_IU5N8yq@l z9hn*1rB}ykNRQ2ZTScaKu#Y*atog_}_JjA%x=w7HuuZ&t!P516r62fnZCkWE_F8n; z;LpxIv)FfFS!d(RH*w+D*3W+T>q8^sqomtsy~f-+iGO_8xfwq%e{A^6x+6Kb+2s3X zBimDJwyaDIZuZauixD(s^;)^*B5(Jz!QbhCKihJMc$dyWsj zHr1%tC%DYe>(Kq&c?-0yW5L9KMh;K=l7m_ zs?mshdYd|a7P@~S@9fI=cBR&Q@bTyY&n%u^=p9}N#a4ZmYx`gkTRdyls?I3cyx;ww z+fzu7Jzr*f3(u{dHz@e>+O6H=k8>xOX?^ZkU9$2oo14cxY3I6Z`h59UTUHZq+$CKj zPe45%f1qD*=*IC+FMslJdhx_%o$D4p)uqd_N7%Yg`o6?v&9B@WDmS3!;-~))+O+jB literal 0 HcmV?d00001 diff --git a/first version/AccessLocalFunctions.m b/first version/AccessLocalFunctions.m new file mode 100644 index 0000000..1953044 --- /dev/null +++ b/first version/AccessLocalFunctions.m @@ -0,0 +1,12 @@ + + +f = []; +for i = 1:length(fcns), + eval(['f.' char(fcns{i}) ' = fcns{i};']); +end + +%% test function 'FindCentroid' +M = getappdata(hfig,'M'); +gIX = getappdata(hfig,'gIX'); +C_test = f.FindCentroid(gIX,M); +figure;imagesc(C_test) \ No newline at end of file diff --git a/first version/BasicPlotMaps.m b/first version/BasicPlotMaps.m new file mode 100644 index 0000000..9196801 --- /dev/null +++ b/first version/BasicPlotMaps.m @@ -0,0 +1,261 @@ +function BasicPlotMaps(cIX,gIX,M,CInfo,photostate,anat_yx,anat_yz,anat_zx) %(cIX,gIX,cIX_0,M_0,CIF,numK,M) +% M = M_0(cIX_0(cIX),:); +numK = length(unique(gIX)); +%% +dataFR = 1; +figure('Position',[100 50 1350 900]);%,'DeleteFcn',@closefigure_Callback); +h1 = axes('Position',[0.05, 0.06, 0.5, 0.85]); % left ~subplot +BasicDrawClusters(h1,M,gIX,dataFR,numK,photostate); + +h2 = axes('Position',[0.58, 0.06, 0.40, 0.85]); % right ~subplot +DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,'hsv'); +% set(gcf,'PaperUnits', 'inches', 'PaperSize', [12, 6]) + +end + +function BasicDrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite) +pos = get(gca,'Position'); +barratio = 0.02; +numK = double(max(double(numK),double(max(gIX)))); + +% sort traces by index +im = M; +[nLines,nFrames] = size(im); +[~,I] = sort(gIX); +im = im(I,:); + +%% convert imagesc effect into rgb matrix 'RGB' +if dataFR, % is drawing cell-response fluorescence + cmap = gray(64); + im = AutoScaleImage0to1(im); % scaling to min/max 0/1 + minlim = 0; + maxlim = 1; +else % create blue-white-red map, for regression results + cmap = zeros(64,3); + cmap(:,1) = [linspace(0,1,32), linspace(1,1,32)]; + cmap(:,2) = [linspace(0,1,32), linspace(1,0,32)]; + cmap(:,3) = [linspace(1,1,32), linspace(1,0,32)]; + minlim = -1; %min(min(im)); + maxlim = 1; %max(max(im)); +end +RGB = ImageToRGB(im,cmap,minlim,maxlim); % map image matrix to range of colormap + +%% add vertical color code +if exist('clrmap','var'), + if strcmp(clrmap,'jet'), + temp = flipud(jet(numK)); + else % 'hsv' + temp = hsv(round(numK*1.1)); + end +else % 'hsv' + temp = hsv(round(numK*1.1)); +end +cmap2 = vertcat(temp(1:numK,:),[0,0,0]); % extend colormap to include black +bwidth = max(round(nFrames/30),1); + +idx = gIX(I); +ix_div = [find(diff(idx));length(idx)]; + +bars = ones(nLines,bwidth); +if numK>1, + bars(1:ix_div(1),:) = idx(ix_div(1)); + for i = 2:length(ix_div), + % paint color + bars(ix_div(i-1)+1:ix_div(i),:) = idx(ix_div(i)); + end +end +im_bars = reshape(cmap2(bars,:),[size(bars),3]); +% add a white division margin +div = ones(nLines,round(bwidth/2),3); +% put 3 parts together +im = horzcat(RGB,div,im_bars); + +%% plot figure +% h_fig = figure('Position',[100 0 1300 900],'Name',['round ' num2str(i_step)]); %[left, bottom, width, height] +[s1,s2,s3] = size(im); + +hold off; +set(h1,'Position',[pos(1),pos(2)+pos(4)*barratio*3,pos(3),pos(4)*(1-4*barratio)]); +if s1<30, + temp = im; + im = ones(30,s2,s3); + im(1:s1,:,:) = temp; +end +image(im); +set(gca, 'box', 'off') + +hold on; + +% plot cluster division lines +if numK>1, + for i = 1:length(ix_div),% = numK-1, + y = ix_div(i)+0.5; + plot([0.5,s2+0.5],[y,y],'k','Linewidth',0.5); +% plot([1,s2*kx],[y,y],'k','Linewidth',0.5); + end +end + +ylabel(['ROI number: ' num2str(s1)]); +set(gca,'YTick',[],'XTick',[]); +set(gcf,'color',[1 1 1]); + +colormap gray; + +% write in number label +x = s2*1.003; +y_last = 0; +for i = 1:length(ix_div), + % avoid label crowding + margin = 0.015*s1; + y0 = ix_div(i)+0.5; + y = max(y_last+margin,y0); + if ilow_high(2)) = low_high(2); +im = mat2gray(temp); +end + +function RGB = ImageToRGB(im,cmap,minlim,maxlim) +L = size(cmap,1); +ix = round(interp1(linspace(minlim,maxlim,L),1:L,im,'linear','extrap')); +RGB = reshape(cmap(ix,:),[size(ix) 3]); % Make RGB image from scaled. +end + +function DrawBars(pos,barratio,nFrames,barlength,photostate) % horizontal stimulus bar +% pos = get(gca,'Position'); % [0.05, 0.06, 0.5, 0.85] + +% global htop hbottom; +barheight = 100; +stimheight = 2*barheight; + +m1 = 0.3*ones(barheight,length(photostate)); +m2 = m1; % bottom half +x = photostate; + +% 2,3,4,5,12,13,14,15,99 +% 3,1,3,2,4 ,10,11,12, + +if max(x)<=3, + % 0 = all black; 1 = black/white; 2 = white/black; 3 = all white; (4 = all gray;) + % 5 = gray/black; 6 = white/gray; 7 = black/gray; 8 = gray/white. + + % top (~ projection right) + m1(:,x==0 | x==2 | x==5) = 0; % black + m1(:,x==4 | x==6 | x==7) = 0.5; % grey + m1(:,x==1 | x==3 | x==8) = 1; % white + % bottom (~ projection left) + m2(:,x==0 | x==1 | x==7) = 0; + m2(:,x==4 | x==5 | x==8) = 0.5; + m2(:,x==2 | x==3 | x==6) = 1; + + temp = repmat(vertcat(m1,m2),[1 1 3]); % 3 color layers in dim3 +else + %% + % 2 = white/white + % 3 = black/white + % 4 = white/white + % 5 = white/black + % + % 12 = gray + % 13 = forward grating (very slow, more for calibration) + % 14 = rightward grating + % 15 = leftward grating (* I hope I got right/left correct - if you think it's flipped it probably is) + % + % 99 = spontaneous + % top (~ projection right) + + m1(:,x==3) = 0; % black + m1(:,x==2 | x==4 | x==5) = 1; % white + % bottom (~ projection left) + m2(:,x==5) = 0; + m2(:,x==2 | x==3 | x==4) = 1; + + temp = repmat(vertcat(m1,m2),[1 1 3]); % 3 color layers in dim3 + %% + temp(:,x==14,1) = 1; % red + temp(:,x==13,2) = 1; % green + temp(:,x==15,3) = 1; % blue + + % grey + temp(:,x==12,1) = 0.8; + temp(:,x==12,2) = 0.8; + temp(:,x==12,3) = 0.8; + + % yellow + temp(:,x==99,1) = 1; + temp(:,x==99,2) = 1; + temp(:,x==99,3) = 0.6; +end + +n = ceil(nFrames/length(photostate)); % tile, to size or bigger +temp = repmat(temp,1,n); +stimbar = ones(stimheight,barlength,3); +stimbar(:,1:nFrames,:) = temp(:,1:nFrames,:); % crop to size + +% if isempty(htop), +axes('Position',[pos(1),pos(2)+pos(4)*(1-barratio),pos(3),pos(4)*barratio]); +% elseif ~ishandle(htop), +% htop = axes('Position',[pos(1),pos(2)+pos(4)*(1-barratio),pos(3),pos(4)*barratio]); +% else axes(htop) +% end +image(stimbar);axis off;hold on; +% plot top border +plot([1,length(photostate)],[1,1],'k','Linewidth',0.5); + +% if isempty(hbottom), +axes('Position',[pos(1),pos(2)+pos(4)*barratio*2,pos(3),pos(4)*barratio]); +% elseif ~ishandle(hbottom), +% hbottom = axes('Position',[pos(1),pos(2),pos(3),pos(4)*barratio]); +% else axes(hbottom) +% end + +image(stimbar);axis off;hold on; +% plot bottom border +plot([1,length(photostate)],[stimheight,stimheight],'k','Linewidth',0.5); +%% +end diff --git a/Conv_test.m b/first version/Conv_test.m similarity index 100% rename from Conv_test.m rename to first version/Conv_test.m diff --git a/DA_2_Solution.m b/first version/DA_2_Solution.m similarity index 100% rename from DA_2_Solution.m rename to first version/DA_2_Solution.m diff --git a/first version/DrawClusters.m b/first version/DrawClusters.m new file mode 100644 index 0000000..964da88 --- /dev/null +++ b/first version/DrawClusters.m @@ -0,0 +1,210 @@ +function DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite) +pos = get(gca,'Position'); +barratio = 0.03; + +%% Prepare cluster data +% down-sample +displaymax = 1000; +numcell = size(M,1); +if numcell > displaymax, + skip = round(numcell/displaymax); + M = M(1:skip:end,:); + gIX = gIX(1:skip:end,:); +end + +numK = double(max(double(numK),double(max(gIX)))); + +% sort traces by index +im = M; +[nLines,nFrames] = size(im); +[~,I] = sort(gIX); +im = im(I,:); + +%% convert imagesc effect into rgb matrix 'RGB' +if dataFR, % is drawing cell-response fluorescence + cmap = gray(64); + im = AutoScaleImage0to1(im); % scaling to min/max 0/1 + minlim = 0; + maxlim = 1; +else % create blue-white-red map, for regression results + cmap = zeros(64,3); + cmap(:,1) = [linspace(0,1,32), linspace(1,1,32)]; + cmap(:,2) = [linspace(0,1,32), linspace(1,0,32)]; + cmap(:,3) = [linspace(1,1,32), linspace(1,0,32)]; + minlim = -1; %min(min(im)); + maxlim = 1; %max(max(im)); +end +RGB = ImageToRGB(im,cmap,minlim,maxlim); % map image matrix to range of colormap + +%% add vertical color code +if exist('clrmap','var'), + if strcmp(clrmap,'jet'), + temp = flipud(jet(numK)); + else % 'hsv' + temp = hsv(round(numK*1.1)); + end +else % 'hsv' + temp = hsv(round(numK*1.1)); +end +cmap2 = vertcat(temp(1:numK,:),[0,0,0]); % extend colormap to include black +bwidth = max(round(nFrames/30),1); + +idx = gIX(I); +ix_div = [find(diff(idx));length(idx)]; + +bars = ones(nLines,bwidth); +if numK>1, + bars(1:ix_div(1),:) = idx(ix_div(1)); + for i = 2:length(ix_div), + % paint color + bars(ix_div(i-1)+1:ix_div(i),:) = idx(ix_div(i)); + end +end +im_bars = reshape(cmap2(bars,:),[size(bars),3]); +% add a white division margin +div = ones(nLines,round(bwidth/2),3); +% put 3 parts together +im = horzcat(RGB,div,im_bars); + +%% plot figure + +[s1,s2,s3] = size(im); + +hold off; +set(h1,'Position',[pos(1),pos(2)+pos(4)*barratio*3,pos(3),pos(4)*(1-4*barratio)]); +if s1<30, + temp = im; + im = ones(30,s2,s3); + im(1:s1,:,:) = temp; +end +image(im); +set(gca, 'box', 'off') + +hold on; + +% plot cluster division lines +plot([0.5,s2+0.5],[0.5,0.5],'k','Linewidth',0.5); +if numK>1, + for i = 1:length(ix_div),% = numK-1, + y = ix_div(i)+0.5; + plot([0.5,s2+0.5],[y,y],'k','Linewidth',0.5); + end +end + +ylabel(['Number of cells: ' num2str(numcell)]); +set(gca,'YTick',[],'XTick',[]); +set(gcf,'color',[1 1 1]); + +colormap gray; + +% write in number label +x = s2*1.003; +y_last = 0; +for i = 1:length(ix_div), + % avoid label crowding + margin = 0.015*s1; + y0 = ix_div(i)+0.5; + y = max(y_last+margin,y0); + if ilow_high(2)) = low_high(2); +im = mat2gray(temp); +end + +function RGB = ImageToRGB(im,cmap,minlim,maxlim) +L = size(cmap,1); +ix = round(interp1(linspace(minlim,maxlim,L),1:L,im,'linear','extrap')); +RGB = reshape(cmap(ix,:),[size(ix) 3]); % Make RGB image from scaled. +end diff --git a/first version/DrawClustersOnMap_LSh.m b/first version/DrawClustersOnMap_LSh.m new file mode 100644 index 0000000..5bbeb48 --- /dev/null +++ b/first version/DrawClustersOnMap_LSh.m @@ -0,0 +1,137 @@ +function [tot_image, dim_totimage] = DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,clrmap,full) +%% formatting +[s1,s2] = size(cIX); +if s2>s1, + cIX = cIX'; +end +[s1,s2] = size(gIX); +if s2>s1, + gIX = gIX'; +end + +% down-sample +if ~exist('full','var'), + displaymax = 8000; + if length(cIX) > displaymax, + skip = round(length(cIX)/displaymax); + cIX = cIX(1:skip:end,:); + gIX = gIX(1:skip:end,:); + end +end + +% get numK +if exist('numK','var'), + numK = double(max(numK,max(gIX))); +else + numK = double(max(gIX)); +end + +if strcmp(clrmap,'jet'), + temp = flipud(jet(numK)); +else % 'hsv' + temp = hsv(round(numK*1.1)); +end +cmap = temp(1:numK,:); % extend colormap to include black + +anat_YX = anat_yx/4; +anat_YZ = anat_yz/4; +anat_ZX = anat_zx/4; +anat_ZX = flipud(anat_ZX); +dimv_yx = size(anat_YX); +dimv_yz = size(anat_YZ); +dimv_zx = size(anat_ZX); + +k_zres = 20; +anat_yz2=zeros(dimv_yz(1),dimv_yz(2)*k_zres,3); +anat_zx2=zeros(dimv_zx(1)*k_zres,dimv_zx(2),3); +dim_totimage = [dimv_yx(1)+dimv_zx(1)*k_zres+10,dimv_yx(2)+dimv_yz(2)*k_zres+10,3]; +tot_image=ones(dim_totimage); +% tot_image=zeros(dim_totimage); +% tot_image(:,dimv_yx(2)+(1:10),:)=1; + +% find index manipulation vector to darw circle +circle=makeDisk2(7,15); % make mask of filled circle % (7,15) +mask = zeros(dimv_yx(1),dimv_yx(2)); +mask(1:15,1:15) = circle; +ix = find(mask); +cix = sub2ind([dimv_yx(1),dimv_yx(2)],8,8);% 8 +circle_inds = ix - cix; + +yzplane_inds = -5:5; +zxplane_inds = -5*dimv_zx(1):dimv_zx(1):5*dimv_zx(1); + +weight = 0.3-min(length(cIX)/1000/100,0.1); +for j=1:length(cIX) + if ~isempty(CInfo(cIX(j)).center), + %% Y-X + % cinds = sub2ind([dim_y,dim_x],cell_info(cellsIX(j)).center(1),cell_info(cellsIX(j)).center(2)); + cinds=(CInfo(cIX(j)).center(2)-1)*dimv_yx(1)+CInfo(cIX(j)).center(1); % faster equivalent, lin px idx + labelinds=find((cinds+circle_inds)>0 & (cinds+circle_inds)<=dimv_yx(1)*dimv_yx(2)); % within bounds + ix = gIX(j);%find(U==gIX(j));U = 1:numK; + ixs = cinds+circle_inds(labelinds); + anat_YX(ixs) = cmap(ix,1)*weight + anat_YX(ixs)*(1-weight); % R + ixs = cinds+circle_inds(labelinds)+dimv_yx(1)*dimv_yx(2); + anat_YX(ixs) = cmap(ix,2)*weight + anat_YX(ixs)*(1-weight); % G + ixs = cinds+circle_inds(labelinds)+dimv_yx(1)*dimv_yx(2)*2; + anat_YX(ixs) = cmap(ix,3)*weight + anat_YX(ixs)*(1-weight); % B + + % Y-Z + zweight = weight/2; + % cinds = sub2ind([dim_y,dim_z],cell_info(cellsIX(j)).center(1),cell_info(cellsIX(j)).slice); + cinds=(CInfo(cIX(j)).slice-1)*dimv_yz(1)+CInfo(cIX(j)).center(1); + labelinds=find((cinds+yzplane_inds)>0 & (cinds+yzplane_inds)<=dimv_yz(1)*dimv_yz(2)); + ixs = cinds+yzplane_inds(labelinds); + anat_YZ(ixs) = cmap(ix,1)*zweight + anat_YZ(ixs)*(1-zweight); + ixs = cinds+yzplane_inds(labelinds)+dimv_yz(1)*dimv_yz(2); + anat_YZ(ixs) = cmap(ix,2)*zweight + anat_YZ(ixs)*(1-zweight); + ixs = cinds+yzplane_inds(labelinds)+dimv_yz(1)*dimv_yz(2)*2; + anat_YZ(ixs) = cmap(ix,3)*zweight + anat_YZ(ixs)*(1-zweight); + + % Z-X + zweight = weight/2; + % cinds = sub2ind([dim_y,dim_z],cell_info(cellsIX(j)).center(1),cell_info(cellsIX(j)).slice); + cinds=(CInfo(cIX(j)).center(2)-1)*dimv_zx(1) +(CInfo(cIX(j)).slice); + labelinds=find((cinds+zxplane_inds)>0 & (cinds+zxplane_inds)<=dimv_zx(1)*dimv_zx(2)); + ixs = cinds+zxplane_inds(labelinds); + anat_ZX(ixs) = cmap(ix,1)*zweight + anat_ZX(ixs)*(1-zweight); + ixs = cinds+zxplane_inds(labelinds)+dimv_zx(1)*dimv_zx(2); + anat_ZX(ixs) = cmap(ix,2)*zweight + anat_ZX(ixs)*(1-zweight); + ixs = cinds+zxplane_inds(labelinds)+dimv_zx(1)*dimv_zx(2)*2; + anat_ZX(ixs) = cmap(ix,3)*zweight + anat_ZX(ixs)*(1-zweight); + end +end +% rescale (low-res) z dimension +for k=1:3 + anat_yz2(:,:,1) = imresize(anat_YZ(:,:,1), [dimv_yz(1), dimv_yz(2)*k_zres],'nearest'); + anat_yz2(:,:,2) = imresize(anat_YZ(:,:,2), [dimv_yz(1), dimv_yz(2)*k_zres],'nearest'); + anat_yz2(:,:,3) = imresize(anat_YZ(:,:,3), [dimv_yz(1), dimv_yz(2)*k_zres],'nearest'); + + anat_zx2(:,:,1) = imresize(anat_ZX(:,:,1), [dimv_zx(1)*k_zres, dimv_zx(2)],'nearest'); + anat_zx2(:,:,2) = imresize(anat_ZX(:,:,2), [dimv_zx(1)*k_zres, dimv_zx(2)],'nearest'); + anat_zx2(:,:,3) = imresize(anat_ZX(:,:,3), [dimv_zx(1)*k_zres, dimv_zx(2)],'nearest'); +end + +tot_image(dimv_zx(1)*k_zres+11:end,1:dimv_yx(2),:) = anat_YX; +tot_image(dimv_zx(1)*k_zres+11:end,dimv_yx(2)+11:end,:) = anat_yz2; +tot_image(1:dimv_zx(1)*k_zres,1:dimv_zx(2),:) = flipud(anat_zx2); + +tot_image(tot_image(:)>1) = 1; +tot_image(tot_image(:)<0) = 0; + +image(tot_image); +axis image;axis off + +end + +function out = makeDisk2(radius, dim) +center=floor(dim/2)+1; +out=zeros(dim); +for x=1:dim + for y=1:dim + if norm([x,y]-[center,center])<=radius + out(x,y)=1; + end + end +end + +end \ No newline at end of file diff --git a/FFT_test.m b/first version/FFT_test.m similarity index 100% rename from FFT_test.m rename to first version/FFT_test.m diff --git a/first version/GUI_FishExplorer.m b/first version/GUI_FishExplorer.m new file mode 100644 index 0000000..a417875 --- /dev/null +++ b/first version/GUI_FishExplorer.m @@ -0,0 +1,3446 @@ +%%% Interactive app for exploratory analysis of calcium imaging data +% (with stimulus, behavior, and anatomy) + +% Input calcium data: 1 trace per cell/ROI, ~50,000 cells per fish +% load collection of cells from multiple fish, or load full data of single fish individually +% main outputs: GUI plots, clusters saved into .mat, export variables to MATLAB workspace + +% Tip: to see the structure of this code, use 'Ctrl' + 'm' + '=' to collapse all cells. +% UI controls are organized by tabs and then by rows, instructions and +% comments are where they are constructed ('User Interface:' -> function hfig... ->) +% General internal functions are at the end, some specialized .m functions are outside. + +% Written in Matlab R2014b running on Windows 7. + +% - Xiuye Chen (xiuyechen@gmail.com), Engert Lab, Spring 2015 + + +% To start, run the following in separate script or command window: +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% clear all;close all;clc +% global VAR; +% load('VAR_current.mat','VAR'); +% load('CONSTs_current.mat','CONSTs'); +% hfig = GUI_FishExplorer(CONSTs,VAR); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Full datasets are stored as 'CONST_F?.mat' (? = fish number), to load from the GUI + +%% User Interface: +% function hfig = GUI_FishExplorer(CONSTs,VAR,CONST,i_fish) +function [hfig,fcns] = GUI_FishExplorer(data_dir,CONSTs,VAR,flag_script,var_script) +if exist('flag_script','var') + if ~exist('var_script','var') + var_script={}; + end + runscript(flag_script,var_script); + return +end + +%% Make figure +scrn = get(0,'Screensize'); +hfig = figure('Position',[scrn(3)*0.1 scrn(4)*0.05 scrn(3)*0.85 scrn(4)*0.86],...% [50 100 1700 900] + 'Name','GUI_LSh','DeleteFcn',@closefigure_Callback); +hold off; axis off + +%% Folder setup +% directory for full fish data (.mat) +setappdata(hfig,'data_dir',data_dir); + +% copy of VAR files will be saved into this subfolder: +currentfolder = pwd; +arcmatfolder = [currentfolder '\arc mat']; +if ~exist(arcmatfolder, 'dir') + mkdir(arcmatfolder); +end +setappdata(hfig,'arcmatfolder',arcmatfolder); + +%% Pass external variables into appdata (stored with main figure handle) +setappdata(hfig,'CONSTs',CONSTs); +setappdata(hfig,'VAR',VAR); +nFish = length(CONSTs); + +% parameters / constants +setappdata(hfig,'z_res',19.7); % resoltion in z, um per slice +% fpsec = 1.97; % hard-coded in ext function 'GetStimRegressor.m' +% approx fpsec of 2 used in ext function 'DrawCluster.m' + +% cache +bC = []; % Cache for going b-ack (bad abbr) +fC = []; % Cache for going f-orward +bC.cIX = cell(1,1); +bC.gIX = cell(1,1); +bC.numK = cell(1,1); +fC.cIX = cell(1,1); +fC.gIX = cell(1,1); +fC.numK = cell(1,1); +setappdata(hfig,'bCache',bC); +setappdata(hfig,'fCache',fC); + +% initialization +i_fish = 1; +QuickUpdateFish(hfig,i_fish,'init'); + +%% Initialize internal params into appdata +% fish protocol sets (different sets have different parameters) +M_fish_set = [1, 1, 1, 1, 1, 1, 1, 2, 1]; % M = Matrix +setappdata(hfig,'M_fish_set',M_fish_set); + +% thresholds +thres_merge = 0.9; +thres_split = 0.7; +thres_reg = 0.7; +thres_size = 10; +setappdata(hfig,'thres_merge',thres_merge); +setappdata(hfig,'thres_split',thres_split); % for function 'pushbutton_iter_split' +setappdata(hfig,'thres_reg',thres_reg); % regression threshold, ~correlation coeff +setappdata(hfig,'thres_size',thres_size); % min size for clusters + +% variables +% (not sure all these need to be initialized, probably not complete either) +setappdata(hfig,'clrmap','hsv'); +setappdata(hfig,'opID',0); +setappdata(hfig,'rankID',0); +setappdata(hfig,'rankscore',[]); +setappdata(hfig,'isCentroid',0); +setappdata(hfig,'isWkmeans',1); % in autoclustering, with/without kmeans +setappdata(hfig,'isflipstim',0); % flag for flip updown +setappdata(hfig,'regchoice',{1,1}); % regressor choice; first cell,1=stim,2=motor,3=centroid +setappdata(hfig,'isfullfish',0); % no if QuickUpdateFish, yes if LoadFullFish +setappdata(hfig,'isPlotCorrHist',0); % option for regression +setappdata(hfig,'dataFR',1); % left plot data type, 1 for fluo trace and 0 for regression result +setappdata(hfig,'isPlotReg',1); % plot regressor when selecting it +setappdata(hfig,'hierinplace',1); % hier. partitioning, no reordering + + +Class = getappdata(hfig,'Class'); +classID = numel(Class); +setappdata(hfig,'classID',classID); +setappdata(hfig,'classheader','Test:'); +setappdata(hfig,'newclassname',''); + +Cluster = getappdata(hfig,'Cluster'); +clusID = 1; %numel(Cluster); +setappdata(hfig,'clusID',clusID); +setappdata(hfig,'clusheader','Test:'); +setappdata(hfig,'newclusname',''); + +setappdata(hfig,'cIX',Cluster(clusID).cIX); % cell-IndeX +setappdata(hfig,'gIX',Cluster(clusID).gIX); % group-IndeX + +%% Create UI controls +set(gcf,'DefaultUicontrolUnits','normalized'); +set(gcf,'defaultUicontrolBackgroundColor',[1 1 1]); + +% tab group setup +tgroup = uitabgroup('Parent', hfig, 'Position', [0.05,0.88,0.91,0.12]); +numtabs = 5; +tab = cell(1,numtabs); +M_names = {'General','Operations','Regression','Clustering etc.','Saved Clusters'}; +for i = 1:numtabs, + tab{i} = uitab('Parent', tgroup, 'BackgroundColor', [1,1,1], 'Title', M_names{i}); +end + +% grid setup, to help align display elements +rheight = 0.2; +yrow = 0.7:-0.33:0;%0.97:-0.03:0.88; +bwidth = 0.03; +grid = 0:bwidth+0.001:1; + +% various UI element handles ('global' easier than passing around..) +global hback hfwd hclassname hclassmenu hclusgroupmenu hclusmenu hclusname... + hdatamenu hopID hcentroid hdataFR hloadfish hfishnum hstimreg hmotorreg... + hcentroidreg; + +%% UI ----- tab one ----- (General) +i_tab = 1; + +%% UI row 1: File +i_row = 1; +i = 1;n = 0; + +i=i+n; +n=2; % saves 'VAR' to workspace. 'VAR' contains clustering indices (Class and Clusgroup) for all fish +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Quick save',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_save_Callback); + +i=i+n; +n=2; % saves 'VAR' both to workspace and to 'VAR_current.mat' and to arc folder +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Save .mat',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_savemat_Callback); + +i=i+n; +n=2; % plots selected cells on anatomy z-stack, display and save tiff stack in current directory +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Save Zstack',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_writeZstack_Callback); + +i=i+n; +n=2; % make new figure without the GUI components, can save manually from default menu +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Popup plot',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_popupplot_Callback); + +i=i+n; +n=3; % export main working variables to workspace, can customize! +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Export to workspace',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_exporttoworkspace_Callback); + +%% UI row 2: Load +i_row = 2; +i = 1;n = 0; + +i=i+n; +n=2; % this design is underused now... Quick-load only depends on CONSTs, +% which is a minimum collection of clusters from all fish, so you can load +% the program without full single-fish data. eventually can use this +% platform to do things across fish (like after anatomical alignment). +uicontrol('Parent',tab{i_tab},'Style','text','String','Quick-load fish:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; % loads 'CONSTs_current.mat' from current directory +temp = {}; for j = 1:nFish, temp = [temp,{num2str(j)}];end +hfishnum = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',temp,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_fishmenu_Callback); + +i=i+n; +n=2; % loads full single-fish data from CONST_F?.mat +uicontrol('Parent',tab{i_tab},'Style','text','String','Load full fish:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; % these CONST_F?.mat are now saved as uncompressed version 6 .mat, loads faster +temp = {}; for j = 1:nFish, temp = [temp,{num2str(j)}];end +temp = [{'(choose)'},temp]; +hloadfish = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',temp,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_loadfullfishmenu_Callback); + +i=i+n; +n=2; % load different presentations (average, all reps etc), pre-stored in CONST(s) +uicontrol('Parent',tab{i_tab},'Style','text','String','Data type:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=3; +hdatamenu = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',CONSTs{i_fish}.datanames,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_datamenu_Callback); + +i=i+n; +n=4; % only centroids (~mean) of clusters shown on left-side plot, the rest is unchanged +hcentroid = uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Display centroids of clusters',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@checkbox_showcentroids_Callback); + +%% UI ----- tab two ----- (Operations) +i_tab = 2; + +%% UI row 1: Range +i_row = 1; +i = 1;n = 0; + +i=i+n; +n=2; % saves up to 20 steps backwards (datatype/datamenu change does not count) +hback = uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Back',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_back_Callback); + +i=i+n; +n=2; % same, 20 steps forward if applicable +hfwd = uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Forward',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_forward_Callback); + +i=i+n; +n=3; % Choose range of clusters to keep. format: e.g. '1:2,4-6,8:end' +uicontrol('Parent',tab{i_tab},'Style','text','String','Choose cluster range:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_choose_range_Callback); + +i=i+n; +n=2; % Choose range of clusters to exclude. format: e.g. '1:2,4-6,8:end' +uicontrol('Parent',tab{i_tab},'Style','text','String','Exclude:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_exclude_range_Callback); + +i=i+n; +n=1; % Choose range of clusters to fuse/combine into single cluster. format: e.g. '1:2,4-6,8:end' +uicontrol('Parent',tab{i_tab},'Style','text','String','Fuse:',... % (eg 1:2,3-5) + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_fuse_range_Callback); + +i=i+n; +n=2; % can choose range in time, but can't see time axes to decide... resets when choosing datamenu +uicontrol('Parent',tab{i_tab},'Style','text','String','Time-range:',... % (eg 1:2,3-5) + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_t_range_Callback); + +%% UI row 2: Operations +i_row = 2; +i = 1;n = 0; + +i=i+n; +n=2; % operates between the current cell selection and the next (in this order). +uicontrol('Parent',tab{i_tab},'Style','text','String','Set operations:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; % 'setdiff' is current minus next, 'rev setdiff' is next minus current. +n=2; % smartUnion = SmartUnique, cells belonging to 2 clusters goes to the more correlated one +menu = {'(choose)','union','intersect','setdiff','rev setdiff','setxor','smartUnion'}; +hopID = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',menu,'Value',1,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@popup_operations_Callback}); + +i=i+n; +n=2; % rank clusters based on various criteria (to choose) +uicontrol('Parent',tab{i_tab},'Style','text','String','Rank by:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; % 'hier' is the same as default (used after every k-means);'stim-lock' uses std across reps; +n=2; % motor stuff uses the best alignment (by cross-correlation) with the fictive trace; +% L+R is average of L & R; stim-motor is combines 'stim-lock' w 'motor' with arbituary weighting. +menu = {'(choose)','hier.','size','stim-lock','corr',... + 'motor','L motor','R motor','L+R motor','stim-motor'}; +uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',menu,'Value',1,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@popup_ranking_Callback}); + +i=i+n; +n=3; % cluster indices will rank from 1 to number of clusters +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Sqeeze clusters',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_sqeeze_Callback); + +i=i+n; +n=3; % flip the sequenc of clusters, first becomes last +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Flip up-down',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_flipud_Callback); + +i=i+n; +n=3; % switch between 2 colormaps now, jet and a cropped version of hsv (so not all circular) +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Switch colormap',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_clrmap_Callback}); + +%% UI row 3: Anatomy +i_row = 3; +i = 1;n = 0; + +i=i+n; +n=4; % Draw a polygon on anatomy maps to select the cells within those boundaries +uicontrol('Parent',tab{i_tab},'Style','text','String','Draw on anatomy map to crop:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; % Draw on the yx-view (main view) +n=2; % Click to make new vertex, double click to connect to first vertex, +% then optionally drag vertices to reposition, and finally double click again to set +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Draw yx',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_polygon_yx_Callback); + +i=i+n; +n=2; % Draw on yz-view (side projection), same as above +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Draw yz',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_polygon_yz_Callback); + +i=i+n; +n=2; % Draw on zx-view (front projection), same as above +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Draw zx',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_polygon_zx_Callback); + +i=i+n; +n=5; +uicontrol('Parent',tab{i_tab},'Style','text','String','Select all cells within boundaries:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; % selects ALL cells contained in dataset that are within the convex shape defined by current cells +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Convex hull',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_withinConvexHull_Callback); + +%% UI ----- tab three ----- (Regression) +i_tab = 3; + +%% UI row 1: regressor +i_row = 1; % Step 1: +i = 1;n = 0; % Choose one type of regressor here, choice highlighted in yellow + +i=i+n; +n=2; % stimulus regressors, go to 'GetStimRegressor.m' to add/update +uicontrol('Parent',tab{i_tab},'Style','text','String','Stim reg.:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=3; % (updated when loading fish) +menu = {'(choose)',''}; +hstimreg = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',menu,'Value',1,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_getstimreg_Callback); + +i=i+n; % motor regressors from fictive, not yet convolved/adjusted for time lag +n=2; % go to 'GetMotorRegressor.m' to add/update +uicontrol('Parent',tab{i_tab},'Style','text','String','Motor reg.:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; % (unlike stim regressors, names hardcoded, not importet from regressor...) +menu = {'(choose)','right swims','left swims','forward swims','raw right','raw left','raw average'}; +hmotorreg = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',menu,'Value',1,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_getmotorreg_Callback); + +i=i+n; +n=2; % if checked, plot regressor during selection, together with stim and motor +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Plot regressor',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],'Value',1,... + 'Callback',@checkbox_popupplotreg_Callback); + +i=i+n+1; +n=4; % Get centroid (~mean) of selected cluster as regressor +uicontrol('Parent',tab{i_tab},'Style','text','String','Regressor from centroid #:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +hcentroidreg = uicontrol('Parent',tab{i_tab},'Style','edit','String',num2str(1),... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_ctrdID_as_reg_Callback); + +i=i+n; +n=2; % too complicated... chomp up centroid, then order as if stimulus were left/right inverted +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Flip stim',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@checkbox_flipstim_Callback); + +%% UI row 2: regression +i_row = 2; % Step 2: +i = 1;n = 0; % Choose regression, using the regressor chosen above, search in full dataset + +i=i+n; +n=3; +uicontrol('Parent',tab{i_tab},'Style','text','String','Choose regression ->',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; % do regression, show all cells with correlation coeff (with regressor) above threshold +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Corr. threshold:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_thres_regression_Callback); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit','String',num2str(thres_reg),... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_regthres_Callback); + +i=i+n; +n=2; % optionally plot histogram of correlation values for all cells in dataset, visualize cut-off +hdataFR = uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Plot corr. hist',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@checkbox_plotcorrhist_Callback); + +i=i+n; +n=3; % probably not so useful. Show top n cells of highest correlation coeff with regressor +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','top corr., number limit:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_topnum_regression_Callback}); + +i=i+n; +n=1; % specify n for above +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_topCorrNumber_Callback); + +i=i+n+1; % more automatic, do a regression with every centroid, then combine (with 'SmartUnique' +n=4; % i.e. overlapping cells are assigned to the cluster with which the correlation is the highest) +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Regression with all centroids',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_AllCentroidRegression_Callback}); + +i=i+n; % this is a remnant button from a failed experiment, idea was to iterate the regression process +n=2; % until the cluster converges, but most of the time it doesn't... +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','iter.reg','Enable','off',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_IterCentroidRegression_Callback}); + +%% UI row 3: (TBD) +i_row = 3; % old idea: display correlation values of multiple regressors (instead of fluo. trace) +i = 1;n = 0; % and then can cluster that and further manipulate... + +i=i+n; +n=4; % did not implement combination of regressors in this version... but display option is coded (dataFR==0) +uicontrol('Parent',tab{i_tab},'Style','text','String','regressor combos??(TBD)',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=4; % also data storage is coded, 'M' could be loaded from M_0_fluo or M_0_reg (storing reg corr values) +hdataFR = uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Display fluo./regression',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@checkbox_dataFR_Callback); + +%% UI ----- tab four ----- (Clustering etc.) +i_tab = 4; + +%% UI row 1: k-means +i_row = 1; +i = 1;n = 0; + +i=i+n; +n=2; % k-means clustering +uicontrol('Parent',tab{i_tab},'Style','text','String','k-means, k =',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_kmeans_Callback); + +i=i+n; +n=4; % anatomy is added to the fluo trace as new dimensions, and (arbituarily) weighted strongly +uicontrol('Parent',tab{i_tab},'Style','text','String','k-means with anatomy, k =',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_kmeans2_Callback); + +i=i+n; % trying to use Silhouette to evaluate cluster quality, find peak to determine optimal k, +n=3; % then display results with that k. But have not set k-means to replicate (speed concern), can be very noisy +uicontrol('Parent',tab{i_tab},'Style','text','String','Find best k in range:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]) + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_kmeans_elbow_Callback); + +%% UI row 2: Auto-clustering +i_row = 2; +i = 1;n = 0; + +i=i+n; % Adjacent clusters (arranged by hier.) will be merged +n=4; % if correlation between centroids is above merging threshold +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Merge thres. (corr. based)',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_merge_Callback}); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit','String',num2str(thres_merge),... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_mergethres_Callback}); + +i=i+n; % further split initial clusters so that the average within-cluster corr coeff is above thres +n=2; % (not so iterative anymore, but could easily restore) +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Iter. split',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_iter_split}); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit','String',num2str(thres_split),... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_splitthres_Callback}); + +i=i+n; +n=3; % minimal size of cluster, otherwise delete at the end +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Cluster size thres.',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_thressize_Callback}); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit','String',num2str(thres_size),... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_sizethres_Callback}); + +i=i+n+1; % longest script here. Splits clusters and prunes them, to yield only very tight clusters. +n=3; % really pretty results, but takes a while when regressing with every centroid. Read code for details. +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Full Auto-Clustering',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_autoclus_Callback}); + +i=i+n; +n=4; % by default it starts with a k-mean of 20 of the current cells. Could skip that if already clustered. +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','(starting with k-mean)',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],'Value',1,... + 'Callback',@checkbox_wkmeans_Callback); + +%% UI row 3: misc plots +i_row = 3; +i = 1;n = 0; + +i=i+n; % k-means are always ranked like in hierachical clustering ~ optimal leaf order +n=2; % here you can rank them again and plot the dengrogram. +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Hier. plot',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_hierplot_Callback); + +i=i+n; % partitioning based on hier. clustering +n=2; % choose between max cluster numbers... +uicontrol('Parent',tab{i_tab},'Style','text','String','Hier.cut, max n:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_hierpartn_Callback); + +i=i+n; % partitioning based on hier. clustering +n=3; % ...or set correlation value threshold +uicontrol('Parent',tab{i_tab},'Style','text','String','Hier.cut, corr thres:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_hierpartthres_Callback); + +i=i+n; % hier. partition in place, i.e. without rearranging order clusters +n=3; +uicontrol('Parent',tab{i_tab},'Style','checkbox','String','Hier.cut in place',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],'Value',1,... + 'Callback',@checkbox_hierinplace_Callback); + +i=i+n; +n=2; % Plots the correlation between all current clusters as a matrix +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Corr. plot',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_corrplot_Callback}); + +i=i+n; +n=2; % random plots not really worth consolidating, only for 1-click convenience +uicontrol('Parent',tab{i_tab},'Style','text','String','temp. plots:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]) + +i=i+n; +n=3; % e.g. this one looks at the history for the fish presented with 4 B/W stimuli +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','plot 4x4(stim)',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_plot4x4_Callback}); + +%% UI ----- tab five ----- (Saved Clusters) +i_tab = 5; + +%% UI row 1: Class +i_row = 1; % There is not really a difference between Class and Cluster +i = 1;n = 0; % I just like to have 2 layers, so I save bigger sets as Class, smaller things as Cluster +% also now Clusters come in cluster-groups ('Clusgroup'), i.e. multiple groups of Clusters... +% the saving is sort of a pain though, several Update___ internal functions dedicated to this. + +i=i+n; +n=1; % +uicontrol('Parent',tab{i_tab},'Style','text','String','Class:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=4; +temp = MakeMenu({Class.name}); +hclassmenu = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',temp,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_classmenu_Callback); + +i=i+n; +n=1; % just edit and press enter to edit current name +uicontrol('Parent',tab{i_tab},'Style','text','String','Edit:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=3; +hclassname = uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_editclassname_Callback}); +set(hclassname,'String',Class(classID).name); + +i=i+n; +n=2; % save the current cells, with current cluster divisions, as 1 class +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Save class',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_saveclass_Callback); + +i=i+n; +n=1; % not really useful, just to help me organize the Class name +uicontrol('Parent',tab{i_tab},'Style','text','String','Header:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit','String','Test',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_class_header_Callback); + +i=i+n; +n=1; % write down new name for new cluster +uicontrol('Parent',tab{i_tab},'Style','text','String','New:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','edit','String','(blank)',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_newclassname_Callback); + +i=i+n; +n=2; % new class is made when pressing this button +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Make class',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_makeclass_Callback); + +i=i+n; +n=2; % will prompt you to confirm +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Delete Class!',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_delclass_Callback}); + +%% UI row 2: Cluster +i_row = 2; % Virtually the same as Class, except added a ranking option +i = 1;n = 0; + +i=i+n; +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','text','String','Cluster (group):',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +num = length(VAR(i_fish).ClusGroup); +temp = {}; for j = 1:num, temp = [temp,{num2str(j)}];end +hclusgroupmenu = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',temp,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_clusgroupmenu_Callback); + +i=i+n; +n=3; +temp = MakeMenu({Cluster.name}); +hclusmenu = uicontrol('Parent',tab{i_tab},'Style','popupmenu','String',temp,... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@popup_clusmenu_Callback); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','text','String','Edit:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=3; +hclusname = uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_editclusname_Callback}); +set(hclusname,'String',Cluster(clusID).name); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Save cluster',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_saveclus_Callback); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','text','String','Header:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit','String','Test',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_clus_header_Callback); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','text','String','New:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','edit','String','(blank)',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_newclusname_Callback); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Make cluster',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@pushbutton_makeclus_Callback); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','text','String','Set rank:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_setrank_Callback}); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','text','String','Notes:',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@edit_notes_Callback}); + +i=i+n; +n=2; +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','Delete Cluster!',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_delclus_Callback}); + +%% UI row 3: misc +i_row = 3; % continuation, dealing with the Cluster groups +i = 1;n = 0; % (Cluster-group number: number menu before the Cluster-name menu) + +i=i+n; +n=2; % Combines the chosen clusters into one view (can save as 1 class or cluster then) +uicontrol('Parent',tab{i_tab},'Style','text','String','Union(cluster):',... % (eg 1,3-5) + 'Position',[grid(i) yrow(i_row) bwidth*n rheight]); + +i=i+n; +n=1; +uicontrol('Parent',tab{i_tab},'Style','edit',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',@edit_clusUnion_Callback); + +i=i+n; % just adds a new number to the Clustergroup-number menu, +n=3; % and saves current view as the first cluster in the new Clustergroup +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','new Clustergroup',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_newclusgroup_Callback}); + +i=i+n; +n=3; % delete current +uicontrol('Parent',tab{i_tab},'Style','pushbutton','String','del Clustergroup',... + 'Position',[grid(i) yrow(i_row) bwidth*n rheight],... + 'Callback',{@pushbutton_delclusgroup_Callback}); + +%% Load figure + +UpdateClusID(hfig,clusID); + +%% get local function handles + +fcns = localfunctions; + +end + +%% Callback functions for UI elements: + +%% ----- tab one ----- (General) + +%% row 1: File + +function pushbutton_save_Callback(hObject,~) +hfig = getParentFigure(hObject); +i_fish = getappdata(hfig,'i_fish'); +global VAR; +VAR(i_fish).Class = getappdata(hfig,'Class'); +VAR(i_fish).ClusGroup = CurrentClusGroup(hfig); +disp('saved to workspace ''VAR'''); +end + +function pushbutton_savemat_Callback(hObject,~) +disp('saving...'); +hfig = getParentFigure(hObject); +i_fish = getappdata(hfig,'i_fish'); +arcmatfolder = getappdata(hfig,'arcmatfolder'); +global VAR; +VAR(i_fish).Class = getappdata(hfig,'Class'); +VAR(i_fish).ClusGroup = CurrentClusGroup(hfig); +timestamp = datestr(now,'mmddyy_HHMM'); +matname = [timestamp '.mat']; +save(fullfile(arcmatfolder,matname),'VAR','-v6'); +save('VAR_current.mat','VAR','-v6'); +disp('saved both to workspace and .mat'); +end + +function pushbutton_writeZstack_Callback(hObject,~) +disp('preparing z-stack...'); +isMarkAllCells = 1; + +hfig = getParentFigure(hObject); +isfullfish = getappdata(hfig,'isfullfish'); +if ~isfullfish, + errordlg('Load full fish first!'); + return; +end + +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +CInfo = getappdata(hfig,'CInfo'); +ave_stack = getappdata(hfig,'ave_stack'); + +timestamp = datestr(now,'mmddyy_HHMMSS'); +tiffName = ['stack_' timestamp '.tif']; + +U = unique(gIX); +numK = length(U); + +% left half: stack with cells marked; +% right half: original anatomy, or mark all cells + +ave_stack2=zeros(size(ave_stack,1), size(ave_stack,2)*2, size(ave_stack,3) ,3); +nPlanes=size(ave_stack,3); +dimv_yxz=size(ave_stack); +stacklen=numel(ave_stack); + +circle=makeDisk2(10,21); +[r, v]=find(circle); +r=r-11;v=v-11; +circle_inds = r*dimv_yxz(1)+v; +cmap = hsv(numK); +weight = 0.3; + +for i=1:nPlanes, + ave_stack2(:,:,i,:)=repmat(imNormalize99(ave_stack(:,:,i)),[1 2 1 3]); +end + +for j=1:length(cIX) + cinds=(CInfo(cIX(j)).center(2)-1)*dimv_yxz(1)+CInfo(cIX(j)).center(1); + labelinds=find((cinds+circle_inds)>0 & (cinds+circle_inds)<=dimv_yxz(1)*dimv_yxz(2)); + zinds=dimv_yxz(1)*dimv_yxz(2)*2*(CInfo(cIX(j)).slice-1); + ix = find(U==gIX(j)); + ixs = cinds+circle_inds(labelinds)+zinds; + ave_stack2(ixs)=cmap(ix,1)*weight + ave_stack2(ixs)*(1-weight); + ixs = cinds+circle_inds(labelinds)+zinds+stacklen*2; + ave_stack2(ixs)=cmap(ix,2)*weight + ave_stack2(ixs)*(1-weight); + ixs = cinds+circle_inds(labelinds)+zinds+stacklen*4; + ave_stack2(ixs)=cmap(ix,3)*weight + ave_stack2(ixs)*(1-weight); +end +if isMarkAllCells, + clr = [0,1,0]; + numcell = getappdata(hfig,'numcell'); + for j=1:numcell, + shift = dimv_yxz(1)*dimv_yxz(2); + cinds=(CInfo(j).center(2)-1)*dimv_yxz(1)+CInfo(j).center(1); + labelinds=find((cinds+circle_inds)>0 & (cinds+circle_inds)<=dimv_yxz(1)*dimv_yxz(2)); + zinds=dimv_yxz(1)*dimv_yxz(2)*2*(CInfo(j).slice-1); + ixs = cinds+circle_inds(labelinds)+zinds + shift; + ave_stack2(ixs)=clr(1)*weight + ave_stack2(ixs)*(1-weight); + ixs = cinds+circle_inds(labelinds)+zinds+stacklen*2 + shift; + ave_stack2(ixs)=clr(2)*weight + ave_stack2(ixs)*(1-weight); + ixs = cinds+circle_inds(labelinds)+zinds+stacklen*4 + shift; + ave_stack2(ixs)=clr(3)*weight + ave_stack2(ixs)*(1-weight); + end +end +h = figure; +for i_plane = 1:nPlanes, + im = squeeze(ave_stack2(:,:,i_plane,:)); + image(im); + % save tiff + if (i_plane == 1) + imwrite(im, tiffName, 'compression','none','writemode','overwrite') + else + imwrite(im, tiffName, 'compression','none','writemode','append') + end + pause(0.2) +end +close(h) +end + +function out = makeDisk2(radius, dim) +center=floor(dim/2)+1; +out=zeros(dim); +for x=1:dim + for y=1:dim + if norm([x,y]-[center,center])<=radius + out(x,y)=1; + end + end +end +end + +function pushbutton_popupplot_Callback(hObject,~) +hfig = getParentFigure(hObject); + +% same as function RefreshFigure(hfig), except new axes not global + +% figure('Position',[50,100,853,512]); % right plot size: 2048x1706 +% h1 = axes('Position',[0, 0, 0.5, 1]); % left ~subplot +% h2 = axes('Position',[0.50, 0, 0.5, 1]); % right ~subplot +figure('Position',[50,100,1600,900]); +h1 = axes('Position',[0.05, 0.03, 0.53, 0.94]); % left ~subplot +h2 = axes('Position',[0.61, 0.03, 0.35, 0.94]); % right ~subplot + +CInfo = getappdata(hfig,'CInfo'); +M = getappdata(hfig,'M'); +dataFR = getappdata(hfig,'dataFR'); +fictive = getappdata(hfig,'fictive'); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); + +numK = getappdata(hfig,'numK'); +stim = getappdata(hfig,'stim'); +anat_yx = getappdata(hfig,'anat_yx'); +anat_yz = getappdata(hfig,'anat_yz'); +anat_zx = getappdata(hfig,'anat_zx'); +isCentroid = getappdata(hfig,'isCentroid'); +clrmap = getappdata(hfig,'clrmap'); +rankscore = getappdata(hfig,'rankscore'); +rankID = getappdata(hfig,'rankID'); + +iswrite = (rankID>=2); + +% left subplot +axes(h1); +if isCentroid, + [C,~] = FindCentroid(gIX,M); + DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); +else + DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); +end + +% right subplot +axes(h2); +DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,clrmap); + +end + +function pushbutton_exporttoworkspace_Callback(hObject,~) +hfig = getParentFigure(hObject); +assignin('base', 'M', getappdata(hfig,'M')); +assignin('base', 'cIX', getappdata(hfig,'cIX')); +assignin('base', 'gIX', getappdata(hfig,'gIX')); +assignin('base', 'numK', getappdata(hfig,'numK')); +assignin('base', 'stim', getappdata(hfig,'stim')); +assignin('base', 'fictive', getappdata(hfig,'fictive')); +assignin('base', 'M_0_fluo', getappdata(hfig,'M_0_fluo')); +end + +%% row 2: Load + +function popup_fishmenu_Callback(hObject,~) +new_i_fish = get(hObject,'Value'); +hfig = getParentFigure(hObject); +QuickUpdateFish(hfig,new_i_fish); +end + +function QuickUpdateFish(hfig,new_i_fish,init) %#ok +CONSTs = getappdata(hfig,'CONSTs'); +setappdata(hfig,'isfullfish',0); + +% load all fields from CONSTs, with names preserved +names = fieldnames(CONSTs{new_i_fish}); % cell of strings +for i = 1:length(names), + setappdata(hfig,names{i},eval(['CONSTs{new_i_fish}.',names{i}])); +end + +% recontruct the 3 big matrices from CIX to original size (numcell) +CIX = CONSTs{new_i_fish}.CIX; +numcell = CONSTs{new_i_fish}.numcell; + +temp = CONSTs{new_i_fish}.CRAZ; +CRAZ = zeros(numcell,size(temp,2)); +CRAZ(CIX,:) = temp; + +temp = CONSTs{new_i_fish}.CRZt; +CRZt = zeros(numcell,size(temp,2)); +CRZt(CIX,:) = temp; + +temp = CONSTs{new_i_fish}.CInfo; +CInfo(numcell).center = ''; +CInfo(numcell).area = ''; +CInfo(numcell).slice = ''; +CInfo(numcell).inds = ''; +CInfo(numcell).y_minmax = ''; +CInfo(numcell).x_minmax = ''; +CInfo(CIX) = temp; + +setappdata(hfig,'CRAZ',CRAZ); % Matrix array, misc collection +setappdata(hfig,'CRZt',CRZt); % Matrix array, misc collection +setappdata(hfig,'CInfo',CInfo); % Matrix array, misc collection + +UpdateFishData(hfig,new_i_fish); +if ~exist('init','var'), + UpdateFishDisplay(hfig,new_i_fish); +end +end + +function UpdateFishData(hfig,new_i_fish) +UpdateDatamenu_Direct(hfig,1); + +% save ClusGroup before updating, if applicable +clusgroupID = getappdata(hfig,'clusgroupID'); +if ~isempty(clusgroupID), + new_clusgroupID = 1; + UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID,'norefresh'); % to save ClusGroup +end +% set new i_fish after UpdateClusGroupID, which saves into old 'i_fish' +setappdata(hfig,'i_fish',new_i_fish); + +% load from VAR +global VAR; +Class = VAR(new_i_fish).Class; % actually collection of classes, each class can have many groups +setappdata(hfig,'Class',Class); + +ClusGroup = VAR(new_i_fish).ClusGroup; % group of 'Cluster', i.e. 2nd level collections +setappdata(hfig,'ClusGroup',ClusGroup); + +clusgroupID = 1; +setappdata(hfig,'clusgroupID',clusgroupID); + +Cluster = ClusGroup{clusgroupID};% collection of clusters, 'Cluster' structurally same as 'Class' +setappdata(hfig,'Cluster',Cluster); +end + +function UpdateFishDisplay(hfig,new_i_fish) +stim = getappdata(hfig,'stim'); +M_fish_set = getappdata(hfig,'M_fish_set'); + +fishset = M_fish_set(new_i_fish); +[~, names] = GetStimRegressor(stim,fishset); +global hstimreg; +set(hstimreg,'String',['(choose)',(names)]); + +% update display +UpdateClassID(hfig,1,'norefresh'); +clusgroupID = 1; +new_clusgroupID = 1; +UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID); % to display new menu +end + +function popup_loadfullfishmenu_Callback(hObject,~) +new_i_fish = get(hObject,'Value')-1; +if new_i_fish>0, + hfig = getParentFigure(hObject); + LoadFullFish(hfig,new_i_fish); +end +global hfishnum; +set(hfishnum,'Value',new_i_fish); +end + +function LoadFullFish(hfig,new_i_fish,CONST) +data_dir=getappdata(hfig,'data_dir'); +if ~exist('CONST','var'), + disp(['loading fish #' num2str(new_i_fish) '...']); + tic + load(fullfile(data_dir,['CONST_F' num2str(new_i_fish) '.mat']),'CONST'); + toc +end +setappdata(hfig,'isfullfish',1); + +% load all fields from CONST, with names preserved +names = fieldnames(CONST); % cell of strings +for i = 1:length(names), + setappdata(hfig,names{i},eval(['CONST.',names{i}])); +end +setappdata(hfig,'numcell',length(CONST.CInfo)); + +UpdateFishData(hfig,new_i_fish); +UpdateFishDisplay(hfig,new_i_fish); +end + +function popup_datamenu_Callback(hObject,~) +ID = get(hObject,'Value'); +hfig = getParentFigure(hObject); +UpdateDatamenu_Direct(hfig,ID); +SetDataFR(hfig,1); % also set M (without updating cIX gIX) +RefreshFigure(hfig); +end + +function UpdateDatamenu_Direct(hfig,ID) +CRAZ = getappdata(hfig,'CRAZ'); +CRZt = getappdata(hfig,'CRZt'); +FcAvr = getappdata(hfig,'FcAvr'); +Fc = getappdata(hfig,'Fc'); +datanames = getappdata(hfig,'datanames'); +tlists = getappdata(hfig,'tlists'); +photostate = getappdata(hfig,'photostate'); + +%% SWITCH LEFT/RIGHT DIRECTION AGAIN +FcAvr = vertcat(FcAvr(2,:),FcAvr(1,:),FcAvr(3:end,:)); +Fc = vertcat(Fc(2,:),Fc(1,:),Fc(3:end,:)); +% now 1=left, 2=right, 3=forward, 4 = raw left, 5 = raw right + +%% +if ID == 1, + M_0 = CRAZ; + fictive = FcAvr; +elseif ID == 2, + M_0 = CRZt; + fictive = Fc; +else + IX = tlists{ID}; + M_0 = CRZt(:,IX); + temp = Fc(:,IX); + % normalize fictive channels, in 2 sets + for k = 1:3,%size(F,1), + m = temp(k,:); + temp(k,:) = (m-min(m))/(max(m)-min(m)); + end + m = temp(4:5,:); + temp(4:5,:) = (m-min(min(m)))/(max(max(m))-min(min(m))); + fictive = temp; +end +% stim from photostate +stim = photostate(tlists{ID}); + +setappdata(hfig,'M_0_fluo',M_0); +setappdata(hfig,'fictive',fictive); +setappdata(hfig,'fictive_0',fictive); +setappdata(hfig,'dataname',datanames(ID)); +setappdata(hfig,'stim',stim); + +global hdatamenu; +if ~isempty(hdatamenu), % before GUI initialization + % 'global' remnant from previous run: hdatamenu is not empty but is invalid object + if isvalid(hdatamenu), + set(hdatamenu,'String',datanames); + set(hdatamenu,'Value',ID); + end +end +end + +function checkbox_showcentroids_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isCentroid',get(hObject,'Value')); +RefreshFigure(hfig); +end + +%% ----- tab two ----- (Operations) + +%% row 1: Range + +function pushbutton_back_Callback(hObject,~) +global hback hfwd; +hfig = getParentFigure(hObject); +bC = getappdata(hfig,'bCache'); +fC = getappdata(hfig,'fCache'); + +if ~isempty(bC.cIX{1}), + % set last into forward cache + fC.cIX = [getappdata(hfig,'cIX'),fC.cIX]; + fC.gIX = [getappdata(hfig,'gIX'),fC.gIX]; + fC.numK = [getappdata(hfig,'numK'),fC.numK]; + % retrieve + cIX = bC.cIX{1}; + gIX = bC.gIX{1}; + numK = bC.numK{1}; + bC.cIX(1) = []; + bC.gIX(1) = []; + bC.numK(1) = []; + % set M + M_0 = GetM_0(hfig); + M = M_0(cIX,:); + % save + setappdata(hfig,'M',M); + setappdata(hfig,'bCache',bC); + setappdata(hfig,'fCache',fC); + setappdata(hfig,'cIX',cIX); + setappdata(hfig,'gIX',gIX); + setappdata(hfig,'numK',numK); + % handle rankID: >=2 means write numbers as text next to colorbar + setappdata(hfig,'rankID',0); + % finish + disp('back (from cache)') + RefreshFigure(hfig); + set(hfwd,'enable','on'); +else % nothing to retrieve + set(hback,'enable','off'); +end +end + +function pushbutton_forward_Callback(hObject,~) +global hback hfwd; +hfig = getParentFigure(hObject); +bC = getappdata(hfig,'bCache'); +fC = getappdata(hfig,'fCache'); + +if ~isempty(fC.cIX{1}), + % set last into (backward) cache + bC.cIX = [getappdata(hfig,'cIX'),bC.cIX]; + bC.gIX = [getappdata(hfig,'gIX'),bC.gIX]; + bC.numK = [getappdata(hfig,'numK'),bC.numK]; + % retrieve + cIX = fC.cIX{1}; + gIX = fC.gIX{1}; + numK = fC.numK{1}; + fC.cIX(1) = []; + fC.gIX(1) = []; + fC.numK(1) = []; + % set M + M_0 = GetM_0(hfig); + M = M_0(cIX,:); + % save + setappdata(hfig,'M',M); + setappdata(hfig,'bCache',bC); + setappdata(hfig,'fCache',fC); + setappdata(hfig,'cIX',cIX); + setappdata(hfig,'gIX',gIX); + setappdata(hfig,'numK',numK); + % handle rankID: >=2 means write numbers as text next to colorbar + setappdata(hfig,'rankID',0); + % finish + disp('forward (from cache)') + RefreshFigure(hfig); + set(hback,'enable','on'); +else + set(hfwd,'enable','off'); +end +end + +function edit_choose_range_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +% get/format range +str = get(hObject,'String'); +if ~isempty(str), + str = strrep(str,'end',num2str(max(gIX))); + range = ParseRange(str); + % update indices + tempI = []; + for i = range, + tempI = [tempI;find(gIX==i)]; + end + cIX = cIX(tempI); + gIX = gIX(tempI); + UpdateIndices(hfig,cIX,gIX); + RefreshFigure(hfig); +end +end + +function range = ParseRange(str) +str = strrep(str,':','-'); % e.g. str= '1,3,5:8'; +C = textscan(str,'%d','delimiter',','); +m = C{:}; +range = []; +for i = 1:length(m), + if m(i)>0, + range = [range,m(i)]; %#ok + else % have '-'sign, + range = [range,m(i-1)+1:-m(i)]; %#ok + end +end +end + +function edit_exclude_range_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +% get/format range +str = get(hObject,'String'); +if ~isempty(str), + str = strrep(str,'end',num2str(max(gIX))); + range = ParseRange(str); + range = setdiff(unique(gIX),range)'; + % update indices + tempI = []; + for i = range, + tempI = [tempI;find(gIX==i)]; + end + cIX = cIX(tempI); + gIX = gIX(tempI); + UpdateIndices(hfig,cIX,gIX); + RefreshFigure(hfig); +end +end + +function edit_fuse_range_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +% get/format range +str = get(hObject,'String'); +if ~isempty(str), + str = strrep(str,'end',num2str(max(gIX))); + range = ParseRange(str); + % update indices + tempI = []; + for i = range, + tempI = [tempI;find(gIX==i)]; + end + gIX(tempI) = gIX(tempI(1)); + [gIX, numK] = SqueezeGroupIX(gIX); + UpdateIndices(hfig,cIX,gIX,numK); + RefreshFigure(hfig); +end +end + +function edit_t_range_Callback(hObject,~) +hfig = getParentFigure(hObject); +M = getappdata(hfig,'M'); + +% get/format range +str = get(hObject,'String'); +if ~isempty(str), + str = strrep(str,'end',num2str(size(M,2))); + range = ParseRange(str); + + UpdateTRange(hfig,range) + RefreshFigure(hfig); +end +end + +function UpdateTRange(hfig,range) +M_0 = GetM_0(hfig); +cIX = getappdata(hfig,'cIX'); +fictive_0 = getappdata(hfig,'fictive_0'); +% photostate_0 = getappdata(hfig,'photostate_0'); + +% set M +M = M_0(cIX,range); +setappdata(hfig,'M',M); +% set fictive +fictive = fictive_0(:,range); +setappdata(hfig,'fictive',fictive); +% % set photostate +% photostate = photostate_0(:,range); +% setappdata(hfig,'photostate',photostate); + +end + +%% row 2: Operations + +function popup_operations_Callback(hObject,~) +opID = get(hObject,'Value') - 1; +hfig = getParentFigure(hObject); +setappdata(hfig,'opID',opID); +% highlight UI +global hopID; +set(hopID,'BackgroundColor',[1,1,0.8]); +end + +function popup_ranking_Callback(hObject,~) +% menu = {'(ranking)','hier.','size','stim-lock','corr','noise'}; +rankID = get(hObject,'Value') - 1; +hfig = getParentFigure(hObject); +setappdata(hfig,'rankID',rankID); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +M = getappdata(hfig,'M'); + +[gIX, numU] = SqueezeGroupIX(gIX); +switch rankID, + case 1, + disp('hier. (default)'); + [gIX, numU] = HierClus(M,gIX); + case 2, + disp('size'); + H = zeros(numU,1); + for i = 1:numU, + H(i) = length(find(gIX==i)); + end + [gIX,rankscore] = SortH(H,gIX,numU,'descend'); + case 3, + disp('stim-lock'); + [gIX,rankscore] = RankByStimLock_Direct(hfig,cIX,gIX,M,numU); + case 4, + disp('corr'); + [~,D] = FindCentroid(gIX,M); + [gIX,rankscore] = SortH(D,gIX,numU); + rankscore = 1-rankscore; + case 5, + disp('motor'); + [gIX,rankscore] = RankByMotorStim_Direct(hfig,gIX,M,numU,1); + case 6, + disp('motor'); + [gIX,rankscore] = RankByMotorStim_Direct(hfig,gIX,M,numU,2); + case 7, + disp('motor'); + [gIX,rankscore] = RankByMotorStim_Direct(hfig,gIX,M,numU,3); + case 8, + disp('motor'); + [gIX,rankscore] = RankByMotorStim_Direct(hfig,gIX,M,numU,4); + case 9, + disp('stim-motor'); + [~,rankscore1] = RankByStimLock_Direct(hfig,cIX,gIX,M,numU); + [~,rankscore2] = RankByMotorStim_Direct(hfig,gIX,M,numU,1); + rankscore = rankscore1 - rankscore2; +end +if rankID>1, + setappdata(hfig,'rankscore',round(rankscore*100)/100); + setappdata(hfig,'clrmap','jet'); +else + setappdata(hfig,'clrmap','hsv'); +end +UpdateIndices(hfig,cIX,gIX,numU); +RefreshFigure(hfig); +disp('ranking complete'); +end + +function [gIX,rankscore] = RankByStimLock_Direct(hfig,cIX,gIX,M,numU) +periods = getappdata(hfig,'periods'); + +if length(periods)==1, + period = periods{1}; + [C,~] = FindCentroid(gIX,M); +else % i_fish>=8, + tlists = getappdata(hfig,'tlists'); + CRZt = getappdata(hfig,'CRZt'); + IX = tlists{6}; % ptomr_circ + M_ = CRZt(cIX,IX); + [C,~] = FindCentroid(gIX,M_); + periods = getappdata(hfig,'periods'); + period = periods{1}+periods{2}; +end +% C2 = zscore(C,0,2); +% C_3D = reshape(C2,size(C2,1),period,[]); +C_3D_0 = reshape(C,size(C,1),period,[]); +C_3D = zscore(C_3D_0,0,2); + +H = nanmean(nanstd(C_3D,0,3),2); +[gIX,rankscore] = SortH(H,gIX,numU); +end + +function [gIX,rankscore] = RankByMotorStim_Direct(hfig,gIX,M,numU,option) +C = FindCentroid(gIX,M); +fictive = getappdata(hfig,'fictive'); + +reg = zeros(3,length(fictive)); +reg(1,:) = fictive(5,:); % Left +reg(2,:) = fictive(4,:); % Right +reg(3,:) = mean(fictive(4:5,:)); +H = zeros(numU,1); +shift = zeros(numU,1); +a = zeros(1,3); +I = zeros(1,3); +for i = 1:numU, + switch option, + case 1, + for j = 1:3, + [a(j),I(j)] = max(abs(xcorr(C(i,:),reg(j,:),'coeff'))); + end + [H(i),jmax] = max(a); + shift(i) = I(jmax) - length(fictive); + case 2, + [H(i),I] = max(xcorr(C(i,:),reg(1,:),'coeff')); + shift(i) = I - length(fictive); + case 3, + [H(i),I] = max(xcorr(C(i,:),reg(2,:),'coeff')); + shift(i) = I - length(fictive); + case 4, + [H(i),I] = max(xcorr(C(i,:),reg(3,:),'coeff')); + shift(i) = I - length(fictive); + end +end +[gIX,rankscore] = SortH(H,gIX,numU,'descend'); +assignin('base', 'shift', shift); +end + +function [gIX,B] = SortH(H,gIX,numU,descend) % new gIX is sorted based on H, size(H)=[numU,1]; +if exist('descend','var'), + [B,I] = sort(H,'descend'); +else + [B,I] = sort(H); +end +gIX_last = gIX; +for i = 1:numU, + gIX(gIX_last==I(i)) = i; +end +end + +function pushbutton_sqeeze_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +[gIX, numU] = SqueezeGroupIX(gIX); +UpdateIndices(hfig,cIX,gIX,numU); +RefreshFigure(hfig); +end + +function pushbutton_flipud_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); + +U = unique(gIX); +U_ = flipud(U); + +gIX_ = gIX; +for i = 1:length(U), + gIX_(gIX==U(i)) = U_(i); +end + +UpdateIndices(hfig,cIX,gIX_); +RefreshFigure(hfig); +end + +function pushbutton_clrmap_Callback(hObject,~) +hfig = getParentFigure(hObject); +clrmap = getappdata(hfig,'clrmap'); +if strcmp(clrmap,'jet'), + setappdata(hfig,'clrmap','hsv'); +else + setappdata(hfig,'clrmap','jet'); +end +RefreshFigure(hfig); +end + +%% row 3: Anatomy + +function pushbutton_polygon_yx_Callback(hObject,~) +k_zres = 20; + +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +% M = getappdata(hfig,'M'); +CInfo = getappdata(hfig,'CInfo'); +numcell = getappdata(hfig,'numcell'); +anat_yx = getappdata(hfig,'anat_yx'); +anat_zx = getappdata(hfig,'anat_zx'); +dimv_yx = size(anat_yx); +dimv_zx = size(anat_zx); +isfullfish = getappdata(hfig,'isfullfish'); + +h_poly_yx = impoly; +wait(h_poly_yx); % double click to finalize position! +% update finalized polygon in bright color +setColor(h_poly_yx,[0 1 1]); + +if isfullfish, + IJs = reshape([CInfo.center],2,[])'; +else % Matlab can't handle CInfo with all the empty entries -> manual padding + IJs_ = reshape([CInfo(cIX).center],2,[])'; + IJs = ones(numcell,2); + IJs(cIX,:) = IJs_; +end +A = sub2ind(dimv_yx(1:2),IJs(:,1),IJs(:,2)); +MaskArray = createMask(h_poly_yx); +MaskArray(1:dimv_zx*k_zres+10,:) = []; +B = find(MaskArray); % find indices of pixels within ROI + +temp = find(ismember(A,B)); +[IX,ia,~] = intersect(cIX,temp); + +cIX = IX; +gIX = gIX(ia); + +UpdateIndices(hfig,cIX,gIX); +RefreshFigure(hfig); +end + +function pushbutton_polygon_yz_Callback(hObject,~) +k_zres = 20; + +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +% M = getappdata(hfig,'M'); +CInfo = getappdata(hfig,'CInfo'); +numcell = getappdata(hfig,'numcell'); +anat_yx = getappdata(hfig,'anat_yx'); +anat_yz = getappdata(hfig,'anat_yz'); +anat_zx = getappdata(hfig,'anat_zx'); +dimv_yx = size(anat_yx); +dimv_yz = size(anat_yz); +dimv_zx = size(anat_zx); +isfullfish = getappdata(hfig,'isfullfish'); + +h_poly_z = impoly; +wait(h_poly_z); % double click to finalize position! +% update finalized polygon in bright color +setColor(h_poly_z,[0 1 1]); + +if isfullfish, + IJs = reshape([CInfo.center],2,[])'; + Zs = [CInfo.slice]'; +else % Matlab can't handle CInfo with all the empty entries -> manual padding + IJs_ = reshape([CInfo(cIX).center],2,[])'; + IJs = ones(numcell,2); + IJs(cIX,:) = IJs_; + Zs_ = [CInfo(cIX).slice]'; + Zs = ones(numcell,1); + Zs(cIX) = Zs_; +end +A = sub2ind(dimv_yz(1:2),IJs(:,1),Zs); + +MaskArray = createMask(h_poly_z); +MaskArray(1:dimv_zx*k_zres+10,:) = []; +MaskArray(:,1:dimv_yx(2)+10) = []; +zMaskArray = imresize(MaskArray,[dimv_yz(1),dimv_yz(2)]); +B = find(zMaskArray); % find indices of pixels within ROI + +temp = find(ismember(A,B)); +[IX,ia,~] = intersect(cIX,temp); + +cIX = IX; +gIX = gIX(ia); + +UpdateIndices(hfig,cIX,gIX); +RefreshFigure(hfig); +end + +function pushbutton_polygon_zx_Callback(hObject,~) +k_zres = 20; + +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +% M = getappdata(hfig,'M'); +CInfo = getappdata(hfig,'CInfo'); +numcell = getappdata(hfig,'numcell'); +% anat_yx = getappdata(hfig,'anat_yx'); +anat_zx = getappdata(hfig,'anat_zx'); +% dimv_yx = size(anat_yx); +dimv_zx = size(anat_zx); +isfullfish = getappdata(hfig,'isfullfish'); + +h_poly_z = impoly; +wait(h_poly_z); % double click to finalize position! +% update finalized polygon in bright color +setColor(h_poly_z,[0 1 1]); + +if isfullfish, + IJs = reshape([CInfo.center],2,[])'; + Zs = [CInfo.slice]'; +else % Matlab can't handle CInfo with all the empty entries -> manual padding + IJs_ = reshape([CInfo(cIX).center],2,[])'; + IJs = ones(numcell,2); + IJs(cIX,:) = IJs_; + Zs_ = [CInfo(cIX).slice]'; + Zs = ones(numcell,1); + Zs(cIX) = Zs_; +end +A = sub2ind(dimv_zx(1:2),Zs,IJs(:,2)); + +MaskArray = createMask(h_poly_z); + +MaskArray(dimv_zx(1)*k_zres+1:end,:) = []; +MaskArray(:,dimv_zx(2)+1:end) = []; +zMaskArray = imresize(MaskArray,[dimv_zx(1),dimv_zx(2)]); +zMaskArray = flipud(zMaskArray); % careful! +B = find(zMaskArray); % find indices of pixels within ROI + +temp = find(ismember(A,B)); +[IX,ia,~] = intersect(cIX,temp); + +cIX = IX; +gIX = gIX(ia); + +UpdateIndices(hfig,cIX,gIX); +RefreshFigure(hfig); +end + +function pushbutton_withinConvexHull_Callback(hObject,~) % only works with full load +disp('find points within boundary...'); +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +CInfo = getappdata(hfig,'CInfo'); +IJs = reshape([CInfo.center],2,[])'; +Zs = [CInfo.slice]'; +% point sets +xyz_all = horzcat(IJs, Zs); +xyz_cIX = horzcat(IJs(cIX,:), Zs(cIX)); + +tri = delaunayn(xyz_cIX); % Generate delaunay triangulization +t = tsearchn(xyz_cIX, tri, xyz_all); % Determine which triangle point is within +I_inside = ~isnan(t); + +cIX_1 = cIX; +gIX_1 = gIX; +cIX_2 = setdiff(find(I_inside),cIX_1); +gIX_2 = ones(length(cIX_2),1)*(max(gIX_1)+1); +cIX = [cIX_1;cIX_2]; +gIX = [gIX_1;gIX_2]; + +UpdateIndices(hfig,cIX,gIX,length(unique(gIX))); +RefreshFigure(hfig); +disp('search complete'); +end + +%% ----- tab three ----- (Regression) + +%% row 1: regressor + +function popup_getstimreg_Callback(hObject,~) +i_reg = get(hObject,'Value')-1; +hfig = getParentFigure(hObject); +if i_reg==0, + return; +end +setappdata(hfig,'regchoice',{1,i_reg}); +% highlight the choice (yellow) +global hstimreg hmotorreg hcentroidreg; +set(hstimreg,'BackgroundColor',[1,1,0.8]); % yellow +set(hmotorreg,'BackgroundColor',[1,1,1]); +set(hcentroidreg,'BackgroundColor',[1,1,1]); + +isPlotReg = getappdata(hfig,'isPlotReg'); +if isPlotReg, + PlotRegWithStimMotor(hfig); +end +end + +function PlotRegWithStimMotor(hfig) +stim = getappdata(hfig,'stim'); +fictive = getappdata(hfig,'fictive'); +regressor = GetRegressor(hfig); +% plot regressor +figure; +halfbarheight = 1; +stimbar = GetStimBar(halfbarheight,stim); +reg = imNormalize99(regressor); +regbar = repmat(reg,[1,1,3]); + +subplot(3,1,1);image(stimbar); axis off; title('stimulus'); +subplot(3,1,2);image(regbar); axis off; title('regressor'); +subplot(3,1,3);imagesc(fictive);colormap hot; axis off; title('motor'); +end + +function out=imNormalize99(im) +im=double(im); +temp=sort(im(:),'descend'); +th1=temp(round(length(im(:))/100)); +th2=min(im(:)); + +out=(im-th2)/(th1-th2); +out(out>1)=1; +end + +function popup_getmotorreg_Callback(hObject,~) +hfig = getParentFigure(hObject); +i_reg = get(hObject,'Value')-1; +% {'left swims','right swims','forward swims','raw left','raw right','raw average'}; +if i_reg>0, + setappdata(hfig,'regchoice',{2,i_reg}); +end +% highlight the choice +global hstimreg hmotorreg hcentroidreg; +set(hstimreg,'BackgroundColor',[1,1,1]); +set(hmotorreg,'BackgroundColor',[1,1,0.8]); % yellow +set(hcentroidreg,'BackgroundColor',[1,1,1]); + +isPlotReg = getappdata(hfig,'isPlotReg'); +if isPlotReg, + PlotRegWithStimMotor(hfig); +end +end + +function checkbox_popupplotreg_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isPlotReg',get(hObject,'Value')); +end + +function edit_ctrdID_as_reg_Callback(hObject,~) +str = get(hObject,'String'); +if ~isempty(str), + temp = textscan(str,'%d'); + ctrdID = temp{:}; +end +hfig = getParentFigure(hObject); +setappdata(hfig,'regchoice',{3,ctrdID}); +% highlight the choice +global hstimreg hmotorreg hcentroidreg; +set(hstimreg,'BackgroundColor',[1,1,1]); +set(hmotorreg,'BackgroundColor',[1,1,1]); +set(hcentroidreg,'BackgroundColor',[1,1,0.8]); % yellow +end + +function checkbox_flipstim_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isflipstim',get(hObject,'Value')); +end + +%% row 2: regression + +function regressor = GetRegressor(hObject) +hfig = getParentFigure(hObject); +regchoice = getappdata(hfig,'regchoice'); +stim = getappdata(hfig,'stim'); + +if regchoice{1}==1, % stim Regressor + i_fish = getappdata(hfig,'i_fish'); + M_fish_set = getappdata(hfig,'M_fish_set'); + fishset = M_fish_set(i_fish); + [regressors, names] = GetStimRegressor(stim,fishset); + regressor = regressors(regchoice{2}).im; + +elseif regchoice{1}==2, % motor Regressor + fictive = getappdata(hfig,'fictive'); + regressors = GetMotorRegressor(fictive); + regressor = regressors(regchoice{2}).im; + +else % regchoice{1}==3, from Centroid + ctrdID = regchoice{2}; + isflipstim = getappdata(hfig,'isflipstim'); + M = getappdata(hfig,'M'); + gIX = getappdata(hfig,'gIX'); + + i = find(unique(gIX)==ctrdID); + if isempty(i), + disp('input is empty!');beep; + regressor = []; + return; + end + [C,~] = FindCentroid(gIX,M); + regressor = C(i,:); + + if isflipstim, % swap left/right stim + % 0 = all black; 1 = black/white; 2 = white/black; 3 = all white; 4 = all gray; + % 10 = forward grating (very slow, more for calibration) + % 11 = rightward grating + % 12 = leftward grating + % swap: 1~2,11~12 + + % if i_fish == 6 || i_fish == 7, + % halflength = length(stim)/2; + % stim = stim(1:halflength); % length is always even + % end + flipstim = stim; + flipstim(stim==1)=2; + flipstim(stim==2)=1; + flipstim(stim==11)=12; + flipstim(stim==12)=11; + + phototrans = GetPhotoTrans(stim); + fliptrans = GetPhotoTrans(flipstim); + + % transition e.g.: + % photostate: 2 3 1 3 3 0 0 1 1 0 3 2 0 2 2 1 + % phototrans: 6 11 13 7 15 12 0 1 5 4 3 14 8 2 10 9 + + IX = []; + targetstates = unique(fliptrans,'stable'); + for i = 1:length(targetstates), + IX = [IX, find(phototrans==targetstates(i))]; + end + % if i_fish == 6 || i_fish == 7, + % reg = regressor(1:halflength); + % reg = reg(IX); + % regressor = repmat(reg,1,2); + % else + regressor = regressor(IX); + % end + end +end + +end + +function checkbox_plotcorrhist_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isPlotCorrHist',get(hObject,'Value')); +end + +function pushbutton_topnum_regression_Callback(hObject,~) +disp('regression...'); +hfig = getParentFigure(hObject); +M_0 = getappdata(hfig,'M_0_fluo'); +numTopCorr = getappdata(hfig,'numTopCorr'); +regressor = GetRegressor(hfig); + +%% for each cell, find correlation coeff +M_corr = corr(regressor',M_0');%cell_resp_ave'); +% M_corr_ctrl = corr(regressor.ctrl',M_0'); + +[~,I] = sort(M_corr,'descend'); +cIX = I(1:numTopCorr)'; +gIX = ceil((1:length(cIX))'/length(cIX)*min(20,length(cIX))); + +score = M_corr(cIX); +rankscore = zeros(1,20); +for i = 1:20, + ix = find(gIX==i); + rankscore(i) = mean(score(ix)); +end +setappdata(hfig,'rankscore',round(rankscore*100)/100); +setappdata(hfig,'rankID',99); + +UpdateIndices(hfig,cIX,gIX,20); +RefreshFigure(hfig); +end + +function edit_topCorrNumber_Callback(hObject,~) +str = get(hObject,'String'); +if ~isempty(str), + temp = textscan(str,'%d'); + numTopCorr = temp{:}; + hfig = getParentFigure(hObject); + setappdata(hfig,'numTopCorr',numTopCorr); +end +end + +function pushbutton_thres_regression_Callback(hObject,~) +disp('regression...'); +hfig = getParentFigure(hObject); +M_0 = getappdata(hfig,'M_0_fluo'); +thres_reg = getappdata(hfig,'thres_reg'); +regressor = GetRegressor(hfig); +if isempty(regressor), + return; +end + +isPlotCorrHist = getappdata(hfig,'isPlotCorrHist'); + +[cIX,gIX] = Regression_direct(M_0,thres_reg,regressor,isPlotCorrHist); + +if ~isempty(gIX), + gIX = ceil((1:length(cIX))'/length(cIX)*min(20,length(cIX))); + UpdateIndices(hfig,cIX,gIX,40); + RefreshFigure(hfig); +end +end + +function [cIX_,gIX_,R] = Regression_direct(M_0,thres_reg,regressor,isPlotCorrHist) % gIX_ is just ones +R = corr(regressor',M_0'); +if thres_reg>0, + cIX_ = (find(R>thres_reg))'; +elseif thres_reg<0, + cIX_ = (find(R + cIX_ = Regression_direct(M_0,thres_reg,regressor); + clusters(i).cIX = cIX_; +end +disp('union...'); +CIX = vertcat(clusters(1:end).cIX); +GIX = []; +for i = 1:numel(clusters), + GIX = [GIX; ones(length(clusters(i).cIX),1)*i]; %#ok +end +[cIX,gIX,numK] = SmartUnique(CIX,GIX,M_0(CIX,:)); +end + +function pushbutton_IterCentroidRegression_Callback(hObject,~) +hfig = getParentFigure(hObject); +M_0 = getappdata(hfig,'M_0_fluo'); +thres_reg = getappdata(hfig,'thres_reg'); +M = getappdata(hfig,'M'); +gIX = getappdata(hfig,'gIX'); +regchoice = getappdata(hfig,'regchoice'); +if regchoice{1}==3, % from Centroid + ctrdID = regchoice{2}; +end + +i = find(unique(gIX)==ctrdID); +if isempty(i), + disp('input is empty!');beep; + return; +end +[C,~] = FindCentroid(gIX,M); +regressor = C(i,:); +[cIX_,gIX_] = Regression_direct(M_0,thres_reg,regressor); +numC = length(cIX_); +regressor_last = regressor; + +if ~isempty(cIX_), + for itr = 1:20, + [cIX_,gIX_] = Regression_direct(M_0,thres_reg,regressor_last); + numC = length(cIX_); + disp(num2str(numC)); + M = M_0(cIX_,:); + regressor = FindCentroid(gIX_,M); + if itr>1 && abs(numC-numC_last)<5,%corr(regressor_last',regressor')>thres_iter, + disp(['converged at itr = ' num2str(itr)]); + break; + end + regressor_last = regressor; + numC_last = numC; + end + gIX_ = ceil((1:length(cIX_))'/length(cIX_)*min(20,length(cIX_))); + UpdateIndices(hfig,cIX_,gIX_,40); + RefreshFigure(hfig); +end +end + +%% row 3: (TBD) + +function checkbox_dataFR_Callback(hObject,~) +hfig = getParentFigure(hObject); +SetDataFR(hfig,get(hObject,'Value')); +RefreshFigure(hfig); +end + +%% ----- tab four ----- (Clustering etc.) + +%% row 1: k-means + +function edit_kmeans_Callback(hObject,~) +hfig = getParentFigure(hObject); +M = getappdata(hfig,'M'); + +str = get(hObject,'String'); +if ~isempty(str), + temp = textscan(str,'%d'); + numK = temp{:}; + disp(['k-means k=' num2str(numK) '...']); + tic + rng('default');% default = 0, but can try different seeds if doesn't converge + if numel(M)*numK < 10^7 && numK~=1, + disp('Replicates = 5'); + [gIX,C,sumd,D] = kmeans(M,numK,'distance','correlation','Replicates',5); + elseif numel(M)*numK < 10^8 && numK~=1, + disp('Replicates = 3'); + [gIX,C,sumd,D] = kmeans(M,numK,'distance','correlation','Replicates',3); + else + [gIX,C,sumd,D] = kmeans(M,numK,'distance','correlation');%,'Replicates',3); + end + toc + beep + + if numK>1, + gIX = HierClusDirect(C,gIX,numK); + end + + cIX = getappdata(hfig,'cIX'); + UpdateIndices(hfig,cIX,gIX,numK); + RefreshFigure(hfig); +end +end + +function edit_kmeans2_Callback(hObject,~) % based on anatomical distance +hfig = getParentFigure(hObject); +z_res = getappdata(hfig,'z_res'); +cIX = getappdata(hfig,'cIX'); +CInfo = getappdata(hfig,'CInfo'); +M = getappdata(hfig,'M'); + +XY = reshape([CInfo(cIX).center],2,[])'; +Z = [CInfo(cIX).slice]'.*z_res; +XYZ = horzcat(XY,Z); +Combo = horzcat(XYZ/50,M);% how to weight???????????????? + +str = get(hObject,'String'); +if ~isempty(str), + temp = textscan(str,'%d'); + numK = temp{:}; + disp(['anat. weighted k-means k=' num2str(numK) '...']); + tic + rng('default');% default = 0, but can try different seeds if doesn't converge + [gIX,C,sumd,D] = kmeans(Combo,numK,'distance','correlation');%,'Replicates',3); + toc + beep + + if numK>1, + gIX = HierClusDirect(C,gIX,numK); + end + + cIX = getappdata(hfig,'cIX'); + UpdateIndices(hfig,cIX,gIX,numK); + RefreshFigure(hfig); +end +end + +function edit_kmeans_elbow_Callback(hObject,~) +hfig = getParentFigure(hObject); +gIX = getappdata(hfig,'gIX'); +M = getappdata(hfig,'M'); + +str = get(hObject,'String'); +if ~isempty(str), + str = strrep(str,'end',num2str(max(gIX))); + range = ParseRange(str); + + Silh_mean = zeros(1,length(range)); + for i = 1:length(range), + disp(['k-means k=' num2str(range(i))]); + gIX = kmeans(M,range(i),'distance','correlation'); + silh = silhouette(M,gIX,'correlation'); + Silh_mean(i) = mean(silh); + end + [~,ix] = max(Silh_mean); + numK = range(ix); + + figure;hold on + plot(range,Silh_mean,'o-','color',[0.5 0.5 0.5]); + + if length(range)~=1, + plot([numK,numK],[min(Silh_mean),max(Silh_mean)],'r--'); + xlim([range(1),range(end)]); + ylim([min(Silh_mean),max(Silh_mean)]) + end + xlabel('k = # of clusters') + ylabel('silhouette mean') + + %% perform regular k-means + disp(['k-means k=' num2str(numK)]); + rng('default');% default = 0, but can try different seeds if doesn't converge + if numel(M)*numK < 10^7 && numK~=1, + disp('Replicates = 5'); + [gIX,C,sumd,D] = kmeans(M,numK,'distance','correlation','Replicates',5); + elseif numel(M)*numK < 10^8 && numK~=1, + disp('Replicates = 3'); + [gIX,C,sumd,D] = kmeans(M,numK,'distance','correlation','Replicates',3); + else + [gIX,C,sumd,D] = kmeans(M,numK,'distance','correlation');%,'Replicates',3); + end + if numK>1, + gIX = HierClusDirect(C,gIX,numK); + end + cIX = getappdata(hfig,'cIX'); + UpdateIndices(hfig,cIX,gIX,numK); + RefreshFigure(hfig); + beep; +end +end + +%% row 2: Auto-clustering + +function pushbutton_merge_Callback(hObject,~) +% disp('merging...'); +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +M = getappdata(hfig,'M'); +U = unique(gIX); +numU = length(U); +[C,D] = FindCentroid(gIX,M); + +thres_merge = getappdata(hfig,'thres_merge'); + +i = 1; +while i thres_merge, + IX = find(gIX == U(i+1)); + gIX(IX)=U(i); + U = unique(gIX); + numU = length(U); + + IX = find(gIX == U(i)); + M_s = M(IX,:); + [~,C1,~,D1] = kmeans(M_s,1,'distance','correlation'); + C(i,:) = C1; + D(i) = mean(D1); + C(i+1,:) = []; + D(i+1) = []; + else + i = i+1; + end +end + +if numU>1, + [gIX, numU] = HierClus(M,gIX); +end + +UpdateIndices(hfig,cIX,gIX,numU); +RefreshFigure(hfig); +disp('merging complete'); +end + +function edit_mergethres_Callback(hObject,~) +str = get(hObject,'String'); +temp = textscan(str,'%f',1); +hfig = getParentFigure(hObject); +setappdata(hfig,'thres_merge',temp{:}); +end + +function pushbutton_iter_split(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +numU = getappdata(hfig,'numK'); +thres_split = getappdata(hfig,'thres_split'); +M_0 = getappdata(hfig,'M_0_fluo'); +classheader = getappdata(hfig,'classheader'); + +disp('iter. split all, beep when done...'); +thres_size = 10; +thres_H = thres_split; +% thres_H = [0.2;0.15;0.1;0.05]; % could have more rounds... + +% initialization +I_rest = []; +% loop +tic +for round = 1:length(thres_H), + disp(['round ' num2str(round) ', numU = ' num2str(numU)]); + dthres = 1-thres_H(round); + gIX_last = gIX; + I_clean_last = cIX; + cIX = []; + gIX = []; + for i = 1:numU, + disp(['i = ' num2str(i)]); + ix = find(gIX_last == i); +% IX = ix; + IX = I_clean_last(ix); + M_s = M_0(IX,:); + [I_rest,cIX,gIX,numU] = CleanClus(M_s,IX,I_rest,cIX,gIX,numU,dthres,thres_size); +% cIX = I_clean_last(I_clean); + end + + [gIX, numU] = SqueezeGroupIX(gIX); + SaveClass(hfig,cIX,gIX,classheader,['clean_round' num2str(round)]); + + SaveClass(hfig,I_rest,ones(length(I_rest),1),classheader,... + ['rest_round' num2str(round)]); +end +toc +beep +end + +function [I_rest,I_clean,gIX_clean,numU] = CleanClus(M_s,IX,I_rest,I_clean,gIX_clean,numU,dthres,thres_size) +I_clean_s = []; + +% find numK_s for kmeans +kmax = min(round(size(M_s,1)/thres_size),30); +% try numK_s = 1 +numK_s = 1; +rng('default'); +[gIX_s,~,~,D] = kmeans(M_s,numK_s,'distance','correlation'); +Dist = min(D,[],2); +if mean(Dist)>dthres, + % try numK_s = kmax + numK_s = kmax; + rng('default'); + [gIX_s,~,~,D] = kmeans(M_s,numK_s,'distance','correlation'); + Dist = min(D,[],2); + if mean(Dist)thres_silh, + % keep the k-means k=2 subsplit + disp('split'); + gIX(IX) = gIX_ + numU; % reassign (much larger) gIX + end +end +[gIX, ~] = SqueezeGroupIX(gIX); + +SaveClass(hfig,cIX,gIX,classheader,'clean_round3'); + +%% rank by stim-lock ?? bug? +% disp('stim-lock'); +% M = M_0(cIX,:); +% [gIX,rankscore] = RankByStimLock_Direct(hfig,cIX,gIX,M,numU); +% disp('ranking complete'); +% % and threshold +% IX = find(rankscore thres_merge, + IX = find(gIX == U(i+1)); + gIX(IX)=U(i); + U = unique(gIX); + numU = length(U); + + IX = find(gIX == U(i)); + M_s = M(IX,:); + [~,C1,~,D1] = kmeans(M_s,1,'distance','correlation'); + C(i,:) = C1; + D(i) = mean(D1); + C(i+1,:) = []; + D(i+1) = []; + else + i = i+1; + end +end +[gIX, numU] = HierClus(M,gIX); +disp('merging complete'); +end + +function checkbox_wkmeans_Callback(hObject,~) +hfig = getParentFigure(hObject); +setappdata(hfig,'isWkmeans',get(hObject,'Value')); +end + +%% row 3: misc plots + +function pushbutton_hierplot_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +M = getappdata(hfig,'M'); + +[gIX2, numU] = HierClus(M,gIX,'isplotfig'); +if ~isequal(gIX,gIX2), + UpdateIndices(hfig,cIX,gIX2,numU); + RefreshFigure(hfig); +end +end + +function edit_hierpartn_Callback(hObject,~) +hfig = getParentFigure(hObject); +M = getappdata(hfig,'M'); +gIX = getappdata(hfig,'gIX'); +hierinplace = getappdata(hfig,'hierinplace'); + +[gIX, numU] = SqueezeGroupIX(gIX); +[C,~] = FindCentroid(gIX,M); + +str = get(hObject,'String'); +if ~isempty(str), + temp = textscan(str,'%d',1); + numCuts = temp{:}; + +% tree = linkage(C,'average','correlation'); +% IX_tree = cluster(tree,'maxclust',numCuts); + IX_tree = clusterdata(C,'criterion','distance','distance','correlation','maxclust',numCuts) + + if hierinplace, + % sort to keep clusters in place + U = unique(IX_tree,'stable'); + temp = zeros(size(IX_tree)); + for i = 1:length(U), + temp(IX_tree==U(i)) = i; + end + IX_tree = temp; + end + + % update gIX + temp = zeros(size(gIX)); + for i = 1:numU, + temp(gIX==i) = IX_tree(i); + end + gIX = temp; + + cIX = getappdata(hfig,'cIX'); + UpdateIndices(hfig,cIX,gIX,length(unique(IX_tree))); + RefreshFigure(hfig); +end +end + +function edit_hierpartthres_Callback(hObject,~) +hfig = getParentFigure(hObject); +M = getappdata(hfig,'M'); +gIX = getappdata(hfig,'gIX'); +hierinplace = getappdata(hfig,'hierinplace'); + +[gIX, numU] = SqueezeGroupIX(gIX); +[C,~] = FindCentroid(gIX,M); + +str = get(hObject,'String'); +if ~isempty(str), + temp = textscan(str,'%f',1); + thres= temp{:}; + + IX_tree = clusterdata(C,'criterion','distance',... + 'distance','correlation','cutoff',thres); + + if hierinplace, + % sort to keep clusters in place + U = unique(IX_tree,'stable'); + temp = zeros(size(IX_tree)); + for i = 1:length(U), + temp(IX_tree==U(i)) = i; + end + IX_tree = temp; + end + + % update gIX + temp = zeros(size(gIX)); + for i = 1:numU, + temp(gIX==i) = IX_tree(i); + end + gIX = temp; + + cIX = getappdata(hfig,'cIX'); + UpdateIndices(hfig,cIX,gIX,length(unique(IX_tree))); + RefreshFigure(hfig); +end +end + +function checkbox_hierinplace_Callback(hObject,~) +hierinplace = get(hObject,'Value'); +hfig = getParentFigure(hObject); +setappdata(hfig,'hierinplace',hierinplace); +end + +function pushbutton_corrplot_Callback(hObject,~) +hfig = getParentFigure(hObject); +gIX = getappdata(hfig,'gIX'); +M = getappdata(hfig,'M'); +[C,~] = FindCentroid(gIX,M); +coeffs = corr(C');%corr(C(1,:)',C(2,:)') +CorrPlot(coeffs); +end + +function CorrPlot(coeffs) +im = coeffs; + +% red-white-blue colormap +cmap = zeros(64,3); +cmap(:,1) = [linspace(0,1,32), linspace(1,1,32)]; +cmap(:,2) = [linspace(0,1,32), linspace(1,0,32)]; +cmap(:,3) = [linspace(1,1,32), linspace(1,0,32)]; +minlim = -1; %min(min(im)); +maxlim = 1; %max(max(im)); + +RGB = ImageToRGB(im,cmap,minlim,maxlim); % map image matrix to range of colormap + +figure('Position',[1000,200,500,500]); +image(RGB); axis equal; axis tight; + +for i = 1:size(im,2), % horizontal.. because of image axis + for j = 1:size(im,1), + text(i-0.3, j, num2str(round(im(i,j)*100)/100));%, 'Units', 'data') + end +end +end + +function RGB = ImageToRGB(im,cmap,minlim,maxlim) +L = size(cmap,1); +ix = round(interp1(linspace(minlim,maxlim,L),1:L,im,'linear','extrap')); +RGB = reshape(cmap(ix,:),[size(ix) 3]); % Make RGB image from scaled. +end + +function pushbutton_plot4x4_Callback(hObject,~) +hfig = getParentFigure(hObject); +PlotBy16StimsFromGUI(hfig); +end + +%% ----- tab five ----- (Saved Clusters) + +%% row 1: Class + +function popup_classmenu_Callback(hObject,~) +classID = get(hObject,'Value') - 1; +if classID>0, + hfig = getParentFigure(hObject); + UpdateClassID(hfig,classID); % update popupmenu +end +end + +function edit_editclassname_Callback(hObject,~) +str = get(hObject,'String'); + +hfig = getParentFigure(hObject); +Class = getappdata(hfig,'Class'); +classID = getappdata(hfig,'classID'); +Class(classID).name = str; +setappdata(hfig,'Class',Class); +UpdateClassID(hfig,classID); % update popupmenu +end + +function pushbutton_saveclass_Callback(hObject,~) +hfig = getParentFigure(hObject); +SaveClass(hfig); +end + +function edit_class_header_Callback(hObject,~) +% get/format range +str = get(hObject,'String'); + +hfig = getParentFigure(hObject); +setappdata(hfig,'classheader',[str ':']); +end + +function edit_newclassname_Callback(hObject,~) +str = get(hObject,'String'); +hfig = getParentFigure(hObject); +setappdata(hfig,'newclassname',str); +end + +function pushbutton_makeclass_Callback(hObject,~) +hfig = getParentFigure(hObject); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); +classheader = getappdata(hfig,'classheader'); +newclassname = getappdata(hfig,'newclassname'); +SaveClass(hfig,cIX,gIX,classheader,newclassname); +end + +function pushbutton_delclass_Callback(hObject,~) +choice = questdlg('Delete current class?','','Cancel','Yes','Yes'); +if strcmp(choice,'Yes'), + hfig = getParentFigure(hObject); + Class = getappdata(hfig,'Class'); + classID = getappdata(hfig,'classID'); + disp(['delete ' num2str(classID)]); + Class(classID) = []; + setappdata(hfig,'Class',Class); + + classID = max(1,classID-1); + disp(['new classID: ' num2str(classID)]); + UpdateClassID(hfig,classID); +end +end + +%% row 2: Cluster + +function popup_clusgroupmenu_Callback(hObject,~) +hfig = getParentFigure(hObject); +clusgroupID = getappdata(hfig,'clusgroupID'); +new_clusgroupID = get(hObject,'Value'); +UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID); +end + +function popup_clusmenu_Callback(hObject,~) +clusID = get(hObject,'Value') - 1; +if clusID>0, + hfig = getParentFigure(hObject); + UpdateClusID(hfig,clusID); +end +end + +function edit_editclusname_Callback(hObject,~) +str = get(hObject,'String'); + +hfig = getParentFigure(hObject); +Cluster = getappdata(hfig,'Cluster'); +clusID = getappdata(hfig,'clusID'); +Cluster(clusID).name = str; +setappdata(hfig,'Cluster',Cluster); +UpdateClusID(hfig,clusID); % update popupmenu +end + +function pushbutton_saveclus_Callback(hObject,~) +hfig = getParentFigure(hObject); +SaveCluster(hfig,'current'); +end + +function edit_clus_header_Callback(hObject,~) +% get/format range +str = get(hObject,'String'); + +hfig = getParentFigure(hObject); +setappdata(hfig,'clusheader',[str ':']); +end + +function edit_newclusname_Callback(hObject,~) +str = get(hObject,'String'); +hfig = getParentFigure(hObject); +setappdata(hfig,'newclusname',str); +end + +function pushbutton_makeclus_Callback(hObject,~) +hfig = getParentFigure(hObject); +SaveCluster(hfig,'new'); +end + +function edit_setrank_Callback(hObject,~) +str = get(hObject,'String'); +C = textscan(str,'%d'); +rank = C{:}; + +hfig = getParentFigure(hObject); +Cluster = getappdata(hfig,'Cluster'); +clusID = getappdata(hfig,'clusID'); +% insert current cluster into new position = 'rank' +if rank > 1, + temp = Cluster(clusID); + Cluster(clusID) = []; + Cluster = [Cluster(1:rank-1),temp,Cluster(rank:end)]; +else + temp = Cluster(clusID); + Cluster(clusID) = []; + Cluster = [temp,Cluster(rank:end)]; +end +setappdata(hfig,'Cluster',Cluster); +% update pointer = clusID +clusID = rank; +UpdateClusID(hfig,clusID); +end + +function edit_notes_Callback(hObject,~) +str = get(hObject,'String'); +hfig = getParentFigure(hObject); +Cluster = getappdata(hfig,'Cluster'); +clusID = getappdata(hfig,'clusID'); +Cluster(clusID).notes = str; +setappdata(hfig,'Cluster',Cluster); +end + +function pushbutton_delclus_Callback(hObject,~) +choice = questdlg('Delete current cluster?','','Cancel','Yes','Yes'); +if strcmp(choice,'Yes'), + hfig = getParentFigure(hObject); + Cluster = getappdata(hfig,'Cluster'); + clusID = getappdata(hfig,'clusID'); + disp(['delete ' num2str(clusID)]); + Cluster(clusID) = []; + setappdata(hfig,'Cluster',Cluster); + + clusID = max(1,clusID-1); + disp(['new clusID: ' num2str(clusID)]); + UpdateClusID(hfig,clusID); +end +end + +%% row 3: misc + +function edit_clusUnion_Callback(hObject,~) +disp('union processing...'); +hfig = getParentFigure(hObject); +M_0 = GetM_0(hfig); +Cluster = getappdata(hfig,'Cluster'); +str = get(hObject,'String'); +if ~isempty(str), + % get/format range + str = strrep(str,':','-'); % e.g. str= '1,3,5:8'; + C = textscan(str,'%d','delimiter',','); + m = C{:}; + range = []; + for i = 1:length(m), + if m(i)>0, + range = [range,m(i)]; %#ok + else % have '-'sign, + range = [range,m(i-1)+1:-m(i)]; %#ok + end + end + + % combine + CIX = vertcat(Cluster(range).cIX); + GIX = []; % gIX to match A + for i = 1:length(range), + GIX = [GIX; ones(length(Cluster(range(i)).gIX),1)*i]; + end + [cIX,gIX,numK] = SmartUnique(CIX,GIX,M_0(CIX,:)); + UpdateIndices(hfig,cIX,gIX,numK); + RefreshFigure(hfig); +end +disp('union complete'); +end + +function [cIX,gIX,numK] = SmartUnique(CIX,GIX,M) % input: simply concatenated groups +disp('unique based on corr coeff...'); +CTRD = FindCentroid(GIX,M); + +[uCIX,ia,~] = unique(CIX); +uGIX = GIX(ia); +if length(uCIX) == 1, + counts = length(x); +else counts = hist(CIX,uCIX); +end +IX1 = find(counts==1); +CIX1 = uCIX(IX1); % single occurence +GIX1 = uGIX(IX1); +ia = find(~ismember(CIX,CIX1)); +C = CIX(ia); % multiple copies/occurence, keep all copies +G = GIX(ia); +M_s = M(ia,:); +Coeff = zeros(length(ia),1); +for i = 1:length(ia), + reg = CTRD(G(i),:); + Coeff(i) = corr(M_s(i,:)',reg'); +end +[~,I] = sort(Coeff,'descend'); % rank by coeff, so unique ('stable') gets highest +C = C(I); % finish sorting +G = G(I); +[CIX2,ia,~] = unique(C,'stable'); % keep the order! +GIX2 = G(ia); % the chosen copy for those with multiple copies +cIX = vertcat(CIX1,CIX2); +gIX = vertcat(GIX1,GIX2); +[gIX, numK] = SqueezeGroupIX(gIX); +disp('smart union complete'); +end + +function pushbutton_newclusgroup_Callback(hObject,~) +hfig = getParentFigure(hObject); +ClusGroup = getappdata(hfig,'ClusGroup'); +ClusGroup = [ClusGroup,cell(1,1)]; +setappdata(hfig,'ClusGroup',ClusGroup); + +clusgroupID = getappdata(hfig,'clusgroupID'); +new_clusgroupID = length(ClusGroup); +UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID); +end + +function pushbutton_delclusgroup_Callback(hObject,~) +choice = questdlg('Delete current Cluster-group?','','Cancel','Yes','Yes'); +if strcmp(choice,'Yes'), + hfig = getParentFigure(hObject); + ClusGroup = getappdata(hfig,'ClusGroup'); + clusgroupID = getappdata(hfig,'clusgroupID'); + ClusGroup(clusgroupID) = []; + setappdata(hfig,'ClusGroup',ClusGroup); + UpdateClusGroupID(hfig,[],max(1,clusgroupID-1)); +end +end + +%% Internal functions + +function SetDataFR(hfig,dataFR) +cIX = getappdata(hfig,'cIX'); +setappdata(hfig,'dataFR',dataFR); +if dataFR, + M_0_fluo = getappdata(hfig,'M_0_fluo'); + setappdata(hfig,'M',M_0_fluo(cIX,:)); +else % Unchecked + M_0_reg = getappdata(hfig,'M_0_reg'); + setappdata(hfig,'M',M_0_reg(cIX,:)); +end +global hdataFR; +set(hdataFR,'Value',dataFR); +end + +function SaveClass(hfig,cIX,gIX,classheader,newclassname) +if ~exist('cIX','var'), + cIX = getappdata(hfig,'cIX'); +end +if ~exist('gIX','var'), + gIX = getappdata(hfig,'gIX'); +end + +Class = getappdata(hfig,'Class'); +dataname = getappdata(hfig,'dataname'); + +if ~exist('newclassname','var'), + classID = getappdata(hfig,'classID'); +else + classID = numel(Class)+1; + Class(classID).name = [classheader newclassname]; +end + +Class(classID).cIX = cIX; +Class(classID).gIX = gIX; +Class(classID).numel = length(cIX); +U = unique(gIX); +numU = length(U); +Class(classID).numK = numU; +Class(classID).dataname = dataname; + +setappdata(hfig,'Class',Class); +UpdateClassID(hfig,classID); +disp('class saved'); +end + +function UpdateClassID(hfig,classID,norefresh) +Class = getappdata(hfig,'Class'); +numK = Class(classID).numK; +i_fish = getappdata(hfig,'i_fish'); +% save +setappdata(hfig,'classID',classID); +% update GUI +global hclassname hclassmenu; +if ishandle(hclassname), + set(hclassname,'String',Class(classID).name); + temp = MakeMenu({Class.name}); + set(hclassmenu, 'String', temp); + set(hclassmenu, 'Value', classID+1); +end + +if ~exist('norefresh','var'), + UpdateIndices(hfig,Class(classID).cIX,Class(classID).gIX,numK); + RefreshFigure(hfig); +end +global VAR; +VAR(i_fish).Class = getappdata(hfig,'Class'); +end + +function UpdateClusGroupID(hfig,clusgroupID,new_clusgroupID,norefresh) +% save/update old Cluster into ClusGroup before exiting, +% as Cluster is the variable handled in hfig but not saved elsewhere +ClusGroup = getappdata(hfig,'ClusGroup'); +Cluster = getappdata(hfig,'Cluster'); +i_fish = getappdata(hfig,'i_fish'); + +ClusGroup{clusgroupID} = Cluster; +setappdata(hfig,'ClusGroup',ClusGroup); +global VAR; +VAR(i_fish).ClusGroup = CurrentClusGroup(hfig); + +Cluster = ClusGroup{new_clusgroupID}; +setappdata(hfig,'Cluster',Cluster); +setappdata(hfig,'clusgroupID',new_clusgroupID); + +global hclusgroupmenu; +if ishandle(hclusgroupmenu), + num = length(ClusGroup); + menu = {}; for i = 1:num, menu = [menu,{num2str(i)}];end + set(hclusgroupmenu,'String', menu,'Value',new_clusgroupID); +end + +if ~exist('norefresh','var'), + if numel(Cluster) == 0, + SaveCluster(hfig,'new'); + else + clusID = 1; + UpdateClusID(hfig,clusID); + end +end +end + +function ClusGroup = CurrentClusGroup(hfig) +ClusGroup = getappdata(hfig,'ClusGroup'); +Cluster = getappdata(hfig,'Cluster'); +clusgroupID = getappdata(hfig,'clusgroupID'); +ClusGroup{clusgroupID} = Cluster; +setappdata(hfig,'ClusGroup',ClusGroup); +end + +function [Cluster,clusID] = SaveCluster(hfig,state,clusheader,name) +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); + +Cluster = getappdata(hfig,'Cluster'); +dataname = getappdata(hfig,'dataname'); + +if strcmp(state,'current'), + clusID = getappdata(hfig,'clusID'); +else %if strcmp(state,'new'), + clusID = numel(Cluster)+1; + if ~exist('name','var'), + name = getappdata(hfig,'newclusname'); + end + if ~exist('clusheader','var'), + clusheader = getappdata(hfig,'clusheader'); + end + Cluster(clusID).name = [clusheader name]; +end + +Cluster(clusID).cIX = cIX; +Cluster(clusID).gIX = gIX; +Cluster(clusID).numel = length(cIX); +U = unique(gIX); +numU = length(U); +Cluster(clusID).numK = numU; +Cluster(clusID).dataname = dataname; + +setappdata(hfig,'Cluster',Cluster); +UpdateClusID(hfig,clusID); +disp('cluster saved'); +end + +function UpdateClusID(hfig,clusID) +Cluster = getappdata(hfig,'Cluster'); + +% save +setappdata(hfig,'clusID',clusID); +% update GUI +global hclusname hclusmenu; +set(hclusname,'String',Cluster(clusID).name); +menu = MakeMenu({Cluster.name}); +set(hclusmenu,'String', menu,'Value',clusID+1); +numK = Cluster(clusID).numK; + +UpdateIndices(hfig,Cluster(clusID).cIX,Cluster(clusID).gIX,numK); +RefreshFigure(hfig); +end + +function menu = MakeMenu(name) % careful: input {Class.name}, otherwise only get 1 somehow +menu = [{'(choose)'},name]; +for j=2:length(menu),menu(j)={[num2str(j-1) ': ' menu{j}]};end +end + +function [gIX, numU] = HierClus(M,gIX,isplotfig) %#ok +[gIX, numU] = SqueezeGroupIX(gIX); +[C,~] = FindCentroid(gIX,M); +D = pdist(C,'correlation'); +tree = linkage(C,'average','correlation'); +leafOrder = optimalleaforder(tree,D); + +if numU>1, + if exist('isplotfig','var'), + figure('Position',[100 400 1300 400]); + subplot(1,3,1); + CORR = corr(C'); + imagesc(CORR);axis equal;axis tight + + subplot(1,3,2); + dendrogram(tree,numU,'orientation','right','reorder',leafOrder); + set(gca,'YDir','reverse'); + colormap(jet); + + subplot(1,3,3); + C2 = C(leafOrder,:); + CORR2 = corr(C2'); + imagesc(CORR2);axis equal;axis tight + end + % sort for uniform colorscale + temp = zeros(size(gIX)); + for i = 1:numU, + temp(gIX==leafOrder(i)) = i; % = T(i) for clusters segmented from tree + end + gIX = temp; +end +end + +function gIX = HierClusDirect(C,gIX,numU) +D = pdist(C,'correlation'); +tree = linkage(C,'average','correlation'); +leafOrder = optimalleaforder(tree,D); + +% sort for uniform colorscale +temp = zeros(size(gIX)); +for i = 1:numU, + temp(gIX==leafOrder(i)) = i; % = T(i) for clusters segmented from tree +end +gIX = temp; +end + +function [gIX, numK] = SqueezeGroupIX(gIX) +U = unique(gIX); +numK = length(U); +for i = 1:numK, + old = U(i); + gIX(gIX==old) = i; +end +end + +% frequently used, updates cell-index,group-index,cluster-number. set-operations included in here. +function UpdateIndices(hfig,cIX,gIX,numK) +global hback hopID; +M_0 = GetM_0(hfig); +if ~exist('gIX','var'), + gIX = getappdata(hfig,'gIX'); +end +if ~exist('cIX','var'), + cIX = getappdata(hfig,'cIX'); +end + +% update cache +bC = getappdata(hfig,'bCache'); +cIX_last = getappdata(hfig,'cIX'); +gIX_last = getappdata(hfig,'gIX'); +if ~(isequal(cIX_last,cIX) && isequal(gIX_last,gIX)), + bC.cIX = [cIX_last,bC.cIX]; + bC.gIX = [gIX_last,bC.gIX]; + bC.numK = [getappdata(hfig,'numK'),bC.numK]; + set(hback,'enable','on'); + if length(bC.cIX)>20, + bC.cIX(end) = []; + bC.gIX(end) = []; + bC.numK(end) = []; + end +end + +% set operations, if applicable +opID = getappdata(hfig,'opID'); +if opID ~= 0, + switch opID, + case 1, + disp('union'); + [~,ia,ib] = union(cIX_last,cIX,'stable'); + IX = vertcat(cIX_last(ia),cIX(ib));% IX; + case 2, + disp('intersect'); + [IX,ia,~] = intersect(cIX_last,cIX); + ib = []; + case 3, + disp('setdiff'); + [IX,ia] = setdiff(cIX_last,cIX); + ib = []; + case 4, + disp('rev setdiff'); + % swap sequence, then same as opID==3 + temp = cIX; + cIX = cIX_last; + cIX_last = temp; + temp = gIX; + gIX = gIX_last; + gIX_last = temp; + [IX,ia] = setdiff(cIX_last,cIX); + ib = []; + case 5, + disp('setxor'); + [IX,ia,ib] = setxor(cIX_last,cIX); + case 6, + disp('smartUnion'); + CIX = vertcat(cIX_last,cIX); + GIX = [gIX_last;gIX+max(gIX_last)]; % gIX to match + [cIX,gIX,numK] = SmartUnique(CIX,GIX,M_0(CIX,:)); + end + if opID<6, + if ~isempty(IX), + cIX = IX; + gIX = vertcat(gIX_last(ia),gIX(ib)+max(gIX_last(ia))); + numK = length(unique(gIX)); + % [gIX, numK] = SqueezeGroupIX(gIX); + else + errordlg('operation result is empty set!') + waitforbuttonpress; + end + end + set(hopID,'Value',1,'BackgroundColor',[1,1,1]); % reset + setappdata(hfig,'opID',0); +end + +if length(cIX)*size(M_0,2)<5*10^8, + % set M + M = M_0(cIX,:); + + setappdata(hfig,'M',M); + setappdata(hfig,'bCache',bC); + setappdata(hfig,'cIX',cIX); + setappdata(hfig,'gIX',gIX); + if exist('numK','var'), + setappdata(hfig,'numK',numK); + end +else + errordlg('dataset too large!') + waitforbuttonpress; +end + +% handle rankID: >=2 means write numbers as text next to colorbar +% first UpdateIndices sets rankID to 100, second sets back to 0 +rankID = getappdata(hfig,'rankID'); +if rankID>=2, + if rankID==100, + setappdata(hfig,'rankID',0); + else + setappdata(hfig,'rankID',100); + end +end +end + +% frequently used, 2 plotting functions are outside ('DrawClusters.m' and 'DrawClustersOnMap_LSh.m') +function RefreshFigure(hfig) +CInfo = getappdata(hfig,'CInfo'); +M = getappdata(hfig,'M'); +dataFR = getappdata(hfig,'dataFR'); +fictive = getappdata(hfig,'fictive'); +cIX = getappdata(hfig,'cIX'); +gIX = getappdata(hfig,'gIX'); + +numK = getappdata(hfig,'numK'); +stim = getappdata(hfig,'stim'); +anat_yx = getappdata(hfig,'anat_yx'); +anat_yz = getappdata(hfig,'anat_yz'); +anat_zx = getappdata(hfig,'anat_zx'); +isCentroid = getappdata(hfig,'isCentroid'); +clrmap = getappdata(hfig,'clrmap'); +rankscore = getappdata(hfig,'rankscore'); +rankID = getappdata(hfig,'rankID'); + +iswrite = (rankID>=2); + +if isempty(cIX), + errordlg('empty set! go back!'); + return; +end + +allAxesInFigure = findall(hfig,'type','axes'); +if ~isempty(allAxesInFigure) + delete(allAxesInFigure); +end + +figure(hfig); +% left subplot +h1 = axes('Position',[0.05, 0.04, 0.55, 0.83]); +if isCentroid, + [C,~] = FindCentroid(gIX,M); + DrawClusters(h1,C,unique(gIX),dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); +else + DrawClusters(h1,M,gIX,dataFR,numK,stim,fictive,clrmap,rankscore,iswrite); +end + +% right subplot +h2 = axes('Position',[0.61, 0.04, 0.35, 0.83]); +DrawClustersOnMap_LSh(CInfo,cIX,gIX,numK,anat_yx,anat_yz,anat_zx,clrmap); +end + +function M_0 = GetM_0(hfig) +dataFR = getappdata(hfig,'dataFR'); +if dataFR, + M_0 = getappdata(hfig,'M_0_fluo'); +else + M_0 = getappdata(hfig,'M_0_reg'); +end +end + +function [C,D] = FindCentroid(gIX,M) +U = unique(gIX); +numU = length(U); +C = zeros(numU,size(M,2)); +D = zeros(numU,1); +for i=1:numU, + IX = find(gIX == U(i)); + if length(IX)==1, + C(i,:) = M(IX,:); + D(i) = 1; + else + M_s = M(IX,:); + [~,C1,~,D1] = kmeans(M_s,1,'distance','correlation'); + C(i,:) = C1; + D(i) = mean(D1); + end +end +end + +function closefigure_Callback(hfig,~) +global EXPORT_autorecover; +EXPORT_autorecover = getappdata(hfig); +end + +function fig = getParentFigure(fig) +% if the object is a figure or figure descendent, return the figure. Otherwise return []. +while ~isempty(fig) && ~strcmp('figure', get(fig,'type')) + fig = get(fig,'parent'); +end +end + +function runscript(flag_script,var_script) +switch flag_script + case 'push_cIX_gIX' + UpdateIndices(var_script{:}); + RefreshFigure(var_script{1}); +end +end +% end diff --git a/first version/GetMotorRegressor.m b/first version/GetMotorRegressor.m new file mode 100644 index 0000000..73f17e7 --- /dev/null +++ b/first version/GetMotorRegressor.m @@ -0,0 +1,86 @@ +function regressors = GetMotorRegressor(fictive) +%% generate GCaMP6f kernel +% GCaMP6f: decay half-time: 400±41; peak: 80±35 +% GCaMP6s: 1796±73, 480±24 + +fpsec = 1.97; + +tlen=size(fictive,2); +t=0:0.05:8; % sec +gc6=exp(-(t-(4+0.48))/1.8); +gc6(t<(4+0.48))=0; +t_im = 0:1/fpsec:1/fpsec*(tlen-1); +t_gc6=0:0.05:tlen; % sec +%% +% turn(data.swimStartIndT(i)-100,1) = turn_directLeft(i); %binary: left turns +% turn(data.swimStartIndT(i)-100,2) = turn_directRight(i); %binary: right turns +% turn(data.swimStartIndT(i)-100,3) = turn_direct(i); %binary: all turns (+1 for left -1 for right) +% turn(data.swimStartIndT(i)-100,4) = forward_direct(i); %binary: forward swims +% turn(data.swimStartIndT(i)-100,5) = swim_direct(i); %binary: all swims +% +% turn(data.swimStartIndT(i)-100,6) = turn_amp(i); %weighted: all turns +% turn(data.swimStartIndT(i)-100,7) = turn_left(i); %weighted: left turns +% turn(data.swimStartIndT(i)-100,8) = turn_right(i); %weighted: right turns +% turn(data.swimStartIndT(i)-100,9) = forward_amp(i); %weighted: forward swims +% turn(data.swimStartIndT(i)-100,10) = swim_amp(i); %weighted: all swims +% +% turn(data.swimStartIndT(i)-100,11) = left_amp(i); %weighted: left channel % 11,12 similar to 10?! +% turn(data.swimStartIndT(i)-100,12) = right_amp(i); %weighted: right channel +% turn(:,13) = fltCh1; %analog: left channel +% turn(:,14) = fltCh2; %analog: right channel +regressor_0={ % rows = [7,8,9,13,14]; + fictive(1,:); %weighted: right turns + fictive(2,:); %weighted: left turns + fictive(3,:); %weighted: forward swims + fictive(4,:); %analog: right channel + fictive(5,:); %analog: left channel + fictive(4,:)+fictive(5,:); %analog: average + }; +nRegType = length(regressor_0); +name_array = {'w_right','w_left','w_fwd','raw_right','raw_left','raw_all'}; + +% segment length and round off, for shuffled control +segLength = floor(tlen/80); +s4_ = tlen-mod(tlen,segLength); + +% initialize/preallocate struct +regressors(nRegType).name = []; +regressors(nRegType).im = []; +regressors(nRegType).ctrl = []; + +%% feed all regressors into struct +idx = 0; +for j=1:nRegType, %run_StimRegType_subset, + len = size(regressor_0{j},1); + for i=1:len, %run_PhotoState_subset, + idx = idx + 1; + reg = regressor_0{j}(i,:); + % idx = (j-1)*nStimRegType + i; + + regressors(idx).name = [name_array{j} '_' num2str(i)]; + % generate regressor in imaging time + regressors(idx).im = gen_reg_im(gc6, t_gc6, t_im, reg); + + % generate shuffled regressor + reg_ = reg(1:s4_); + reg2D = reshape(reg_, segLength, []); + indices = randperm(size(reg2D,2)); % reshuffle by indexing + shffreg = reg2D(:, indices); + shffreg = reshape(shffreg,1,[]); + temp = reg; + temp(1:s4_) = shffreg; + shffreg = temp; + regressors(idx).ctrl = gen_reg_im(gc6, t_gc6, t_im, shffreg); + + end +end + +end + +function reg_im = gen_reg_im(gc6, t_gc6, t_im, reg) + +temp1=interp1(t_im,reg,t_gc6,'linear','extrap'); +temp2=conv(temp1,gc6,'same'); +reg_im = interp1(t_gc6,temp2,t_im,'linear','extrap'); + +end diff --git a/GetPhotoTrans.m b/first version/GetPhotoTrans.m similarity index 100% rename from GetPhotoTrans.m rename to first version/GetPhotoTrans.m diff --git a/first version/GetStimBar.m b/first version/GetStimBar.m new file mode 100644 index 0000000..2f7215d --- /dev/null +++ b/first version/GetStimBar.m @@ -0,0 +1,25 @@ +function stimbar = GetStimBar(halfbarheight,stim) % horizontal stimulus bar. nFrames is length of actual content, barlength is raw length including padding +m1 = 0.3*ones(halfbarheight,length(stim)); +m2 = m1; % bottom half +x = stim; + +% 0 = all black; 1 = black/white; 2 = white/black; 3 = all white; 4 = all gray; +% 5 = gray/black; 6 = white/gray; 7 = black/gray; 8 = gray/white. +% 10 = forward grating (very slow, more for calibration) +% 11 = rightward grating +% 12 = leftward grating + +% top (~ projection left) +m1(:,x==0 | x==2 | x==5) = 0; % black +m1(:,x==4 | x==6 | x==7) = 0.8; % grey +m1(:,x==1 | x==3 | x==8) = 1; % white +% bottom (~ projection right) +m2(:,x==0 | x==1 | x==7) = 0; +m2(:,x==4 | x==5 | x==8) = 0.8; +m2(:,x==2 | x==3 | x==6) = 1; +% colors for OMR +stimbar = repmat(vertcat(m1,m2),[1 1 3]); % 3 color layers in dim3 +stimbar(:,x==11,1) = 1; % red ~ left +stimbar(:,x==10,2) = 1; % green ~ forward +stimbar(:,x==12,3) = 1; % blue ~ right +end diff --git a/first version/GetStimRegressor.m b/first version/GetStimRegressor.m new file mode 100644 index 0000000..ba2d9d5 --- /dev/null +++ b/first version/GetStimRegressor.m @@ -0,0 +1,186 @@ +%% +% all photoStates: +% 0 = all black; 1 = black/white; 2 = white/black; 3 = all white; 4 = all gray; +% 5 = gray/black; 6 = white/gray; 7 = black/gray; 8 = gray/white. +% 10 = forward grating (very slow, more for calibration) +% 11 = rightward grating +% 12 = leftward grating + +% 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +% 00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 +% transition e.g.: +% photostate: 2 3 1 3 3 0 0 1 1 0 3 2 0 2 2 1 +% phototrans: 6 11 13 7 15 12 0 1 5 4 3 14 8 2 10 9 + +%% +function [regressors, names] = GetStimRegressor(stim,fishset) + +fpsec = 1.97; % should import from data... + +%% generate GCaMP6f kernel +% GCaMP6f: decay half-time: 400±41; peak: 80±35 (ms) +halftime = 0.4; +delay = 0.08; +% GCaMP6s: 1796±73, 480±24 (ms) +% halftime = 1.8; +% delay = 0.48; + +% the 2014-15 Janelia dataset used nucleus localized GCaMP6f... + +T = 8; % kernel time length in sec + +tlen=size(stim,2); +t=0:0.05:T; % sec +gc6=exp(-(t-(T/2+delay))/halftime); +gc6(t<(T/2+delay))=0; +t_im = 0:1/fpsec:1/fpsec*(tlen-1); +t_gc6=0:0.05:tlen; % sec + +%% +if fishset == 1, + States = [0,1,2,3]; + names = {'black','phototaxis left','phototaxis right','white',... + 'right on','left on','right off','left off'}; +elseif fishset == 2, + States = [0,1,2,3,4,10,11,12]; + names = {'black','phototaxis left','phototaxis right','white','grey','OMR forward','OMR left','OMR right',... + 'left PT&OMR','right PT&OMR'}; +end +tlen=length(stim); +impulse = 6; % in frames % arbiturary at this point + +%% single photoStates +numPS = length(States); +stimPS_on = zeros(numPS,tlen); % PS = photoState +% stimPS_start = zeros(numPS,tlen); % binary, ~ impulse1 +% stimPS_stop = zeros(numPS,tlen); +for i = 1:numPS, + if ~isempty(find(stim==States(i),1)), + stimPS_on(i,:) = (stim==States(i)); + +% diff_A = [stimPS_on(i,1) diff(stimPS_on(i,:))]; +% % if i==8, diff_A(end)=-1; end % manual correction: series always ends with PS=8 +% ind_start_A = find(diff_A==1); +% ind_stop_A = find(diff_A==-1); +% +% for k = 1:length(ind_start_A), +% if (ind_start_A(k)+impulse1)=1; + + diff --git a/first version/push_cIX_gIX.m b/first version/push_cIX_gIX.m new file mode 100644 index 0000000..0663718 --- /dev/null +++ b/first version/push_cIX_gIX.m @@ -0,0 +1,10 @@ +function push_cIX_gIX(hfig,cIX,gIX,numK) +% push cIX, gIX to hfig +if ~exist('numK','var') + numK=max(gIX); +end + +var_script={hfig,cIX,gIX,numK}; +GUI_FishExplorer('_place_holder_',0,0,0,1,'push_cIX_gIX',var_script); + +end \ No newline at end of file diff --git a/first version/read_LSstack_fast_float.m b/first version/read_LSstack_fast_float.m new file mode 100644 index 0000000..b17963a --- /dev/null +++ b/first version/read_LSstack_fast_float.m @@ -0,0 +1,15 @@ +function [stack,dim]=read_LSstack_fast_float(fpath,dim) +%% reads whole stack + +info=dir(fpath); +if (isempty(info)) + error('File Does not exist'); +end + + +stack=read_LSstack_fast_float_mex64(fpath,int32(dim(1:2))); + +size(stack) + + +stack=reshape(stack,dim); diff --git a/fishpic.jpg b/fishpic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef40e83648fc2b5cb2a7645747ad264e97279344 GIT binary patch literal 25805 zcmeEucbpVO)9~IMcSp`q1&$n6n9bn`=j?3GX*P-AY@W@tI~x&B5m8Y)j>#sL%7hzxVs^>HFPoPj_{7byanBRZsWU9jiM-X)YJb z#FT=`$Zc1Tt08&y;Lxo`s+k0O$T4}cP+!6gSwj8Dw-;S zxV*SD6LlFfYNI3LckrEED+V_lm?}>NBS9C5m8F7#P^>&PsA~>+Iml;=p{`{)5Yj)W zE1d1M%%akkA>pX2jNPBs&q3qR$~g4?bS8_-VfEdK0k|w!RQAd`o+>A5=-?9#k%E@ zJisG##T-#QLgL|2Sr#v5568(tUAtyGdYJO#aylON6NyIyxn7(O$Q5t}T_G|Ccu0pH zA_r{;IvwTia5RXKgYh8daY4R_%QFyqRQjloEGUVS0atE-5BtrH6b_J2mS$0BNe$px z4wMJ5kY`ZWR6nQ7jl~0G*TLE-9wG-qd5|ZY=1ZT%n4{>4&ht=@apnP%T(^&v|2vKW zy?Ds7zf`%uVl!wYkp;n!{z+Fo5FYeA$!*ZCTm{t4iIJEPBVB`mBC-3?nEhxhBb`&u z=9V+SBO9pGKg#2&~muUP+9tFoFC!J;vj zF&%LY>Z*6e!ttoX^|)?7QqlY3Do^50(mR;J=H%$gO7MQsBdY$jk=KQLyyRdG?@^=s zMgP?5{-ly?MUqGD?ic=3yWA`s4Do#s3x;(DzfG?9zyjDKLd>w-3d2H(tAk^x#G7zR z_!&&XPs)U9$2172wP#54u-8}b;cp0 z8}$M30FnVU31w2XK~IzAo}c*8+5u5n)G$m-(`6_E(1COxVK)1}N_s0(kalL9nsV(k zIfOQeDPv870&A8sLCne2Cv!mV91v;^lKE#(j&jry@Y>8-db~NJP#gXJGoE}%A!>a9 z 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 literal 0 HcmV?d00001 diff --git a/read_LSstack_fast_float_mex64.mexw64 b/read_LSstack_fast_float_mex64.mexw64 new file mode 100644 index 0000000000000000000000000000000000000000..d5df9b022a2336f90c02a4f9df83f539ef484984 GIT binary patch literal 7680 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 AmjD0& literal 0 HcmV?d00001