Cainvas
Model Files
Sheep_detection.h5
keras
Model
deepSea Compiled Models
Sheep_detection.exe
deepSea
Ubuntu

Sheep Detection

Credit: AITS Cainvas Community

Photo by Süha Eryaşar on Dribbble

In [1]:
!wget https://cainvas-static.s3.amazonaws.com/media/user_data/Rodio346/Sheep_Dataset.zip
--2021-08-07 07:16:34--  https://cainvas-static.s3.amazonaws.com/media/user_data/Rodio346/Sheep_Dataset.zip
Resolving cainvas-static.s3.amazonaws.com (cainvas-static.s3.amazonaws.com)... 52.219.158.51
Connecting to cainvas-static.s3.amazonaws.com (cainvas-static.s3.amazonaws.com)|52.219.158.51|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 77705281 (74M) [application/x-zip-compressed]
Saving to: ‘Sheep_Dataset.zip.1’

Sheep_Dataset.zip.1 100%[===================>]  74.11M  68.3MB/s    in 1.1s    

2021-08-07 07:16:35 (68.3 MB/s) - ‘Sheep_Dataset.zip.1’ saved [77705281/77705281]

In [2]:
!unzip -qo "Sheep_Dataset.zip"
In [3]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, callbacks, optimizers
from sklearn.metrics import confusion_matrix, f1_score
from tensorflow import keras 
import os, shutil
import random
from PIL import Image
import cv2

Looking at the Dataset

In [4]:
img = Image.open("Sheep Dataset/Yes/17.jpg")
In [5]:
plt.imshow(img)
Out[5]:
<matplotlib.image.AxesImage at 0x7feb5c1db4a8>
In [6]:
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import cv2
import imutils
import matplotlib.pyplot as plt
from os import listdir
import time    

Applying data Augmentation

In [7]:
def augment_data(file_dir, n_generated_samples, save_to_dir):
    """
    Arguments:
        file_dir: A string representing the directory where images that we want to augment are found.
        n_generated_samples: A string representing the number of generated samples using the given image.
        save_to_dir: A string representing the directory in which the generated images will be saved.
    """
    
    #from keras.preprocessing.image import ImageDataGenerator
    #from os import listdir
    
    data_gen = ImageDataGenerator(rotation_range=10, 
                                  width_shift_range=0.1, 
                                  height_shift_range=0.1, 
                                  shear_range=0.1, 
                                  brightness_range=(0.3, 1.0),
                                  horizontal_flip=True, 
                                  vertical_flip=True, 
                                  fill_mode='nearest'
                                 )

    
    for filename in listdir(file_dir):
        # load the image
        if filename ==".ipynb_checkpoints":
            continue
        image = cv2.imread(file_dir +'/' + filename)
        # reshape the image
        image = cv2.resize(image,(240,240))
        image = image.reshape((1,)+image.shape)
        # prefix of the names for the generated sampels.
        save_prefix = 'aug_' + filename[:-4]
        # generate 'n_generated_samples' sample images
        i=0
        for batch in data_gen.flow(x=image, batch_size=1, save_to_dir=save_to_dir, 
                                           save_prefix=save_prefix, save_format='jpg'):
            i += 1
            if i > n_generated_samples:
                break
In [8]:
data_dir = 'Sheep Dataset/'

batch_size = 64
# image_size = (32, 32)
image_size = (240, 240)

print("Training set")
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
                data_dir,
                validation_split=0.2,
                subset="training",
                color_mode="grayscale",
                image_size=image_size, 
                seed=113,
                shuffle=True,
                batch_size=batch_size
            )

print("Validation set")
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
                data_dir,
                validation_split=0.2,
                subset="validation",
                color_mode="grayscale",
                image_size=image_size, 
                seed=113,
                shuffle=True,
                batch_size=batch_size
            )
