Fourier transform for fiber alignment

I'm working on an application to determine from an image the degree of alignment of a fiber network. I've read several papers on this issue and they basically do this:

  • Find the 2D discrete Fourier transform ( DFT = F(u,v) ) of the image (gray, range 0-255)
  • Find the Fourier Spectrum ( FS = abs(F(u,v)) ) and the Power Spectrum ( PS = FS^2 )
  • Convert spectrum to polar coordinates and divide it into 1º intervals.
  • Calculate number-averaged line intensities ( FI ) for each interval ( theta ), that is, the average of all the intensities (pixels) forming "theta" degrees with respect to the horizontal axis.
  • Transform FI(theta) to cartesian coordinates

    Cxy(theta) = [FI*cos(theta), FI*sin(theta)]

  • Find eigenvalues ( lambda1 and lambda2 ) of the matrix Cxy'*Cxy

  • Find alignment index as alpha = 1 - lamda2/lambda1
  • I've implemented this in MATLAB (code below), but I'm not sure whether it is ok since point 3 and 4 are not really clear for me (I'm getting similar results to those of the papers, but not in all cases). For instance, in point 3, "spectrum" is referring to FS or to PS?. And in point 4, how should this average be done? are all the pixels considered? (even though there are more pixels in the diagonal).

    rgb = imread('network.tif');%513x513 pixels
    im = rgb2gray(rgb);
    im = imrotate(im,-90);%since FFT space is rotated 90º
    FT = fft2(im) ;
    FS = abs(FT); %Fourier spectrum
    PS = FS.^2; % Power spectrum
    FS = fftshift(FS);
    PS = fftshift(PS);
    
    xoffset = (513-1)/2;
    yoffset = (513-1)/2;
    
    % Avoid low frequency points
    x1 = 5;
    y1 = 0;
    
    % Maximum high frequency pixels
    x2 = 255;
    y2 = 0;
    
    for theta = 0:pi/180:pi
        % Transposed rotation matrix
        Rt = [cos(theta) sin(theta); 
             -sin(theta) cos(theta)]; 
    
        % Find radial lines necessary for improfile
        xy1_rot = Rt * [x1; y1] + [xoffset; yoffset]; 
        xy2_rot = Rt * [x2; y2] + [xoffset; yoffset];
    
        plot([xy1_rot(1) xy2_rot(1)], ...
             [xy1_rot(2) xy2_rot(2)], ...
             'linestyle','none', ...
             'marker','o', ...
             'color','k');
    
         prof = improfile(F,[xy1_rot(1) xy2_rot(1)],[xy1_rot(2) xy2_rot(2)]);
         i = i + 1;
         FI(i) = sum(prof(:))/length(prof);
         Cxy(i,:) = [FI(i)*cos(theta), FI(i)*sin(theta)];
    end
    
    C = Cxy'*Cxy;
    [V,D] = eig(C)
    lambda2 = D(1,1);
    lambda1 = D(2,2);
    
    alpha = 1 - lambda2/lambda1
    

    A)原始图像,B)log(P + 1)的图,C)FI的极坐标图 Figure: A) original image, B) plot of log(P+1), C) polar plot of FI.

    My main concern is that when I choose an artificial image perfectly aligned (attached figure), I get alpha = 0.91, and it should be exactly 1. Any help will be greatly appreciated.

    PD: those black dots in the middle plot are just the points used by improfile.


    I believe that there are a couple sources of potential error here that are leading to you not getting a perfect alpha value.

    Discrete Fourier Transform

    You have discrete imaging data which forces you to take a discrete Fourier transform which inevitably (depending on the resolution of the input data) have some accuracy issues.

    Binning vs. Sampling Along a Line

    The way that you have done the binning is that you literally drew a line (rotated by a particular angle) and sampled the image along that line using improfile . Using improfile performs interpolation of your data along that line introducing yet another potential source of error. The default is nearest neighbor interpolation which in the example shown below can cause multiple "profiles" to all pick up the same points.

    This was with a rotation of 1-degree off-vertical when technically you'd want those peaks to only appear for a perfectly vertical line. It is clear to see how this sort of interpolation of the Fourier spectrum can lead to a spread around the "correct" answer.

    Data Undersampling

    Similar to Nyquist sampling in the Fourier domain, sampling in the spatial domain has some requirements as well.

    Imagine for a second that you wanted to use 45-degree bin widths instead of the 1-degree. Your approach would still sample along a thin line and use that sample to represent 45-degrees worth or data. Clearly, this is a gross under-sampling of the data and you can imagine that the result wouldn't be very accurate.

    It becomes more and more of an issue the further you get from the center of the image since the data in this "bin" is really pie wedge shaped and you're approximating it with a line.

    A Potential Solution

    A different approach to binning would be to determine the polar coordinates (r, theta) for all pixel centers in the image. Then to bin the theta components into 1-degree bins. Then sum all of the values that fall into that bin.

    This has several advantages:

  • It removes the undersampling that we talked about and draws samples from the entire "pie wedge" regardless of the sampling angle.
  • It ensures that each pixel belongs to one and only one angular bin
  • I have implemented this alternate approach in the code below with some false horizontal line data and am able to achieve an alpha value of 0.988 which I'd say is pretty good given the discrete nature of the data.

    % Draw a bunch of horizontal lines
    data = zeros(101);
    data([5:5:end],:) = 1;
    
    fourier = fftshift(fft2(data));
    
    FS = abs(fourier);
    PS = FS.^2;
    
    center = fliplr(size(FS)) / 2;
    
    [xx,yy] = meshgrid(1:size(FS,2), 1:size(FS, 1));
    
    coords = [xx(:), yy(:)];
    
    % De-mean coordinates to center at the middle of the image
    coords = bsxfun(@minus, coords, center);
    
    [theta, R] = cart2pol(coords(:,1), coords(:,2));
    
    % Convert to degrees and round them to the nearest degree
    degrees = mod(round(rad2deg(theta)), 360);
    
    degreeRange = 0:359;
    
    % Band pass to ignore high and low frequency components;
    lowfreq = 5;
    highfreq = size(FS,1)/2;
    
    % Now average everything with the same degrees (sum over PS and average by the number of pixels)
    for k = degreeRange
        ps_integral(k+1) = mean(PS(degrees == k & R > lowfreq & R < highfreq));
        fs_integral(k+1) = mean(FS(degrees == k & R > lowfreq & R < highfreq));
    end
    
    thetas = deg2rad(degreeRange);
    
    Cxy = [ps_integral.*cos(thetas);
           ps_integral.*sin(thetas)]';
    
    C = Cxy' * Cxy;
    [V,D] = eig(C);
    
    lambda2 = D(1,1);
    lambda1 = D(2,2);
    
    alpha = 1 - lambda2/lambda1;
    
    链接地址: http://www.djcxy.com/p/89780.html

    上一篇: Python中图像像素的强度和色彩度量

    下一篇: 傅里叶变换用于光纤对准