Computer Vision • CNN • Python • TensorFlow

Picture Classification using CNN in Python

A complete 0 to infinity lesson: pixels, arrays, labels, one-hot encoding, Convolutional Neural Network layers, training, prediction, and graphs.

1. What We Are Making

We are making a Python program that can classify a picture as mango, dog, or cat.

Input image → CNN model → Predicted class

Possible outputs:
mango
dog
cat
CNN means Convolutional Neural Network. A CNN is a Deep Learning model specially designed for image data.
Image Pixels CNN Probabilities Class

2. Zero Level: What Is a Picture?

A picture is a grid of pixels. Each pixel has color values. A normal color image has three color channels:

Red First color channel.
Green Second color channel.
Blue Third color channel.

So an image is stored as:

height × width × 3

In our lesson every image becomes:

64 × 64 × 3
Part Meaning
64 Image height
64 Image width
3 Red, Green, and Blue color channels

3. Required Folder Structure

Keep the training images inside class folders. The folder names are the class names.

classification/
    picture/
        cnnclassifier.py
        test.png
        pictures/
            mango/
                mango1.png
                mango2.png
            dog/
                dog1.png
                dog2.png
            cat/
                cat1.png
                cat2.png
Folder Class Label Number
mango Mango 0
dog Dog 1
cat Cat 2

4. Install Required Libraries

Run this command in the terminal:

pip install pillow numpy matplotlib tensorflow
Library Use
pillow To open and resize images
numpy To handle image arrays and numbers
matplotlib To show images and graphs
tensorflow To create and train the CNN model

5. Import Libraries

from PIL import Image
import numpy as np
import os
import matplotlib.pyplot as plt

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.utils import to_categorical
Image Opens and processes image files.
NumPy Converts images into numerical arrays.
Sequential Builds the CNN layer by layer.
Conv2D Learns visual patterns from images.
MaxPooling2D Reduces size but keeps important features.
Dense Makes the final classification decision.

6. Basic Settings

base_folder = "classification/picture/pictures"

classes = ["mango", "dog", "cat"]

image_width = 64
image_height = 64

number_of_classes = len(classes)

The model will learn three classes. Every image is resized to 64 × 64 so all inputs have the same size.

Machine Learning models need a fixed input shape. That is why we resize every image.

7. Convert Image into CNN Input

def image_to_array(image_path):
    img = Image.open(image_path).convert("RGB")
    img = img.resize((image_width, image_height))

    arr = np.array(img)

    arr = arr / 255.0

    return arr

What this function does

Open Image Convert to RGB Resize Array Normalize

Why divide by 255?

Pixel values normally go from 0 to 255. CNN training works better when values are small, so we convert them to the range 0 to 1.

0   / 255 = 0.0
128 / 255 = 0.501
255 / 255 = 1.0

8. X, y, and One-Hot Labels

In Machine Learning, X usually stores input data and y stores correct answers.

X = []
y = []
Name Meaning
X Stores image arrays
y Stores correct labels

Normal labels

mango → 0
dog   → 1
cat   → 2

One-hot labels

mango → [1, 0, 0]
dog   → [0, 1, 0]
cat   → [0, 0, 1]

In code:

y_categorical = to_categorical(y, number_of_classes)
The final CNN layer gives probabilities for all classes. One-hot encoding matches that style.

9. Create the CNN Model

model = Sequential()

model.add(
    Conv2D(
        filters=16,
        kernel_size=(3, 3),
        activation="relu",
        input_shape=(image_height, image_width, 3)
    )
)

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(
    Conv2D(
        filters=32,
        kernel_size=(3, 3),
        activation="relu"
    )
)

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())

model.add(Dense(64, activation="relu"))

model.add(Dense(number_of_classes, activation="softmax"))

Layer-by-layer meaning

Layer Meaning
Conv2D Finds patterns like edges, textures, shapes, and small visual features.
MaxPooling2D Reduces size and keeps important information.
Flatten Converts 2D feature maps into a 1D list.
Dense Combines learned features and makes decisions.
softmax Gives probability for each class.
Edges Shapes Textures Object Parts Class

10. Compile and Train the CNN

Compile

