diff --git "a/bento/annotations\\color_profiles.txt" "b/bento/annotations\\color_profiles.txt" deleted file mode 100644 index 61a6c71..0000000 --- "a/bento/annotations\\color_profiles.txt" +++ /dev/null @@ -1,158 +0,0 @@ -m mount 0.046 0.773 0.205 -r intromission 0.049 0.403 0.227 -a attack 0.973 0.125 0.134 -g sniff_genitals 0.103 0.352 0.510 -f sniff_face 1.000 0.887 0.000 -i closeinvestigation 0.340 0.694 0.960 -n intruder_introduction 0.527 0.527 0.527 -w walk 0.220 0.220 0.220 -l alone 0.840 0.840 0.840 -_ groom_genital 0.629 0.170 0.867 -_ agg_investigation 0.997 0.683 0.265 -f cable_fix 0.987 0.677 0.936 -_ Mesh_contact 0.960 0.876 0.529 -_ Outside_hide 0.460 0.947 0.753 -_ Inside_hide 0.519 0.716 1.000 -r rat 0.843 0.087 0.890 -q checked 0.500 1.000 0.931 -_ fixed_attack 0.793 0.500 0.638 -_ photo 1.000 0.000 0.517 -_ contact 0.986 0.990 0.554 -_ No_hide 0.512 0.927 0.329 -h hide 0.319 0.635 0.990 -i investigation 0.867 0.744 0.068 -_ introduction 0.000 0.000 0.241 -_ sniffface 0.970 0.817 0.178 -_ sniffgenitals 0.877 0.740 0.085 -b sniffbody 0.777 0.676 0.015 -i interacting 1.000 0.500 1.000 -n no_head_tracking 0.000 0.002 0.003 -w with_head_tracking 0.037 0.769 0.937 -_ approach 0.552 0.690 0.483 -n mount_attempt 0.793 1.000 0.000 -_ post_mount 0.655 0.034 0.276 -k attack_attempt 0.000 0.621 0.586 -g sniff_genital 1.000 0.448 0.000 -b sniff_body 1.000 0.724 0.621 -_ touch 0.655 0.345 0.000 -_ touch_attempt 0.966 0.069 0.724 -_ intruder_kick 0.069 0.655 0.345 -e intruder_enter 0.069 0.172 0.000 -e escape 0.000 0.000 1.000 -z freezing 0.448 0.034 0.414 -a stretch_attend 1.000 0.707 0.500 -n no_hide 0.000 1.000 0.000 -_ sniff_rat 0.500 0.707 0.534 -r rear 1.000 0.569 0.500 -_ dom_mount 0.586 0.000 0.034 -_ fix_cable 0.414 0.621 0.034 -_ intruder_push 1.000 0.276 0.379 -_ aggressive_investigation 0.448 0.345 0.207 -_ dom_mount_attempt 0.000 0.310 0.690 -_ attend 0.448 0.276 0.379 -_ retreat 0.586 0.276 0.724 -_ grab 0.724 0.069 0.483 -_ climb 0.069 0.379 0.379 -_ sniff 0.724 0.655 0.828 -_ clean_qtip 0.621 0.862 0.483 -_ female_qtip 0.000 0.414 0.000 -_ male_qtip 0.724 0.828 0.172 -_ enter 0.655 0.897 1.000 -_ clean_genitals 0.483 0.448 0.000 -_ sniff_bedding 0.862 1.000 0.793 -g groom 1.000 0.759 0.828 -l allogrooming 0.276 0.345 0.069 -h headinvestigation 0.000 0.138 0.241 -i bodyinvestigation 0.207 0.138 0.000 -j anogenitalinvestigation 0.276 0.172 0.379 -t tailrattling 0.690 0.000 0.621 -d digging 0.655 0.690 0.310 -c chasing 0.207 0.000 0.379 -r runningaway 0.448 0.069 0.241 -n dominantbehavior 0.552 0.379 1.000 -_ defensiveposturing 0.793 0.310 0.310 -w weirdstereotypies 0.690 0.310 0.621 -e exploration 0.897 0.828 0.655 -_ grooming 0.345 0.034 0.000 -_ Approach 0.414 0.000 0.621 -_ Close_investigation_Sniff_genital 0.448 0.448 0.621 -_ Close_investigation_Sniff_body 0.345 0.241 0.586 -_ Close_investigation_Sniff_face 0.724 0.586 0.483 -_ Close_investigation_Social_grooming 0.517 0.793 0.759 -_ Attack 0.690 0.276 0.448 -_ Aggressive_investigation 0.000 0.931 1.000 -_ Chase 0.000 0.207 0.448 -_ Mount 0.517 0.517 0.276 -_ Mount_attempt 0.724 0.517 0.069 -_ Tail_rattling 0.000 0.517 1.000 -_ Digging 0.345 0.172 0.172 -_ Freezing 0.000 0.724 0.552 -_ Grooming 0.379 0.552 0.207 -_ Corner 0.793 0.414 1.000 -_ stim_on 0.000 0.138 0.828 -_ attemptedattack 0.172 0.034 0.138 -_ quickinvestigate 0.000 0.345 0.931 -_ anogeninvestigation 0.310 0.379 0.276 -_ attemptedmount 0.000 1.000 0.379 -_ x473nm 0.552 0.276 0.138 -_ x593nm 0.552 1.000 0.103 -i ignore 0.414 0.310 0.793 -c corner 0.000 0.138 0.483 -_ circling 0.586 0.621 1.000 -f snifface 0.793 0.000 0.172 -u sniffurogenital 0.621 0.690 0.759 -_ sniffheld 0.276 0.000 0.241 -n intruder_enters 0.759 0.621 0.655 -_ introduce 0.862 0.483 0.034 -c closeinvestigate 0.837 0.729 0.134 -n attempted_mount 0.931 0.862 1.000 -t attempted_attack 0.379 0.000 0.759 -h chase 0.586 0.862 0.034 -_ song 0.920 0.411 0.066 -x aggressivemount 0.379 0.552 0.483 -_ bite 0.950 0.517 0.311 -_ dig 0.207 0.000 0.000 -_ socialgrooming 0.241 0.586 0.379 -_ xb 0.621 0.690 0.621 -_ follow 0.966 1.000 0.000 -_ feeding 0.000 0.828 0.414 -r run 0.310 0.207 0.793 -_ stand 0.724 0.517 1.000 -_ freeze 0.759 0.828 0.552 -_ wildsniff 0.000 0.828 0.759 -_ manipulation 0.931 0.552 0.310 -_ social_grooming 0.000 0.276 0.000 -t tail_rattling 0.552 0.172 0.207 -y intruder_attacks 0.000 0.483 0.759 -r tail_rattle 0.448 0.241 1.000 -_ dash 0.500 0.862 0.983 -c mice_close 0.500 0.862 0.983 -a attack_frontal 0.759 1.000 0.966 -x attack_dorsal 0.552 1.000 0.655 -u intruder_out 1.000 0.310 0.931 -_ circle 0.586 0.621 0.000 -_ aggro 0.310 0.379 0.414 -_ tailrattle 0.793 0.000 1.000 -_ L473nm 0.345 0.172 0.000 -_ L593nm 0.207 0.862 0.000 -c interaction 0.793 0.414 0.828 -_ standing 0.500 0.707 1.000 -j jumping 0.387 0.927 0.428 -c crouch 0.957 0.699 0.365 -_ push 0.773 0.223 0.772 -j jump 1.000 0.500 0.793 -_ push_or_paws_out 0.500 1.000 1.000 -_ stand_stretched 0.500 0.828 1.000 -p pushing 0.569 0.776 1.000 -_ singing 0.276 0.276 0.414 -_ USV 0.862 0.103 0.379 -_ genital_groom 0.379 0.759 0.310 -_ Anogenital_Sniffing 1.000 0.448 0.586 -_ Other_items_of_interest 0.172 0.552 0.655 -_ White_Mouse_Anogenital_Sniffing 0.000 0.000 0.517 -_ Investigation 0.621 0.379 0.379 -_ Freezing_Thigmotaxis 0.586 0.448 0.759 -_ Chasing 0.655 0.483 0.241 -_ Mounting_aggressive_grooming 1.000 0.379 0.276 -_ Self_grooming 0.000 1.000 0.724 -_ Escape 0.793 1.000 0.483 diff --git a/bento/data_builder/util/loadAnnotFileObserver.m b/bento/data_builder/util/loadAnnotFileObserver.m index c41059a..a3044d7 100644 --- a/bento/data_builder/util/loadAnnotFileObserver.m +++ b/bento/data_builder/util/loadAnnotFileObserver.m @@ -1,5 +1,6 @@ function [annot, maxTime] = loadAnnotFileObserver(cleantext, fr) + cleantext = cellfun(@(x) regexprep(x,{'\"','(',')'},''), cleantext, 'uniformoutput',false); headers = split(cleantext{1},';'); @@ -24,7 +25,7 @@ flag = 1; elseif strcmpi(event,'state stop') flag = 2; - elseif strcmpi(event,'state point') % not sure what this means. + elseif strcmpi(event,'state point') % start==stop continue else % does this ever happen?? flag = 0; diff --git a/bento/data_builder/util/loadAnnotFileXls.m b/bento/data_builder/util/loadAnnotFileXls.m new file mode 100644 index 0000000..4bd2534 --- /dev/null +++ b/bento/data_builder/util/loadAnnotFileXls.m @@ -0,0 +1,41 @@ +function [annot,maxTime,hotkeys,FR] = loadAnnotFileXls(fname,defaultFR, tmin,tmax) +% +% (C) Ann Kennedy, 2019 +% California Institute of Technology +% Licensing: https://github.com/annkennedy/bento/blob/master/LICENSE.txt + +if(nargin<2) + defaultFR = 1000; +elseif(nargin<3) + tmin = 1; + tmax = inf; +elseif(isstr(tmin)) + tmin = str2num(tmin); + tmax = str2num(tmax); +end +FR = nan; %framerate not usually specified in these kinds of files, return nan to let the gui decide. + +fid = fopen(fname); +if(fid==-1) + keyboard +end + +[~,sheetlist] = xlsfinfo(fname); +fid = sheetlist{1}; % let's assume there's just one sheet for now- go back and fix this later +[~,~,M] = xlsread(fname,fid); + +hotkeys=struct(); + +if(any(strcmpi(M(1,:),'Event_type'))) % it's probably an Ethovision Observer log + [annot,maxTime] = loadAnnotSheetObserver(M, defaultFR); % need to add tmin/tmax support + FR = defaultFR; +elseif(any(strcmpi(M(1,:), 'Sinuosity'))) % it's probably DeepSqueak + if defaultFR<1024 + defaultFR = 1024; + end + [annot,maxTime] = loadAnnotSheetDeepSqueak(M, defaultFR); % need to add tmin/tmax support + FR = defaultFR; +else + annot=[];maxTime=[];hotkeys=struct(); +end +end \ No newline at end of file diff --git a/bento/data_builder/util/loadAnnotSheetDeepSqueak.m b/bento/data_builder/util/loadAnnotSheetDeepSqueak.m new file mode 100644 index 0000000..e15e198 --- /dev/null +++ b/bento/data_builder/util/loadAnnotSheetDeepSqueak.m @@ -0,0 +1,47 @@ +function [annot, maxTime] = loadAnnotSheetDeepSqueak(M, defaultFR) + +headers = M(1,:); +label_index = find(strcmpi(headers,'Label')); +accepted_index = find(strcmpi(headers,'Accepted')); +time_start = find(strcmpi(headers,'Begin Time (s)')|strcmpi(headers,'BeginTime_s_')); +time_stop = find(strcmpi(headers,'End Time (s)')|strcmpi(headers,'EndTime_s_')); +cluster_index = find(strcmpi(headers,'cluster')); + +if length([label_index accepted_index time_start time_stop])~=4 + disp('something went wrong- I couldn''t find fields Label, Accepted, Begin Time, and/or End Time.'); + return +end + +annot.Ch1 = []; +tstop = 0; +for i=2:length(M) + if (isstr(M{i,accepted_index}) && ~strcmpi(M{i,accepted_index},'TRUE')) || ~M{i,accepted_index} + continue; + end + + if isempty(cluster_index) + beh = strrep(M{i, label_index},' ','_'); + else + beh = strrep(M{i, label_index},' ','_'); + beh = [beh '_' num2str(M{i,cluster_index})]; + end + beh = regexprep(beh, '[^\w]', ''); % remove bad characters + if ~isfield(annot.Ch1,beh) + annot.Ch1.(beh) = []; + end + tstart = ceil(M{i, time_start}*defaultFR); + tstop = ceil(M{i, time_stop}*defaultFR); + annot.Ch1.(beh)(end+1,:) = [tstart tstop]; +end +maxTime = tstop; + + + + + + + + + + + diff --git a/bento/data_builder/util/loadAnnotSheetObserver.m b/bento/data_builder/util/loadAnnotSheetObserver.m new file mode 100644 index 0000000..d7fe85c --- /dev/null +++ b/bento/data_builder/util/loadAnnotSheetObserver.m @@ -0,0 +1,54 @@ +function [annot, maxTime] = loadAnnotSheetObserver(M, defaultFR) + +headers = M(1,:); +behavior_name_index = find(strcmpi(headers,'behavior')); +event_type_index = find(strcmpi(headers,'event_type')); +timestamp_index = find(strcmpi(headers,'Time_Relative_sf')); + +if length([behavior_name_index event_type_index timestamp_index])~=3 + disp('something went wrong- I couldn''t find fields Behavior, Event_Type, and/or Time_Relative_sf.'); + return +end + +annot.Ch1 = []; +counts=[]; +for i=2:length(M) + event = M{i,event_type_index}; + if isnan(event) + continue; + end + if strcmpi(event,'state start') + flag = 1; + elseif strcmpi(event,'state stop') + flag = 2; + elseif strcmpi(event,'state point') % start==stop + continue + else % does this ever happen?? + flag = 0; + keyboard + end + + beh = strrep(M{i, behavior_name_index},' ','_'); + beh = regexprep(beh, '[^\w]', ''); % remove bad characters + time = ceil(M{i, timestamp_index}*defaultFR); + if ~isfield(annot.Ch1,beh) + annot.Ch1.(beh) = []; + counts.(beh) = 0; + end + if flag==1 + counts.(beh) = counts.(beh)+1; + end + annot.Ch1.(beh)(counts.(beh),flag) = time; +end +maxTime = time; + + + + + + + + + + + diff --git a/bento/data_builder/util/loadAnnotSheetTxt.m b/bento/data_builder/util/loadAnnotSheetTxt.m index d41ef5a..d30d86a 100644 --- a/bento/data_builder/util/loadAnnotSheetTxt.m +++ b/bento/data_builder/util/loadAnnotSheetTxt.m @@ -108,7 +108,7 @@ end end if((vals(2)+tmin)>=winStart && (vals(1)+tmax)<=winStop) %if this annotation is within the range we're loading - annot.(ch).(beh)(end+1,:) = min(max(vals(1:2)-winStart,1),winStop-winStart+1); + annot.(ch).(beh)(end+1,:) = min(max(vals(1:2)-winStart+1-tmin+1,0),winStop-winStart+1); if(vals(2)-tmin+1 > tmax) tmax = vals(2)-tmin+2; end diff --git a/bento/data_builder/util/loadAnyAnnot.m b/bento/data_builder/util/loadAnyAnnot.m index 26f4ee6..b83d087 100644 --- a/bento/data_builder/util/loadAnyAnnot.m +++ b/bento/data_builder/util/loadAnyAnnot.m @@ -11,29 +11,43 @@ hotkeys = struct(); switch ext - case '.xls' %old .annot format - fid = filename; - if(loadRange) - [annotations,~] = loadAnnotSheet(filename,tmin,tmax); - else - [annotations,tmax] = loadAnnotSheet(filename); - tmin = 1; - end - FR = nan; %framerate not specified in file- trust user input to gui - case '.annot' %new .annot format - fid = filename; - if(loadRange) - [annotations,~,~,FR] = loadAnnotSheetTxt(filename,tmin,tmax); - else - try - [annotations,tmin,tmax,FR] = loadAnnotSheetTxt(filename); - catch - keyboard - [annotations,tmax,tmin,fid,hotkeys] = deal([]); - errordlg(['Couldn''t load annotation file at ' filename]); + fid = filename; + if(loadRange) + [annotations,~,~,FR] = loadAnnotSheetTxt(filename,tmin,tmax); + else + try + [annotations,tmin,tmax,FR] = loadAnnotSheetTxt(filename); + catch + [annotations,tmax,tmin,fid,hotkeys] = deal([]); + errordlg(['Couldn''t load annotation file at ' filename]); + end + end + case {'.xls', '.xlsx'} + fid = filename; + if(loadRange) + [annotations, ~, hotkeys, FR] = loadAnnotFileXls(filename, defaultFR, tmin, tmax); + else + [annotations,tmax,hotkeys,FR] = loadAnnotFileXls(filename, defaultFR); + tmin = 1; + end + case '.csv' % Erin's grouped sequences + fid = filename; + M = dlmread(filename,',',1,0); + FR = 1024; % 1024 is the framerate I use for deepsqueak + t_start = ceil(M(:,3)*FR); + t_stop = ceil(M(:,4)*FR); + drop = M(:,6)<2; + t_start(drop)=[]; + t_stop(drop)=[]; + tmin = 1; + tmax = t_stop(end); + annotations.USV_clusters.all = [t_start t_stop] + 0.382; % whyyyy + for type = 0:5 + inds = M(:,end)==type; + inds(drop)=[]; + annotations.USV_clusters.(['type_' num2str(type)]) = [t_start(inds) t_stop(inds)] + round(0.382*FR); end - end % case 'csv' %temporary MUPET support % fid = filename; % M = dlmread(filename,',',1,0); @@ -42,7 +56,7 @@ % tmin = 1; % tmax = length(strtemp.audio.t); - case '.txt' %load data in the old format, OR ETHOVISION, prepare to convert to sheet format when saved + case '.txt' fid = filename; if(loadRange) [annotations,~,hotkeys,FR] = loadAnnotFile(filename,defaultFR, tmin,tmax); @@ -61,4 +75,5 @@ otherwise [annotations,tmax,tmin,fid,hotkeys] = deal([]); + errordlg(['Unrecognized extension for annotation file ' filename]); end \ No newline at end of file diff --git a/bento/data_builder/util/unpackExperiment.m b/bento/data_builder/util/unpackExperiment.m index 6298fac..7185b55 100644 --- a/bento/data_builder/util/unpackExperiment.m +++ b/bento/data_builder/util/unpackExperiment.m @@ -337,7 +337,7 @@ [y,fs] = audioread(fid); disp('Generating spectrogram...'); win = hann(1024); - [~,f,t,psd] = spectrogram(y,win,[],[],fs,'yaxis'); + [~,f,t,psd] = spectrogram(y,win,[],256,fs,'yaxis'); psd = 10*log10(abs(double(psd)+eps)); disp('Saving spectrogram for future use...'); fid = [pth strip(strip(strrep(data{i,match.Audio_file},ext,'_spectrogram.mat'),'left','.'),'left',filesep)]; diff --git a/bento/gui/readBehMovieFrame.m b/bento/gui/readBehMovieFrame.m index beea858..26b3f6e 100644 --- a/bento/gui/readBehMovieFrame.m +++ b/bento/gui/readBehMovieFrame.m @@ -36,7 +36,8 @@ time = min(time, tMax); frnum = round(time*reader{col,i}.reader.FrameRate); end - reader{col,i}.reader.currentTime = min(time, reader{col,i}.reader.Duration - 1/reader{col,i}.reader.FrameRate); + reader{col,i}.reader.currentTime = min(time + 1/reader{col,i}.FrameRate, ... + (reader{col,i}.NumFrames - 1)/reader{col,i}.FrameRate); movies{col,i} = readFrame(reader{col,i}.reader); % movies{col,i} = read(reader{col,i},frnum); diff --git a/bento/loading data/loadMovie.m b/bento/loading data/loadMovie.m index 5370214..98c85b1 100644 --- a/bento/loading data/loadMovie.m +++ b/bento/loading data/loadMovie.m @@ -31,10 +31,15 @@ % % inaccurate! trust the experimenter instead. % tMax = reader{col,i}.numFrames/Fr; otherwise - reader{col,i}.reader = VideoReader([data.io.movie.fid{col,i}]); - reader{col,i}.reader.CurrentTime = 0; - Fr = reader{col,i}.reader.FrameRate; - tMax = reader{col,i}.reader.Duration; + rtemp = VideoReader([data.io.movie.fid{col,i}]); + rtemp.CurrentTime = 0; + Fr = rtemp.FrameRate; + tMax = rtemp.Duration; + + reader{col,i}.reader = rtemp; + reader{col,i}.FrameRate = Fr; + reader{col,i}.Duration = tMax; + reader{col,i}.NumFrames = rtemp.NumFrames; timestamps = getVideoTimestamps(data.io.movie.fid{col,i}); if timestamps diff --git a/bento/loading data/unpackAnnotFromLoader.m b/bento/loading data/unpackAnnotFromLoader.m index 70ae30d..fcbe8ec 100644 --- a/bento/loading data/unpackAnnotFromLoader.m +++ b/bento/loading data/unpackAnnotFromLoader.m @@ -30,12 +30,7 @@ if(subFrames) [atemp,tmax(j),tmin(j),FR,fid{j},~] = loadAnyAnnot([pth annoList{j}], defaultFR, startAnno,stopAnno); else - try - [atemp,tmax(j),tmin(j),FR,fid{j},~] = loadAnyAnnot([pth annoList{j}], defaultFR); - catch - disp(['Couldn''t load annotations at ' pth annoList{j}]); - keyboard - end + [atemp,tmax(j),tmin(j),FR,fid{j},~] = loadAnyAnnot([pth annoList{j}], defaultFR); end % generate a filename to save modified annotations diff --git a/bento/loading data/unpackCaData.m b/bento/loading data/unpackCaData.m index 37bd433..b9d2c12 100644 --- a/bento/loading data/unpackCaData.m +++ b/bento/loading data/unpackCaData.m @@ -9,7 +9,7 @@ [~,fname,ext] = fileparts(pth); disp(['Loading Ca file ' fname '...']); -[rast,time,ROIs]=deal([]); +[rast,time,ROIs,spikes]=deal([]); % add cases to this switch statement for other data types~? switch ext case '.csv' @@ -21,6 +21,7 @@ % rast(i+1,:) = temp{6}(temp{1}==i); % rast(i+1,temp{5}(temp{1}==i)==0)=nan; % end + temp = readtable(pth); if(size(temp,2)==5) % it's an Anderson lab miniscope file time = temp(:,1)'; diff --git a/bento/tracking/tracking unpackers/MARS.m b/bento/tracking/tracking unpackers/MARS.m index 2655a4e..42fee3d 100644 --- a/bento/tracking/tracking unpackers/MARS.m +++ b/bento/tracking/tracking unpackers/MARS.m @@ -10,7 +10,7 @@ v = data.keypoints{fr}; v = permute(reshape(v,[2 2 7]),[2 1 3]); else - v = squeeze(data.keypoints(fr,:,:,:)); + v = permute(data.keypoints(fr,:,:,:),[2,3,4,1]); end pts={};count=0; diff --git a/bento/tracking/tracking unpackers/MARS_bbox_only.m b/bento/tracking/tracking unpackers/MARS_bbox_only.m index bacc31c..f487e13 100644 --- a/bento/tracking/tracking unpackers/MARS_bbox_only.m +++ b/bento/tracking/tracking unpackers/MARS_bbox_only.m @@ -5,12 +5,14 @@ if(iscell(data.bbox)) %support for both jsondecode and loadjson b = data.bbox{fr}; else - b = squeeze(data.bbox(:,:,fr)); + b = permute(data.bbox(fr,:,:),[2,3,1]); + end + + pts = {[]}; + for i = 1:size(b,1) + bx = [b(i,[1 3 3 1 1]); b(i,[2 2 4 4 2])]; + pts{1} = [pts{1} [i bx(:)']]; end - b1 = [b(1,[1 3 3 1 1]); b(1,[2 2 4 4 2])]; - b2 = [b(2,[1 3 3 1 1]); b(2,[2 2 4 4 2])]; - - pts = {[1 b1(:)'] [2 b2(:)']}; if(isfield(data,'crop_bounds') && ~isempty(data.crop_bounds)) % support for multi-arena MARS bounds = data.crop_bounds;