Title: Local Binary Patterns with Python & OpenCV - PyImageSearch

URL Source: https://pyimagesearch.com/2015/12/07/local-binary-patterns-with-python-opencv/

Published Time: 2015-12-07T15:00:25+00:00

Markdown Content:
[![Image 1: lbp_results_montage](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20486'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_results_montage.jpg)

Well. I’ll just come right out and say it. _**Today is my 27th birthday.**_

As a kid I was always _super excited_ about my birthday. It was another year closer to being able to drive a car. Go to R rated movies. Or buy alcohol.

But now as an adult, I don’t care too much for my birthday — I suppose it’s just another reminder of the passage of time and how it can’t be stopped. And to be totally honest with you, I guess I’m a bit nervous about turning the “Big 3-0” in a few short years.

In order to rekindle some of that “little kid excitement”, I want to do something special with today’s post. Since today is both a _Monday_ (when new PyImageSearch blog posts are published) and _my birthday_ (two events that will not coincide again until 2020), I’ve decided to put together a really great tutorial on _**texture and pattern recognition in images**_.

In the remainder of this blog post I’ll show you how to use the [Local Binary Patterns](https://en.wikipedia.org/wiki/Local_binary_patterns) image descriptor (along with a bit of machine learning) to automatically _classify_ and _identify_ textures and patterns in images (such as the texture/pattern of wrapping paper, cake icing, or candles, for instance).

Read on to find out more about Local Binary Patterns and how they can be used for texture classification.

![Image 2](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20128%20128'%3E%3C/svg%3E)

#### Looking for the source code to this post?

[Jump Right To The Downloads Section](#download-the-code)

PyImageSearch Gurus
-------------------

The majority of this blog post on texture and pattern recognition is based on the Local Binary Patterns lesson inside the _**[PyImageSearch Gurus course](https://pyimagesearch.com/pyimagesearch-gurus/)**_.

While the lesson in PyImageSearch Gurus goes into _**a lot more detail**_ than what this tutorial does, I still wanted to give you a taste of what PyImageSearch Gurus — my _magnum opus_ on computer vision — has in store for you.

If you like this tutorial, there are over **29 lessons** spanning **324 pages** covering image descriptors (HOG, Haralick, Zernike, etc.), keypoint detectors (FAST, DoG, GFTT, etc.), and local invariant descriptors (SIFT, SURF, RootSIFT, etc.), inside the course.

At the time of this writing, the PyImageSearch Gurus course also covers an additional **166** lessons and **1,291 pages** including computer vision topics such as _face recognition_, _deep learning_, _automatic license plate recognition_, and _training your own custom object detectors_, just to name a few.

**If this sounds interesting to you, _[be sure to take a look and consider signing up for the next open enrollment!](https://pyimagesearch.com/pyimagesearch-gurus/)_**

What are Local Binary Patterns?
-------------------------------

Local Binary Patterns, or LBPs for short, are a texture descriptor made popular by the work of Ojala et al. in their 2002 paper, _[Multiresolution Grayscale and Rotation Invariant Texture Classification with Local Binary Patterns](http://www.outex.oulu.fi/publications/pami_02_opm.pdf)_ (although the concept of LBPs were introduced as early as 1993).

Unlike [Haralick texture features](http://haralick.org/journals/TexturalFeatures.pdf) that compute a _global representation_ of texture based on the [Gray Level Co-occurrence Matrix](http://www.mathworks.com/help/images/gray-level-co-occurrence-matrix-glcm.html), LBPs instead compute a _local representation_ of texture. This local representation is constructed by comparing each pixel with its surrounding neighborhood of pixels.

The first step in constructing the LBP texture descriptor is to convert the image to grayscale. For each pixel in the grayscale image, we select a neighborhood of size _r_ surrounding the center pixel. A LBP value is then calculated for this center pixel and stored in the output 2D array with the same width and height as the input image.

For example, let’s take a look at the original LBP descriptor which operates on a fixed _3 x 3_ neighborhood of pixels just like this:

[![Image 3: Figure 1: The first step in constructing a LBP is to take the 8 pixel neighborhood surrounding a center pixel and construct it to construct a set of 8 binary digits.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20218'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_thresholding.jpg)

**Figure 1:** The first step in constructing a LBP is to take the 8 pixel neighborhood surrounding a center pixel and threshold it to construct a set of 8 binary digits.

In the above figure we take the center pixel (highlighted in red) and threshold it against its neighborhood of 8 pixels. If the intensity of the center pixel is greater-than-or-equal to its neighbor, then we set the value to _1_; otherwise, we set it to _0_. With 8 surrounding pixels, we have a total of _2 ^ 8 = 256_ possible combinations of LBP codes.

From there, we need to calculate the LBP value for the center pixel. We can start from any neighboring pixel and work our way clockwise or counter-clockwise, but our ordering must be kept _consistent_ for all pixels in our image and all images in our dataset. Given a _3 x 3_ neighborhood, we thus have 8 neighbors that we must perform a binary test on. The results of this binary test are stored in an 8-bit array, which we then convert to decimal, like this:

[![Image 4: Figure 2: Taking the 8-bit binary neighborhood of the center pixel and converting it into a decimal representation. (Thanks to Hanzra Tech for the inspiration on this visualization!)](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20175'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_calculation.jpg)

**Figure 2:** Taking the 8-bit binary neighborhood of the center pixel and converting it into a decimal representation. (Thanks to Bikramjot of [Hanzra Tech](http://hanzratech.in/) for the inspiration on this visualization!)

In this example we start at the top-right point and work our way **_clockwise_** accumulating the binary string as we go along. We can then convert this binary string to decimal, yielding a value of 23.

This value is stored in the output LBP 2D array, which we can then visualize below:

[![Image 5: Figure 3: The calculated LBP value is then stored in an output array with the same width and height as the original image.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20304'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_to_output.jpg)

**Figure 3:** The calculated LBP value is then stored in an output array with the same width and height as the original image.

This process of thresholding, accumulating binary strings, and storing the output decimal value in the LBP array is then repeated for each pixel in the input image.

Here is an example of computing and visualizing a full LBP 2D array:

[![Image 6: Figure 4: An example of computing the LBP representation (right) from the original input image (left).](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20292'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_2d_array.jpg)

**Figure 4:** An example of computing the LBP representation _(right)_ from the original input image _(left)_.

The last step is to compute a histogram over the output LBP array. Since a _3 x 3_ neighborhood has _2 ^ 8 = 256_ possible patterns, our LBP 2D array thus has a _minimum value of 0_ and a _maximum value of 255_, allowing us to construct a 256-bin histogram of LBP codes as our final feature vector:

[![Image 7: Figure 5: Finally, we can compute a histogram that tabulates the number of times each LBP pattern occurs. We can treat this histogram as our feature vector.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20380'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_256bin_histogram.jpg)

**Figure 5:** Finally, we can compute a histogram that tabulates the number of times each LBP pattern occurs. We can treat this histogram as our feature vector.

A primary benefit of this original LBP implementation is that we can capture extremely fine-grained details in the image. However, being able to capture details at such a small scale is also the _biggest drawback to the algorithm_ — we cannot capture details at varying scales, only the fixed _3 x 3_ scale!

To handle this, an extension to the original LBP implementation was proposed by Ojala et al. to handle variable neighborhood sizes. To account for variable neighborhood sizes, two parameters were introduced:

1.  The number of points _p_ in a circularly symmetric neighborhood to consider (thus removing relying on a square neighborhood).
2.  The radius of the circle _r_, which allows us to account for different scales.

Below follows a visualization of these parameters:

[![Image 8: Figure 6: Three neighborhood examples with varying p and r used to construct Local Binary Patterns.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20358%20118'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_num_points_radii.jpg)

**Figure 6:** Three neighborhood examples with varying _p_ and _r_ used to construct Local Binary Patterns.

Lastly, it’s important that we consider the concept of LBP _**uniformity**_. A LBP is considered to be uniform if it has _**at most**_ two _0-1_ or _1-0_ transitions. For example, the pattern `00001000` (2 transitions) and `10000000` (1 transition) are both considered to be **uniform patterns** since they contain at most two _0-1_ and _1-0_ transitions. The pattern `01010010` ) on the other hand is _not_ considered a uniform pattern since it has six _0-1_ or _1-0_ transitions.

The number of uniform prototypes in a Local Binary Pattern is completely dependent on the number of points _p_. As the value of _p_ increases, so will the dimensionality of your resulting histogram. Please refer to the original Ojala et al. paper for the full explanation on deriving the number of patterns and uniform patterns based on this value. However, for the time being simply keep in mind that given the number of points _p_ in the LBP there are _p + 1_ **uniform patterns**. The final dimensionality of the histogram is thus _p + 2_, where the added entry tabulates all patterns that are _**not uniform**_.

So why are uniform LBP patterns so interesting? Simply put: they add an extra level of _rotation and grayscale invariance_, hence they are commonly used when extracting LBP feature vectors from images.

Local Binary Pattern implementations can be found in both the [scikit-image](http://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.local_binary_pattern) and [mahotas](http://mahotas.readthedocs.org/en/latest/lbp.html) packages. OpenCV also implements LBPs, but strictly in the context of face recognition — the underlying LBP extractor is not exposed for raw LBP histogram computation.

In general, I recommend using the scikit-image implementation of LBPs as they offer more control of the types of LBP histograms you want to generate. Furthermore, the scikit-image implementation also includes variants of LBPs that improve rotation and grayscale invariance.

Before we get started extracting Local Binary Patterns from images and using them for classification, we first need to create a dataset of textures. To form this dataset, earlier today I took a walk through my apartment and collected 20 photos of various textures and patterns, including an **area rug**:

[![Image 9: Figure 7: Example images of the area rug texture and pattern.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20119'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_area_rug_examples.jpg)

**Figure 7:** Example images of the area rug texture and pattern.

Notice how the area rug images have a geometric design to it.

I also gathered a few examples of **carpet**:

[![Image 10: Figure 8: Four examples of the carpet texture.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20120'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_carpet_examples.jpg)

**Figure 8:** Four examples of the carpet texture.

Notice how the carpet has a distinct pattern with a coarse texture.

I then snapped a few photos of the **keyboard** sitting on my desk:

[![Image 11: Figure 9: Example images of my keyboard.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20119'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_keyboard_examples.jpg)

**Figure 9:** Example images of my keyboard.

Notice how the keyboard has little texture — but it does demonstrate a repeatable pattern of white keys and silver metal spacing in between them.

Finally, I gathered a few final examples of **wrapping paper** (since it is my birthday after all):

[![Image 12: Figure 10: Our final texture we are going to classify -- wrapping paper.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20120'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_wrapping_paper_examples.jpg)

**Figure 10:** Our final texture we are going to classify — wrapping paper.

The wrapping paper has a very smooth texture to it, but also demonstrates a unique pattern.

Given this dataset of **area rug**, **carpet**, **keyboard**, and **wrapping paper**, our goal is to extract Local Binary Patterns from these images and apply machine learning to automatically recognize and categorize these texture images.

Let’s go ahead and get this demonstration started by defining the directory structure for our project:

$ tree --dirsfirst -L 3
.
├── images
│   ├── testing
│   │   ├── area\_rug.png
│   │   ├── carpet.png
│   │   ├── keyboard.png
│   │   └── wrapping\_paper.png
│   └── training
│       ├── area\_rug \[4 entries\]
│       ├── carpet \[4 entries\]
│       ├── keyboard \[4 entries\]
│       └── wrapping\_paper \[4 entries\]
├── pyimagesearch
│   ├── \_\_init\_\_.py
│   └── localbinarypatterns.py
└── recognize.py

8 directories, 7 files

The `images/` directory contains our `testing/` and `training/` images.

We’ll be creating a `pyimagesearch` module to keep our code organized. And within the `pyimagesearch` module we’ll create `localbinarypatterns.py` , which as the name suggests, is where our Local Binary Patterns implementation will be stored.

Speaking of Local Binary Patterns, let’s go ahead and create the descriptor class now:

\# import the necessary packages
from skimage import feature
import numpy as np

class LocalBinaryPatterns:
	def \_\_init\_\_(self, numPoints, radius):
		# store the number of points and radius
		self.numPoints = numPoints
		self.radius = radius

	def describe(self, image, eps=1e-7):
		# compute the Local Binary Pattern representation
		# of the image, and then use the LBP representation
		# to build the histogram of patterns
		lbp = feature.local\_binary\_pattern(image, self.numPoints,
			self.radius, method="uniform")
		(hist, \_) = np.histogram(lbp.ravel(),
			bins=np.arange(0, self.numPoints + 3),
			range=(0, self.numPoints + 2))

		# normalize the histogram
		hist = hist.astype("float")
		hist /= (hist.sum() + eps)

		# return the histogram of Local Binary Patterns
		return hist

We start of by importing the `feature` sub-module of [scikit-image](http://scikit-image.org/) which contains the implementation of the Local Binary Patterns descriptor.

**Line 5** defines our constructor for our `LocalBinaryPatterns` class. As mentioned in the section above, we know that LBPs require two parameters: the _radius_ of the pattern surrounding the central pixel, along with the _number of points_ along the outer radius. We’ll store both of these values on **Lines 8 and 9**.

From there, we define our `describe` method on **Line 11**, which accepts a single required argument — the image we want to extract LBPs from.

The actual LBP computation is handled on **Lines 15 and 16** using our supplied radius and number of points. The `uniform` method indicates that we are computing the rotation and grayscale invariant form of LBPs.

However, the `lbp` variable returned by the `local_binary_patterns` function is not directly usable as a feature vector. Instead, `lbp` is a 2D array with the same width and height as our input image — each of the values inside `lbp` ranges from _\[0, numPoints + 2\]_, a value for each of the possible _numPoints + 1_ possible rotation invariant prototypes (see the discussion of _uniform patterns_ at the top of this post for more information) along with an extra dimension for all patterns that _are not_ uniform, yielding a total of _numPoints + 2_ unique possible values.

Thus, to construct the actual feature vector, we need to make a call to `np.histogram` which counts the number of times each of the LBP prototypes appears. The returned histogram is _numPoints + 2_\-dimensional, an integer count for each of the prototypes. We then take this histogram and normalize it such that it sums to _1_, and then return it to the calling function.

Now that our `LocalBinaryPatterns` descriptor is defined, let’s see how we can use it to recognize textures and patterns. Create a new file named `recognize.py` , and let’s get coding:

\# import the necessary packages
from pyimagesearch.localbinarypatterns import LocalBinaryPatterns
from sklearn.svm import LinearSVC
from imutils import paths
import argparse
import cv2
import os

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add\_argument("-t", "--training", required=True,
	help="path to the training images")
ap.add\_argument("-e", "--testing", required=True, 
	help="path to the tesitng images")
args = vars(ap.parse\_args())

# initialize the local binary patterns descriptor along with
# the data and label lists
desc = LocalBinaryPatterns(24, 8)
data = \[\]
labels = \[\]

We start off on **Lines 2-7** by importing our necessary command line arguments. Notice how we are importing the `LocalBinaryPatterns` descriptor from the `pyimagesearch` sub-module that we defined above.

From there, **Lines 10-15** handle parsing our command line arguments. We’ll only need two switches here: the path to the `--training` data and the path to the `--testing` data.

In this example, we have partitioned our textures into two sets: a training set of 4 images per texture (4 textures x 4 images per texture = 16 total images), and a testing set of one image per texture (4 textures x 1 image per texture = 4 images). The training set of 16 images will be used to “teach” our classifier — and then we’ll evaluate performance on our testing set of 4 images.

On **Line 19** we initialize our `LocalBinaryPattern` descriptor using a _numPoints=24_ and _radius=8_.

In order to store the LBP feature vectors and the label names associated with each of the texture classes, we’ll initialize two lists: `data` to store the feature vectors and `labels` to store the names of each texture (**Lines 20 and 21**).

Now it’s time to extract LBP features from our set of training images:

\# loop over the training images
for imagePath in paths.list\_images(args\["training"\]):
	# load the image, convert it to grayscale, and describe it
	image = cv2.imread(imagePath)
	gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
	hist = desc.describe(gray)

	# extract the label from the image path, then update the
	# label and data lists
	labels.append(imagePath.split(os.path.sep)\[-2\])
	data.append(hist)

# train a Linear SVM on the data
model = LinearSVC(C=100.0, random\_state=42)
model.fit(data, labels)

We start looping over our training images on **Line 24**. For each of these images, we load them from disk, convert them to grayscale, and extract Local Binary Pattern features. The label (i.e., texture name) is then extracted from the image path and both our `labels` and `data` lists are updated, respectively.

Once we have our features and labels extracted, we can train our [Linear Support Vector Machine](https://en.wikipedia.org/wiki/Support_vector_machine#Linear_SVM) on **Lines 36 and 37** to learn the difference between the various texture classes.

Once our Linear SVM is trained, we can use it to classify subsequent texture images:

\# loop over the testing images
for imagePath in paths.list\_images(args\["testing"\]):
	# load the image, convert it to grayscale, describe it,
	# and classify it
	image = cv2.imread(imagePath)
	gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
	hist = desc.describe(gray)
	prediction = model.predict(hist.reshape(1, -1))
	
	# display the image and the prediction
	cv2.putText(image, prediction\[0\], (10, 30), cv2.FONT\_HERSHEY\_SIMPLEX,
		1.0, (0, 0, 255), 3)
	cv2.imshow("Image", image)
	cv2.waitKey(0)

Just as we looped over the _training images_ on **Line 24** to gather data to train our classifier, we now loop over the _testing images_ on **Line 40** to test the performance and accuracy of our classifier.

Again, all we need to do is load our image from disk, convert it to grayscale, extract Local Binary Patterns from the grayscale image, and then pass the features onto our Linear SVM for classification (**Lines 43-46**).

I’d like to draw your attention to `hist.reshape(1, -1)` on **Line 46**. This reshapes our histogram from a 1D array to a 2D array allowing for the potential of multiple feature vectors to run predictions on.

**Lines 49-52** show the output classification to our screen.

Results
-------

Let’s go ahead and give our texture classification system a try by executing the following command:

$ python recognize.py --training images/training --testing images/testing

And here’s the first output image from our classification:

[![Image 13: Figure 11: Our Linear SVM + Local Binary Pattern combination is able to correctly classify the area rug pattern.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20424'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_area_rug_result.jpg)

**Figure 11:** Our Linear SVM + Local Binary Pattern combination is able to correctly classify the area rug pattern.

Sure enough, the image is correctly classified as “area rug”.

Let’s try another one:

[![Image 14: Figure 12: We are also able to recognize the carpet pattern.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20424'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_carpet_result.jpg)

**Figure 12:** We are also able to recognize the carpet pattern.

Once again, our classifier correctly identifies the texture/pattern of the image.

Here’s an example of the keyboard pattern being correctly labeled:

[![Image 15: Figure 13: Classifying the keyboard pattern is also easy for our method.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20424'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_keyboard_result.jpg)

**Figure 13:** Classifying the keyboard pattern is also easy for our method.

Finally, we are able to recognize the texture and pattern of the wrapping paper as well:

[![Image 16: Figure 14: Using Local Binary Patterns to classify the texture of an image.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20405'%3E%3C/svg%3E)](https://pyimagesearch.com/wp-content/uploads/2015/12/lbp_wrapping_paper_result.jpg)

**Figure 14:** Using Local Binary Patterns to classify the texture of an image.

While this example was quite small and simple, it was still able to demonstrate that by using Local Binary Pattern features and a bit of machine learning, we are able to correctly classify the texture and pattern of an image.

### What's next? We recommend [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend).

**Course information:**  
84 total classes • 114+ hours of on-demand code walkthrough videos • Last updated: February 2024  
★★★★★ 4.84 (128 Ratings) • 16,000+ Students Enrolled

**I strongly believe that if you had the right teacher you could _master_ computer vision and deep learning.**

Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?

That’s _not_ the case.

All you need to master computer vision and deep learning is for someone to explain things to you in _simple, intuitive_ terms. _And that’s exactly what I do_. My mission is to change education and how complex Artificial Intelligence topics are taught.

If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to _successfully_ and _confidently_ apply computer vision to your work, research, and projects. Join me in computer vision mastery.

**Inside PyImageSearch University you'll find:**

*   ✓ **84 courses** on essential computer vision, deep learning, and OpenCV topics
*   ✓ **84 Certificates** of Completion
*   ✓ **114+ hours** of on-demand video
*   ✓ **Brand new courses released _regularly_**, ensuring you can keep up with state-of-the-art techniques
*   ✓ **Pre-configured Jupyter Notebooks in Google Colab**
*   ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
*   ✓ Access to **centralized code repos for _all_ 536+ tutorials** on PyImageSearch
*   ✓ **Easy one-click downloads** for code, datasets, pre-trained models, etc.
*   ✓ **Access** on mobile, laptop, desktop, etc.

[Click here to join PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend)

Summary
-------

In this blog post we learned how to extract Local Binary Patterns from images and use them (along with a bit of machine learning) to perform **_texture and pattern recognition_**.

If you enjoyed this blog post, be sure to take a look at the _**[PyImageSearch Gurus course](https://pyimagesearch.com/pyimagesearch-gurus/)**_ where the majority this lesson was derived from.

Inside the course you’ll find over **166+ lessons** covering **1,291** pages of computer vision topics such as:

*   Face recognition.
*   Deep learning.
*   Automatic license plate recognition.
*   Training your own custom object detectors.
*   Building image search engines.
*   _…and much more!_

**If this sounds interesting to you, _[be sure to take a look and consider signing up for the next open enrollment!](https://pyimagesearch.com/pyimagesearch-gurus/)_**

See you next week!

![Image 17](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20410%20567'%3E%3C/svg%3E)

#### Download the Source Code and FREE 17-page Resource Guide

Enter your email address below to get a .zip of the code and a **FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning.** Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!