Pixels2GenAI
Path i Foundations
M 01 · 1.3.1 · hands-on

1.3.1 Flags (Array Slicing)

Select and modify rectangular regions of NumPy arrays using slice notation. Build structured colour compositions — national flags — by combining row slices, column slices, and broadcasting.

Duration15–20 min
Levelbeginner
Load2 core concepts
Prereqs1.1.1 (RGB arrays)

Overview

Flags are structured patterns made of rectangular colour blocks. Creating them teaches you one of NumPy’s most powerful features: array slicing. You will learn to select and modify rectangular regions of images using simple slice notation, building the foundation for complex compositions and transformations.

Learning objectives

  1. Use NumPy slice notation [start:stop] to select array regions.
  2. Apply multi-dimensional slicing to RGB images [rows, cols, channels].
  3. Create structured compositions by manipulating rectangular regions.
  4. Transfer slicing knowledge to tiling and pattern generation tasks.

Quick start — create your first flag

Let’s create the flag of France using vertical colour bands. Run this code:

python · france_flag.py
import numpy as np
from PIL import Image

# Create blank canvas (standard 2:3 flag ratio)
height, width = 300, 450
flag = np.zeros((height, width, 3), dtype=np.uint8)

# Blue stripe (left third: columns 0-149)
flag[:, 0:150, 0] = 0    # Red channel
flag[:, 0:150, 1] = 85   # Green channel
flag[:, 0:150, 2] = 164  # Blue channel

# White stripe (middle third: columns 150-299)
flag[:, 150:300, :] = 255

# Red stripe (right third: columns 300-449)
flag[:, 300:450, 0] = 239
flag[:, 300:450, 1] = 65
flag[:, 300:450, 2] = 53

# Save the flag
result = Image.fromarray(flag, mode='RGB')
result.save('france_flag.png')
Flag of France with blue, white, and red vertical stripes
Fig. 1 The flag of France created using column slicing.

What you just did: You used NumPy array slicing to select vertical columns. The notation [:, 0:150] means “all rows, columns 0 to 149”. The colon : selects all rows, while 0:150 specifies which columns to modify.

Core concepts

Concept 1 — Array slicing syntax

Array slicing lets you select rectangular regions from arrays using the colon operator. For 2D images, the basic pattern is:

python
image[row_start:row_stop, col_start:col_stop]

The fundamental rules:

  • start is inclusive, stop is exclusive: [0:100] selects indices 0 through 99.
  • Omitting values: : alone means “all”, :10 means “0 to 10”, 10: means “10 to end”.
  • Negative indices: -1 is the last element, -10: selects the last 10 elements.
python · slicing_basics.py
import numpy as np

# Create 200x300 image
img = np.zeros((200, 300, 3), dtype=np.uint8)

# Top half (rows 0-99)
img[0:100, :] = [255, 0, 0]  # Red

# Bottom half (rows 100-199)
img[100:200, :] = [0, 0, 255]  # Blue

# Right quarter (all rows, last 75 columns)
img[:, 225:] = [0, 255, 0]  # Green (overwrites part of each half)
Visual diagram showing array slicing notation with highlighted region
Fig. 2 Array slicing notation: image[1:4, 2:5] selects rows 1-3 and columns 2-4 (start inclusive, stop exclusive).

Concept 2 — Multi-dimensional slicing

RGB images are 3D arrays with shape (height, width, 3). The third dimension holds colour channels. You can slice across all three dimensions simultaneously:

python · multidim_slicing.py
import numpy as np

img = np.zeros((200, 300, 3), dtype=np.uint8)

# Select a rectangular region, all channels
img[0:100, 0:100, :] = [255, 0, 0]  # Top-left square, red

# Select entire red channel only
red_channel = img[:, :, 0]

# Select a single row across all channels (horizontal line)
horizontal_line = img[50, :, :]

# Select a single column across all channels (vertical line)
vertical_line = img[:, 100, :]

Broadcasting shortcut: NumPy automatically expands (broadcasts) values to match array dimensions. This means you can assign a single value or a short list to a multi-dimensional slice:

python
# These are equivalent:
img[0:100, 0:100, 0] = 255
img[0:100, 0:100, 1] = 0
img[0:100, 0:100, 2] = 0

# Can be written as (NumPy broadcasts the list):
img[0:100, 0:100] = [255, 0, 0]
3D visualization showing RGB array structure with layered red, green, and blue channels
Fig. 3 RGB images as 3D arrays: (height, width, channels). Each pixel has three values stored in the third dimension.

Exercises

Three progressively challenging exercises, each building on the previous using the Execute, Modify, Create approach.

EXECUTE I.

Run the France flag

Run the France flag code from the Quick Start section and observe the output. Then answer the reflection questions below.

france_flag.py — France flag script