Training set
Found 2022 files belonging to 2 classes.
Using 1618 files for training.
Validation set
Found 2022 files belonging to 2 classes.
Using 404 files for validation.
In [9]:
class_names = train_ds.class_names
print(class_names)
['No', 'Yes']
In [10]:
Xtrain = np.empty((0,*image_size,1))
ytrain = np.empty((0,1))

for x in train_ds.enumerate():
    for y in x[1][0]:  
        Xtrain = np.append(Xtrain, np.expand_dims(np.array(y),0), axis = 0)
    #print(Xtrain.shape)
    ytrain = np.append(ytrain, np.array(x[1][1]))
    #print(ytrain.shape)
    
Xtrain.shape, ytrain.shape
Out[10]:
((1618, 240, 240, 1), (1618,))
In [11]:
print("Number of samples - ")
for i in range(len(class_names)):
    print(class_names[i], "-", ytrain.tolist().count(float(i)))
Number of samples - 
No - 1287
Yes - 331
In [12]:
data_augmentation = tf.keras.Sequential(
    [
        layers.experimental.preprocessing.RandomFlip("horizontal"),    # Flip along vertical axes
        layers.experimental.preprocessing.RandomZoom(0.1),    # Randomly zoom images in dataset
    ])


print("Train size (number of samples) before augmentation: ", len(Xtrain))

aug_sample_count = ytrain.tolist().count(float(0.0))//2 - ytrain.tolist().count(float(1.0))
cur_augmented = 0
# Apply only to train set
while(cur_augmented!=aug_sample_count):
    for i in range(len(Xtrain)):
        if ytrain[i] == 1: # not elephant
            aug_image = np.array(data_augmentation(np.expand_dims(Xtrain[0], 0)))
            Xtrain = np.append(Xtrain, aug_image.reshape((1, *image_size, 1)), axis = 0)
            ytrain = np.append(ytrain, [1])
            
            cur_augmented += 1
            if (cur_augmented == aug_sample_count):
                break

    
print("Size (number of samples) of final dataset: ", len(Xtrain))

print(" Dataset shapes: ", Xtrain.shape, ytrain.shape)
Train size (number of samples) before augmentation:  1618
WARNING:tensorflow:Layer random_flip is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2.  The layer has dtype float32 because its dtype defaults to floatx.

If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.

To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.

Size (number of samples) of final dataset:  1930
 Dataset shapes:  (1930, 240, 240, 1) (1930,)
In [13]:
print("Number of samples - ")
for i in range(len(class_names)):
    print(class_names[i], "-", ytrain.tolist().count(float(i)))
Number of samples - 
No - 1287
Yes - 643
In [14]:
Xval = np.empty((0,*image_size,1))
yval = np.empty((0,1))

for x in val_ds.enumerate():
    for y in x[1][0]:  
        Xval = np.append(Xval, np.expand_dims(np.array(y),0), axis = 0)
    #print(Xtrain.shape)
    yval = np.append(yval, np.array(x[1][1]))
    #print(ytrain.shape)
    
Xval.shape, yval.shape
Out[14]:
((404, 240, 240, 1), (404,))
In [15]:
print("Number of samples - ")
for i in range(len(class_names)):
    print(class_names[i], "-", yval.tolist().count(float(i)))
Number of samples - 
No - 329
Yes - 75

Visulatizing Samples

In [16]:
num_samples = 4    # the number of samples to be displayed in each class

for x in class_names:
    plt.figure(figsize=(10, 10))

    filenames = os.listdir(data_dir + x)

    for i in range(num_samples):
        ax = plt.subplot(1, num_samples, i + 1)
        img = Image.open(os.path.join(data_dir, x, filenames[i]))
        plt.imshow(img)
        plt.title(x)
        plt.axis("off")
In [17]:
Xtrain = Xtrain/255
Xval = Xval/255

Model

