Information along contours

Attneave (1954) argued that information along contours is concentrated along points of locally maximal curvature. In a recent paper, we provide a formal proof of this, based on Shannon's definition of information (Feldman & Singh, Psychological Review, in press). This paper also extends Attneave's original claim by demonstrating the asymmetry in information content that arises from the sign of curvature. Whereas Attneave's analysis treated positive and negative curvature—i.e., convex and concave contour segments—symmetrically, we show that for closed contours, regions of negative curvature carry greater information than corresponding segments of positive curvature. MATLAB code for computing and plotting information along contours (i.e., the surprisal) is included below.

The option signed = 0 treats positive and negative curvature symmetrically (consistent with Attneave), whereas the option signed = 1takes into account (for closed contours) the sign of curvature as well, yielding a different distribution of information. The two figures below demonstrate this difference; the left one is produced from the unsigned version, the right one from the signed version.

curv shape info

The code assumes that the contour is sampled uniformly, in counter-clockwise direction, and is defined by a pair of vectors of equal length (x and y). resolution alters the number of neighboring points the code takes into account in computing the tangent direction at any given point.

function surprisal = contourinfoplot(x, y, signed, resolution)

x = x(:); y = y(:);

if length(x) ~= length(y)
error('the x and y vectors defining the contour must have equal lengths!');

N = length(x);
b = 1; % spread term for the Von Mises distribution

if (x(1) == x(N) & y(1) == y(N))
N = N-1;
open = 0;
open = 1;

normalmap = [];
surprisalmap = [];

for j = 1:N

% define the previous and next points relative to the current one
xprev = 0;
yprev = 0;
xnext = 0;
ynext = 0;

for k = 1:resolution
prev(k) = j-k;
next(k) = j+k;
if(prev(k) < 1) prev(k) = prev(k) + N; end
if(next(k) > N) next(k) = next(k) - N; end

xprev = xprev + x(prev(k));
yprev = yprev + y(prev(k));
xnext = xnext + x(next(k));
ynext = ynext + y(next(k));

xprev = xprev/k;
yprev = yprev/k;
xnext = xnext/k;
ynext = ynext/k;

% vector _from_ the previous point; vector _to_ the next point
vecprev = [x(j), y(j)] - [xprev, yprev];
vecnext = [xnext, ynext] - [x(j), y(j)];

% compute the magnitude of the turning angle using dot product
alpha = acos( dot(vecprev, vecnext) / (norm(vecprev)*norm(vecnext)) ); 

% compute the sign of turning using cross product
% (assumes a counterclockwise sampling of the contour)
cp = cross( [vecprev, 0], [vecnext,0] );
alpha = sign(cp(3))*alpha;

% compute the surprisal using -log von mises
surprisal = -log( exp(b*cos(alpha - (2*pi/(N/resolution))) )/( 2*pi*besseli(0,b) ) );
surprisal = -log( exp(b*cos(alpha) )/( 2*pi*besseli(0,b) ) );

% turning angles not defined near the end points of an open curve
if(open & (j - resolution < 1 | j + resolution > N)) surprisal = 0; end

% compute the tangent and normal vectors
tangvec = vecprev + vecnext;
tangvec = tangvec/norm(tangvec);
normvec = [[0 1; -1 0]*tangvec']';

normalmap = [normalmap; normvec];
surprisalmap = [surprisalmap; surprisal];


% scale the size of histogram bars
surprisalmap = (1/range(surprisalmap))*(surprisalmap(:) - min(surprisalmap));
needlesize = max(range(x), range(y))/10;

% define the histogram normal to the shape
normalmap(:,1) = surprisalmap.*normalmap(:,1);
normalmap(:,2) = surprisalmap.*normalmap(:,2);
xnormals = [x(1:N), x(1:N) + needlesize*normalmap(:,1)];
ynormals = [y(1:N), y(1:N) + needlesize*normalmap(:,2)];

% plot
figure, plot(x,y,'r'), axis equal;
on, plot(xnormals', ynormals', 'k-');