NOTE: This Use Case is not purposed for resource constrained devices.
Indian Currency Notes Classifier¶
Credit: AITS Cainvas Community
Photo by Alexander Barton for NJI Media on Dribbble
Using the images of the currency notes in circulation to classify them. This application can be of use to the visually impaired in their everyday lives.
In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from tensorflow.keras import layers, optimizers, models, preprocessing, losses, callbacks
import os
import random
from PIL import Image
import tensorflow as tf
import tensorflow.keras
The dataset¶
On Kaggle by Gaurav Rajesh Sahani
The dataset contains 195 images of 7 categories of Indian Currency Notes - Tennote, Fiftynote, Twentynote, 2Thousandnote, 2Hundrednote, Hundrednote, 1Hundrednote.
There are 2 folders in the dataset - train and test, each with 7 sub-folders corresponding to the currency categories.
In [2]:
!wget -N https://cainvas-static.s3.amazonaws.com/media/user_data/cainvas-admin/indian-currency-notes-classifier.zip
!unzip -qo indian-currency-notes-classifier.zip -d currency
A peek into the number of images in the folders -
In [3]:
data_dir = 'currency'
print("Number of samples")
for f in os.listdir(data_dir + '/'):
print()
if os.path.isdir(data_dir + '/' + f):
print(f.upper())
for fx in os.listdir(data_dir + '/' + f):
if os.path.isdir(data_dir + '/' + f + '/' + fx):
print(fx, " : ", len(os.listdir(data_dir + '/' + f +'/' + fx)))
This is a balanced dataset.
In [4]:
# Loading the dataset
path = 'currency/'
input_shape = (256, 256, 3) # default input shape while loading the images
batch = 64
# The train and test datasets
print("Train dataset")
train_ds = preprocessing.image_dataset_from_directory(path+'Train', batch_size=batch, label_mode='categorical')#, color_mode='grayscale')
print("Test dataset")
test_ds = preprocessing.image_dataset_from_directory(path+'Test', batch_size=batch, label_mode='categorical')#, color_mode='grayscale')
In [5]:
# Looking into the class labels
class_names = train_ds.class_names
print("Train class names: ", train_ds.class_names)
print("Test class names: ", test_ds.class_names)
Visualization¶
In [6]:
num_samples = 4 # the number of samples to be displayed in each class
for x in class_names:
plt.figure(figsize=(20, 20))
filenames = os.listdir(path + 'Train/' + x)
for i in range(num_samples):
ax = plt.subplot(1, num_samples, i + 1)
img = Image.open(path +'Train/' + x + '/' + filenames[i])
plt.imshow(img)
plt.title(x)
plt.axis("off")
Preprocessing¶
In [7]:
# Normalizing the pixel values for faster convergence
normalization_layer = layers.experimental.preprocessing.Rescaling(1./255)
train_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
test_ds = test_ds.map(lambda x, y: (normalization_layer(x), y))
The model¶
Using transfer learning
In [8]:
base_model = tensorflow.keras.applications.DenseNet121(weights='imagenet', input_shape=input_shape, include_top=False) # False, do not include the classification layer of the model
base_model.trainable = False
inputs = tf.keras.Input(shape=input_shape)
x = base_model(inputs, training=False)
x = tensorflow.keras.layers.GlobalAveragePooling2D()(x)
outputs = tensorflow.keras.layers.Dense(len(class_names), activation = 'softmax')(x) # Add own classififcation layer
model = tensorflow.keras.Model(inputs, outputs)
cb = [callbacks.EarlyStopping(monitor = 'val_loss', patience = 5, restore_best_weights = True)]
model.summary()
In [9]:
model.compile(loss=losses.CategoricalCrossentropy(), optimizer=optimizers.Adam(0.01), metrics=['accuracy'])
history = model.fit(train_ds, validation_data = test_ds, epochs=256, callbacks = cb)
In [10]:
model.evaluate(test_ds)
Out[10]:
In [11]:
Xtest = []
ytest = []
for x in test_ds.enumerate():
for y in x[1][0]:
Xtest.append(np.array(y).tolist())
ytest.extend(np.array(x[1][1]).tolist())
len(ytest), len(Xtest)
Out[11]:
In [12]:
cm = confusion_matrix(np.argmax(ytest, axis = 1), np.argmax(model.predict(Xtest), axis = 1))
cm = cm.astype('int') / cm.sum(axis=1)[:, np.newaxis]
fig = plt.figure(figsize = (10, 10))
ax = fig.add_subplot(111)
for i in range(cm.shape[1]):
for j in range(cm.shape[0]):
if cm[i,j] > 0.8:
clr = "white"
else:
clr = "black"
ax.text(j, i, format(cm[i, j], '.2f'), horizontalalignment="center", color=clr)
_ = ax.imshow(cm, cmap=plt.cm.Blues)
ax.set_xticks(range(len(class_names)))
ax.set_yticks(range(len(class_names)))
ax.set_xticklabels(class_names, rotation = 90)
ax.set_yticklabels(class_names)
plt.xlabel('Predicted')
plt.ylabel('True')
plt.show()
Plotting the metrics¶
In [13]:
def plot(history, variable, variable2):
plt.plot(range(len(history[variable])), history[variable])
plt.plot(range(len(history[variable2])), history[variable2])
plt.legend([variable, variable2])
plt.title(variable)
In [14]:
plot(history.history, "accuracy", 'val_accuracy')
In [15]:
plot(history.history, "loss", 'val_loss')
Predictions¶
In [16]:
# pick random test data sample from one batch
x = random.randint(0, 41) # test set has 42 samples
for i in test_ds.as_numpy_iterator():
img, label = i
plt.axis('off') # remove axes
#print(img.shape, x)
plt.imshow(img[x]) # shape from (64, 256, 256, 3) --> (256, 256, 3)
output = model.predict(np.expand_dims(img[x],0)) # getting output; input shape (256, 256, 3) --> (1, 256, 256, 3)
pred = np.argmax(output[0]) # finding max
print("Prdicted: ", class_names[pred]) # Picking the label from class_names base don the model output
print("True: ", class_names[np.argmax(label[x])])
print("Probability: ", output[0][pred])
break
deepC¶
In [17]:
model.save('currency.h5')
# !deepCC currency.h5