In [21]:
model = keras.models.Sequential([
    layers.Conv2D(8, 3, activation='relu', input_shape=Xtrain[0].shape),
    layers.MaxPool2D(pool_size=(2, 2)),
    
    layers.Conv2D(16, 3, activation='relu'),
    layers.MaxPool2D(pool_size=(2, 2)),
    
    layers.Conv2D(32, 3, activation='relu'),
    layers.MaxPool2D(pool_size=(2, 2)),
    
    layers.Conv2D(32, 3, activation='relu'),
    layers.MaxPool2D(pool_size=(2, 2)),
    
    
    layers.Flatten(),
    layers.Dense(32, activation='relu'),
    layers.Dense(1, activation='sigmoid')
])

cb = [callbacks.EarlyStopping(monitor = 'val_loss', patience = 5, restore_best_weights = True)]
In [22]:
model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_4 (Conv2D)            (None, 238, 238, 8)       80        
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 119, 119, 8)       0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 117, 117, 16)      1168      
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 58, 58, 16)        0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 56, 56, 32)        4640      
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 28, 28, 32)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 26, 26, 32)        9248      
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 5408)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 32)                173088    
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 33        
=================================================================
Total params: 188,257
Trainable params: 188,257
Non-trainable params: 0
_________________________________________________________________
In [23]:
model.compile(loss=keras.losses.BinaryCrossentropy(), optimizer=optimizers.Adam(0.0001), metrics=['accuracy'])

history = model.fit(Xtrain, ytrain, validation_data=(Xval, yval), epochs=300, callbacks=cb)
Epoch 1/300
61/61 [==============================] - 1s 22ms/step - loss: 0.6762 - accuracy: 0.6539 - val_loss: 0.6170 - val_accuracy: 0.8144
Epoch 2/300
61/61 [==============================] - 1s 16ms/step - loss: 0.6533 - accuracy: 0.6668 - val_loss: 0.5742 - val_accuracy: 0.8144
Epoch 3/300
61/61 [==============================] - 1s 17ms/step - loss: 0.5698 - accuracy: 0.6668 - val_loss: 0.3746 - val_accuracy: 0.8144
Epoch 4/300
61/61 [==============================] - 1s 17ms/step - loss: 0.4571 - accuracy: 0.6674 - val_loss: 0.3497 - val_accuracy: 0.8168
Epoch 5/300
61/61 [==============================] - 1s 17ms/step - loss: 0.3554 - accuracy: 0.8155 - val_loss: 0.1468 - val_accuracy: 0.9901
Epoch 6/300
61/61 [==============================] - 1s 17ms/step - loss: 0.2873 - accuracy: 0.8632 - val_loss: 0.0914 - val_accuracy: 0.9851
Epoch 7/300
61/61 [==============================] - 1s 17ms/step - loss: 0.2174 - accuracy: 0.9073 - val_loss: 0.1423 - val_accuracy: 0.9554
Epoch 8/300
61/61 [==============================] - 1s 17ms/step - loss: 0.1784 - accuracy: 0.9420 - val_loss: 0.0555 - val_accuracy: 0.9926
Epoch 9/300
61/61 [==============================] - 1s 17ms/step - loss: 0.1536 - accuracy: 0.9430 - val_loss: 0.0873 - val_accuracy: 0.9703
Epoch 10/300
61/61 [==============================] - 1s 17ms/step - loss: 0.1377 - accuracy: 0.9492 - val_loss: 0.0333 - val_accuracy: 0.9950
Epoch 11/300
61/61 [==============================] - 1s 17ms/step - loss: 0.1233 - accuracy: 0.9565 - val_loss: 0.0345 - val_accuracy: 0.9950
Epoch 12/300
61/61 [==============================] - 1s 17ms/step - loss: 0.1062 - accuracy: 0.9632 - val_loss: 0.0162 - val_accuracy: 0.9975
Epoch 13/300
61/61 [==============================] - 1s 17ms/step - loss: 0.0900 - accuracy: 0.9756 - val_loss: 0.0670 - val_accuracy: 0.9802
Epoch 14/300
61/61 [==============================] - 1s 17ms/step - loss: 0.0716 - accuracy: 0.9865 - val_loss: 0.0435 - val_accuracy: 0.9851
Epoch 15/300
61/61 [==============================] - 1s 17ms/step - loss: 0.0644 - accuracy: 0.9860 - val_loss: 0.0291 - val_accuracy: 0.9901
Epoch 16/300
61/61 [==============================] - 1s 17ms/step - loss: 0.0573 - accuracy: 0.9891 - val_loss: 0.0280 - val_accuracy: 0.9901
Epoch 17/300
61/61 [==============================] - 1s 17ms/step - loss: 0.0503 - accuracy: 0.9886 - val_loss: 0.1105 - val_accuracy: 0.9480
In [24]:
model.evaluate(Xval, yval)
13/13 [==============================] - 0s 7ms/step - loss: 0.0162 - accuracy: 0.9975
Out[24]:
[0.016183629631996155, 0.9975247383117676]
In [25]:
ypred = (model.predict(Xval)>0.5).astype('int')
In [26]:
cm = confusion_matrix(yval, ypred)

