Let's put the ideas from this chapter into practice. We'll use Python along with a couple of standard libraries, NumPy for numerical operations (which are essential for handling pixel data) and Pillow (a fork of the Python Imaging Library, PIL) for image manipulation tasks like creating, saving, and modifying images.
Before we start generating images, you need to make sure you have the necessary tools installed. If you have Python set up, you can typically install these libraries using pip, Python's package installer. Open your terminal or command prompt and run:
pip install numpy Pillow
This command downloads and installs NumPy and Pillow, making them available for use in your Python scripts.
Images, especially digital ones, are fundamentally grids of pixels. We can represent these grids using multi-dimensional arrays. For a color image, this is often a 3D array: (height, width, color_channels)
. The color channels typically represent Red, Green, and Blue (RGB) values, usually ranging from 0 to 255 for each channel.
Let's create a small, simple image: a 100x100 pixel black background with a 50x50 red square in the center.
import numpy as np
from PIL import Image
# Define image dimensions
height = 100
width = 100
channels = 3 # RGB
# Create a black background using a NumPy array filled with zeros
# Data type 'uint8' is standard for 8-bit pixel values (0-255)
image_array = np.zeros((height, width, channels), dtype=np.uint8)
# Define the coordinates for the red square
# Top-left corner (y1, x1) and bottom-right corner (y2, x2)
y1, x1 = 25, 25
y2, x2 = 75, 75
# Define the color red (R=255, G=0, B=0)
red_color = [255, 0, 0]
# Fill the square region with red
# Slicing format is [start_row:end_row, start_col:end_col]
image_array[y1:y2, x1:x2] = red_color
# Convert the NumPy array to a Pillow Image object
img = Image.fromarray(image_array, 'RGB')
# Save the image to a file
img.save('simple_square.png')
# Optionally, display the image (behavior might depend on your environment)
# img.show()
print("Generated image 'simple_square.png'")
After running this script, you'll find a file named simple_square.png
in the same directory. It will contain the black image with the red square we defined. This demonstrates how programming allows direct control over pixel values to generate specific visual patterns.
Adding random noise is a common technique in image processing, often used to simulate sensor imperfections or as a basic form of data augmentation. Let's take the red square image we just created and add some simple "salt and pepper" noise. This type of noise randomly introduces white (salt) and black (pepper) pixels.
import numpy as np
from PIL import Image
import random
# Load the previously created image
try:
img = Image.open('simple_square.png')
image_array = np.array(img)
except FileNotFoundError:
print("Error: 'simple_square.png' not found. Run Example 1 first.")
exit()
# Define the amount of noise (proportion of pixels to affect)
noise_amount = 0.05 # Affect 5% of the pixels
# Get image dimensions
height, width, channels = image_array.shape
# Create a copy to modify
noisy_image_array = image_array.copy()
# Add salt noise (white pixels)
num_salt = int(noise_amount * height * width * 0.5)
for _ in range(num_salt):
# Pick a random pixel location
y = random.randint(0, height - 1)
x = random.randint(0, width - 1)
# Set pixel to white
noisy_image_array[y, x] = [255, 255, 255]
# Add pepper noise (black pixels)
num_pepper = int(noise_amount * height * width * 0.5)
for _ in range(num_pepper):
# Pick a random pixel location
y = random.randint(0, height - 1)
x = random.randint(0, width - 1)
# Set pixel to black
noisy_image_array[y, x] = [0, 0, 0]
# Convert the modified NumPy array back to a Pillow Image object
noisy_img = Image.fromarray(noisy_image_array, 'RGB')
# Save the noisy image
noisy_img.save('noisy_square.png')
# noisy_img.show()
print("Generated noisy image 'noisy_square.png'")
This script loads the simple_square.png
, calculates the number of pixels to change based on noise_amount
, and then randomly selects pixels, setting half to white and half to black. The result, saved as noisy_square.png
, shows the original image speckled with random dots. This simulates a simple corruption process and generates a slightly different version of the original image.
Image augmentation involves applying transformations to existing images to create slightly modified versions. This increases dataset variety. While sometimes used on real data, the same techniques can generate synthetic variations. Let's rotate our original square image.
import numpy as np
from PIL import Image
# Load the original image
try:
img = Image.open('simple_square.png')
except FileNotFoundError:
print("Error: 'simple_square.png' not found. Run Example 1 first.")
exit()
# Define the rotation angle
angle = 45 # degrees
# Rotate the image using Pillow's rotate function
# 'expand=True' allows the image canvas to grow to fit the rotated image
rotated_img = img.rotate(angle, expand=True)
# Save the rotated image
rotated_img.save('rotated_square.png')
# rotated_img.show()
print("Generated rotated image 'rotated_square.png'")
Here, we used Pillow's built-in rotate
method. The expand=True
argument is useful because rotating a square image by 45 degrees makes it wider and taller; expand=True
resizes the image boundaries to avoid cropping the corners. The output file rotated_square.png
shows our red square tilted by 45 degrees.
In this practical session, you generated simple synthetic images using fundamental techniques:
These methods are foundational. Creating images that look truly realistic and diverse often requires much more sophisticated approaches, such as the rendering techniques mentioned earlier or advanced machine learning models like Generative Adversarial Networks (GANs) or Diffusion Models, which are beyond the scope of this introductory course. However, understanding these basic building blocks provides a solid start for appreciating the process and challenges of synthetic image generation.
© 2025 ApX Machine Learning