model.compile(
    optimizer="adam",
    loss="categorical_crossentropy",
    metrics=["accuracy"]
)
Term Meaning
adam Optimizer that improves the model during training.
categorical_crossentropy Loss function for multi-class classification.
accuracy Shows how many predictions are correct.

Train

history = model.fit(
    X,
    y_categorical,
    epochs=50,
    batch_size=2,
    verbose=1
)
Epoch One full pass through all training images.
Batch size Number of images processed at a time.
History Stores accuracy and loss values after training.

11. Predict a New Image

test_image_path = "classification/picture/test.png"

test_arr = image_to_array(test_image_path)

test_arr = np.array(test_arr).reshape(1, image_height, image_width, 3)

prediction = model.predict(test_arr)

predicted_number = np.argmax(prediction[0])

predicted_class = classes[predicted_number]

Why reshape?

One image has shape:

(64, 64, 3)

But CNN expects a batch:

(1, 64, 64, 3)

Meaning:

1 image
64 height
64 width
3 color channels

Prediction probabilities

[[0.12, 0.18, 0.70]]
Class Probability
mango 12%
dog 18%
cat 70%
np.argmax() finds the position of the highest probability. In this example the highest probability is for cat.

12. Complete CNN Code

from PIL import Image
import numpy as np
import os
import matplotlib.pyplot as plt

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.utils import to_categorical


# --------------------------------------------------
# 1. Basic settings
# --------------------------------------------------

base_folder = "classification/picture/pictures"

# Mango 0, Dog 1, Cat 2
classes = ["mango", "dog", "cat"]

image_width = 64
image_height = 64

number_of_classes = len(classes)


# --------------------------------------------------
# 2. Function to convert image into CNN input
# --------------------------------------------------

def image_to_array(image_path):
    """
    This function:
    1. Opens the image
    2. Converts it to RGB
    3. Resizes it to 64 x 64
    4. Converts it to NumPy array
    5. Divides pixel values by 255
    """

    img = Image.open(image_path).convert("RGB")
    img = img.resize((image_width, image_height))

    arr = np.array(img)

    # Normalize pixel values from 0-255 to 0-1
    arr = arr / 255.0

    return arr


# --------------------------------------------------
# 3. Read training images manually
# --------------------------------------------------

X = []
y = []

# Mango images
folder_path = os.path.join(base_folder, "mango")

image_path = os.path.join(folder_path, "mango1.png")
print(image_path)
arr = image_to_array(image_path)
X.append(arr)
y.append(0)

image_path = os.path.join(folder_path, "mango2.png")
print(image_path)
arr = image_to_array(image_path)
X.append(arr)
y.append(0)


# Dog images
folder_path = os.path.join(base_folder, "dog")

image_path = os.path.join(folder_path, "dog1.png")
print(image_path)
arr = image_to_array(image_path)
X.append(arr)
y.append(1)

image_path = os.path.join(folder_path, "dog2.png")
print(image_path)
arr = image_to_array(image_path)
X.append(arr)
y.append(1)


# Cat images
folder_path = os.path.join(base_folder, "cat")

image_path = os.path.join(folder_path, "cat1.png")
print(image_path)
arr = image_to_array(image_path)
X.append(arr)
y.append(2)

image_path = os.path.join(folder_path, "cat2.png")
print(image_path)
arr = image_to_array(image_path)
X.append(arr)
y.append(2)


# --------------------------------------------------
# 4. Convert lists to NumPy arrays
# --------------------------------------------------

X = np.array(X)
y = np.array(y)

print("X shape:", X.shape)
print("y:", y)

# Convert labels into one-hot format
y_categorical = to_categorical(y, number_of_classes)

print("One-hot labels:")
print(y_categorical)


# --------------------------------------------------
# 5. Create CNN model
# --------------------------------------------------

model = Sequential()

# First convolution layer
model.add(
    Conv2D(
        filters=16,
        kernel_size=(3, 3),
        activation="relu",
        input_shape=(image_height, image_width, 3)
    )
)

model.add(MaxPooling2D(pool_size=(2, 2)))

# Second convolution layer
model.add(
    Conv2D(
        filters=32,
        kernel_size=(3, 3),
        activation="relu"
    )
)

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())

model.add(Dense(64, activation="relu"))

model.add(Dense(number_of_classes, activation="softmax"))


# --------------------------------------------------
# 6. Compile CNN model
# --------------------------------------------------