Reflection questions

  1. What does [:, 0:150] select? Why does the colon alone select all rows?
  2. Why does flag[:, 150:300, :] = 255 create white instead of requiring three separate channel assignments?
  3. How would you modify the code to create horizontal stripes instead of vertical ones?
MODIFY II.

Create the German flag

Modify the France flag code to create the flag of Germany (horizontal stripes: black on top, red in middle, gold on bottom).

Goals

  1. Switch orientation — change from vertical stripes to horizontal stripes.
  1. Divide into three equal bands — calculate the row boundaries.
  1. Apply the official German colours — Black [0, 0, 0], Red [221, 0, 0], Gold [255, 206, 0].
Flag of Germany with black, red, and gold horizontal stripes
Fig. 4 The flag of Germany created using row slicing.
CREATE III.

Build the Swiss flag

Create the flag of Switzerland from scratch. This flag has a red field with a white cross at its centre, requiring you to combine both horizontal and vertical slicing.

Requirements

  • Square format: 300 x 300 pixels.
  • Red background: [255, 0, 0].
  • White cross: [255, 255, 255].
  • Cross proportions: bars are 1/5 of the flag dimension, centred.

Starter code

python · switzerland_starter.py
import numpy as np
from PIL import Image

# Create square flag (Switzerland uses 1:1 ratio)
size = 300
flag = np.zeros((size, size, 3), dtype=np.uint8)

# TODO Step 1: Fill entire background with red
# Hint: Use flag[:, :] = [255, 0, 0]

# TODO Step 2: Create vertical bar of cross (centred, white)
# The bar width should be size // 5 (60 pixels)
# Calculate left and right positions to centre it
# Hint: left = (size - bar_width) // 2

# TODO Step 3: Create horizontal bar of cross (centred, white)
# Same proportions as vertical bar

# Save
result = Image.fromarray(flag, mode='RGB')
result.save('switzerland_flag.png')
switzerland_starter.py — starter code
Flag of Switzerland with a white cross on a red background
Fig. 5 The flag of Switzerland created by overlapping vertical and horizontal slices.

Downloads

france_flag.py — France flag (Execute) germany_flag.py — Germany flag (Modify solution) switzerland_flag.py — Switzerland flag (Create solution) switzerland_starter.py — Switzerland starter code

Summary

In 15-20 minutes, two core concepts: array slicing syntax and multi-dimensional slicing.

Common pitfalls to avoid

  • Confusing row and column order: remember [row, col] not [x, y]. Rows are vertical position, columns are horizontal.
  • Off-by-one errors: [0:100] selects 100 elements (0-99), not 101. The stop index is exclusive.
  • Forgetting the channel dimension: img[0:100, 0:100] modifies all channels. Use img[0:100, 0:100, 0] for the red channel only.

Connection to future learning

The rectangular slicing you practised here is the foundation for:

  • 1.3.2 (Repeat): tiling patterns using slicing to place repeated elements.
  • 1.3.3-1.3.4: Truchet and Wang tiles (complex tiling systems).
  • Module 3: image transformations and masking (selecting regions for effects).
  • Module 7: image segmentation (dividing images into regions for analysis).

References

  1. [1] Harris, C. R., Millman, K. J., van der Walt, S. J., Gommers, R., Virtanen, P., Cournapeau, D., Wieser, E., Taylor, J., Berg, S., Smith, N. J., Kern, R., Picus, M., Hoyer, S., van Kerkwijk, M. H., Brett, M., Haldane, A., del Rio, J. F., Wiebe, M., Peterson, P., … Oliphant, T. E. (2020). Array programming with NumPy. Nature, 585(7825), 357-362. doi:10.1038/s41586-020-2649-2
  2. [2] Van Rossum, G., and Drake, F. L. (2009). Python 3 Reference Manual. CreateSpace. ISBN: 978-1-4414-1269-0.
  3. [3] NumPy Developers. (2024). Indexing on ndarrays. NumPy Documentation. Retrieved January 30, 2025, from numpy.org/doc/stable/user/basics.indexing.html
  4. [4] Gonzalez, R. C., and Woods, R. E. (2007). Digital Image Processing (3rd ed.). Pearson. ISBN: 978-0-13-168728-8.
  5. [5] Sweller, J., van Merriënboer, J. J. G., and Paas, F. (2019). Cognitive Architecture and Instructional Design: 20 Years Later. Educational Psychology Review, 31(2), 261-292. doi:10.1007/s10648-019-09465-5
  6. [6] Mayer, R. E. (2021). Multimedia Learning (3rd ed.). Cambridge University Press. ISBN: 978-1-108-89485-3.
  7. [7] Reas, C., and Fry, B. (2014). Getting Started with Processing: A Hands-On Introduction to Making Interactive Graphics (2nd ed.). Maker Media. ISBN: 978-1-4571-8707-4.