Writing your own features

A feature is nothing magical. It is simply a number that we computed from an image. There are several feature sets already defined in literature. These often have the added advantage that they have been designed and studied to be invariant to many unimportant factors. For example, linear binary patterns are completely invariant to multiplying all pixel values by a number or adding a constant to all these values. This makes this feature set robust against illumination changes.

However, it is also possible that your particular use case would benefit from a few specially designed features.

A simple type of feature that is not shipped with mahotas is a color histogram. Fortunately, this feature is easy to implement. A color histogram partitions the color space into a set of bins, and then counts how many pixels fall into each of the bins.

The images are in RGB format, that is, each pixel has three values: R for red, G for green, and B for blue. Since each of these components is an 8-bit value, the total is 17 million different colors. We are going to reduce this number to only 64 colors by grouping colors into bins. We will write a function to encapsulate this algorithm as follows:

def chist(im): 

To bin the colors, we first divide the image by 64, rounding down the pixel values as follows:

im = im // 64 

This makes the pixel values range from zero to three, which gives us a total of 64 different colors.

Separate the red, green, and blue channels as follows:

r,g,b = im.transpose((2,0,1)) 
pixels = 1 * r + 4 * b + 16 * g 
hist = np.bincount(pixels.ravel(), minlength=64) 
hist = hist.astype(float) 

Convert to log scale, as seen in the following code snippet. This is not strictly necessary, but makes for better features. We use np.log1p, which computes log(h+1). This ensures that zero values are kept as zero values (mathematically, the logarithm of zero is not defined, and NumPy prints a warning if you attempt to compute it):

hist = np.log1p(hist) 
return hist 

We can adapt the previous processing code to use the function we wrote very easily:

features = [] 
for im in images: 
    image = mh.imread(im) 
    features.append(chist(im)) 

Using the same cross-validation code we used earlier, we obtain 90 percent accuracy. The best results, however, come from combining all the features, which we can implement as follows:

features = [] 
for im in images: 
    imcolor = mh.imread(im) 
    im = mh.colors.rgb2gray(imcolor, dtype=np.uint8) 
    features.append(np.concatenate([ 
          mh.features.haralick(im).ravel(), 
          chist(imcolor), 
      ])) 

By using all of these features, we get 95.6 percent accuracy, as shown in the following code snippet:

scores = model_selection.cross_val_score( 
    clf, features, labels, cv=cv) 
print('Accuracy: {:.1%}'.format(scores.mean())) 
Accuracy: 95.6% 

This is a perfect illustration of the principle that good algorithms are the easy part. You can always use an implementation of state-of-the-art classification from scikit-learn. The real secret and added value often comes in feature design and engineering. This is where knowledge of your dataset is valuable.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset