Files
scikit-image/doc/examples/plot_windowed_histogram.py
T

128 lines
4.1 KiB
Python

from __future__ import division
"""
========================
Sliding window histogram
========================
This example extracts a single coin from the coins image and generates a
histogram of its greyscale values.
It then computes a sliding window histogram of the complete image using
rank.windowed_histogram. The local histogram for the region surrounding
each pixel in the image is compared to that of the single coin, with
a similarity measure being computed and displayed.
To demonstrate the rotational invariance of the technique, the same
test is performed on a version of the coins image rotated by 45 degrees.
"""
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from skimage import data
from skimage.util.dtype import dtype_range
from skimage.util import img_as_ubyte
from skimage import exposure
from skimage.morphology import disk
from skimage.filter import rank
from skimage import transform
matplotlib.rcParams['font.size'] = 9
def windowed_histogram_similarity(image, selem, reference_hist, n_bins):
# Compute normalized windowed histogram feature vector for each pixel
px_histograms = rank.windowed_histogram(image, selem, n_bins=n_bins)
# Reshape coin histogram to (1,1,N) for broadcast when we want to use it in
# arithmetic operations with the windowed histograms fro the image
reference_hist = reference_hist.reshape((1,1) + reference_hist.shape)
# Compute Chi squared distance metric: sum((X-Y)**2 / (X+Y);
# a measure of distance between histograms
X = px_histograms
Y = reference_hist
num = (X-Y)*(X-Y)
denom = X+Y
frac = num / denom
frac[denom==0] = 0
chi_sqr = np.sum(frac, axis=2) * 0.5
# Generate a similarity measure. It needs to be low when distance is high.
# and high when distance is low; taking the reciprocal will do this.
# Chi squared will always be >= 0, add small value to prevent divide by 0.
similarity = 1 / (chi_sqr + 1.0e-4)
return similarity
# Load the coins image
img = img_as_ubyte(data.coins())
# img = img_as_ubyte(plt.imread('../../skimage/data/coins.png'))
# Quantize to 16 levels of grayscale; this way the output image will have a
# 16-dimensional feature vector per pixel
quantized_img = img//16
# Select the coin from the 4th column, second row.
# Co-ordinate ordering: [x1,y1,x2,y2]
coin_coords = [184,100,228,148] # 44 x 44 region
coin = quantized_img[coin_coords[1]:coin_coords[3], coin_coords[0]:coin_coords[2]]
# Compute coin histogram and normalize
coin_hist, _ = np.histogram(coin.flatten(), bins=16, range=(0,16))
coin_hist = coin_hist.astype(float) / np.sum(coin_hist)
# Compute a disk shaped mask that will define the shape of our sliding window
# Example coin is ~44px across, so make a disk 61px wide (2*rad+1) to be big
# enough for other coins too.
selem = disk(30)
# Compute the similarity across the complete image
similarity = windowed_histogram_similarity(quantized_img, selem, coin_hist,
coin_hist.shape[0])
# Now try a rotated image
rotated_img = img_as_ubyte(transform.rotate(img, 45.0, resize=True))
# Quantize to 16 levels as before
quantized_rotated_image = rotated_img//16
# Similarity on rotated image
rotated_similarity = windowed_histogram_similarity(quantized_rotated_image,
selem, coin_hist,
coin_hist.shape[0])
# Plot it all
fig, axes = plt.subplots(nrows=5, figsize=(6, 18))
ax0, ax1, ax2, ax3, ax4 = axes
ax0.imshow(img, cmap='gray')
ax0.set_title('Original image')
ax0.axis('off')
ax1.imshow(quantized_img, cmap='gray')
ax1.set_title('Quantized image')
ax1.axis('off')
ax2.imshow(coin, cmap='gray')
ax2.set_title('Coin from 2nd row, 4th column')
ax2.axis('off')
ax3.imshow(img, cmap='gray')
# While jet is not a great colormap, it makes the high similarity areas
# stand out
ax3.imshow(similarity, cmap='jet', alpha=0.5)
ax3.set_title('Original image with overlayed similarity')
ax3.axis('off')
ax4.imshow(rotated_img, cmap='gray')
ax4.imshow(rotated_similarity, cmap='jet', alpha=0.5)
ax4.set_title('Rotated image with overlayed similarity')
ax4.axis('off')
plt.show()