From c76daa97a9a1f3014345e6e1bd0fceae2822fc0a Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Fri, 28 Jun 2013 16:00:03 -0500 Subject: [PATCH 1/3] Add explanation to LBP example --- doc/examples/plot_local_binary_pattern.py | 176 +++++++++++++++++++--- 1 file changed, 156 insertions(+), 20 deletions(-) diff --git a/doc/examples/plot_local_binary_pattern.py b/doc/examples/plot_local_binary_pattern.py index 8c9cabae..98ea08d4 100644 --- a/doc/examples/plot_local_binary_pattern.py +++ b/doc/examples/plot_local_binary_pattern.py @@ -4,27 +4,158 @@ Local Binary Pattern for texture classification =============================================== In this example, we will see how to classify textures based on LBP (Local -Binary Pattern). The histogram of the LBP result is a good measure to classify -textures. For simplicity the histogram distributions are then tested against -each other using the Kullback-Leibler-Divergence. +Binary Pattern). LBP looks at points surrounding a central point and tests +whether the surrounding points are greater than or less than the central point +(i.e. gives a binary result). +Before trying out LBP on an image, it helps to look at a schematic of LBPs. +The below code is just used to plot the schematic. """ from __future__ import print_function - import numpy as np -import matplotlib import matplotlib.pyplot as plt + +METHOD = 'uniform' +plt.rcParams['font.size'] = 9 + + +def plot_circle(ax, center, radius, color): + circle = plt.Circle(center, radius, facecolor=color, edgecolor='0.5') + ax.add_patch(circle) + + +def plot_lbp_model(ax, binary_values): + """Draw the schematic for a local binary pattern.""" + # Geometry spec + theta = np.deg2rad(45) + R = 1 + r = 0.15 + w = 1.5 + gray = '0.5' + + # Draw the central pixel. + plot_circle(ax, (0, 0), radius=r, color=gray) + # Draw the surrounding pixels. + for i, facecolor in enumerate(binary_values): + x = R * np.cos(i * theta) + y = R * np.sin(i * theta) + plot_circle(ax, (x, y), radius=r, color=str(facecolor)) + + # Draw the pixel grid. + for x in np.linspace(-w, w, 4): + ax.axvline(x, color=gray) + ax.axhline(x, color=gray) + + # Tweak the layout. + ax.axis('image') + ax.axis('off') + size = w + 0.2 + ax.set_xlim(-size, size) + ax.set_ylim(-size, size) + + +fig, axes = plt.subplots(ncols=5, figsize=(7, 2)) + +titles = ['flat', 'flat', 'edge', 'corner', 'non-uniform'] + +binary_patterns = [np.zeros(8), + np.ones(8), + np.hstack([np.ones(4), np.zeros(4)]), + np.hstack([np.zeros(3), np.ones(5)]), + [1, 0, 0, 1, 1, 1, 0, 0]] + +for ax, values, name in zip(axes, binary_patterns, titles): + plot_lbp_model(ax, values) + ax.set_title(name) + +""" +.. image:: PLOT2RST.current_figure + +The figure above shows example results with black (or white) representing +pixels that are less (or more) intense than the central pixel. When surrounding +pixels are all black or all white, then that image region is flat (i.e. +featureless). Groups of continuous black or white pixels are considered +"uniform" patterns that can be interpreted as corners or edges. If pixels +switch back-and-forth between black and white pixels, the pattern is considered +"non-uniform". + +When using LBP to detect texture, you measure a collection of LBPs over an +image patch and look at the distribution of these LBPs. Lets apply LBP to +a brick texture. +""" + from skimage.transform import rotate from skimage.feature import local_binary_pattern from skimage import data - +from skimage.color import label2rgb # settings for LBP -METHOD = 'uniform' -P = 16 -R = 2 -matplotlib.rcParams['font.size'] = 9 +radius = 3 +n_points = 8 * radius + + +def overlay_labels(image, lbp, labels): + mask = np.logical_or.reduce([lbp == each for each in labels]) + return label2rgb(mask, image=image, bg_label=0, alpha=0.5) + + +def highlight_bars(bars, indexes): + for i in indexes: + bars[i].set_facecolor('r') + + +image = data.load('brick.png') +lbp = local_binary_pattern(image, n_points, radius, METHOD) + +def hist(ax, lbp): + n_bins = lbp.max() + 1 + return ax.hist(lbp.ravel(), normed=True, bins=n_bins, range=(0, n_bins), + facecolor='0.5') + +# plot histograms of LBP of textures +fig, (ax_img, ax_hist) = plt.subplots(nrows=2, ncols=3, figsize=(9, 6)) +plt.gray() + +titles = ('edge', 'flat', 'corner') +w = width = radius - 1 +edge_labels = range(n_points // 2 - w, n_points // 2 + w + 1) +flat_labels = range(0, w + 1) + range(n_points - w, n_points + 2) +i_14 = n_points // 4 # 1/4th of the histogram +i_34 = 3 * (n_points // 4) # 3/4th of the histogram +corner_labels = range(i_14 - w, i_14 + w + 1) + range(i_34 - w, i_34 + w + 1) + +label_sets = (edge_labels, flat_labels, corner_labels) + +for ax, labels in zip(ax_img, label_sets): + ax.imshow(overlay_labels(image, lbp, labels)) + +for ax, labels, name in zip(ax_hist, label_sets, titles): + counts, _, bars = hist(ax, lbp) + highlight_bars(bars, labels) + ax.set_ylim(ymax=np.max(counts[:-1])) + ax.set_xlim(xmax=n_points + 2) + ax.set_title(name) + +ax_hist[0].set_ylabel('Percentage') +for ax in ax_img: + ax.axis('off') + + +""" +.. image:: PLOT2RST.current_figure + +The above plot highlights flat, edge-like, and corner-like regions of the +image. + +The histogram of the LBP result is a good measure to classify textures. For +simplicity the histogram distributions are then tested against each other using +the Kullback-Leibler-Divergence. +""" + +# settings for LBP +radius = 2 +n_points = 8 * radius def kullback_leibler_divergence(p, q): @@ -37,11 +168,12 @@ def kullback_leibler_divergence(p, q): def match(refs, img): best_score = 10 best_name = None - lbp = local_binary_pattern(img, P, R, METHOD) - hist, _ = np.histogram(lbp, normed=True, bins=P + 2, range=(0, P + 2)) + lbp = local_binary_pattern(img, n_points, radius, METHOD) + n_bins = lbp.max() + 1 + hist, _ = np.histogram(lbp, normed=True, bins=n_bins, range=(0, n_bins)) for name, ref in refs.items(): - ref_hist, _ = np.histogram(ref, normed=True, bins=P + 2, - range=(0, P + 2)) + ref_hist, _ = np.histogram(ref, normed=True, bins=n_bins, + range=(0, n_bins)) score = kullback_leibler_divergence(hist, ref_hist) if score < best_score: best_score = score @@ -54,9 +186,9 @@ grass = data.load('grass.png') wall = data.load('rough-wall.png') refs = { - 'brick': local_binary_pattern(brick, P, R, METHOD), - 'grass': local_binary_pattern(grass, P, R, METHOD), - 'wall': local_binary_pattern(wall, P, R, METHOD) + 'brick': local_binary_pattern(brick, n_points, radius, METHOD), + 'grass': local_binary_pattern(grass, n_points, radius, METHOD), + 'wall': local_binary_pattern(wall, n_points, radius, METHOD) } # classify rotated textures @@ -75,16 +207,20 @@ plt.gray() ax1.imshow(brick) ax1.axis('off') -ax4.hist(refs['brick'].ravel(), normed=True, bins=P + 2, range=(0, P + 2)) +hist(ax, refs['brick']) ax4.set_ylabel('Percentage') ax2.imshow(grass) ax2.axis('off') -ax5.hist(refs['grass'].ravel(), normed=True, bins=P + 2, range=(0, P + 2)) +hist(ax5, refs['grass']) ax5.set_xlabel('Uniform LBP values') ax3.imshow(wall) ax3.axis('off') -ax6.hist(refs['wall'].ravel(), normed=True, bins=P + 2, range=(0, P + 2)) +hist(ax6, refs['wall']) + +""" +.. image:: PLOT2RST.current_figure +""" plt.show() From 42e07a8ef6b75343ddc8b6382ca1180bdcf2d87f Mon Sep 17 00:00:00 2001 From: tonysyu Date: Mon, 1 Jul 2013 20:33:30 -0500 Subject: [PATCH 2/3] Fix naming bug --- doc/examples/plot_local_binary_pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/examples/plot_local_binary_pattern.py b/doc/examples/plot_local_binary_pattern.py index 98ea08d4..c97a792d 100644 --- a/doc/examples/plot_local_binary_pattern.py +++ b/doc/examples/plot_local_binary_pattern.py @@ -207,7 +207,7 @@ plt.gray() ax1.imshow(brick) ax1.axis('off') -hist(ax, refs['brick']) +hist(ax4, refs['brick']) ax4.set_ylabel('Percentage') ax2.imshow(grass) From 516d6efa5d65991e87a8b657d8d22cc005e35ab4 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 6 Jul 2013 00:55:51 -0500 Subject: [PATCH 3/3] Fix Python 3 incompatibility (also, some wording tweaks) --- doc/examples/plot_local_binary_pattern.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/examples/plot_local_binary_pattern.py b/doc/examples/plot_local_binary_pattern.py index c97a792d..6789b238 100644 --- a/doc/examples/plot_local_binary_pattern.py +++ b/doc/examples/plot_local_binary_pattern.py @@ -120,10 +120,11 @@ plt.gray() titles = ('edge', 'flat', 'corner') w = width = radius - 1 edge_labels = range(n_points // 2 - w, n_points // 2 + w + 1) -flat_labels = range(0, w + 1) + range(n_points - w, n_points + 2) +flat_labels = list(range(0, w + 1)) + list(range(n_points - w, n_points + 2)) i_14 = n_points // 4 # 1/4th of the histogram i_34 = 3 * (n_points // 4) # 3/4th of the histogram -corner_labels = range(i_14 - w, i_14 + w + 1) + range(i_34 - w, i_34 + w + 1) +corner_labels = (list(range(i_14 - w, i_14 + w + 1)) + + list(range(i_34 - w, i_34 + w + 1))) label_sets = (edge_labels, flat_labels, corner_labels) @@ -148,9 +149,9 @@ for ax in ax_img: The above plot highlights flat, edge-like, and corner-like regions of the image. -The histogram of the LBP result is a good measure to classify textures. For -simplicity the histogram distributions are then tested against each other using -the Kullback-Leibler-Divergence. +The histogram of the LBP result is a good measure to classify textures. Here, +we test the histogram distributions against each other using the +Kullback-Leibler-Divergence. """ # settings for LBP