model.compile(
    optimizer="adam",
    loss="categorical_crossentropy",
    metrics=["accuracy"]
)

print()
print("CNN Model Summary:")
model.summary()


# --------------------------------------------------
# 7. Train CNN model
# --------------------------------------------------

history = model.fit(
    X,
    y_categorical,
    epochs=50,
    batch_size=2,
    verbose=1
)

print()
print("CNN training completed successfully.")


# --------------------------------------------------
# 8. Predict a new image
# --------------------------------------------------

test_image_path = "classification/picture/test.png"

test_arr = image_to_array(test_image_path)

# Convert one image into a batch of one image
test_arr = np.array(test_arr).reshape(1, image_height, image_width, 3)

prediction = model.predict(test_arr)

print()
print("Prediction probabilities:")
print(prediction)

predicted_number = np.argmax(prediction[0])

predicted_class = classes[predicted_number]

print("Predicted class number:", predicted_number)
print("Predicted class:", predicted_class)


# --------------------------------------------------
# 9. Show test image with prediction
# --------------------------------------------------

img = Image.open(test_image_path)

plt.imshow(img)
plt.axis("off")
plt.title("Predicted class: " + predicted_class)
plt.show()


# --------------------------------------------------
# 10. Show training accuracy graph
# --------------------------------------------------

plt.figure(figsize=(8, 5))
plt.plot(history.history["accuracy"], marker="o")
plt.title("CNN Training Accuracy")
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.grid(True)
plt.show()


# --------------------------------------------------
# 11. Show training loss graph
# --------------------------------------------------

plt.figure(figsize=(8, 5))
plt.plot(history.history["loss"], marker="o")
plt.title("CNN Training Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid(True)
plt.show()

13. Improved Version: Read All Images Automatically

The previous code manually reads mango1.png, mango2.png, dog1.png, and so on. This improved version reads all images from folders.

X = []
y = []

for label_number, class_name in enumerate(classes):
    folder_path = os.path.join(base_folder, class_name)

    if not os.path.exists(folder_path):
        print("Folder not found:", folder_path)
        continue

    for file_name in os.listdir(folder_path):
        file_name_lower = file_name.lower()

        if (
            file_name_lower.endswith(".png")
            or file_name_lower.endswith(".jpg")
            or file_name_lower.endswith(".jpeg")
        ):
            image_path = os.path.join(folder_path, file_name)

            try:
                arr = image_to_array(image_path)

                X.append(arr)
                y.append(label_number)

                print("Loaded:", image_path, "Label:", class_name)

            except Exception as e:
                print("Could not load:", image_path)
                print("Error:", e)
This is better because you can add more images without changing the code.

14. Very Important Warning

This lesson uses only 2 mango images, 2 dog images, and 2 cat images. That is enough for understanding the program, but it is not enough for a real CNN.

With only 6 images, the CNN may memorize the images instead of learning real patterns.
Level Images Needed
Demo only 2 images per class
Beginner practice 20 images per class
Better learning 50 images per class
Good project 100+ images per class

15. Final Mental Model

Image Resize Array Normalize CNN Softmax Prediction
Image
↓
Resize to 64 × 64
↓
Convert to NumPy array
↓
Normalize pixels from 0-255 to 0-1
↓
CNN layer learns edges and patterns
↓
Pooling reduces size
↓
More CNN layers learn better patterns
↓
Flatten converts features to list
↓
Dense layer decides
↓
Softmax gives probabilities
↓
Highest probability becomes final class

16. Practice Questions

Question 1

What is the shape of one image after resizing?

64 × 64 × 3

Question 2

Why do we divide pixel values by 255?

To normalize pixel values from 0-255 to 0-1.

Question 3

What does Conv2D do?

It scans the image and learns visual patterns.

Question 4

What does MaxPooling2D do?

It reduces size while keeping important features.

Question 5

What does softmax do?

It gives probability for each class.

17. Next Step: From Demo to Real Project

After this lesson, improve the project by adding:

More images Use at least 20 to 50 images per class.
Train-test split Test the model on unseen images.
Data augmentation Rotate, zoom, and flip images for better learning.
Model saving Save trained model using model.save().
Model loading Load saved model for later prediction.
Web interface Upload an image and show prediction in a webpage.