├── lbp.pkl ├── data └── lbp │ ├── class_test.txt │ ├── test │ ├── rock-1.png │ ├── grass-1.jpg │ └── checkered-1.jpg │ ├── train │ ├── grass-1.jpg │ ├── grass-2.jpg │ ├── rock-1.jpg │ ├── rock-2.jpg │ ├── checkered-1.jpg │ └── checkered-2.jpg │ └── class_train.txt ├── docs └── images │ ├── query-image-1.png │ ├── query-image-2.png │ ├── query-image-3.png │ ├── training-set.png │ ├── query-image-results-1.png │ ├── query-image-results-2.png │ └── query-image-results-3.png ├── .gitignore ├── README.md ├── perform-training.py └── perform-testing.py /lbp.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/lbp.pkl -------------------------------------------------------------------------------- /data/lbp/class_test.txt: -------------------------------------------------------------------------------- 1 | rock-1.png 0 2 | grass-1.jpg 1 3 | checkered-1.jpg 2 4 | -------------------------------------------------------------------------------- /data/lbp/test/rock-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/test/rock-1.png -------------------------------------------------------------------------------- /data/lbp/test/grass-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/test/grass-1.jpg -------------------------------------------------------------------------------- /data/lbp/train/grass-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/train/grass-1.jpg -------------------------------------------------------------------------------- /data/lbp/train/grass-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/train/grass-2.jpg -------------------------------------------------------------------------------- /data/lbp/train/rock-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/train/rock-1.jpg -------------------------------------------------------------------------------- /data/lbp/train/rock-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/train/rock-2.jpg -------------------------------------------------------------------------------- /data/lbp/test/checkered-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/test/checkered-1.jpg -------------------------------------------------------------------------------- /docs/images/query-image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/query-image-1.png -------------------------------------------------------------------------------- /docs/images/query-image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/query-image-2.png -------------------------------------------------------------------------------- /docs/images/query-image-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/query-image-3.png -------------------------------------------------------------------------------- /docs/images/training-set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/training-set.png -------------------------------------------------------------------------------- /data/lbp/train/checkered-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/train/checkered-1.jpg -------------------------------------------------------------------------------- /data/lbp/train/checkered-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/data/lbp/train/checkered-2.jpg -------------------------------------------------------------------------------- /docs/images/query-image-results-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/query-image-results-1.png -------------------------------------------------------------------------------- /docs/images/query-image-results-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/query-image-results-2.png -------------------------------------------------------------------------------- /docs/images/query-image-results-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikz05/texture-matching/HEAD/docs/images/query-image-results-3.png -------------------------------------------------------------------------------- /data/lbp/class_train.txt: -------------------------------------------------------------------------------- 1 | rock-1.jpg 0 2 | rock-2.jpg 0 3 | grass-1.jpg 1 4 | grass-2.jpg 1 5 | checkered-1.jpg 2 6 | checkered-2.jpg 2 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | # Ubuntu crap 60 | *~ 61 | *.swp 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Texture Matching using Local Binary Patterns (LBP) 2 | 3 | Related blog post explaining the code - [Click 4 | Here](http://hanzratech.in/2015/05/30/local-binary-patterns.html) 5 | 6 | ## Usage 7 | 8 | __Perform training using__ 9 | 10 | ``` 11 | python perform-training.py -t data/lbp/train/ -l data/lbp/class_train.txt 12 | ``` 13 | 14 | __Perform testing using__ 15 | 16 | ``` 17 | python perform-testing.py -t data/lbp/test/ -l data/lbp/class_test.txt 18 | ``` 19 | ## Results 20 | 21 | Let's check out the results. 22 | 23 | __Input Image -- ROCK Class__ 24 | 25 | Here is an input image of a rock texture. 26 | 27 | ![alt tag](docs/images/query-image-1.png) 28 | 29 | _Matching Scores_ - Below are the sorted results of matching. The top 2 scores are of rock texture. 30 | 31 | ![alt tag](docs/images/query-image-results-1.png) 32 | 33 | __Input Image -- CHECKERED Class__ 34 | 35 | Here is an input image of a checkered texture . 36 | 37 | ![alt tag](docs/images/query-image-2.png) 38 | 39 | _Matching Scores_ - Below are the sorted results of matching. The top 2 scores are of checkered texture. 40 | 41 | ![alt tag](docs/images/query-image-results-2.png) 42 | 43 | __Input Image -- GRASS Class__ 44 | 45 | Here is an input image of a grass texture. 46 | 47 | ![alt tag](docs/images/query-image-3.png) 48 | 49 | _Matching Scores_ - Below are the sorted results of matching. The top 2 scores are of grass texture. 50 | 51 | ![alt tag](docs/images/query-image-results-3.png) 52 | -------------------------------------------------------------------------------- /perform-training.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # OpenCV bindings 3 | import cv2 4 | # To performing path manipulations 5 | import os 6 | # Local Binary Pattern function 7 | from skimage.feature import local_binary_pattern 8 | # To calculate a normalized histogram 9 | from scipy.stats import itemfreq 10 | from sklearn.preprocessing import normalize 11 | # To read class from file 12 | import csv 13 | # For plotting 14 | import matplotlib.pyplot as plt 15 | # For array manipulations 16 | import numpy as np 17 | # For saving histogram values 18 | from sklearn.externals import joblib 19 | # For command line input 20 | import argparse as ap 21 | # Utility Package 22 | import cvutils 23 | 24 | # Get the path of the training set 25 | parser = ap.ArgumentParser() 26 | parser.add_argument("-t", "--trainingSet", help="Path to Training Set", required="True") 27 | parser.add_argument("-l", "--imageLabels", help="Path to Image Label Files", required="True") 28 | args = vars(parser.parse_args()) 29 | 30 | # Store the path of training images in train_images 31 | train_images = cvutils.imlist(args["trainingSet"]) 32 | # Dictionary containing image paths as keys and corresponding label as value 33 | train_dic = {} 34 | with open(args['imageLabels'], 'rb') as csvfile: 35 | reader = csv.reader(csvfile, delimiter=' ') 36 | for row in reader: 37 | train_dic[row[0]] = int(row[1]) 38 | 39 | # List for storing the LBP Histograms, address of images and the corresponding label 40 | X_test = [] 41 | X_name = [] 42 | y_test = [] 43 | 44 | # For each image in the training set calculate the LBP histogram 45 | # and update X_test, X_name and y_test 46 | for train_image in train_images: 47 | # Read the image 48 | im = cv2.imread(train_image) 49 | # Convert to grayscale as LBP works on grayscale image 50 | im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) 51 | radius = 3 52 | # Number of points to be considered as neighbourers 53 | no_points = 8 * radius 54 | # Uniform LBP is used 55 | lbp = local_binary_pattern(im_gray, no_points, radius, method='uniform') 56 | # Calculate the histogram 57 | x = itemfreq(lbp.ravel()) 58 | # Normalize the histogram 59 | hist = x[:, 1]/sum(x[:, 1]) 60 | # Append image path in X_name 61 | X_name.append(train_image) 62 | # Append histogram to X_name 63 | X_test.append(hist) 64 | # Append class label in y_test 65 | y_test.append(train_dic[os.path.split(train_image)[1]]) 66 | 67 | # Dump the data 68 | joblib.dump((X_name, X_test, y_test), "lbp.pkl", compress=3) 69 | 70 | # Display the training images 71 | nrows = 2 72 | ncols = 3 73 | fig, axes = plt.subplots(nrows,ncols) 74 | for row in range(nrows): 75 | for col in range(ncols): 76 | axes[row][col].imshow(cv2.imread(X_name[row*ncols+col])) 77 | axes[row][col].axis('off') 78 | axes[row][col].set_title("{}".format(os.path.split(X_name[row*ncols+col])[1])) 79 | 80 | # Convert to numpy and display the image 81 | fig.canvas.draw() 82 | im_ts = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='') 83 | im_ts = im_ts.reshape(fig.canvas.get_width_height()[::-1] + (3,)) 84 | cv2.imshow("Training Set", im_ts) 85 | cv2.waitKey() 86 | -------------------------------------------------------------------------------- /perform-testing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # OpenCV bindings 3 | import cv2 4 | # To performing path manipulations 5 | import os 6 | # Local Binary Pattern function 7 | from skimage.feature import local_binary_pattern 8 | # To calculate a normalized histogram 9 | from scipy.stats import itemfreq 10 | from sklearn.preprocessing import normalize 11 | # To read class from file 12 | import csv 13 | # For plotting 14 | import matplotlib.pyplot as plt 15 | # For array manipulations 16 | import numpy as np 17 | # For saving histogram values 18 | from sklearn.externals import joblib 19 | # For command line input 20 | import argparse as ap 21 | # Utility Package 22 | import cvutils 23 | 24 | # Get the path of the training set 25 | parser = ap.ArgumentParser() 26 | parser.add_argument("-t", "--testingSet", help="Path to Testing Set", required="True") 27 | parser.add_argument("-l", "--imageLabels", help="Path to Image Label Files", required="True") 28 | args = vars(parser.parse_args()) 29 | 30 | # Load the List for storing the LBP Histograms, address of images and the corresponding label 31 | X_name, X_test, y_test = joblib.load("lbp.pkl") 32 | 33 | # Store the path of testing images in test_images 34 | test_images = cvutils.imlist(args["testingSet"]) 35 | # Dictionary containing image paths as keys and corresponding label as value 36 | test_dic = {} 37 | with open(args["imageLabels"], 'rb') as csvfile: 38 | reader = csv.reader(csvfile, delimiter=' ') 39 | for row in reader: 40 | test_dic[row[0]] = int(row[1]) 41 | 42 | # Dict containing scores 43 | results_all = {} 44 | 45 | for test_image in test_images: 46 | print "\nCalculating Normalized LBP Histogram for {}".format(test_image) 47 | # Read the image 48 | im = cv2.imread(test_image) 49 | # Convert to grayscale as LBP works on grayscale image 50 | im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) 51 | radius = 3 52 | # Number of points to be considered as neighbourers 53 | no_points = 8 * radius 54 | # Uniform LBP is used 55 | lbp = local_binary_pattern(im_gray, no_points, radius, method='uniform') 56 | # Calculate the histogram 57 | x = itemfreq(lbp.ravel()) 58 | # Normalize the histogram 59 | hist = x[:, 1]/sum(x[:, 1]) 60 | # Display the query image 61 | results = [] 62 | # For each image in the training dataset 63 | # Calculate the chi-squared distance and the sort the values 64 | for index, x in enumerate(X_test): 65 | score = cv2.compareHist(np.array(x, dtype=np.float32), np.array(hist, dtype=np.float32), cv2.cv.CV_COMP_CHISQR) 66 | results.append((X_name[index], round(score, 3))) 67 | results = sorted(results, key=lambda score: score[1]) 68 | results_all[test_image] = results 69 | print "Displaying scores for {} ** \n".format(test_image) 70 | for image, score in results: 71 | print "{} has score {}".format(image, score) 72 | 73 | for test_image, results in results_all.items(): 74 | # Read the image 75 | im = cv2.imread(test_image) 76 | # Display the results 77 | nrows = 2 78 | ncols = 3 79 | fig, axes = plt.subplots(nrows,ncols) 80 | fig.suptitle("** Scores for -> {}**".format(test_image)) 81 | for row in range(nrows): 82 | for col in range(ncols): 83 | axes[row][col].imshow(cv2.imread(results[row*ncols+col][0])) 84 | axes[row][col].axis('off') 85 | axes[row][col].set_title("Score {}".format(results[row*ncols+col][1])) 86 | fig.canvas.draw() 87 | im_ts = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='') 88 | im_ts = im_ts.reshape(fig.canvas.get_width_height()[::-1] + (3,)) 89 | cv2.imshow("** Query Image -> {}**".format(test_image), im) 90 | cv2.imshow("** Scores for -> {}**".format(test_image), im_ts) 91 | cv2.waitKey() 92 | cv2.destroyAllWindows() 93 | --------------------------------------------------------------------------------