cm = cm.astype('int') / cm.sum(axis=1)[:, np.newaxis]

fig = plt.figure(figsize = (4, 4))
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()
In [27]:
f1_score(yval, ypred, average = 'binary')
Out[27]:
0.9932885906040269
In [28]:
def plot(history, variable1, variable2):
    plt.plot(range(len(history[variable1])), history[variable1])
    plt.plot(range(len(history[variable2])), history[variable2])
    plt.legend([variable1, variable2])
    plt.title(variable1)
    

Result

In [29]:
plot(history.history, "accuracy", 'val_accuracy')

Predcition

In [30]:
x = random.randint(0, 32 - 1) # default batch size is 32

for i in val_ds.as_numpy_iterator():
    img, label = i    
    plt.axis('off')   # remove axes
    plt.imshow(img[x])    # shape from (64, 64, 64, 1) --> (64, 64, 1)
    output = model.predict(np.expand_dims(img[x],0))[0][0]    # getting output; input shape (64, 64, 3) --> (1, 64, 64, 1)
    pred = (output > 0.5).astype('int')
    print("Predicted: ", class_names[pred], '(', output, '-->', pred, ')')    # Picking the label from class_names base don the model output
    print("True: ", class_names[label[x]])
    break
Predicted:  Yes ( 1.0 --> 1 )
True:  Yes
In [33]:
model.save("Sheep_detection.h5")

DeepCC

In [ ]:
!deepCC Sheep_detection.h5
[INFO]
Reading [keras model] 'Sheep_detection.h5'
[SUCCESS]
Saved 'Sheep_detection_deepC/Sheep_detection.onnx'
[INFO]
Reading [onnx model] 'Sheep_detection_deepC/Sheep_detection.onnx'
[INFO]
Model info:
  ir_vesion : 5
  doc       : 
[WARNING]
[ONNX]: terminal (input/output) conv2d_4_input's shape is less than 1. Changing it to 1.
[WARNING]
[ONNX]: terminal (input/output) dense_3's shape is less than 1. Changing it to 1.
WARN (GRAPH): found operator node with the same name (dense_3) as io node.
[INFO]
Running DNNC graph sanity check ...
[SUCCESS]
Passed sanity check.
[INFO]
Writing C++ file 'Sheep_detection_deepC/Sheep_detection.cpp'
[INFO]
deepSea model files are ready in 'Sheep_detection_deepC/' 
[RUNNING COMMAND]
g++ -std=c++11 -O3 -fno-rtti -fno-exceptions -I. -I/opt/tljh/user/lib/python3.7/site-packages/deepC-0.13-py3.7-linux-x86_64.egg/deepC/include -isystem /opt/tljh/user/lib/python3.7/site-packages/deepC-0.13-py3.7-linux-x86_64.egg/deepC/packages/eigen-eigen-323c052e1731 "Sheep_detection_deepC/Sheep_detection.cpp" -D_AITS_MAIN -o "Sheep_detection_deepC/Sheep_detection.exe"