source: trunk/src/series/sub_background.m @ 501

Last change on this file since 501 was 478, checked in by sommeria, 12 years ago

series fcts updated to fit with new waitbar fct and background run mode, and various bug repairs

File size: 20.8 KB
RevLine 
[457]1%'sub_background': substract a sliding background to an image series
2% This is an example of action on a series of input images
[169]3%------------------------------------------------------------------------
[24]4% Method:
5    %calculate the background image by sorting the luminosity of each point
6    % over a sliding sub-sequence of 'nbaver_ima' images.
7    % The luminosity value of rank 'rank' is selected as the
8    % 'background'. rank=nbimages/2 gives the median value.  Smaller values are appropriate
9    % for a dense set of particles. The extrem value rank=1 gives the true minimum
10    % luminosity, but it can be polluted by noise.
11% Organization of image indices:
[454]12    % The program is working on a series of images,
13    % The processing can be done over groups of nbfield2 consecutive files in slices (parameter NbSlice)
14    % In the mode 'volume', nbfield2=1 (1 image at each level)and NbSlice (=nbfield_j)
15    % Else nbfield2=nbfield_j =nbre of images in a burst (j index)
[451]16   
[457]17% function GUI_config=sub_background(Param)
[169]18%
[451]19%%%%%%%%%%% GENERAL TO ALL SERIES ACTION FCTS %%%%%%%%%%%%%%%%%%%%%%%%%%%
[457]20%
21% This function is used in four modes by the GUI series:
22%           1) config GUI: with no input argument, the function determine the suitable GUI configuration
23%           2) interactive input: the function is used to interactively introduce input parameters, and then stops
24%           3) RUN: the function itself runs, when an appropriate input  structure Param has been introduced.
25%           4) BATCH: the function itself proceeds in BATCH mode, using an xml file 'Param' as input.
26%
[451]27%OUTPUT
[454]28% GUI_series_config=list of options in the GUI series.fig needed for the function
[451]29%
30%INPUT:
31% In run mode, the input parameters are given as a Matlab structure Param copied from the GUI series.
32% In batch mode, Param is the name of the corresponding xml file containing the same information
33% In the absence of input (as activated when the current Action is selected
[454]34% in series), the function ouput GUI_series_config set the activation of the needed GUI elements
[451]35%
36% Param contains the elements:(use the menu bar command 'export/GUI config' in series to see the current structure Param)
37%    .InputTable: cell of input file names, (several lines for multiple input)
38%                      each line decomposed as {RootPath,SubDir,Rootfile,NomType,Extension}
39%    .OutputSubDir: name of the subdirectory for data outputs
[474]40%    .OutputDirExt: extension for the directory for data outputs
[451]41%    .Action: .ActionName: name of the current activated function
42%             .ActionPath:   path of the current activated function
43%    .IndexRange: set the file or frame indices on which the action must be performed
44%    .FieldTransform: .TransformName: name of the selected transform function
45%                     .TransformPath:   path  of the selected transform function
46%                     .TransformHandle: corresponding function handle
47%    .InputFields: sub structure describing the input fields withfields
48%              .FieldName: name of the field
49%              .VelType: velocity type
50%              .FieldName_1: name of the second field in case of two input series
51%              .VelType_1: velocity type of the second field in case of two input series
52%    .ProjObject: %sub structure describing a projection object (read from ancillary GUI set_object)
53%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54
55   
[459]56function ParamOut=sub_background (Param)
[24]57
[451]58%% set the input elements needed on the GUI series when the action is selected in the menu ActionName
59if ~exist('Param','var') % case with no input parameter
[474]60    ParamOut={'AllowInputSort';'off';...% allow alphabetic sorting of the list of input files (options 'off'/'on', 'off' by default)
[457]61        'WholeIndexRange';'on';...% prescribes the file index ranges from min to max (options 'off'/'on', 'off' by default)
[24]62        'NbSlice';'on'; ...%nbre of slices ('off' by default)
[451]63        'VelType';'off';...% menu for selecting the velocity type (options 'off'/'one'/'two',  'off' by default)
64        'FieldName';'off';...% menu for selecting the field (s) in the input file(options 'off'/'one'/'two', 'off' by default)
65        'FieldTransform'; 'off';...%can use a transform function
66        'ProjObject';'off';...%can use projection object(option 'off'/'on',
67        'Mask';'off';...%can use mask option   (option 'off'/'on', 'off' by default)
[454]68        'OutputDirExt';'.sback';...%set the output dir extension
[24]69               ''};
[451]70        return
[24]71end
72
[451]73%%%%%%%%%%%% STANDARD PART (DO NOT EDIT) %%%%%%%%%%%%
[457]74%% select different modes,  RUN, parameter input, BATCH
[451]75% BATCH  case: read the xml file for batch case
[457]76if ischar(Param)
[454]77        Param=xml2struct(Param);
78        checkrun=0;
[457]79% RUN case: parameters introduced as the input structure Param
[454]80else
[374]81    hseries=guidata(Param.hseries);%handles of the GUI series
[462]82    if isfield(Param,'Specific')&& strcmp(Param.Specific,'?')
83        checkrun=1;% will search input parameters (preparation of BATCH mode)
84    else
85        checkrun=2; % indicate the RUN option is used
86    end
[374]87end
[462]88ParamOut=Param; %default output
[474]89OutputDir=[Param.OutputSubDir Param.OutputDirExt];
[457]90
91%% root input file(s) and type
92RootPath=Param.InputTable(:,1);
93RootFile=Param.InputTable(:,3);
94SubDir=Param.InputTable(:,2);
95NomType=Param.InputTable(:,4);
96FileExt=Param.InputTable(:,5);
[451]97[filecell,i1_series,i2_series,j1_series,j2_series]=get_file_series(Param);
[474]98%%%%%%%%%%%%
99% The cell array filecell is the list of input file names, while
100% filecell{iview,fileindex}:
[451]101%        iview: line in the table corresponding to a given file series
102%        fileindex: file index within  the file series,
103% i1_series(iview,ref_j,ref_i)... are the corresponding arrays of indices i1,i2,j1,j2, depending on the input line iview and the two reference indices ref_i,ref_j
104% i1_series(iview,fileindex) expresses the same indices as a 1D array in file indices
[474]105%%%%%%%%%%%%
[451]106NbSlice=1;%default
[457]107if isfield(Param.IndexRange,'NbSlice')&&~isempty(Param.IndexRange.NbSlice)
[451]108    NbSlice=Param.IndexRange.NbSlice;
[54]109end
[454]110nbview=numel(i1_series);%number of input file series (lines in InputTable)
111nbfield_j=size(i1_series{1},1); %nb of fields for the j index (bursts or volume slices)
112nbfield_i=size(i1_series{1},2); %nb of fields for the i index
113nbfield=nbfield_j*nbfield_i; %total number of fields
114nbfield_i=floor(nbfield/NbSlice);%total number of  indexes in a slice (adjusted to an integer number of slices)
115nbfield=nbfield_i*NbSlice; %total number of fields after adjustement
[451]116
117%determine the file type on each line from the first input file
[454]118ImageTypeOptions={'image','multimage','mmreader','video'};%allowed input file types(images)
[451]119
[454]120[FileType{1},FileInfo{1},MovieObject{1}]=get_file_type(filecell{1,1});
121CheckImage{1}=~isempty(find(strcmp(FileType,ImageTypeOptions)));% =1 for images
122if ~isempty(j1_series{1})
123    frame_index{1}=j1_series{1};
124else
125    frame_index{1}=i1_series{1};
126end
[451]127
128%% calibration data and timing: read the ImaDoc files
129%not relevant here
130
131%% check coincidence in time for several input file series
132%not relevant here
133
134%% coordinate transform or other user defined transform
135%not relevant here
136
137%%%%%%%%%%%% END STANDARD PART  %%%%%%%%%%%%
138 % EDIT FROM HERE
139
140 %% check the validity of  input file types
[454]141if CheckImage{1}
[451]142    FileExtOut='.png'; % write result as .png images for image inputs
[454]143    if strcmp(lower(NomType{1}(end)),'a')
144        NomTypeOut=NomType{1};%case of letter appendix
[462]145    elseif isempty(j1_series)
146        NomTypeOut='_1';
[454]147    else
148        NomTypeOut='_1_1';% caseof purely numerical indexing
149    end
[451]150else
151    msgbox_uvmat('ERROR',['invalid file type input: ' FileType{1} ' not an image'])
[54]152    return
153end
154
[451]155%% Set field names and velocity types
156%not relevant here
157
158%% Initiate output fields
159%not relevant here
160 
161%%% SPECIFIC PART BEGINS HERE
162NbSlice=Param.IndexRange.NbSlice; %number of slices
[454]163%siz=size(i1_series);
[24]164nbaver_init=23;%approximate number of images used for the sliding background: to be adjusted later to include an integer number of bursts
[394]165j1=[];%default
[24]166
[214]167%% adjust the proposed number of images in the sliding average to include an integer number of bursts
[454]168if nbfield_i~=1
169    nbaver=floor(nbaver_init/nbfield_j); % number of bursts used for the sliding background,
[24]170    if isequal(floor(nbaver/2),nbaver)
171        nbaver=nbaver+1;%put the number of burst to an odd number (so the middle burst is defined)
172    end
[454]173    nbaver_init=nbaver*nbfield_j;%propose by default an integer number of bursts
[24]174end
175
[457]176%% input of specific parameters
177if checkrun %get specific parameters interactively
[454]178    prompt = {'volume scan mode (Yes/No)';'Number of images for the sliding background (MUST FIT IN COMPUTER MEMORY)';...
179        'the luminosity rank chosen to define the background (0.1=for dense particle seeding, 0.5 (median) for sparse particles'};
[478]180    dlg_title = ['get (slice by slice) a sliding background and substract to each image, result in subdir ' OutputDir];
[454]181    num_lines= 3;
182    def     = { 'No';num2str(nbaver_init);'0.1'};
183    answer = inputdlg(prompt,dlg_title,num_lines,def);
184   
185    %check input consistency
186    if strcmp(answer{1},'No') && ~isequal(NbSlice,1)
187        check=msgbox_uvmat('INPUT_Y-N',['confirm the multi-level splitting into ' num2str(NbSlice) ' slices']);
188        if ~strcmp(check,'Yes')
189            return
190        end
191    end
192    if strcmp(answer{1},'Yes')
193        step=1;
194    else
195        step=nbfield_j;%case of bursts: the sliding background is shifted by the length of one burst
196    end
197    nbaver_ima=str2num(answer{2});%number of images for the sliding background
198    nbaver=ceil(nbaver_ima/step);%number of bursts for the sliding background
199    if isequal(floor(nbaver/2),nbaver)
200        nbaver=nbaver+1;%set the number of bursts to an odd number (so the middle burst is defined)
201    end
202    nbaver_ima=nbaver*step;
203    if nbaver_ima > nbfield
204        msgbox_uvmat('ERROR','number of images in a slice smaller than the proposed number of images for the sliding average')
205        return
206    end
[459]207    ParamOut.Specific.CheckVolume=strcmp(answer{1},'Yes');
208    ParamOut.Specific.SlidingSequenceSize=nbaver_ima;
209    ParamOut.Specific.BrightnessRankThreshold=str2num(answer{3});
[454]210   
211    % apply the image rescaling function 'level' (avoid the blinking effects of bright particles)
212    answer=msgbox_uvmat('INPUT_Y-N','apply image rescaling function levels.m after sub_background');
[459]213    ParamOut.Specific.CheckLevelTransform=strcmp(answer,'Yes');
214    if checkrun==1
[457]215         return
216    end
217    %%%%%%%%%%%%%%%%%%%%%%  STOP HERE FOR PAMETER INPUT MODE  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[24]218else
[459]219    if isequal(Param.Specific.CheckVolume,1)
[454]220        step=1;
[24]221    else
[454]222        step=nbfield_j;%case of bursts: the sliding background is shifted by the length of one burst
[24]223    end
[459]224    nbaver_ima=Param.Specific.SlidingSequenceSize;%number of images for the sliding background
[454]225    nbaver=ceil(nbaver_ima/step);%number of bursts for the sliding background
226    if isequal(floor(nbaver/2),nbaver)
227        nbaver=nbaver+1;%set the number of bursts to an odd number (so the middle burst is defined)
228    end
229    nbaver_ima=nbaver*step;
230    if nbaver_ima > nbfield
231        msgbox_uvmat('ERROR','number of images in a slice smaller than the proposed number of images for the sliding average')
[24]232        return
233    end
234end
[454]235
236% calculate absolute brightness rank
[459]237rank=floor(ParamOut.Specific.BrightnessRankThreshold*nbaver_ima);
[24]238if rank==0
239    rank=1;%rank selected in the sorted image series
240end
241
[239]242%% prealocate memory for the sliding background
[394]243try
[454]244    Afirst=read_image(filecell{1,1},FileType{1},MovieObject{1},frame_index{1}(1));
245    [npy,npx]=size(Afirst);
246    if strcmp(class(Afirst),'uint8') % case of 8bit images
247        Ak=zeros(npy,npx,nbaver_ima,'uint8'); %prealocate memory
248        Asort=zeros(npy,npx,nbaver_ima,'uint8'); %prealocate memory
249    else
250        Ak=zeros(npy,npx,nbaver_ima,'uint16'); %prealocate memory
251        Asort=zeros(npy,npx,nbaver_ima,'uint16'); %prealocate memory
252    end
[239]253catch ME
254    msgbox_uvmat('ERROR',ME.message)
255    return
256end
257
[442]258%% update the xml file
[454]259% SubDirBase=regexprep(Param.InputTable{1,2},'\..*','');%take the root part of SubDir, before the first dot '.'
260% filexml=fullfile(RootPath{1},[SubDirBase '.xml']);
261% if ~exist(filexml,'file') && exist([fullfile(RootPath{1},SubDir{1},RootFile{1}) '.xml'],'file')% xml inside the image directory
262%     copyfile([filebase '.xml'],filexml);% copy the .xml file
263% end
264% if exist(filexml,'file')
265%     t=xmltree(filexml); 
266%     %update information on the first image name in the series
267%     uid_Heading=find(t,'ImaDoc/Heading');
268%     if isempty(uid_Heading)
269%         [t,uid_Heading]=add(t,1,'element','Heading');
270%     end
271%     uid_ImageName=find(t,'ImaDoc/Heading/ImageName');
272%     if ~isempty(j1_series{1})
273%         j1=j1_series{1}(1);
274%     end
275%     ImageName=fullfile_uvmat([dir_images term],'',RootFile{1},'.png',NomType,i1_series(1,1),[],j1);
276%     [pth,ImageName]=fileparts(ImageName);
277%     ImageName=[ImageName '.png'];
278%     if isempty(uid_ImageName)
279%         [t,uid_ImageName]=add(t,uid_Heading,'element','ImageName');
280%     end
281%     uid_value=children(t,uid_ImageName);
282%     if isempty(uid_value)
283%         t=add(t,uid_ImageName,'chardata',ImageName);%indicate  name of the first image, with ;png extension
284%     else
285%         t=set(t,uid_value(1),'value',ImageName);%indicate  name of the first image, with ;png extension
286%     end
287%     
288%     %add information about image transform
289%     [t,new_uid]=add(t,1,'element','ImageTransform');
290%     [t,NameFunction_uid]=add(t,new_uid,'element','NameFunction');
291%     [t]=add(t,NameFunction_uid,'chardata','sub_background');
292%     if GUI_config.CheckLevel
293%         [t,NameFunction_uid]=add(t,new_uid,'element','NameFunction');
294%         [t]=add(t,NameFunction_uid,'chardata','levels');
295%     end
296%     [t,NbSlice_uid]=add(t,new_uid,'element','NbSlice');
297%     [t]=add(t,new_uid,'chardata',num2str(NbSlice));
298%     [t,NbSlidingImages_uid]=add(t,new_uid,'element','NbSlidingImages');
299%     [t]=add(t,NbSlidingImages_uid,'chardata',num2str(nbaver));
300%     [t,LuminosityRank_uid]=add(t,new_uid,'element','RankBackground');
301%     [t]=add(t,LuminosityRank_uid,'chardata',num2str(rank));% luminosity rank almong the nbaver sliding images
302%     save(t,filexml)
303% end
[24]304%copy the mask
[442]305% if exist([filebase '_1mask_1'],'file')
306%     copyfile([filebase '_1mask_1'],[filebase_b '_1mask_1']);% copy the mask file
307% end
[24]308
309%MAIN LOOP ON SLICES
[451]310for islice=1:NbSlice
[214]311    %% select the series of image indices at the level islice
[454]312    indselect=islice:NbSlice*step:nbfield;% select file indices of the slice
313    for ifield=1:step-1
314        indselect=[indselect;indselect(end,:)+1];
[394]315    end
[214]316   
317    %% read the first series of nbaver_ima images and sort by luminosity at each pixel
[24]318    for ifield = 1:nbaver_ima
[54]319        ifile=indselect(ifield);
[394]320        filename=filecell{1,ifile};
[454]321        Aread=read_image(filename,FileType{1},MovieObject{1},frame_index{1}(ifile));
322        if ndims(Aread)==3;%color images
323            Aread=sum(double(Aread),3);% take the sum of color components
324        end
[394]325        Ak(:,:,ifield)=Aread;
[24]326    end
327    Asort=sort(Ak,3);%sort the luminosity of images at each point
328    B=Asort(:,:,rank);%background image
[394]329    display( 'first background image will be substracted')
[454]330    nbfirst=(ceil(nbaver/2))*step;
[24]331    for ifield=1:nbfirst
[394]332        Acor=double(Ak(:,:,ifield))-double(B);%substract background to the current image
333        Acor=(Acor>0).*Acor; % put to 0 the negative elements in Acor
334        ifile=indselect(ifield);
335        if ~isempty(j1_series{1})
336            j1=j1_series{1}(ifile);
337        end
[474]338        newname=fullfile_uvmat(RootPath{1},OutputDir,RootFile{1},FileExtOut,NomTypeOut,i1_series{1}(ifile),[],j1);
[454]339       
340        %write result file
[459]341        if ParamOut.Specific.CheckLevelTransform
[454]342            C=levels(Acor);
[394]343            imwrite(C,newname,'BitDepth',8); % save the new image
344        else
[454]345            if isequal(FileInfo{1}.BitDepth,16)
346                C=uint16(Acor);
347                imwrite(C,newname,'BitDepth',16); % save the new image
348            else
349                C=uint8(Acor);
350                imwrite(C,newname,'BitDepth',8); % save the new image
351            end
[394]352        end
[454]353        display([newname ' written'])
[24]354    end
[214]355   
[454]356    %% repeat the operation on a sliding series of images
[214]357    display('sliding background image will be substracted')
[454]358    if nbfield_i > nbaver_ima
359        for ifield = step*ceil(nbaver/2)+1:step:nbfield_i-step*floor(nbaver/2)
[442]360            if checkrun
361                stopstate=get(hseries.RUN,'BusyAction');
[478]362                update_waitbar(hseries.Waitbar,(ifield+(islice-1)*nbfield_i)/(nbfield_i*NbSlice))
[442]363            else
364                stopstate='queue';
365            end
366            if isequal(stopstate,'queue')% enable STOP command
[240]367                Ak(:,:,1:nbaver_ima-step)=Ak(:,:,1+step:nbaver_ima);% shift the current image series by one burst (step)
[24]368                %incorporate next burst in the current image series
369                for iburst=1:step
370                    ifile=indselect(ifield+step*floor(nbaver/2)+iburst-1);
[454]371                    filename=fullfile_uvmat(RootPath{1},SubDir{1},RootFile{1},FileExt{1},NomType{1},i1_series{1}(ifile),[],j1_series{1}(ifile));
372                    Aread=read_image(filename,FileType{1},MovieObject{1},i1_series{1}(ifile));
373                    if ndims(Aread)==3;%color images
374                        Aread=sum(double(Aread),3);% take the sum of color components
375                    end
[24]376                    Ak(:,:,nbaver_ima-step+iburst)=Aread;
377                end
378                Asort=sort(Ak,3);%sort the new current image series by luminosity
379                B=Asort(:,:,rank);%current background image
380                for iburst=1:step
381                    index=step*floor(nbaver/2)+iburst;
382                    Acor=double(Ak(:,:,index))-double(B);
383                    Acor=(Acor>0).*Acor; % put to 0 the negative elements in Acor
384                    ifile=indselect(ifield+iburst-1);
[394]385                    if ~isempty(j1_series{1})
386                        j1=j1_series{1}(ifile);
387                    end
[474]388                    newname=fullfile_uvmat(RootPath{1},OutputDir,RootFile{1},FileExtOut,NomTypeOut,i1_series{1}(ifile),[],j1);
[454]389                    %write result file
[459]390                    if ParamOut.Specific.CheckLevelTransform
[454]391                        C=levels(Acor);
[214]392                        imwrite(C,newname,'BitDepth',8); % save the new image
393                    else
[454]394                        if isequal(FileInfo{1}.BitDepth,16)
395                            C=uint16(Acor);
396                            imwrite(C,newname,'BitDepth',16); % save the new image
397                        else
398                            C=uint8(Acor);
399                            imwrite(C,newname,'BitDepth',8); % save the new image
400                        end
[214]401                    end
[454]402                    display([newname ' written'])
403                   
[214]404                end
[24]405            else
406                return
407            end
408        end
409    end
[394]410   
411    %% substract the background from the last images
[214]412    display('last background image will be substracted')
[454]413    ifield=nbfield_i-(step*ceil(nbaver/2))+1:nbfield_i;
414    for ifield=nbfield_i-(step*floor(nbaver/2))+1:nbfield_i
415        index=ifield-nbfield_i+step*(2*floor(nbaver/2)+1);
[394]416        Acor=double(Ak(:,:,index))-double(B);
417        Acor=(Acor>0).*Acor; % put to 0 the negative elements in Acor
418        ifile=indselect(ifield);
419        if ~isempty(j1_series{1})
420            j1=j1_series{1}(ifile);
421        end
[474]422        newname=fullfile_uvmat(RootPath{1},OutputDir,RootFile{1},FileExtOut,NomTypeOut,i1_series{1}(ifile),[],j1);
[454]423       
424        %write result file
[459]425        if ParamOut.Specific.CheckLevelTransform
[454]426            C=levels(Acor);
[394]427            imwrite(C,newname,'BitDepth',8); % save the new image
428        else
[454]429            if isequal(FileInfo{1}.BitDepth,16)
430                C=uint16(Acor);
431                imwrite(C,newname,'BitDepth',16); % save the new image
432            else
433                C=uint8(Acor);
434                imwrite(C,newname,'BitDepth',8); % save the new image
435            end
[394]436        end
[454]437        display([newname ' written'])
[394]438    end
[24]439end
440
[214]441
442function C=levels(A)
443%whos A;
444B=double(A(:,:,1));
445windowsize=round(min(size(B,1),size(B,2))/20);
446windowsize=floor(windowsize/2)*2+1;
[239]447ix=1/2-windowsize/2:-1/2+windowsize/2;%
[214]448%del=np/3;
449%fct=exp(-(ix/del).^2);
450fct2=cos(ix/(windowsize-1)/2*pi/2);
451%Mfiltre=(ones(5,5)/5^2);
452%Mfiltre=fct2';
453Mfiltre=fct2'*fct2;
454Mfiltre=Mfiltre/(sum(sum(Mfiltre)));
455
456C=filter2(Mfiltre,B);
457C(:,1:windowsize)=C(:,windowsize)*ones(1,windowsize);
458C(:,end-windowsize+1:end)=C(:,end-windowsize+1)*ones(1,windowsize);
459C(1:windowsize,:)=ones(windowsize,1)*C(windowsize,:);
460C(end-windowsize+1:end,:)=ones(windowsize,1)*C(end-windowsize,:);
461C=tanh(B./(2*C));
462[n,c]=hist(reshape(C,1,[]),100);
463% figure;plot(c,n);
464
465[m,i]=max(n);
466c_max=c(i);
467[dummy,index]=sort(abs(c-c(i)));
468n=n(index);
469c=c(index);
470i_select = find(cumsum(n)<0.95*sum(n));
471if isempty(i_select)
472    i_select = 1:length(c);
473end
474c_select=c(i_select);
475n_select=n(i_select);
476cmin=min(c_select);
477cmax=max(c_select);
478C=(C-cmin)/(cmax-cmin)*256;
479C=uint8(C);
Note: See TracBrowser for help on using the repository browser.