function dF_saveAsOMETiff(dFLIMImageData,dFLIMXYTData,dFLIMHistoData) % dFLIM.ome.tif format % save a single dFLIM acquisition (contents of a .dFLIM.mat file) to % the OME-Tiff format. % series 0 = dFLIM_Image (XYCTZ, with subchannels of C) % series 1 = dFLIM_XYT (XYtCZ) % series 2 = dFLIM_Histo (xyCTZ, where x=t and y=1) % % Gary Yellen 2014-10-31 %% OME PEOPLE: THIS FILE IS INCLUDED ONLY AS AN EXAMPLE OF USING THE % OMETiffWriter class. It requires data classes that are particular % to our lab, but hopefully will help you see both the standard and % non-standard uses of the OMETiffWriter. %% global state savedHeaderString % DEBUG OPTIONS useAnnotations=1; useDatasets=1 && useAnnotations; useIRFAnnotations=0 && useAnnotations; usePlaneDeltaAnnotation=1; %% give the user a chance to set the save name fileName=''; [fname, path]=uiputfile('*.ome.tif','Save data to OME-Tiff as...',fileName); fname=[path fname]; fname0=fname(1:end-8); % without the extension % use the OMETiffWriter_gy object for writing OTW = OMETiffWriter_gy(fname,'FluorescenceLifetime','Peredox in DGN with stimulation'); %% first figure out the sizes and numbers to be saved sz=size(dFLIMImageData); % number of Z slices if numel(sz)<3 szZ=1; else szZ=sz(3); end % number of dFLIM channels used = arrayfun(@(x) x.nPixels~=0, dFLIMImageData); nChan = sum(used(:,1,1)); channelsAvail = find(arrayfun(@(x) ~isempty(x.img), dFLIMImageData(:,1,1))'); % size of images typicalIndex = find(used,1,'first'); typicalDFI=dFLIMImageData(typicalIndex); szX = typicalDFI.nPixels; szY = typicalDFI.nLines; nFrames = sz(2); % histo info nBins = numel(dFLIMHistoData(typicalIndex).Data); % XYT available? xytAvail = ~isempty(dFLIMXYTData) && ~isempty(dFLIMXYTData(channelsAvail(1),1).Data); tic; %% Create metadata stateVal = @(x) valueFromHeaderString(x,savedHeaderString); toInt = @(x) ome.xml.model.primitives.PositiveInteger(java.lang.Integer(x)); toNNInt = @(x) ome.xml.model.primitives.NonNegativeInteger(java.lang.Integer(x)); %% experiment annotation if useAnnotations expt=state.exptNotes; % --- Comment annotations OTW.addImageAnnotation('Comment','Genotype',expt.prep.genotype); OTW.addImageAnnotation('Comment','Gender',expt.prep.gender); OTW.addImageAnnotation('Comment','Fluor1',expt.prep.fluor1); OTW.addImageAnnotation('Comment','Vector1',expt.prep.vector1); OTW.addImageAnnotation('Comment','Fluor1Note',expt.prep.vectorNote1); OTW.addImageAnnotation('Comment','Fluor2',expt.prep.fluor2); OTW.addImageAnnotation('Comment','Vector2',expt.prep.vector2); OTW.addImageAnnotation('Comment','Fluor2Note',expt.prep.vectorNote2); OTW.addImageAnnotation('Comment','Fluor3',expt.prep.fluor3); OTW.addImageAnnotation('Comment','Vector3',expt.prep.vector3); OTW.addImageAnnotation('Comment','Fluor3Note',expt.prep.vectorNote3); OTW.addImageAnnotation('Comment','InjectionSite',expt.prep.injectionSite); OTW.addImageAnnotation('Comment','Surgery',expt.prep.surgery); OTW.addImageAnnotation('Comment','SliceNotes',expt.prep.sliceNotes); OTW.addImageAnnotation('Comment','PerfusionEvents',toString(expt.perfusion.events)); OTW.addImageAnnotation('Comment','Anesthesia',toString(expt.anesthesia.events)); psetname=stateVal('state.pulses.pulseSetName'); if ~isempty(psetname) OTW.addImageAnnotation('Comment','PulseSetName',psetname); end % --- Timestamp annotations OTW.addImageAnnotation('Timestamp','SliceTime',expt.prep.sliceTime); % --- Double Annotations % first the non-channel Double-valued annotations OTW.addImageAnnotation('Double','AgeNow',expt.prep.ageNow); OTW.addImageAnnotation('Double','AgeInj',expt.prep.ageInj); if ~isempty(stateVal('state.motor.absXPosition')) spos={'absX' 'absY' 'absZ' 'relX' 'relY' 'relZ'} ; for k=1:numel(spos) posval = stateVal(['state.motor.' spos{k} 'Position']); OTW.addImageAnnotation('Double',['Position:' spos{k}],posval); end end for k=1:6 % possible lineMonitor settings lmname = stateVal(['state.lm.paramName' num2str(k)]); if ~isempty(lmname) lmvalue = stateVal(['state.lm.paramValue' num2str(k)]); OTW.addImageAnnotation('Double',['LM:' lmname],lmvalue); end end cycpos = stateVal('state.cycle.currentCyclePosition'); if ~isempty(cycpos) OTW.addImageAnnotation('Double','CyclePosition',cycpos); end zspace = stateVal('state.acq.zStepSize'); if ~isempty(zspace) OTW.addImageAnnotation('Double','Z-Spacing',zspace); end % now set the TPeakIRF annotations for each included channel if useIRFAnnotations % TODO get these from the DFI object? tPeakIRFannot=zeros(8,1); for ch=channelsAvail chID = num2str(ch,'%02i'); idx = OTW.addAnnotation('Double',['TPeakIRF:Ch' chID],state.dFLIM.dChannels{ch}.tPeakIRF); tPeakIRFannot(ch)=idx; end end % if useIRFAnnotations % --- Boolean Annotations tf=stateVal('state.acq.lineScan'); if ~isempty(tf), tf=0; end OTW.addImageAnnotation('Boolean','Linescan[X vs T]',tf); tf=stateVal('state.acq.bidi'); if ~isempty(tf), tf=0; end OTW.addImageAnnotation('Boolean','BidirectionalScan',tf); % XML annotation OTW.addImageAnnotation('XML','exptNotes', makeXML('exptNotes',state.exptNotes, ... {'dichroics','emfilters','objectives','gh','genotypes','perfusionEvents','anesthesiaEvents','fluors','vectors','configGlobals','globalGUIPairs'} ... )); end % if useAnnotations %% instrument and experiment metadata OTW.metadata.setInstrumentID('Instrument:0',0); OTW.metadata.setMicroscopeManufacturer('Thorlabs',0); OTW.metadata.setMicroscopeModel(state.exptNotes.optics.instrument,0); OTW.metadata.setObjectiveID('Objective:0',0,0) OTW.metadata.setObjectiveModel(state.exptNotes.optics.objective,0,0); OTW.metadata.setObjectiveNominalMagnification(java.lang.Double(state.exptNotes.optics.mag),0,0); idxFilter=0; channelFilter(1:8)=-1; for chan=channelsAvail ch = 1+mod(chan-1,4); if channelFilter(ch)>=0 % already assigned channelFilter(chan)=channelFilter(ch); else OTW.metadata.setFilterID(['Filter:' num2str(ch)],0,idxFilter); OTW.metadata.setFilterModel(state.exptNotes.channels{ch}.em,0,idxFilter); OTW.metadata.setDichroicID(['Dichroic:' num2str(ch)],0,idxFilter); OTW.metadata.setDichroicModel(state.exptNotes.channels{ch}.dc,0,idxFilter); channelFilter(chan)=idxFilter; idxFilter = idxFilter+1; end end %% create the Dataset references if useDatasets OTW.metadata.setDatasetID('Dataset:All',0); OTW.metadata.setDatasetName('BigExperimentToday',0); OTW.metadata.setDatasetID('Dataset:dfi',1); OTW.metadata.setDatasetName('BigExperimentToday_DFI',1); OTW.metadata.setDatasetID('Dataset:xyt',2); OTW.metadata.setDatasetName('BigExperimentToday_XYT',2); OTW.metadata.setDatasetID('Dataset:dfh',3); OTW.metadata.setDatasetName('BigExperimentToday_DFH',3); end %% create the metadata for each series for series=0:2 sSeries = num2str(series); % a modest provision for cases where acqTime does not include the date. acqdate=state.dFLIM.acqTime; if isempty(strfind(acqdate,'201')) % no year included datepos=strfind(state.dFLIM.filename,'_201'); if ~isempty(datepos) % try using the last instance acqdate2=state.dFLIM.filename(datepos(end)+1:datepos(end)+8); acqdate2=[acqdate2(1:4) '-' acqdate2(5:6) '-' acqdate2(7:8) ' ' acqdate]; try datenum(acqdate2); acqdate=acqdate2; catch disp('Warning: Could not infer AcquisitionDate from filename'); end else disp('Warning: Could not infer AcquisitionDate from filename'); end end % Set pixels type if usePlaneDeltaAnnotation && series~=1 msPerLine = stateVal('state.acq.msPerLine'); if isempty(msPerLine), msPerLine = 2.080; end frameDeltaT = szY * msPerLine/1000; else frameDeltaT = 0; end switch series case 0 % dFLIMImages szXYZCT = [szX szY szZ 3*nChan nFrames]; imageDescription = ['dFLIM_Images from ' state.dFLIM.filename]; OTW.defineSeries(acqdate, imageDescription, 'XYCTZ', szXYZCT, 'uint16', frameDeltaT); OTW.metadata.setImageInstrumentRef('Instrument:0',series); if useDatasets OTW.metadata.setDatasetImageRef(['Image:' sSeries],0,series); OTW.metadata.setDatasetImageRef(['Image:' sSeries],1,series); end case 1 % XYT if xytAvail szXYZCT = [szX szY szZ nChan nBins]; else szXYZCT = [0 0 0 0 0]; end %OMEXMLService.addModuloAlong(md, coreMetadata, series); % % interferes with LightPath in 2nd and 3rd series imageDescription = ['dFLIM_XYTs from ' state.dFLIM.filename]; OTW.defineSeries(acqdate, imageDescription, 'XYTCZ', szXYZCT, 'uint16', frameDeltaT); OTW.metadata.setImageInstrumentRef('Instrument:0',series); if useDatasets OTW.metadata.setDatasetImageRef(['Image:' sSeries],0,series); OTW.metadata.setDatasetImageRef(['Image:' sSeries],2,series); end case 2 % Histos szXYZCT = [nBins 1 szZ nChan nFrames]; imageDescription = ['dFLIM_Histos from ' state.dFLIM.filename]; OTW.defineSeries(acqdate, imageDescription, 'XYCTZ', szXYZCT, 'double', frameDeltaT); OTW.metadata.setImageInstrumentRef('Instrument:0',series); if useDatasets OTW.metadata.setDatasetImageRef(['Image:' sSeries],0,series); OTW.metadata.setDatasetImageRef(['Image:' sSeries],3,series); end end % switch series (size assignment) % Set channels ID and samples per pixel suffix={':img' ':nmg' ':tmg'}; for i = 1:numel(channelsAvail) chan = channelsAvail(i); chID = num2str(chan,'%02i'); laserChoice = num2str(1 + floor(chan/5)); if (series)==0 for j=1:3 ch = (i-1)*3+j-1; chname = ['dChannel:' chID suffix{j}]; OTW.defineChannel(chname, series, ch); % next line intended to be setChannelAnnotationRef (but it doesn't work properly) if useIRFAnnotations, OTW.metadata.setChannelAnnotationRef(OTW.metadata.getDoubleAnnotationID(tPeakIRFannot(chan)),series,ch,tPeakIRFannot(chan)); end OTW.metadata.setLightPathEmissionFilterRef(['Filter:' num2str(1+mod(chan-1,4))],series,ch,channelFilter(chan)); OTW.metadata.setLightPathDichroicRef(['Dichroic:' num2str(1+mod(chan-1,4))],series,ch); OTW.metadata.setChannelExcitationWavelength(toInt(state.exptNotes.optics.(['laser' laserChoice])),series,ch); pcellsetting = stateVal(['state.pcell.pcellScanning' laserChoice]); if ~isempty(pcellsetting) OTW.metadata.setChannelPockelCellSetting(java.lang.Integer(100*pcellsetting),series,ch); end end else ch = (i-1); chname = ['dChannel:' chID ]; OTW.defineChannel(chname, series, ch); % next line intended to be setChannelAnnotationRef (but it doesn't work properly) if useIRFAnnotations, OTW.metadata.setChannelAnnotationRef(OTW.metadata.getDoubleAnnotationID(tPeakIRFannot(chan)),series,ch,tPeakIRFannot(chan)); end OTW.metadata.setLightPathDichroicRef(['Dichroic:' num2str(1+mod(chan-1,4))],series,ch); OTW.metadata.setLightPathEmissionFilterRef(['Filter:' num2str(1+mod(chan-1,4))],series,ch,channelFilter(chan)); OTW.metadata.setChannelExcitationWavelength(toInt(state.exptNotes.optics.(['laser' laserChoice])),series,ch); pcellsetting = stateVal(['state.pcell.pcellScanning' laserChoice]); if ~isempty(pcellsetting) OTW.metadata.setChannelPockelCellSetting(java.lang.Integer(100*pcellsetting),series,ch); end end end % for channels avail end % for series % add the ModuloAlongT annotation to series 1 OTW.finalizeImageAnnotations(); % have to do this last! can't add annotations after this OTW.defineModuloAlongT(1 , 0, state.dFLIM.nsPerPoint, 255*state.dFLIM.nsPerPoint); OTW.finalizeMetadata(); %% metadata are created, now write the metadata and the data verbose=0; %% SERIES 0 [dFLIM_Images] % define the data conversion index=0; for z=1:szZ for frame=1:nFrames for ch=channelsAvail if verbose infostr=['DFI: z=' num2str(z) '/' num2str(szZ) '; ' ... 'fr=' num2str(frame) '/' num2str(nFrames) '; ' ... 'dChannel=' num2str(ch) ' [' num2str(find(ch==channelsAvail)) '/' num2str(nChan) ']; ']; disp([num2str(index) ' ' infostr]); end % get the data dfi = dFLIMImageData(ch,frame,z); OTW.writePlane(uint16(dfi.img),index==0); OTW.writePlane(uint16(dfi.nmg)); OTW.writePlane(uint16(dfi.tmg)); index = index + 3; end end end %% SERIES 1 [dFLIM_XYTs] index=0; for z=1:szZ for ch=channelsAvail % get the xyt data structure (not shifted!!) dfxyt = dFLIMXYTData(ch,1,z).Data; dfxyt = permute(dfxyt,[2 3 1]); % this structure is ordered y/x/bin for bin=1:nBins if verbose infostr=['XYT: z=' num2str(z) '/' num2str(szZ) '; ' ... 'bin=' num2str(bin) '/' num2str(nBins) '; ' ... 'dChannel=' num2str(ch) ' [' num2str(find(ch==channelsAvail)) '/' num2str(nChan) ']; ']; disp([num2str(index) ' ' infostr]); end OTW.writePlane(dfxyt(:,:,bin),index==0); index=index+1; end end end %% SERIES 2 [dFLIM_Histos] index=0; for z=1:szZ for frame=1:nFrames for ch=channelsAvail % get the histo data structure (not shifted!!) dfh = dFLIMHistoData(ch,frame,z); if verbose infostr=['Histo: z=' num2str(z) '/' num2str(szZ) '; ' ... 'fr=' num2str(frame) '/' num2str(nFrames) '; ' ... 'dChannel=' num2str(ch) ' [' num2str(find(ch==channelsAvail)) '/' num2str(nChan) ']; ']; disp([num2str(index) ' ' infostr]); end OTW.writePlane(dfh.Data(:)',index==0); index=index+1; end end end OTW.close; toc; end % function [dF_saveDataToOMETiff] function str = toString(cellStrings) str=''; for k=1:numel(cellStrings) str = [str cellStrings{k}]; if k