In [7]:
import numpy as np
In [8]:
import tensorflow as tf
physical_devices = tf.config.list_physical_devices('GPU')
if len(physical_devices) > 0:
    tf.config.experimental.set_memory_growth(physical_devices[0], True)
from tensorflow.keras import layers, Sequential
from tensorflow.keras.preprocessing.image import ImageDataGenerator
In [15]:
# Set batch size and image dimensions
batch_size = 32
img_height, img_width = 224, 224


data_gen = ImageDataGenerator(rescale = 1./255,
                         horizontal_flip=True,
                         vertical_flip=True,
                         brightness_range=(.2, 1),
                         rotation_range=30,
                         width_shift_range=0.2,
                         height_shift_range=0.2,
                         zoom_range=0.8)

flow_gen = data_gen.flow_from_directory(
    'test_images',
    target_size=(img_height, img_width),
    batch_size=batch_size,
    class_mode='sparse'
)
Found 28 images belonging to 15 classes.
In [16]:
# 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 [17]:
model = Sequential([
    layers.Input(shape=(224, 224, 3)),
    layers.Rescaling(1./255),
    
    layers.Conv2D(32, 3, activation='relu'),
    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 [18]:
hist = model.fit(
    flow_gen,
    validation_data=val_ds,
    epochs=20
)
Epoch 1/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 2s 2s/step - accuracy: 0.0357 - loss: 2.7081 - val_accuracy: 0.2000 - val_loss: 15.4397
Epoch 2/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 380ms/step - accuracy: 0.0714 - loss: 2.7038 - val_accuracy: 0.2000 - val_loss: 23.9809
Epoch 3/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 385ms/step - accuracy: 0.0714 - loss: 2.6999 - val_accuracy: 0.2000 - val_loss: 23.3276
Epoch 4/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 380ms/step - accuracy: 0.0714 - loss: 2.6883 - val_accuracy: 0.2000 - val_loss: 24.6694
Epoch 5/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 391ms/step - accuracy: 0.0714 - loss: 2.6824 - val_accuracy: 0.2000 - val_loss: 26.4286
Epoch 6/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 388ms/step - accuracy: 0.1429 - loss: 2.6788 - val_accuracy: 0.2000 - val_loss: 27.9244
Epoch 7/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 376ms/step - accuracy: 0.1429 - loss: 2.6782 - val_accuracy: 0.2000 - val_loss: 27.6619
Epoch 8/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 377ms/step - accuracy: 0.1429 - loss: 2.6702 - val_accuracy: 0.2000 - val_loss: 29.8185
Epoch 9/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 387ms/step - accuracy: 0.0714 - loss: 2.6624 - val_accuracy: 0.2000 - val_loss: 33.1833
Epoch 10/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 382ms/step - accuracy: 0.0714 - loss: 2.6694 - val_accuracy: 0.2000 - val_loss: 36.2355
Epoch 11/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 378ms/step - accuracy: 0.1429 - loss: 2.6504 - val_accuracy: 0.2000 - val_loss: 41.2374
Epoch 12/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 381ms/step - accuracy: 0.1786 - loss: 2.6513 - val_accuracy: 0.2000 - val_loss: 47.5227
Epoch 13/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 374ms/step - accuracy: 0.1429 - loss: 2.6497 - val_accuracy: 0.2000 - val_loss: 50.9747
Epoch 14/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 382ms/step - accuracy: 0.1429 - loss: 2.6402 - val_accuracy: 0.2000 - val_loss: 54.6064
Epoch 15/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 380ms/step - accuracy: 0.1786 - loss: 2.6151 - val_accuracy: 0.2000 - val_loss: 58.3921
Epoch 16/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 381ms/step - accuracy: 0.1786 - loss: 2.6106 - val_accuracy: 0.2000 - val_loss: 61.9589
Epoch 17/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 386ms/step - accuracy: 0.2143 - loss: 2.6108 - val_accuracy: 0.2000 - val_loss: 66.4298
Epoch 18/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 388ms/step - accuracy: 0.1786 - loss: 2.5995 - val_accuracy: 0.2000 - val_loss: 68.8060
Epoch 19/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 388ms/step - accuracy: 0.1429 - loss: 2.5761 - val_accuracy: 0.2000 - val_loss: 69.8512
Epoch 20/20
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 379ms/step - accuracy: 0.1429 - loss: 2.6335 - val_accuracy: 0.2000 - val_loss: 74.2540
In [19]:
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 [20]:
predict_tartan('predict_images/MacDonald Clanranald(1).png')
1/1 ━━━━━━━━━━━━━━━━━━━━ 1s 1s/step
This image likely belongs to Caledonia with 15.63% confidence.
In [21]:
from pathlib import Path
In [22]:
p = Path('predict_images')
for itm in p.iterdir():
    print(itm.name)
    predict_tartan(itm)
Abercrombie.JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step
This image likely belongs to California State with 16.26% confidence.
Lamont and None.JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step
This image likely belongs to California State with 16.26% confidence.
Lamont(1).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 24ms/step
This image likely belongs to California State with 16.26% confidence.
Lamont(2).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 23ms/step
This image likely belongs to California State with 16.26% confidence.
Lamont(3).jpeg
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 22ms/step
This image likely belongs to California State with 16.26% confidence.
MacDonald Clanranald(1).png
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 28ms/step
This image likely belongs to Caledonia with 15.63% confidence.
MacDonald ClanRanald(2).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step
This image likely belongs to California State with 16.26% confidence.
MacDonald Clanranald(3).JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step
This image likely belongs to California State with 16.26% confidence.
None.JPG
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 25ms/step
This image likely belongs to California State with 16.26% confidence.
In [ ]: