I needed to show prior information for the orientation of contours in natural images showing a preference for cardinal axis. A polar plot showing seemed to be a natural choice for showing the probability distribution function. However, this seems visually flawed...

In [1]:
%matplotlib inline
%config InlineBackend.figure_format='retina'
import matplotlib.pyplot as plt
import numpy as np
np.set_printoptions(precision=2, suppress=True)

The data is obtained via a sparse coding technique on a set of natural images (see http://pythonhosted.org/SparseEdges/ for more information).

In [2]:
edgeslist = np.load('/tmp/prior_vanilla_serre07_distractors_edges.npy')
thetas = np.linspace(-np.pi/2, np.pi/2, 24+1)[1:]
print 'angles (deg) = ',  thetas*180/np.pi
theta_bin_width = thetas[1] - thetas[0]
theta_bin = np.hstack((thetas-theta_bin_width/2, thetas[-1]+theta_bin_width/2)) 
print 'angles (deg) = ',  theta_bin*180/np.pi

theta = edgeslist[2, ...].ravel()
value = edgeslist[4, ...].ravel()
weights = np.absolute(value)/(np.absolute(value)).sum()

v_hist, v_theta_edges_ = np.histogram(theta, bins=theta_bin, density=False, weights=weights)
print 'angles (deg) = ',  v_theta_edges_*180/np.pi
angles (deg) =  [-82.5 -75.  -67.5 -60.  -52.5 -45.  -37.5 -30.  -22.5 -15.   -7.5   0.
   7.5  15.   22.5  30.   37.5  45.   52.5  60.   67.5  75.   82.5  90. ]
angles (deg) =  [-86.25 -78.75 -71.25 -63.75 -56.25 -48.75 -41.25 -33.75 -26.25 -18.75
 -11.25  -3.75   3.75  11.25  18.75  26.25  33.75  41.25  48.75  56.25
  63.75  71.25  78.75  86.25  93.75]
angles (deg) =  [-86.25 -78.75 -71.25 -63.75 -56.25 -48.75 -41.25 -33.75 -26.25 -18.75
 -11.25  -3.75   3.75  11.25  18.75  26.25  33.75  41.25  48.75  56.25
  63.75  71.25  78.75  86.25  93.75]

Polar histogram

In [3]:
fig = plt.figure(figsize=(13, 13))
ax = fig.add_subplot(111, axisbg='w', polar=True)
ax.bar(v_theta_edges_[1:], v_hist, width=v_theta_edges_[:-1] - v_theta_edges_[1:], color='b')# edgecolor="none")
ax.bar(v_theta_edges_[1:]+np.pi, v_hist, width=v_theta_edges_[:-1] - v_theta_edges_[1:], color='g')
ax.plot(v_theta_edges_, np.ones_like(v_theta_edges_)*v_hist.mean(), 'r--')
ax.plot(v_theta_edges_+np.pi, np.ones_like(v_theta_edges_)*v_hist.mean(), 'r--')
_ = plt.setp(ax, yticks=[])

So far, so good... But notice that the cardinals are really dominating the histogram while their relative value is more modest:

In [4]:
print 'Contrast= ', v_hist.max()/v_hist.mean()
Contrast=  2.70647158989

This is due to the fact that visually the strength of a bar is given by its surface. However the surface of a wedge if equal to $d\theta . p(\theta)^2$, where $d\theta$ is the bin's width and $p(\theta)$ the value of the distribution. This can be visually checked by comparing the area above the red dashed line which gives the average distribution: the area below the curve is clearly not equal to the area above it. A solution is therefore to transform the height of each bar such that its surface is proportional to $p(\theta)$. This is easily achieved using a polar bar plot with bar of height $\sqrt{p(\theta)}$. The resulting histogram looks like:

Visually-balanced polar histogram

In [5]:
fig = plt.figure(figsize=(13, 13))
ax = fig.add_subplot(111, axisbg='w', polar=True)
v_hist /= v_hist.mean() # convenience normalization to compare with uniform histogram
ax.bar(v_theta_edges_[1:], np.sqrt(v_hist), width=v_theta_edges_[:-1] - v_theta_edges_[1:], color='b')# edgecolor="none")
ax.bar(v_theta_edges_[1:]+np.pi, np.sqrt(v_hist), width=v_theta_edges_[:-1] - v_theta_edges_[1:], color='g')
ax.plot(v_theta_edges_, np.ones_like(v_theta_edges_), 'r--')
ax.plot(v_theta_edges_+np.pi, np.ones_like(v_theta_edges_), 'r--')
_ = plt.setp(ax, yticks=[])

Certainly more modest graphically, but at least, there is no visual cheating.