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

Last change on this file since 917 was 917, checked in by sommeria, 10 years ago

series modified to deal with NbSlice? in local mode, subbackground fixed, edge detection introduced

File size: 21.9 KB
Line 
1%'sub_background': substract a sliding background to an image series
2%------------------------------------------------------------------------
3% Method:
4    %calculate the background image by sorting the luminosity of each point
5    % over a sliding sub-sequence of 'nbaver_ima' images.
6    % The luminosity value of rank 'rank' is selected as the
7    % 'background'. rank=nbimages/2 gives the median value.  Smaller values are appropriate
8    % for a dense set of particles. The extrem value rank=1 gives the true minimum
9    % luminosity, but it can be polluted by noise.
10% Organization of image indices:
11    % The program is working on a series of images,
12    % In the mode 'volume', nbfield2=1 (1 image at each level)and NbSlice (=nbfield_j)
13    % Else nbfield2=nbfield_j =nbre of images in a burst (j index)
14   
15% function GUI_config=sub_background(Param)
16%
17%%%%%%%%%%% GENERAL TO ALL SERIES ACTION FCTS %%%%%%%%%%%%%%%%%%%%%%%%%%%
18%
19%OUTPUT
20% ParamOut: sets options in the GUI series.fig needed for the function
21%
22%INPUT:
23% In run mode, the input parameters are given as a Matlab structure Param copied from the GUI series.
24% In batch mode, Param is the name of the corresponding xml file containing the same information
25% when Param.Action.RUN=0 (as activated when the current Action is selected
26% in series), the function ouput paramOut set the activation of the needed GUI elements
27%
28% Param contains the elements:(use the menu bar command 'export/GUI config' in series to
29% see the current structure Param)
30%    .InputTable: cell of input file names, (several lines for multiple input)
31%                      each line decomposed as {RootPath,SubDir,Rootfile,NomType,Extension}
32%    .OutputSubDir: name of the subdirectory for data outputs
33%    .OutputDirExt: directory extension for data outputs
34%    .Action: .ActionName: name of the current activated function
35%             .ActionPath:   path of the current activated function
36%             .ActionExt: fct extension ('.m', Matlab fct, '.sh', compiled   Matlab fct
37%             .RUN =0 for GUI input, =1 for function activation
38%             .RunMode='local','background', 'cluster': type of function  use
39%             
40%    .IndexRange: set the file or frame indices on which the action must be performed
41%    .FieldTransform: .TransformName: name of the selected transform function
42%                     .TransformPath:   path  of the selected transform function
43%    .InputFields: sub structure describing the input fields withfields
44%              .FieldName: name(s) of the field
45%              .VelType: velocity type
46%              .FieldName_1: name of the second field in case of two input series
47%              .VelType_1: velocity type of the second field in case of two input series
48%              .Coord_y: name of y coordinate variable
49%              .Coord_x: name of x coordinate variable
50%    .ProjObject: %sub structure describing a projection object (read from ancillary GUI set_object)
51%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52
53%=======================================================================
54% Copyright 2008-2015, LEGI UMR 5519 / CNRS UJF G-INP, Grenoble, France
55%   http://www.legi.grenoble-inp.fr
56%   Joel.Sommeria - Joel.Sommeria (A) legi.cnrs.fr
57%
58%     This file is part of the toolbox UVMAT.
59%
60%     UVMAT is free software; you can redistribute it and/or modify
61%     it under the terms of the GNU General Public License as published
62%     by the Free Software Foundation; either version 2 of the license,
63%     or (at your option) any later version.
64%
65%     UVMAT is distributed in the hope that it will be useful,
66%     but WITHOUT ANY WARRANTY; without even the implied warranty of
67%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
68%     GNU General Public License (see LICENSE.txt) for more details.
69%=======================================================================
70
71function ParamOut=sub_background (Param)
72
73%%%%%%%%%%%%%%%%%    INPUT PREPARATION MODE (no RUN)    %%%%%%%%%%%%%%%%%
74if isstruct(Param) && isequal(Param.Action.RUN,0)
75    ParamOut.AllowInputSort='off';% allow alphabetic sorting of the list of input file SubDir (options 'off'/'on', 'off' by default)
76    ParamOut.WholeIndexRange='on';% prescribes the file index ranges from min to max (options 'off'/'on', 'off' by default)
77    ParamOut.NbSlice='on'; % edit box nbre of slices made active
78    ParamOut.VelType='off';% menu for selecting the velocity type (options 'off'/'one'/'two',  'off' by default)
79    ParamOut.FieldName='off';% menu for selecting the field (s) in the input file(options 'off'/'one'/'two', 'off' by default)
80    ParamOut.FieldTransform = 'off';%can use a transform function
81    ParamOut.ProjObject='off';%can use projection object(option 'off'/'on',
82    ParamOut.Mask='off';%can use mask option   (option 'off'/'on', 'off' by default)
83    ParamOut.OutputDirExt='.sback';%set the output dir extension
84    ParamOut.OutputFileMode='NbInput';% '=NbInput': 1 output file per input file index, '=NbInput_i': 1 file per input file index i, '=NbSlice': 1 file per slice
85   
86    %% root input file(s) and type
87    % check the existence of the first file in the series
88        first_j=[];% note that the function will propose to cover the whole range of indices
89    if isfield(Param.IndexRange,'MinIndex_j'); first_j=Param.IndexRange.MinIndex_j; end
90    last_j=[];
91    if isfield(Param.IndexRange,'MaxIndex_j'); last_j=Param.IndexRange.MaxIndex_j; end
92    PairString='';
93    if isfield(Param.IndexRange,'PairString'); PairString=Param.IndexRange.PairString; end
94    [i1,i2,j1,j2] = get_file_index(Param.IndexRange.first_i,first_j,PairString);
95    FirstFileName=fullfile_uvmat(Param.InputTable{1,1},Param.InputTable{1,2},Param.InputTable{1,3},...
96        Param.InputTable{1,5},Param.InputTable{1,4},i1,i2,j1,j2);
97    if ~exist(FirstFileName,'file')
98        msgbox_uvmat('WARNING',['the first input file ' FirstFileName ' does not exist'])
99    else
100        [i1,i2,j1,j2] = get_file_index(Param.IndexRange.last_i,last_j,PairString);
101        LastFileName=fullfile_uvmat(Param.InputTable{1,1},Param.InputTable{1,2},Param.InputTable{1,3},...
102        Param.InputTable{1,5},Param.InputTable{1,4},i1,i2,j1,j2);
103        if ~exist(FirstFileName,'file')
104             msgbox_uvmat('WARNING',['the last input file ' LastFileName ' does not exist'])
105        end
106    end
107
108    %% check the validity of  input file types
109    ImageTypeOptions={'image','multimage','mmreader','video'};%allowed input file types(images)
110    FileInfo=get_file_info(FirstFileName);
111    FileType=FileInfo.FileType;
112    CheckImage=~isempty(find(strcmp(FileType,ImageTypeOptions), 1));% =1 for images
113    if ~CheckImage
114        msgbox_uvmat('ERROR',['invalid file type input: ' FileType ' not an image'])
115        return
116    end
117   
118    %% numbers of fields
119    NbSlice_i=1;%default
120    if isfield(Param.IndexRange,'NbSlice')&&~isempty(Param.IndexRange.NbSlice)
121        NbSlice_i=Param.IndexRange.NbSlice;
122    end
123    incr_j=1;%default
124    if isfield(Param.IndexRange,'incr_j')&&~isempty(Param.IndexRange.incr_j)
125        incr_j=Param.IndexRange.incr_j;
126    end
127    if isempty(first_j)||isempty(last_j)
128        nbfield_j=1;
129    else
130        nbfield_j=numel(first_j:incr_j:last_j);%nb of fields for the j index (bursts or volume slices)
131    end
132    first_i=1;last_i=1;incr_i=1;%default
133    if isfield(Param.IndexRange,'MinIndex_i'); first_i=Param.IndexRange.MinIndex_i; end   
134    if isfield(Param.IndexRange,'MaxIndex_i'); last_i=Param.IndexRange.MaxIndex_i; end
135    if isfield(Param.IndexRange,'incr_i')&&~isempty(Param.IndexRange.incr_i)
136        incr_i=Param.IndexRange.incr_i;
137    end
138    nbfield_i=numel(first_i:incr_i:last_i);%nb of fields for the i index (bursts or volume slices)
139    nbfield=nbfield_j*nbfield_i; %total number of fields
140    nbfield_i=floor(nbfield/NbSlice_i);%total number of  indexes in a slice (adjusted to an integer number of slices)
141   
142    %% setting of  parameters specific to sub_background
143    nbaver_init=23; %default number of images used for the sliding background: to be adjusted later to include an integer number of bursts 
144    if nbfield_i~=1
145        nbaver=floor(nbaver_init/nbfield_j); % number of bursts used for the sliding background,
146        if isequal(mod(nbaver,2),0)% if nbaver is even
147            nbaver=nbaver+1;%put the number of burst to an odd number (so the middle burst is defined)
148        end
149        nbaver_init=nbaver*nbfield_j;%propose by default an integer number of bursts
150    end
151   
152    prompt = {'volume scan mode (Yes/No)';'Number of images for the sliding background (MUST FIT IN COMPUTER MEMORY)';...
153        'the luminosity rank chosen to define the background (0.1=for dense particle seeding, 0.5 (median) for sparse particles'};
154    dlg_title = 'get (slice by slice) a sliding background and substract to each image';
155    num_lines= 3;
156    def     = { 'No';num2str(nbaver_init);'0.1'};
157    answer = inputdlg(prompt,dlg_title,num_lines,def);
158    if isempty(answer)
159        return
160    end
161    %check input consistency
162    if strcmp(answer{1},'No') && ~isequal(NbSlice_i,1)
163        check=msgbox_uvmat('INPUT_Y-N',['confirm the multi-level splitting into ' num2str(NbSlice_i) ' slices']);
164        if ~strcmp(check,'Yes')
165            return
166        end
167    end
168    if strcmp(answer{1},'Yes')
169        step=2;%the sliding background is shifted by the length of one burst, assumed =2 for volume ;ode
170        ParamOut.NbSlice=1; %nbre of slices displayed
171    else
172        step=nbfield_j;%case of bursts: the sliding background is shifted by the length of one burst
173    end
174    nbaver_ima=str2double(answer{2});%number of images for the sliding background
175    nbaver=ceil(nbaver_ima/step);%number of bursts for the sliding background
176    if isequal(mod(nbaver,2),0)% if nbaver is even
177        nbaver=nbaver+1;%set the number of bursts to an odd number (so the middle burst is defined)
178    end
179    nbaver_ima=nbaver*step;% correct the nbre of images corresponding to nbaver
180    ParamOut.ActionInput.CheckVolume=strcmp(answer{1},'Yes');
181    ParamOut.ActionInput.SlidingSequenceLength=nbaver_ima;
182    ParamOut.ActionInput.BrightnessRankThreshold=str2double(answer{3});
183   
184    % apply the image rescaling function 'level' (avoid the blinking effects of bright particles)
185    answer=msgbox_uvmat('INPUT_Y-N','apply image rescaling function levels.m after sub_background');
186    ParamOut.ActionInput.CheckLevelTransform=strcmp(answer,'Yes');
187    return
188end
189%%%%%%%%%%%%%%%%%    STOP HERE FOR PAMETER INPUT MODE   %%%%%%%%%%%%%%%%%
190
191%% read input parameters from an xml file if input is a file name (batch mode)
192checkrun=1;
193RUNHandle=[];
194WaitbarHandle=[];
195if ischar(Param)
196    Param=xml2struct(Param);% read Param as input file (batch case)
197    checkrun=0;
198else
199hseries=findobj(allchild(0),'Tag','series');
200RUNHandle=findobj(hseries,'Tag','RUN');%handle of RUN button in GUI series
201WaitbarHandle=findobj(hseries,'Tag','Waitbar');%handle of waitbar in GUI series
202end
203
204%% input preparation
205NbSlice_i=Param.IndexRange.NbSlice;
206if ~isequal(NbSlice_i,1)
207    display(['multi-level splitting into ' num2str(NbSlice_i) ' slices']);
208end
209RootPath=Param.InputTable(:,1);
210RootFile=Param.InputTable(:,3);
211SubDir=Param.InputTable(:,2);
212NomType=Param.InputTable(:,4);
213FileExt=Param.InputTable(:,5);
214%hdisp=disp_uvmat('WAITING...','checking the file series',checkrun);
215[filecell,i1_series,i2_series,j1_series]=get_file_series(Param);
216% if ~isempty(hdisp),delete(hdisp),end;
217%%%%%%%%%%%%
218    % The cell array filecell is the list of input file names, while
219    % filecell{iview,fileindex}:
220    %        iview: line in the table corresponding to a given file series
221    %        fileindex: file index within  the file series,
222    % 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
223    % i1_series(iview,fileindex) expresses the same indices as a 1D array in file indices
224%%%%%%%%%%%%
225[FileInfo{1},MovieObject{1}]=get_file_info(filecell{1,1});
226FileType{1}=FileInfo{1}.FileType;
227    if ~isempty(j1_series{1})
228        frame_index{1}=j1_series{1};
229    else
230        frame_index{1}=i1_series{1};
231    end
232
233
234%% output file naming
235FileExtOut='.png'; % write result as .png images for image inputs
236if strcmp(lower(NomType{1}(end)),'a')
237    NomTypeOut=NomType{1};%case of letter appendix
238elseif isempty(j1_series{1})
239    NomTypeOut='_1';
240else
241    NomTypeOut='_1_1';% caseof purely numerical indexing
242end
243OutputDir=[Param.OutputSubDir Param.OutputDirExt];
244
245%% file index parameters
246% NbSlice_i: nbre of slices for i index: different of of 1 for multi-level,
247% the function sub_background is then relaunched by the GUI series for each
248%      slice, incrementing the first index i by 1
249% NbSlice_j: nbre of slices in volume mode
250% nbfield : total number of images treated per slice
251% step: shift of image index at each step of the sliding background (corresponding to the nbre of images in a burst)
252% nbaver_ima: nbre of the images in the sliding sequence used for the background
253% nbaver=nbaver_ima/step: nbre of bursts corresponding to nbaver_ima images. It has been adjusted so that nbaver is an odd integer
254nbfield_j=size(i1_series{1},1); %nb of fields for the j index (bursts or volume slices)
255nbfield_i=size(i1_series{1},2); %nb of fields for the i index
256
257if Param.ActionInput.CheckVolume
258    step=2;% we assume the burst contains only one image pair
259    NbSlice_j=nbfield_j;
260    NbSlice=nbfield_j;
261    nbfield_series=nbfield_i;
262else
263    step=nbfield_j;%case of bursts: the sliding background is shifted by the length of one burst
264        NbSlice_j=1;
265        NbSlice=NbSlice_i;
266    %nbfield_i=floor(nbfield_i/NbSlice_i);%total number of  indexes in a slice (adjusted to an integer number of slices)
267    %nbfield=nbfield_i*NbSlice_i; %total number of fields after adjustement
268    nbfield_series=nbfield_i*nbfield_j;
269end
270nbfield=nbfield_j*nbfield_i; %total number of fields
271nbaver_ima=Param.ActionInput.SlidingSequenceLength;%number of images for the sliding background
272nbaver=ceil(nbaver_ima/step);%number of bursts for the sliding background
273if isequal(mod(nbaver,2),0)
274    nbaver=nbaver+1;%set the number of bursts to an odd number (so the middle burst is defined)
275end
276nbaver_ima=nbaver*step;
277if nbaver_ima > nbfield
278    display('number of images in a slice smaller than the proposed number of images for the sliding average')
279    return
280end
281halfnbaver=floor(nbaver/2); % half width (in unit of bursts) of the sliding background
282
283%% calculate absolute brightness rank
284rank=floor(Param.ActionInput.BrightnessRankThreshold*nbaver_ima);
285if rank==0
286    rank=1;%rank selected in the sorted image series
287end
288
289%% prealocate memory for the sliding background
290try
291    Afirst=read_image(filecell{1,1},FileType{1},MovieObject{1},frame_index{1}(1));
292    [npy,npx,nbcolor]=size(Afirst);% the argument nbcolor is important to get npx right for color images
293    if strcmp(class(Afirst),'uint8') % case of 8bit images
294        Ak=zeros(npy,npx,nbaver_ima,'uint8'); %prealocate memory
295        Asort=zeros(npy,npx,nbaver_ima,'uint8'); %prealocate memory
296    else
297        Ak=zeros(npy,npx,nbaver_ima,'uint16'); %prealocate memory
298        Asort=zeros(npy,npx,nbaver_ima,'uint16'); %prealocate memory
299    end
300catch ME
301    msgbox_uvmat('ERROR',['sub_background/read_image/' ME.message])
302    return
303end
304
305
306%%%%%%%  LOOP ON SLICES FOR VOLUME SCAN %%%%%%%
307for j_slice=1:NbSlice_j
308    %% select the series of i indices to process
309    indselect=j_slice:step*NbSlice_j:nbfield;% select file indices of the slice
310    for ifield=1:step-1
311        indselect=[indselect;indselect(end,:)+NbSlice_j];
312    end
313   
314    %% read the first series of nbaver_ima images and sort by luminosity at each pixel
315    for ifield = 1:nbaver_ima
316        ifile=indselect(ifield);
317        filename=filecell{1,ifile};
318        Aread=read_image(filename,FileType{1},MovieObject{1},frame_index{1}(ifile));
319        if ndims(Aread)==3;%color images
320            Aread=sum(double(Aread),3);% take the sum of color components
321        end
322        Ak(:,:,ifield)=Aread;
323    end
324    Asort=sort(Ak,3);%sort the luminosity of images at each point
325    B=Asort(:,:,rank);%background image
326   
327    %% substract the first background image to the first images
328    display( 'first background image will be substracted')
329    for ifield=1:step*(halfnbaver+1);% nbre of images treated by the first background image
330        Acor=double(Ak(:,:,ifield))-double(B);%substract background to the current image
331        Acor=(Acor>0).*Acor; % put to 0 the negative elements in Acor
332        ifile=indselect(ifield);
333        j1=1;
334        if ~isempty(j1_series{1})
335            j1=j1_series{1}(ifile);
336        end
337        newname=fullfile_uvmat(RootPath{1},OutputDir,RootFile{1},FileExtOut,NomTypeOut,i1_series{1}(ifile),[],j1);
338       
339        %write result file
340        if Param.ActionInput.CheckLevelTransform
341            C=levels(Acor);
342            imwrite(C,newname,'BitDepth',8); % save the new image
343        else
344            if isequal(FileInfo{1}.BitDepth,16)
345                C=uint16(Acor);
346                imwrite(C,newname,'BitDepth',16); % save the new image
347            else
348                C=uint8(Acor);
349                imwrite(C,newname,'BitDepth',8); % save the new image
350            end
351        end
352        display([newname ' written'])
353    end
354   
355    %% repeat the operation on a sliding series of images
356    display('sliding background image will be substracted')
357    if nbfield_series > nbaver_ima
358        for ifield = step*(halfnbaver+1):step:nbfield_series-step*(halfnbaver+1)% ifield +iburst=index of the current processed image
359            update_waitbar(WaitbarHandle,ifield/nbfield_series)
360            if  ~isempty(RUNHandle)&&~strcmp(get(RUNHandle,'BusyAction'),'queue')
361                disp('program stopped by user')
362                return
363            end
364            if nbaver_ima>step
365            Ak(:,:,1:nbaver_ima-step)=Ak(:,:,1+step:nbaver_ima);% shift the current image series by one burst (step)
366            end
367            %incorporate next burst in the current image series
368            for iburst=1:step
369                ifile=indselect(ifield+iburst+step*halfnbaver);
370                j1=1;
371                if ~isempty(j1_series{1})
372                    j1=j1_series{1}(ifile);
373                end
374                filename=fullfile_uvmat(RootPath{1},SubDir{1},RootFile{1},FileExt{1},NomType{1},i1_series{1}(ifile),[],j1);
375                Aread=read_image(filename,FileType{1},MovieObject{1},i1_series{1}(ifile));
376                if ndims(Aread)==3;%color images
377                    Aread=sum(double(Aread),3);% take the sum of color components
378                end
379                Ak(:,:,nbaver_ima-step+iburst)=Aread;% fill the last burst of the current image series by the new image
380            end
381            Asort=sort(Ak,3);%sort the new current image series by luminosity
382            B=Asort(:,:,rank);%current background image
383            %substract the background for the current burst
384            for iburst=1:step
385                Acor=double(Ak(:,:,step*halfnbaver+iburst))-double(B); %the current image has been already read ans stored as index step*halfnbaver+iburst in the current series
386                Acor=(Acor>0).*Acor; % put to 0 the negative elements in Acor
387                ifile=indselect(ifield+iburst);
388                if ~isempty(j1_series{1})
389                    j1=j1_series{1}(ifile);
390                end
391                newname=fullfile_uvmat(RootPath{1},OutputDir,RootFile{1},FileExtOut,NomTypeOut,i1_series{1}(ifile),[],j1);
392                %write result file
393                if Param.ActionInput.CheckLevelTransform
394                    C=levels(Acor);
395                    imwrite(C,newname,'BitDepth',8); % save the new image
396                else
397                    if isequal(FileInfo{1}.BitDepth,16)
398                        C=uint16(Acor);
399                        imwrite(C,newname,'BitDepth',16); % save the new image
400                    else
401                        C=uint8(Acor);
402                        imwrite(C,newname,'BitDepth',8); % save the new image
403                    end
404                end
405                display([newname ' written'])
406            end
407        end
408    end
409   
410    %% substract the background from the last images
411    display('last background image will be substracted')
412    for  ifield=nbfield_series-step*halfnbaver+1:nbfield_series
413        Acor=double(Ak(:,:,ifield-nbfield_series+step*(2*halfnbaver+1)))-double(B);
414        Acor=(Acor>0).*Acor; % put to 0 the negative elements in Acor
415        ifile=indselect(ifield);
416        if ~isempty(j1_series{1})
417            j1=j1_series{1}(ifile);
418        end
419        newname=fullfile_uvmat(RootPath{1},OutputDir,RootFile{1},FileExtOut,NomTypeOut,i1_series{1}(ifile),[],j1);
420        %write result file
421        if Param.ActionInput.CheckLevelTransform
422            C=levels(Acor);
423            imwrite(C,newname,'BitDepth',8); % save the new image
424        else
425            if isequal(FileInfo{1}.BitDepth,16)
426                C=uint16(Acor);
427                imwrite(C,newname,'BitDepth',16); % save the new image
428            else
429                C=uint8(Acor);
430                imwrite(C,newname,'BitDepth',8); % save the new image
431            end
432        end
433        display([newname ' written'])
434    end
435end
436
437
438function C=levels(A)
439%whos A;
440B=double(A(:,:,1));
441windowsize=round(min(size(B,1),size(B,2))/20);
442windowsize=floor(windowsize/2)*2+1;
443ix=1/2-windowsize/2:-1/2+windowsize/2;%
444%del=np/3;
445%fct=exp(-(ix/del).^2);
446fct2=cos(ix/(windowsize-1)/2*pi/2);
447%Mfiltre=(ones(5,5)/5^2);
448%Mfiltre=fct2';
449Mfiltre=fct2'*fct2;
450Mfiltre=Mfiltre/(sum(sum(Mfiltre)));
451
452C=filter2(Mfiltre,B);
453C(:,1:windowsize)=C(:,windowsize)*ones(1,windowsize);
454C(:,end-windowsize+1:end)=C(:,end-windowsize+1)*ones(1,windowsize);
455C(1:windowsize,:)=ones(windowsize,1)*C(windowsize,:);
456C(end-windowsize+1:end,:)=ones(windowsize,1)*C(end-windowsize,:);
457C=tanh(B./(2*C));
458[n,c]=hist(reshape(C,1,[]),100);
459% figure;plot(c,n);
460
461[m,i]=max(n);
462c_max=c(i);
463[dummy,index]=sort(abs(c-c(i)));
464n=n(index);
465c=c(index);
466i_select = find(cumsum(n)<0.95*sum(n));
467if isempty(i_select)
468    i_select = 1:length(c);
469end
470c_select=c(i_select);
471n_select=n(i_select);
472cmin=min(c_select);
473cmax=max(c_select);
474C=(C-cmin)/(cmax-cmin)*256;
475C=uint8(C);
Note: See TracBrowser for help on using the repository browser.