source: trunk/src/parciv.m @ 1203

Last change on this file since 1203 was 1202, checked in by sommeria, 2 days ago

parciv added and erasing bad file in complement series

File size: 16.9 KB
RevLine 
[1202]1%--------------------------------------------------------------------------
2%  'parciv': key function  for image correlations (called by series/cvi_series.m)
3% function  [xtable,ytable,utable,vtable,ctable,FF,CheckPar,errormsg] = parciv (par_civ)
4%
5% OUTPUT:
6% xtable: set of x positions of the first image in integer image coordinates , the measurement position is xtable-0.5+utable/2
7% ytable: set of y coordinates of the first image in integer image coordinates (starting in the image bottom, image index= npy-ytable+1 , the measurement position is ytable-0.5+vtable/2
8% utable: set of u displacements (along x), in pixels
9% vtable: set of v displacements (along y)
10% ctable: max image correlation for each vector
11% FF: flag for false vectors
12% CheckPar: =true, to indicate the use of parallel computing, and also to
13               % fit with the twin function civ, which differ only by the use of 'for' and
14               % the output of the fully image correlation of the last vector
15%errormsg: ='' by default
16
17%INPUT:
18% par_civ: structure of input parameters, with fields:
19%  .ImageA: first image for correlation (matrix)
20%  .ImageB: second image for correlation(matrix)
21%  .CorrBoxSize: 1,2 vector giving the size of the correlation box in x and y
22%  .SearchBoxSize:  1,2 vector giving the size of the search box in x and y
23%  .SearchBoxShift: 1,2 vector or 2 column matrix (for civ2) giving the shift of the search box in x and y
24%  .CorrSmooth: =1 or 2 determines the choice of the sub-pixel determination of the correlation max
25%  .ImageWidth: nb of pixels of the image in x
26%  .Dx, Dy: mesh for the PIV calculation
27%  .Grid: grid giving the PIV calculation points (alternative to .Dx .Dy): centres of the correlation boxes in Image A
28%  .Mask: name of a mask file or mask image matrix itself
29%  .MinIma: thresholds for image luminosity
30%  .MaxIma
31%  .CheckDeformation=1 for subpixel interpolation and image deformation (linear transform)
32%  .DUDX: matrix of deformation obtained from patch at each grid point
33%  .DUDY
34%  .DVDX:
35%  .DVDY
36
37function [xtable,ytable,utable,vtable,ctable,FF,CheckPar,errormsg] = parciv (par_civ)
38CheckPar=true;
39
40%% check input images
41par_civ.ImageA=sum(double(par_civ.ImageA),3);%sum over rgb component for color images
42par_civ.ImageB=sum(double(par_civ.ImageB),3);
43[npy_ima,npx_ima]=size(par_civ.ImageA);
44if ~isequal(size(par_civ.ImageB),[npy_ima npx_ima])
45    errormsg='image pair with unequal size';
46    return
47end
48
49%% prepare measurement grid if not given as input
50if ~isfield(par_civ,'Grid')% grid points defining central positions of the sub-images in image A
51    nbinterv_x=floor((npx_ima-1)/par_civ.Dx);%expected number of intervals Dx
52    gridlength_x=nbinterv_x*par_civ.Dx;
53    minix=ceil((npx_ima-gridlength_x)/2);
54    nbinterv_y=floor((npy_ima-1)/par_civ.Dy);%expected number of intervals Dy
55    gridlength_y=nbinterv_y*par_civ.Dy;
56    miniy=ceil((npy_ima-gridlength_y)/2);
57    [GridX,GridY]=meshgrid(minix+0.5:par_civ.Dx:npx_ima-0.5,miniy+0.5:par_civ.Dy:npy_ima-0.5);% grid of initial positions in pixel coordinates (y upward)
58    par_civ.Grid(:,1)=reshape(GridX,[],1);
59    par_civ.Grid(:,2)=reshape(GridY,[],1);% increases with array index
60end
61nbvec=size(par_civ.Grid,1);
62
63
64%% prepare correlation and search boxes
65CorrBoxSizeX=par_civ.CorrBoxSize(:,1);
66CorrBoxSizeY=par_civ.CorrBoxSize(:,2);
67if size(par_civ.CorrBoxSize,1)==1
68    CorrBoxSizeX=par_civ.CorrBoxSize(1)*ones(nbvec,1);
69    CorrBoxSizeY=par_civ.CorrBoxSize(2)*ones(nbvec,1);
70end
71
72shiftx=par_civ.SearchBoxShift(:,1);%use the input shift estimate, rounded to the next integer value
73shifty=par_civ.SearchBoxShift(:,2);%
74if isscalar(shiftx)% case of a unique shift for the whole field( civ1)
75    shiftx=shiftx*ones(nbvec,1);
76    shifty=shifty*ones(nbvec,1);
77end
78
79%% shift the grid by half the expected displacement to get the velocity closer to the initial grid
80par_civ.Grid(:,1)=par_civ.Grid(:,1)-shiftx/2;% initial positions in image coordinates (y upward)
81par_civ.Grid(:,2)=par_civ.Grid(:,2)-shifty/2;
82
83%% Array initialisation and default output  if par_civ.CorrSmooth=0 (just the grid calculated, no civ computation)
84xtable=round(par_civ.Grid(:,1)+0.5);% tables of image indices corresponding to the input grid
85ytable=round(npy_ima-par_civ.Grid(:,2)+0.5);% y image indices corresponding to the position in image coordinates
86shiftx=round(shiftx);
87shifty=round(shifty);
88utable=shiftx;%zeros(nbvec,1);% expected displacements in pixel indices (y downward)
89vtable=-shifty;%zeros(nbvec,1);
90ctable=zeros(nbvec,1);
91FF=zeros(nbvec,1);
92errormsg='';
93
94%% prepare mask
95if isfield(par_civ,'Mask') && ~isempty(par_civ.Mask)
96    if strcmp(par_civ.Mask,'all')
97        return    % get the grid only, no civ calculation
98    elseif ischar(par_civ.Mask)
99        par_civ.Mask=imread(par_civ.Mask);% read the mask if not allready done
100    end
101end
102check_MinIma=isfield(par_civ,'MinIma');% test for image luminosity threshold
103check_MaxIma=isfield(par_civ,'MaxIma') && ~isempty(par_civ.MaxIma);
104
105%% Apply mask, velocity calculated for mask value >=200
106checkmask=false;
107check_undefined=false(npy_ima, npx_ima);
108MinA=min(min(par_civ.ImageA));
109if isfield(par_civ,'Mask') && ~isempty(par_civ.Mask)
110    checkmask=true;
111    if ~isequal(size(par_civ.Mask),[npy_ima npx_ima])
112        errormsg='mask must be an image with the same size as the images';
113        return
114    end
115    check_undefined=(par_civ.Mask<200);
116end
117
118%% compute image correlations: MAINLOOP on velocity vectors
119%sum_square=1;% default
120mesh=1;% default
121CheckDeformation=isfield(par_civ,'CheckDeformation')&& par_civ.CheckDeformation==1;
122if CheckDeformation
123    mesh=0.25;%mesh in pixels for subpixel image interpolation (x 4 in each direction)
124    par_civ.CorrSmooth=2;% use SUBPIX2DGAUSS (take into account more points near the max)
125end
126SearchRange_1=par_civ.SearchRange(1);
127SearchRange_2=par_civ.SearchRange(2);
128if par_civ.CorrSmooth~=0 % par_civ.CorrSmooth=0 implies no civ computation (just input image and grid points given)
129    parfor ivec=1:nbvec
130        iref=xtable(ivec);% xindex on the image A for the middle of the correlation box
131        jref=ytable(ivec);%  j index  for the middle of the correlation box in the image A
132        FF(ivec)=0;
133        ibx2=floor(CorrBoxSizeX(ivec)/2);
134        iby2=floor(CorrBoxSizeY(ivec)/2);
135        isx2=ibx2+ceil(SearchRange_1);
136        isy2=iby2+ceil(SearchRange_2);
137        subrange1_x=iref-ibx2:iref+ibx2;% x indices defining the first subimage
138        subrange1_y=jref-iby2:jref+iby2;% y indices defining the first subimage
139        subrange2_x=iref+shiftx(ivec)-isx2:iref+shiftx(ivec)+isx2;%x indices defining the second subimage
140        subrange2_y=jref-shifty(ivec)-isy2:jref-shifty(ivec)+isy2;%y indices defining the second subimage
141        image1_crop=MinA*ones(numel(subrange1_y),numel(subrange1_x));% default value=min of image A
142        image2_crop=MinA*ones(numel(subrange2_y),numel(subrange2_x));% default value=min of image A
143        check1_x=subrange1_x>=1 & subrange1_x<=npx_ima;% check which points in the subimage 1 are contained in the initial image 1
144        check1_y=subrange1_y>=1 & subrange1_y<=npy_ima;
145        check2_x=subrange2_x>=1 & subrange2_x<=npx_ima;% check which points in the subimage 2 are contained in the initial image 2
146        check2_y=subrange2_y>=1 & subrange2_y<=npy_ima;
147        image1_crop(check1_y,check1_x)=par_civ.ImageA(subrange1_y(check1_y),subrange1_x(check1_x));%extract a subimage (correlation box) from image A
148        image2_crop(check2_y,check2_x)=par_civ.ImageB(subrange2_y(check2_y),subrange2_x(check2_x));%extract a larger subimage (search box) from image B
149        if checkmask
150            mask1_crop=ones(numel(subrange1_y),numel(subrange1_x));% default value=1 for mask
151            mask2_crop=ones(numel(subrange2_y),numel(subrange2_x));% default value=1 for mask
152            mask1_crop(check1_y,check1_x)=check_undefined(subrange1_y(check1_y),subrange1_x(check1_x));%extract a mask subimage (correlation box) from image A
153            mask2_crop(check2_y,check2_x)=check_undefined(subrange2_y(check2_y),subrange2_x(check2_x));%extract a mask subimage (search box) from image B
154            sizemask=sum(sum(mask1_crop))/(numel(subrange1_y)*numel(subrange1_x));%size of the masked part relative to the correlation sub-image
155            if sizemask > 1/2% eliminate point if more than half of the correlation box is masked
156                FF(ivec)=1; %
157                utable(ivec)=NaN;
158                vtable(ivec)=NaN;
159            else
160                image1_crop=image1_crop.*~mask1_crop;% put to zero the masked pixels (mask1_crop='true'=1)
161                image2_crop=image2_crop.*~mask2_crop;
162                image1_mean=mean(mean(image1_crop))/(1-sizemask);
163                image2_mean=mean(mean(image2_crop))/(1-sizemask);
164            end
165        else
166            image1_mean=mean(mean(image1_crop));
167            image2_mean=mean(mean(image2_crop));
168        end
169        %threshold on image minimum
170        if FF(ivec)==0
171            if check_MinIma && (image1_mean < par_civ.MinIma || image2_mean < par_civ.MinIma)
172                FF(ivec)=1;
173                %threshold on image maximum
174            elseif check_MaxIma && (image1_mean > par_civ.MaxIma || image2_mean > par_civ.MaxIma)
175                FF(ivec)=1;
176            end
177            if FF(ivec)==1
178                utable(ivec)=NaN;
179                vtable(ivec)=NaN;
180            else
181                if checkmask   % mask application
182                    image1_crop=(image1_crop-image1_mean).*~mask1_crop;%substract the mean, put to zero the masked parts
183                    image2_crop=(image2_crop-image2_mean).*~mask2_crop;
184                else
185                    image1_crop=(image1_crop-image1_mean);
186                    image2_crop=(image2_crop-image2_mean);
187                end
188                %deformation
189                if CheckDeformation
190                    xi=(1:mesh:size(image1_crop,2));
191                    yi=(1:mesh:size(image1_crop,1))';
192                    [XI,YI]=meshgrid(xi-ceil(size(image1_crop,2)/2),yi-ceil(size(image1_crop,1)/2));
193                    XIant=XI-par_civ.DUDX(ivec)*XI+par_civ.DUDY(ivec)*YI+ceil(size(image1_crop,2)/2);
194                    YIant=YI+par_civ.DVDX(ivec)*XI-par_civ.DVDY(ivec)*YI+ceil(size(image1_crop,1)/2);
195                    image1_crop=interp2(image1_crop,XIant,YIant);
196                    image1_crop(isnan(image1_crop))=0;
197                    xi=(1:mesh:size(image2_crop,2));
198                    yi=(1:mesh:size(image2_crop,1))';
199                    image2_crop=interp2(image2_crop,xi,yi,'*spline');
200                    image2_crop(isnan(image2_crop))=0;
201                end
202                sum_square=sum(sum(image1_crop.*image1_crop));
203                %reference: Oliver Pust, PIV: Direct Cross-Correlation
204                %%%%%% correlation calculation
205                result_conv= conv2(image2_crop,flip(flip(image1_crop,2),1),'valid');
206                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207                corrmax= max(max(result_conv));
208
209                %result_conv=(result_conv/corrmax); %normalize, peak=always 255
210                %Find the correlation max, at 255
211                [y,x] = find(result_conv==corrmax,1);
212                subimage2_crop=image2_crop(y:y+2*iby2/mesh,x:x+2*ibx2/mesh);%subimage of image 2 corresponding to the optimum displacement of first image
213                sum_square=sum_square*sum(sum(subimage2_crop.*subimage2_crop));% product of variances of image 1 and 2
214                sum_square=sqrt(sum_square);% srt of the variance product to normalise correlation
215                if ~isempty(y) && ~isempty(x)
216                    if par_civ.CorrSmooth==1
217                        [vector,FF(ivec)] = SUBPIXGAUSS (result_conv,x,y);
218                    elseif par_civ.CorrSmooth==2
219                        [vector,FF(ivec)] = SUBPIX2DGAUSS (result_conv,x,y);
220                    else
221                        [vector,FF(ivec)] = quadr_fit(result_conv,x,y);
222                    end
223                    utable(ivec)=vector(1)*mesh+shiftx(ivec);
224                    vtable(ivec)=-vector(2)*mesh+shifty(ivec);% vtable and shifty in image coordinates (opposite to pixel shift)
225                    ctable(ivec)=corrmax/sum_square;% correlation value
226                else
227                    FF(ivec)=1;
228                end
229            end
230        end
231    end
232end
233ytable=npy_ima-ytable+1;%reverse from j index to image coordinate y
234% result_conv=result_conv/sum_square;% keep the last correlation matrix for output
235
236
237
238%------------------------------------------------------------------------
239% --- Find the maximum of the correlation function with subpixel resolution
240% make a fit with a gaussian curve from the three correlation values across the max, along each direction.
241% OUPUT:
242% vector = optimum displacement vector with subpixel correction
243% FF =flag: =0 OK
244%           =1 , max too close to the edge of the search box (1 pixel margin)
245% INPUT:
246% result_conv: 2D correlation fct
247% x,y: position of the maximum correlation at integer values
248
249function [vector,FF] = SUBPIXGAUSS (result_conv,x,y)
250%------------------------------------------------------------------------
251% vector=[0 0]; %default
252FF=true;% error flag for vector truncated by the limited search box
253[npy,npx]=size(result_conv);
254
255peaky = y; peakx=x;
256if y < npy && y > 1 && x < npx-1 && x > 1
257    FF=false; % no error by the limited search box
258    max_conv=result_conv(y,x);% max correlation
259    %peak2noise= max(4,max_conv/std(reshape(result_conv,1,[])));% ratio of max conv to standard deviation of correlations (estiamtion of noise level), set to value 4 if it is too low
260    peak2noise=100;% TODO: make this threshold more precise, depending on the image noise
261    result_conv=result_conv*peak2noise/max_conv;% renormalise the correlation with respect to the noise
262    result_conv(result_conv<1)=1; %set to 1 correlation values smaller than 1  (=0 by discretisation, to avoid divergence in the log)
263   
264    f0 = log(result_conv(y,x));
265    f1 = log(result_conv(y-1,x));
266    f2 = log(result_conv(y+1,x));
267    peaky = peaky+ (f1-f2)/(2*f1-4*f0+2*f2);
268    f1 = log(result_conv(y,x-1));
269    f2 = log(result_conv(y,x+1));
270    peakx = peakx+ (f1-f2)/(2*f1-4*f0+2*f2);
271end
272
273vector=[peakx-floor(npx/2)-1 peaky-floor(npy/2)-1];
274
275%------------------------------------------------------------------------
276% --- Find the maximum of the correlation function after interpolation
277function [vector,FF] = SUBPIX2DGAUSS (result_conv,x,y)
278%------------------------------------------------------------------------
279% vector=[0 0]; %default
280FF=true;
281peaky=y;
282peakx=x;
283[npy,npx]=size(result_conv);
284if (x < npx) && (y < npy) && (x > 1) && (y > 1)
285    FF=false;
286max_conv=result_conv(y,x);% max correlation
287    peak2noise= max(4,max_conv/std(reshape(result_conv,1,[])));% ratio of max conv to standard deviation of correlations (estiamtion of noise level), set to value 4 if it is too low
288    result_conv=result_conv*peak2noise/max_conv;% renormalise the correlation with respect to the noise
289    result_conv(result_conv<1)=1; %set to 1 correlation values smaller than 1  (=0 by discretisation, to avoid divergence in the log)
290    for i=-1:1
291        for j=-1:1
292  %following 15 lines based on  H. Nobach and M. Honkanen (2005)
293  % Two-dimensional Gaussian regression for sub-pixel displacement
294  % estimation in particle image velocimetry or particle position
295  % estimation in particle tracking velocimetry
296  % Experiments in Fluids (2005) 38: 511-515
297            c10(j+2,i+2)=i*log(result_conv(y+j, x+i));
298            c01(j+2,i+2)=j*log(result_conv(y+j, x+i));
299            c11(j+2,i+2)=i*j*log(result_conv(y+j, x+i));
300            c20(j+2,i+2)=(3*i^2-2)*log(result_conv(y+j, x+i));
301            c02(j+2,i+2)=(3*j^2-2)*log(result_conv(y+j, x+i));
302        end
303    end
304    c10=(1/6)*sum(sum(c10));
305    c01=(1/6)*sum(sum(c01));
306    c11=(1/4)*sum(sum(c11));
307    c20=(1/6)*sum(sum(c20));
308    c02=(1/6)*sum(sum(c02));
309    deltax=(c11*c01-2*c10*c02)/(4*c20*c02-c11*c11);
310    deltay=(c11*c10-2*c01*c20)/(4*c20*c02-c11*c11);
311    if abs(deltax)<1
312        peakx=x+deltax;
313    end
314    if abs(deltay)<1
315        peaky=y+deltay;
316    end
317end
318vector=[peakx-floor(npx/2)-1 peaky-floor(npy/2)-1];
319
320%------------------------------------------------------------------------
321% --- Find the maximum of the correlation function after quadratic interpolation
322function [vector,F] = quadr_fit(result_conv,x,y)
323[npy,npx]=size(result_conv);
324if x<4 || y<4 || npx-x<4 ||npy-y <4
325    F=1;
326    vector=[x y];
327else
328    F=0;
329    x_ind=x-4:x+4;
330    y_ind=y-4:y+4;
331    x_vec=0.25*(x_ind-x);
332    y_vec=0.25*(y_ind-y);
333    [X,Y]=meshgrid(x_vec,y_vec);
334    coord=[reshape(X,[],1) reshape(Y,[],1)];
335    result_conv=reshape(result_conv(y_ind,x_ind),[],1);
336   
337   
338    % n=numel(X);
339    % x=[X Y];
340    % X=X-0.5;
341    % Y=Y+0.5;
342    % y = (X.*X+2*Y.*Y+X.*Y+6) + 0.1*rand(n,1);
343    p = polyfitn(coord,result_conv,2);
344    A(1,1)=2*p.Coefficients(1);
345    A(1,2)=p.Coefficients(2);
346    A(2,1)=p.Coefficients(2);
347    A(2,2)=2*p.Coefficients(4);
348    vector=[x y]'-A\[p.Coefficients(3) p.Coefficients(5)]';
349    vector=vector'-[floor(npx/2) floor(npy/2)]-1 ;
350    % zg = polyvaln(p,coord);
351    % figure
352    % surf(x_vec,y_vec,reshape(zg,9,9))
353    % hold on
354    % plot3(X,Y,reshape(result_conv,9,9),'o')
355    % hold off
356end
357
358
Note: See TracBrowser for help on using the repository browser.