function gs = gridScoreForActivity(Activity, opt) % gridScoreForActivity % Activity - Acitvity pattern of a (simulated) cell. % opt - Structure with fields: % * minRadius -- Minimum radius for circular histogram % computed from values within the disk % defined by minRadius and maxRadius. % * maxRadius -- Maximum radius. % % RETURN % gs - Returns the grid score for the activity pattern. % % Copyright (C) 2015 Florian Raudies, 05/02/2015, Palo Alto, CA. % License, GNU GPL, free software, without any warranty. % if isfield(opt, 'minRadius'), minRadius = opt.minRadius; else minRadius = 0.05; end if isfield(opt, 'maxRadius'), maxRadius = opt.maxRadius; else maxRadius = 0.95; end % Determine sizes and their ratio. [nY nX] = size(Activity); r = nX/nY; [Y X] = ndgrid(linspace(-1, +1, nY), linspace(-r, +r, nX)); Radius = hypot(X, Y); Phi = mod(atan2(Y, X) + 2*pi, 2*pi); Selection = (minRadius < Radius) & (Radius < maxRadius); % Calculate the product moment correlation coefficient of Pearson AutoCorr = normAutoCorrWithFFT2D(Activity); % Extract a circular disk of the auto-correlation binNum = 12; binNum2 = binNum/2; dPhi = 2*pi/binNum; dPsi = dPhi/2; DiskSelection = AutoCorr(Selection); PhiSelection = Phi(Selection); % Define bins for circular distribution. BinCenter = 0 : dPhi : (2*pi - dPhi); DiskVoting = zeros(binNum, 1); PhiVoting = zeros(binNum, 1); % Select everything above and below adding an subtracting half of the bin % distance, circular boundary condition. IndexSelection = PhiSelection < BinCenter(1)+dPsi ... | PhiSelection >= 2*pi-dPsi; DiskVoting(1) = sum(DiskSelection(IndexSelection)); PhiVoting(1) = sum(IndexSelection); % Do the remaining bins. for iBin = 2:binNum, IndexSelection = PhiSelection >= BinCenter(iBin)-dPsi ... & PhiSelection < BinCenter(iBin)+dPsi; DiskVoting(iBin) = sum(DiskSelection(IndexSelection)); PhiVoting(iBin) = sum(IndexSelection); end % Calculate the circular correlation % In one group correlate rotations of 60° and 120° and in the other group % correlate rotations of 30°, 90°, and 150°. % Conversion into orientations. Shift = [30 60 90 120 150]/180*pi; GroupIndexOne = [2 4]; GroupIndexTwo = [1 3 5]; sNum = length(Shift); % Use function pearsonCorr because it works for even number (unlike the fft solution) DiskCorr = pearsonCorr(DiskVoting, 'circular'); ShiftCorr = zeros(sNum, 1); for iShift = 1:sNum, DiskCorrShift = vecShiftCyc(DiskCorr, -Shift(iShift)*binNum2/pi); ShiftCorr(iShift) = DiskCorrShift(binNum2); end ValGroupOne = ShiftCorr(GroupIndexOne); % 60° and 120° ValGroupTwo = ShiftCorr(GroupIndexTwo); % 30°, 90°, and 150° gs = min(ValGroupOne) - max(ValGroupTwo); function X = vecShiftCyc(X, s) % vecShiftCyc % X - Vector to shift for amount 's' with cyclic boundary % condition. % s - Amount to shift, which could be also a FRACTION of one pixel. % % RETURN % X - Shifted version of the vector. % ds1 = s - floor(s); ds0 = 1 - ds1; s0 = floor(s); s1 = ceil(s); X0 = circshift(X(:), s0); X1 = circshift(X(:), s1); X = ds0 * X0 + ds1 * X1; function X = pearsonCorr(X, boundary) % personCorr % X - Input data which could be a multi-dimensional array. % boundary - Boundary condition for correlation. % % RETURN % Y - Full correlation result for all shifts which data X % provides, basicially determined by the dimensions of X. % % DESCRIPTION % Pearson's product moment correlation coefficient. % n = numel(X); I = ones(size(X)); A = n * imfilter(X, X, 'same', boundary); B = sum(X(:)) * imfilter(X, I, 'same', boundary); C = sqrt( n * sum(X(:).^2) - sum(X(:)).^2 ); D = sqrt( n * imfilter(X.^2, I, 'same', boundary) - imfilter(X, I, 'same', boundary).^2 ); X = (A - B) ./ (C.*D + eps); function AC = normAutoCorrWithFFT2D(S) % normAutoCorrWithFFT2D % S - Signal one assumed to be a row-vector. % % RETURN % NA - Normalized auto-correlation result. % F = fft2(S-mean(S(:))); F = F .* conj(F); AC = fftshift(ifft2(F)); AC = real(AC); AC = AC/AC(1);