Polar bar plots
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...
%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).
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
Polar histogram¶
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:
print 'Contrast= ', v_hist.max()/v_hist.mean()
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¶
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.