In [24]:
import numpy as np
In [25]:
import tensorflow as tf
from tensorflow.keras import layers, Sequential
In [26]:
# Set batch size and image dimensions
batch_size = 32
img_height, img_width = 224, 224

# Make a training set from the directory
train_ds = tf.keras.utils.image_dataset_from_directory(
    'test_images/',
    validation_split=0.2,
    subset='training',
    seed=76,
    image_size=(img_height, img_width),
    batch_size=batch_size
)

# Get class names and the number of classes
class_names = train_ds.class_names
num_classes = len(class_names)

# Make a validation set from the directory 
val_ds = tf.keras.utils.image_dataset_from_directory(
    'test_images/',
    validation_split=0.2,
    subset='validation',
    seed=76,
    image_size=(img_height, img_width),
    batch_size=batch_size
)
Found 28 files belonging to 15 classes.
Using 23 files for training.
Found 28 files belonging to 15 classes.
Using 5 files for validation.
In [27]:
data_augmentation = Sequential([
    layers.RandomFlip('horizontal'),
    layers.RandomRotation(0.2),
    layers.RandomContrast(0.1),
], name = 'data_augmentation')

model = Sequential([
    layers.Input(shape=(224, 224, 3)),
    data_augmentation,
    layers.Rescaling(1./255),
    
    layers.Conv2D(32, 3, activation='relu', kernel_regularizer='l2'),
    layers.MaxPooling2D(),
    layers.Conv2D(64, 3, activation='relu', kernel_regularizer='l2'),
    layers.MaxPooling2D(),
    layers.Conv2D(128, 3, activation='relu', kernel_regularizer='l2'),
    layers.MaxPooling2D(),
    
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dense(num_classes, activation='softmax')
])

model.compile(
    optimizer='adam',
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)
In [28]:
hist = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=20
)
Epoch 1/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 12s 12s/step - accuracy: 0.0000e+00 - loss: 4.0419 - val_accuracy: 0.0000e+00 - val_loss: 5.1998
Epoch 2/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 128ms/step - accuracy: 0.0870 - loss: 3.9378 - val_accuracy: 0.0000e+00 - val_loss: 4.6775
Epoch 3/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 111ms/step - accuracy: 0.0870 - loss: 3.5814 - val_accuracy: 0.0000e+00 - val_loss: 4.7341
Epoch 4/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 104ms/step - accuracy: 0.5652 - loss: 3.2227 - val_accuracy: 0.0000e+00 - val_loss: 5.1688
Epoch 5/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 108ms/step - accuracy: 0.4348 - loss: 2.9905 - val_accuracy: 0.0000e+00 - val_loss: 5.3293
Epoch 6/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 104ms/step - accuracy: 0.6522 - loss: 2.5667 - val_accuracy: 0.0000e+00 - val_loss: 5.4940
Epoch 7/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 107ms/step - accuracy: 0.6957 - loss: 2.1733 - val_accuracy: 0.0000e+00 - val_loss: 5.8128
Epoch 8/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 100ms/step - accuracy: 0.8261 - loss: 1.8269 - val_accuracy: 0.2000 - val_loss: 6.0881
Epoch 9/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 101ms/step - accuracy: 0.9565 - loss: 1.5591 - val_accuracy: 0.2000 - val_loss: 7.1343
Epoch 10/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 102ms/step - accuracy: 0.9565 - loss: 1.4587 - val_accuracy: 0.2000 - val_loss: 8.1284
Epoch 11/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 105ms/step - accuracy: 1.0000 - loss: 1.2308 - val_accuracy: 0.2000 - val_loss: 9.6158
Epoch 12/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 108ms/step - accuracy: 0.9130 - loss: 1.2299 - val_accuracy: 0.2000 - val_loss: 10.2614
Epoch 13/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 101ms/step - accuracy: 0.9565 - loss: 1.3385 - val_accuracy: 0.2000 - val_loss: 11.1809
Epoch 14/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 106ms/step - accuracy: 1.0000 - loss: 1.0952 - val_accuracy: 0.2000 - val_loss: 12.9401
Epoch 15/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 102ms/step - accuracy: 0.9130 - loss: 1.1586 - val_accuracy: 0.2000 - val_loss: 15.1162
Epoch 16/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 104ms/step - accuracy: 0.9565 - loss: 1.0572 - val_accuracy: 0.2000 - val_loss: 15.9038
Epoch 17/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 106ms/step - accuracy: 0.8261 - loss: 1.9907 - val_accuracy: 0.4000 - val_loss: 14.8837
Epoch 18/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 111ms/step - accuracy: 1.0000 - loss: 1.0228 - val_accuracy: 0.0000e+00 - val_loss: 15.0257
Epoch 19/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 103ms/step - accuracy: 0.8696 - loss: 1.2061 - val_accuracy: 0.2000 - val_loss: 15.1873
Epoch 20/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 100ms/step - accuracy: 1.0000 - loss: 0.9996 - val_accuracy: 0.2000 - val_loss: 15.3104
In [33]:
def predict_tartan(image_path):
    # 1. Load and resize the image
    img = tf.keras.utils.load_img(image_path, target_size=(224, 224))
    
    # 2. Convert to array and expand dimensions to (1, 224, 224, 3)
    img_array = tf.keras.utils.img_to_array(img)
    img_array = tf.expand_dims(img_array, axis=0)
    
    # 3. Make prediction
    predictions = model.predict(img_array)
    score = tf.nn.softmax(predictions[0])

    # 4. Get the result
    predicted_class = class_names[np.argmax(score)]
    confidence = 100 * np.max(score)
    
    print(f"This image likely belongs to {predicted_class} with {confidence:.2f}% confidence.")
In [34]:
predict_tartan('predict_images/MacDonald Clanranald(1).png')
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 181ms/step
This image likely belongs to Henkel with 16.17% confidence.
In [35]:
from pathlib import Path
In [37]:
p = Path('predict_images')
for itm in p.iterdir():
    print(itm.name)
    predict_tartan(itm)
Abercrombie.JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step
This image likely belongs to Abergavenny with 16.24% confidence.
Lamont and None.JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step
This image likely belongs to Abergavenny with 14.50% confidence.
Lamont(1).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step
This image likely belongs to Lamont with 15.99% confidence.
Lamont(2).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step
This image likely belongs to Abergavenny with 16.26% confidence.
Lamont(3).jpeg
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step
This image likely belongs to Lamont with 12.66% confidence.
MacDonald Clanranald(1).png
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 26ms/step
This image likely belongs to Henkel with 16.17% confidence.
MacDonald ClanRanald(2).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step
This image likely belongs to Abergavenny with 16.26% confidence.
MacDonald Clanranald(3).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 29ms/step
This image likely belongs to Abergavenny with 15.56% confidence.
None.JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 27ms/step
This image likely belongs to Abergavenny with 16.25% confidence.
In [ ]: