Color & Seeing: Detecting a Color is a Mask

Lesson 5 of 8 in the Pre-COSMOS series, 41 slides, on how a computer "sees" a color. A pixel is three numbers, [R, G, B], each from 0 to 255, and a color image is a grid of pixels with shape (H, W, 3). Asking whether a pixel is red means checking ranges - R high, G low, B low - to get a True or False. You then do it for every pixel at once with a MASK: red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80), after which red.sum() counts the red cells and np.argwhere(red) finds where they are. It is built in NumPy so that it RUNS; OpenCV (cv2) and HSV are NAMED as the camp's real post-it-maze tools, previewed but not taught today. The two traps are the classic NumPy color bugs: using Python's and and or instead of the array operators & and |, which raises ValueError: ambiguous truth value, and comparing a whole pixel to a single number instead of picking a channel with [:,:,0]. There are five checks and a scaffolded your-turn Maze Rover Simulator build that counts and locates red cells with no camera. Every snippet was run on CPython 3.12, and the printed output was copied verbatim.

Subject: Python · 70 slides · code lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Color & Seeing: Detecting a Color is a Mask

Title

Pre-COSMOS · Lesson 5 of 8

How does a robot 'see' a red post-it on the wall? It never sees 'red' - it sees three numbers per pixel and checks them. Today you build that check with numpy.

2. What you will be able to do

Objectives

A camera hands the robot a grid of numbers, not a picture. By the end you can teach it to find one color. You will be able to:

3. What survived from Capstone: Maze Rover + Reading Tracebacks?

Warm-up

Discussion prompt

Before we open Color & Seeing: Detecting a Color is a Mask: without looking back, what was the main idea of Capstone: Maze Rover + Reading Tracebacks, and what could you do by the end of it that you could not do before?

Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.

Answer:

Pre-COSMOS Lesson 8 of 8 (39 slides): the capstone. Snap the whole series together into a dry run of camp's signature task - a Rover class (L2) driven by a control loop (L3) whose state is steered by an FSM next_state dict (L3), fed by a NumPy camera grid (L4) and a red color mask (L5), paced by time.sleep - to follow colored markers through a maze.

4. Today's roadmap

Concept

Five stops, each one a step toward seeing a color:

A pixel
Three numbers: [R, G, B].
Is it red?
A range check -> True/False.
A mask
Check all pixels at once.
Count & find
.sum() and argwhere.

5. Which is which: Today's roadmap

Matching

Match the pairs

From Today's roadmap — match each one to what it actually does. The descriptions have been shuffled.

  • c1. A pixel
  • c2. Is it red?
  • c3. A mask
  • b1. Three numbers: [R, G, B].
  • b2. A range check -> True/False.
  • b3. Check all pixels at once.

Why: A pixel, Is it red?, A mask are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.

6. A Pixel is Three Numbers

Section

Section 1

7. What a color pixel is

Concept

Figure (svg): One pixel shown as three stacked bars labeled R 200, G 30, B 30, with a swatch showing the resulting red color.

Lots of R, little G and B -> the eye reads red.

A single pixel is three numbers: Red, Green, Blue. Each runs from 0 (none) to 255 (full).

[200, 30, 30] is mostly red light with little green or blue - so your eye sees red. The computer only ever sees the three numbers.

8. Break it if you can: What a color pixel is

Counterexample

Discussion prompt

A single pixel is three numbers: Red, Green, Blue. Each runs from 0 (none) to 255 (full).

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

[200, 30, 30] is mostly red light with little green or blue - so your eye sees red. The computer only ever sees the three numbers.

9. Three words for today

Concept

pixel — One dot of the image, stored as three numbers [R, G, B], each from 0 to 255.

channel — One of the three colors across the whole image. The R channel is every pixel's red number. You pick it with pixels[:,:,0].

mask — A grid of True/False, one per pixel, marking which pixels pass a test (like 'is red?').

10. Term to definition: Color & Seeing: Detecting a Color is a Mask

Matching

Match the pairs

Match each term to the definition this lesson gave it — not the one you would guess from the word.

  • t1. pixel
  • t2. channel
  • t3. mask
  • d1. One dot of the image, stored as three numbers [R, G, B], each from 0 to 255.
  • d2. One of the three colors across the whole image. The R channel is every pixel's red number. You pick it with pixels[:,:,0].
  • d3. A grid of True/False, one per pixel, marking which pixels pass a test (like 'is red?').

Why: These are the working definitions of pixel, channel, mask as Color & Seeing: Detecting a Color is a Mask uses them. Pairing them correctly is the test of whether you could state each one with the slide switched off.

11. A whole image is a grid of pixels

Concept

Stack pixels into a grid and you have an image. Its shape is (H, W, 3): H rows, W columns, and 3 numbers per pixel.

A 3 x 3 color image therefore has shape (3, 3, 3) - nine pixels, each holding its own [R, G, B].

12. By analogy: A whole image is a grid of pixels

Analogy

Discussion prompt

Explain A whole image is a grid of pixels by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Stack pixels into a grid and you have an image. Its shape is (H, W, 3): H rows, W columns, and 3 numbers per pixel.

13. Picture it first: A spreadsheet with three sheets

Picture it

Figure (svg): Three stacked square grids labeled R, G, B, with an arrow showing that one pixel reads one cell from each grid.

Three sheets; one pixel reads one cell from each.

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

Picture three stacked grids: one holds every pixel's red number, one the green, one the blue. Pixel (0,0) reads across all three to get its color.

14. A spreadsheet with three sheets

Intuition

Picture three stacked grids: one holds every pixel's red number, one the green, one the blue. Pixel (0,0) reads across all three to get its color.

Figure (svg): Three stacked square grids labeled R, G, B, with an arrow showing that one pixel reads one cell from each grid.

Three sheets; one pixel reads one cell from each.

Picking a channel with pixels[:,:,0] is like pulling out just the red sheet - one number per pixel instead of three.

15. Teach it back: A spreadsheet with three sheets

Explain it

Discussion prompt

Explain A spreadsheet with three sheets to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Picture three stacked grids: one holds every pixel's red number, one the green, one the blue. Pixel (0,0) reads across all three to get its color.

16. Build a 3x3 image and check its shape

Worked example

Here is our sample wall: a 3 x 3 grid of pixels. We hand it to numpy and ask for its shape.

import numpy as np
pixels = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
print(pixels.shape)

Three rows, three columns, three numbers per pixel -> (3, 3, 3).

expressionmeansvalue
rows (H)how many rows of pixels3
columns (W)how many pixels per row3
last 3numbers in each pixel [R,G,B]3
pixels.shapethe whole shape(3, 3, 3)

17. Which is which, by value

Discrimination

Sort into buckets

Sort these by value, from memory, without looking back at Build a 3x3 image and check its shape. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.

3
rows (H); columns (W); last 3
(3, 3, 3)
pixels.shape
g1
value is "3" for rows (H), columns (W), last 3 — that is what the table on "Build a 3x3 image and check its shape" records, and it is the single property separating this group from the rest.
g2
value is "(3, 3, 3)" for pixels.shape — that is what the table on "Build a 3x3 image and check its shape" records, and it is the single property separating this group from the rest.

18. Pick one channel: the red sheet

Worked example

pixels[:,:,0] says: every row, every column, number index 0 (red). That pulls out a flat 3 x 3 grid of red values.

import numpy as np
pixels = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
print(pixels[:,:,0])

Index 0 = R, index 1 = G, index 2 = B. The two : mean 'all rows, all columns'.

rowred values printed
row 0[200 20 210]
row 1[ 10 190 30]
row 2[180 15 20]

19. Fill in: red values printed for Pick one channel: the red sheet

Comparison

Comparison matrix

From Pick one channel: the red sheet: refill the red values printed column from what you know. The rest of the table is as it appeared.

rowred values printed
row 0[200 20 210]
row 1[ 10 190 30]
row 2[180 15 20]

20. Is This Pixel Red?

Section

Section 2

21. Red is a range, not a name

Concept

There is no 'red' number. Red means a range: R is high, G is low, B is low. We pick thresholds: R > 150, G < 80, B < 80.

A pixel is red only if all three are true at once. Checking those gives a single True/False.

22. Check one pixel by hand

Worked example

Take pixel [200, 30, 30]. Test each part against the red rule, then combine with and. (One pixel, plain Python - that's fine here.)

R, G, B = 200, 30, 30
is_red = R > 150 and G < 80 and B < 80
print(is_red)

All three parts pass, so is_red is True.

parttestresult
R > 150200 > 150True
G < 8030 < 80True
B < 8030 < 80True
is_redall three TrueTrue

23. What each one costs: Check one pixel by hand

Trade off

Comparison matrix

From Check one pixel by hand: every row here is a choice with a cost. Fill the result column, then say which row you would actually pick and what you give up for it.

parttestresult
R > 150200 > 150True
G < 8030 < 80True
B < 8030 < 80True
is_redall three TrueTrue

24. Three gates in a row

Intuition

Think of three gates: 'enough red?', 'little green?', 'little blue?'. A pixel only earns the label red if it walks through all three.

Miss any one gate - say a bright pink with high blue - and it's out. That's why the rule is and, not 'any of these'.

25. Check Every Pixel: a Mask

Section

Section 3

26. From one pixel to all of them

Concept

Doing the check on every pixel one-by-one would be slow. NumPy does the whole grid at once: compare a channel to a number and get a True/False grid back.

pixels[:,:,0] > 150 returns a 3 x 3 grid of True/False - 'is each pixel's red big enough?'. Combine three of those with & to get the red mask.

27. Teach it back: From one pixel to all of them

Explain it

Discussion prompt

Explain From one pixel to all of them to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Doing the check on every pixel one-by-one would be slow. NumPy does the whole grid at once: compare a channel to a number and get a True/False grid back.

28. Build the red mask

Worked example

Pick each channel, compare it, and join with &. Wrap each comparison in parentheses.

import numpy as np
pixels = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80)
print(red)

Each & is 'AND, pixel by pixel'. A cell is True only where all three comparisons are True for that pixel.

rowmask printedwhy
row 0[ True False True][200,30,30] and [210,40,50] pass; middle fails
row 1[False True False]only [190,20,20] passes
row 2[ True False False]only [180,25,25] passes

29. Something is wrong here: using `and` instead of `&`

Anomaly

Predict first

A student writes this, and it looks reasonable:

You join the channel comparisons with Python's and, like you did for one pixel.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: and wants ONE True/False, but each side is a whole grid of them.

Use & (element-wise AND) with parentheses around each comparison.

Why: and wants ONE True/False, but each side is a whole grid of them. Python can't reduce a grid to a single yes/no.

30. Trap: using `and` instead of `&`

Trap

The trap

You join the channel comparisons with Python's and, like you did for one pixel.

Write red = (pixels[:,:,0] > 150) and (pixels[:,:,1] < 80)

Why: and wants ONE True/False, but each side is a whole grid of them. Python can't reduce a grid to a single yes/no.

Crashes: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Why: NumPy is saying: a 3x3 grid isn't one True/False, so and has nothing to decide.

The fix

Use & (element-wise AND) with parentheses around each comparison.

Write red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80)

Why: & combines the grids cell by cell, giving a 3x3 mask - exactly what you want.

Use | for OR the same way

Why: On arrays: & is AND, | is OR. Always parenthesize each comparison, because & binds tighter than >.

31. Break it on purpose: using `and` instead of `&`

Break the constraint

Discussion prompt

The rule this trap just fixed:

& combines the grids cell by cell, giving a 3x3 mask - exactly what you want.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

and wants ONE True/False, but each side is a whole grid of them. Python can't reduce a grid to a single yes/no.

32. Something is wrong here: forgetting to pick a channel

Anomaly

Predict first

A student writes this, and it looks reasonable:

You compare the whole pixel to one number instead of picking a channel.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: No [:,:,0] - so this tests all three numbers of every pixel, not just red.

Pick the red channel first with [:,:,0], then compare.

Why: No [:,:,0] - so this tests all three numbers of every pixel, not just red.

33. Trap: forgetting to pick a channel

Trap

The trap

You compare the whole pixel to one number instead of picking a channel.

Write red = pixels > 150

Why: No [:,:,0] - so this tests all three numbers of every pixel, not just red.

Get a (3, 3, 3) grid, not a (3, 3) answer

Why: You wanted one True/False per pixel. Instead you get one per NUMBER - three answers per pixel, the wrong shape entirely.

The fix

Pick the red channel first with [:,:,0], then compare.

Write pixels[:,:,0] > 150

Why: Now you compare just the red number of each pixel, giving a clean (3, 3) grid.

A mask should be shaped (H, W), one answer per pixel

Why: If your mask is still 3D, you forgot to pick a channel somewhere.

34. Which of these survive contact with Color & Seeing: Detecting a Color is a Mask?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
Five stops, each one a step toward seeing a color:; A single pixel is three numbers: Red, Green, Blue. Each runs from 0 (none) to 255 (full).; Stack pixels into a grid and you have an image. Its shape is (H, W, 3): H rows, W columns, and 3 numbers per pixel.
Breaks
You join the channel comparisons with Python's and, like you did for one pixel.; You compare the whole pixel to one number instead of picking a channel.
sound
These are stated as this lesson states them — each one survives the edge cases Color & Seeing: Detecting a Color is a Mask puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

35. Rebuild the recipe: How to detect any color

Ranking

Put in order

These are the steps of How to detect any color, scrambled. Put them back in order before the next slide shows you.

  1. Pick each channel: pixels[:,:,0] (R), [:,:,1] (G), [:,:,2] (B).
  2. Compare each to a threshold, each comparison in ().
  3. Join with & (AND) - or | (OR) - to get the mask.
  4. Count with mask.sum(); locate with np.argwhere(mask).

Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.

36. How to detect any color

Pattern

This four-line recipe works for any color and any image - swap the thresholds to chase green, blue, or a post-it's exact shade:

  1. Pick each channel: pixels[:,:,0] (R), [:,:,1] (G), [:,:,2] (B).
  2. Compare each to a threshold, each comparison in ().
  3. Join with & (AND) - or | (OR) - to get the mask.
  4. Count with mask.sum(); locate with np.argwhere(mask).

37. Where does each piece belong: Color & Seeing: Detecting a Color is a Mask

Sorting

Sort into buckets

These are the pieces of Color & Seeing: Detecting a Color is a Mask, out of order. Put each one back under the part of the lesson it belongs to.

A Pixel is Three Numbers
What a color pixel is; Three words for today; A whole image is a grid of pixels
Is This Pixel Red?
Red is a range, not a name; Check one pixel by hand; Three gates in a row
Check Every Pixel: a Mask
From one pixel to all of them; Build the red mask; How to detect any color
s1
A Pixel is Three Numbers is where Color & Seeing: Detecting a Color is a Mask puts What a color pixel is, Three words for today, A whole image is a grid of pixels. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s2
Is This Pixel Red? is where Color & Seeing: Detecting a Color is a Mask puts Red is a range, not a name, Check one pixel by hand, Three gates in a row. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s3
Check Every Pixel: a Mask is where Color & Seeing: Detecting a Color is a Mask puts From one pixel to all of them, Build the red mask, How to detect any color. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.

38. Rule out three: Check: what is a mask?

Elimination

Eliminate the wrong options

After red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80), what is red?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. A grid of True/False, one per pixel, marking which pixels are red
  • B. A single True or False for the whole image
  • C. The count of red pixels
  • D. A grid of the red color values (the R numbers)

Survives elimination: A

Why: A mask is a grid of booleans with the same height and width as the image. Each cell is True where that pixel passed all three range checks, False otherwise.

39. Check: what is a mask?

Check

Think about what red holds after the mask line runs.

Check your understanding

After red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80), what is red?

  • A. A grid of True/False, one per pixel, marking which pixels are red (correct)
  • B. A single True or False for the whole image
  • C. The count of red pixels
  • D. A grid of the red color values (the R numbers)

Answer: A

Why: A mask is a grid of booleans with the same height and width as the image. Each cell is True where that pixel passed all three range checks, False otherwise.

Why B tempts people
That's what Python's and would try to make (and it crashes). & keeps the answer per-pixel, so you get a whole grid of True/False, not one.
Why C tempts people
The count comes later, from red.sum(). The mask itself is the True/False grid that sum then counts.
Why D tempts people
That would be pixels[:,:,0] - the raw red numbers. A mask holds the True/False results of comparing them, not the numbers.

40. Answer it before you see the options: Check: `&` or `and`?

Prediction

Predict first

Why does (pixels[:,:,0] > 150) and (pixels[:,:,1] < 80) raise a ValueError?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: Python's and needs one True/False, but each side is a grid of many

Why: Each comparison is a grid of booleans. Python's and tries to collapse a side to a single truth value, but a multi-element array is ambiguous - hence 'the truth value of an array with more than one element is ambiguous'. Use & instead.

41. Check: `&` or `and`?

Check

Both sides of the join are whole 3x3 grids.

Check your understanding

Why does (pixels[:,:,0] > 150) and (pixels[:,:,1] < 80) raise a ValueError?

  • A. Python's and needs one True/False, but each side is a grid of many (correct)
  • B. You can't compare a NumPy array to a number at all
  • C. The two channels have different shapes
  • D. and is not a real Python keyword

Answer: A

Why: Each comparison is a grid of booleans. Python's and tries to collapse a side to a single truth value, but a multi-element array is ambiguous - hence 'the truth value of an array with more than one element is ambiguous'. Use & instead.

Why B tempts people
You can compare an array to a number - that's exactly what pixels[:,:,0] > 150 does, returning a grid of True/False. The error is about and, not the comparison.
Why C tempts people
Both channels are the same (3, 3) shape, so shape isn't the problem. The problem is and on a multi-element array.
Why D tempts people
and is a real keyword; it just works on single True/False values, not on whole arrays. For arrays you need the & operator.

42. Count Them and Find Them

Section

Section 4

43. True counts as 1

Concept

In NumPy, True acts like 1 and False like 0. So adding up the mask counts the red pixels: red.sum().

And np.argwhere(red) hands back the [row, col] of every True cell - the locations of the red pixels.

44. By analogy: True counts as 1

Analogy

Discussion prompt

Explain True counts as 1 by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

In NumPy, True acts like 1 and False like 0. So adding up the mask counts the red pixels: red.sum().

45. Count the red pixels

Worked example

Sum the mask. Each True adds 1, each False adds 0.

import numpy as np
pixels = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80)
print(red.sum())

Four cells were True, so the sum is 4.

rowTrue cells in rowrunning total
row 022
row 113
row 214
red.sum()-prints 4

46. Which is which, by True cells in row

Discrimination

Sort into buckets

Sort these by True cells in row, from memory, without looking back at Count the red pixels. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.

2
row 0
1
row 1; row 2
-
red.sum()
g1
True cells in row is "2" for row 0 — that is what the table on "Count the red pixels" records, and it is the single property separating this group from the rest.
g2
True cells in row is "1" for row 1, row 2 — that is what the table on "Count the red pixels" records, and it is the single property separating this group from the rest.
g3
True cells in row is "-" for red.sum() — that is what the table on "Count the red pixels" records, and it is the single property separating this group from the rest.

47. Find where they are

Worked example

np.argwhere(red) returns the [row, col] of each True, top-to-bottom, left-to-right.

import numpy as np
pixels = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
red = (pixels[:,:,0] > 150) & (pixels[:,:,1] < 80) & (pixels[:,:,2] < 80)
print(np.argwhere(red).tolist())

Each pair is [row, col]. Read them against the mask to confirm.

coordinatepixel therered?
[0, 0][200, 30, 30]True
[0, 2][210, 40, 50]True
[1, 1][190, 20, 20]True
[2, 0][180, 25, 25]True

48. Fill in: red? for Find where they are

Comparison

Comparison matrix

From Find where they are: refill the red? column from what you know. The rest of the table is as it appeared.

coordinatepixel therered?
[0, 0][200, 30, 30]True
[0, 2][210, 40, 50]True
[1, 1][190, 20, 20]True
[2, 0][180, 25, 25]True

49. Check: count the red

Check

The mask has True at exactly four cells.

Check your understanding

Given the red mask above, what does print(red.sum()) show?

  • A. 4 (correct)
  • B. True
  • C. 9
  • D. [[0,0],[0,2],[1,1],[2,0]]

Answer: A

Why: True counts as 1 and False as 0, so summing the mask counts the True cells. There are four red pixels, so red.sum() is 4.

Why B tempts people
red.sum() adds the booleans into a number, not a single True/False. You'd get True from something like red.any(), which only checks 'is any pixel red?'.
Why C tempts people
9 is the total number of pixels in the 3x3 grid. sum() counts only the True (red) cells, which is 4, not every pixel.
Why D tempts people
Those are the locations - that's np.argwhere(red).tolist(). sum() gives the COUNT (4), not the coordinates.

50. Answer it before you see the options: Check: what makes a pixel 'red'?

Prediction

Predict first

Which pixel passes the rule R > 150 and G < 80 and B < 80?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: [180, 25, 25]

Why: Red needs high R and low G and low B. [180, 25, 25] has R=180 > 150, G=25 < 80, and B=25 < 80 - all three pass, so it is red.

51. Check: what makes a pixel 'red'?

Check

Red light is strong; green and blue are weak.

Check your understanding

Which pixel passes the rule R > 150 and G < 80 and B < 80?

  • A. [180, 25, 25] (correct)
  • B. [200, 120, 30]
  • C. [90, 20, 20]
  • D. [160, 40, 200]

Answer: A

Why: Red needs high R and low G and low B. [180, 25, 25] has R=180 > 150, G=25 < 80, and B=25 < 80 - all three pass, so it is red.

Why B tempts people
R=200 passes, but G=120 is NOT < 80, so the green gate fails. Too much green means it isn't red by this rule.
Why C tempts people
G and B are low enough, but R=90 is NOT > 150. Without enough red, a dark pixel fails the first gate.
Why D tempts people
R=160 passes and G=40 passes, but B=200 is NOT < 80. High blue (a purple-ish pixel) fails the blue gate.

52. The Camp's Real Tools

Section

Section 5 · preview

53. Meet OpenCV and HSV (preview only)

Concept

At camp you won't hand-type pixel grids - a camera gives you the image, and you'll read it with OpenCV, imported as cv2. It's the real-world tool for vision.

HSV — Hue, Saturation, Value - another way to name a color. Hue is the pure color on a wheel, so 'is it red?' becomes one hue range instead of three RGB gates. cv2 converts RGB to HSV for you.

54. Take the definitions apart: channel vs HSV

Definition probe

Sort into buckets

Every line below is part of the definition of channel or of HSV — one or the other, never both. Put each where it belongs.

channel
One of the three colors across the whole image.; The R channel is every pixel's red number.; You pick it with pixels[:,:,0].
HSV
Hue, Saturation, Value - another way to name a color.; Hue is the pure color on a wheel, so 'is it red?' becomes one hue range instead of three RGB gates.; cv2 converts RGB to HSV for you.
b1
One of the three colors across the whole image. The R channel is every pixel's red number. You pick it with pixels[:,:,0].
b2
Hue, Saturation, Value - another way to name a color. Hue is the pure color on a wheel, so 'is it red?' becomes one hue range instead of three RGB gates. cv2 converts RGB to HSV for you.

55. Why HSV beats RGB for color

Intuition

In RGB, a red post-it in shadow vs sunlight has very different numbers - your thresholds break. In HSV, the hue stays put (it's still red); only brightness changes.

So pros pick the color with one hue range in HSV, then build the same kind of mask you built today - count and locate with .sum() and argwhere. The idea you learned is exactly the one the camp uses; cv2/HSV just make it robust. You are not writing cv2 today.

56. Break it if you can: Why HSV beats RGB for color

Counterexample

Discussion prompt

In RGB, a red post-it in shadow vs sunlight has very different numbers - your thresholds break. In HSV, the hue stays put (it's still red); only brightness changes.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

57. Rule out three: Check: what are cv2 and HSV?

Elimination

Eliminate the wrong options

What are OpenCV (cv2) and HSV in this lesson?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. The camp's real tools, previewed today - you built the same masking idea in NumPy instead
  • B. NumPy functions you called in every example above
  • C. Two names for the exact same thing
  • D. Required to count red pixels - red.sum() won't work without them

Survives elimination: A

Why: cv2 (OpenCV) reads images from a camera and HSV is a color space where hue makes color-picking robust. Today you previewed them but implemented the masking idea in plain NumPy - the concept transfers directly.

58. Check: what are cv2 and HSV?

Check

Think about today's job versus the camp's real setup.

Check your understanding

What are OpenCV (cv2) and HSV in this lesson?

  • A. The camp's real tools, previewed today - you built the same masking idea in NumPy instead (correct)
  • B. NumPy functions you called in every example above
  • C. Two names for the exact same thing
  • D. Required to count red pixels - red.sum() won't work without them

Answer: A

Why: cv2 (OpenCV) reads images from a camera and HSV is a color space where hue makes color-picking robust. Today you previewed them but implemented the masking idea in plain NumPy - the concept transfers directly.

Why B tempts people
Every example used numpy (np.array, &, .sum(), np.argwhere) - never cv2. cv2 was only named as the real tool, not called.
Why C tempts people
They're different: cv2 is a library (code that reads and processes images); HSV is a color space (a way to describe a color). One is a tool, the other is a representation.
Why D tempts people
red.sum() is pure NumPy and works on any boolean mask, as you saw - it printed 4. cv2 isn't needed to count; it just helps get a clean color mask from a real camera.

59. Your Turn: Maze Rover Simulator

Section

Section 6 · build it yourself

60. The build: find the red cells, no camera

Concept

You're handed a grid of pixel colors - a tiny 'photo' of the maze wall. Your job: count and locate the red cells, color detection with no camera. Type every line yourself, run after each one, and read errors - don't erase them.

#do thistool you'll use
1build the pixel gridnp.array(...)
2make the red mask[:,:,0], &, parentheses
3count the red cells.sum()
4find their coordinatesnp.argwhere(...)

61. Milestone 1 — build the pixel grid

Worked example

Your turn: make the 3 x 3 grid below with np.array and print its shape. Say out loud what shape you expect before you run it.

Hint: wrap the rows in np.array(...); the shape of a 3x3 color image is (rows, columns, 3).

import numpy as np
grid = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
print(grid.shape)
lineprints
print(grid.shape)(3, 3, 3)

62. Milestone 2 — make the red mask

Worked example

Your turn: build the red mask: R high, G low, B low. Predict which cells will be True before you run it.

Hint: pick each channel with [:,:,0], [:,:,1], [:,:,2]; join with &; parenthesize each comparison. Use &, not and.

red = (grid[:,:,0] > 150) & (grid[:,:,1] < 80) & (grid[:,:,2] < 80)
print(red)
rowmask printed
row 0[ True False True]
row 1[False True False]
row 2[ True False False]

63. What each one costs: Milestone 2 — make the red mask

Trade off

Comparison matrix

From Milestone 2 — make the red mask: every row here is a choice with a cost. Fill the mask printed column, then say which row you would actually pick and what you give up for it.

rowmask printed
row 0[ True False True]
row 1[False True False]
row 2[ True False False]

64. Milestone 3 — count the red cells

Worked example

Your turn: count the red cells by summing the mask. Predict the number first.

Hint: True counts as 1; red.sum() adds them all up.

print(red.sum())
lineprints
print(red.sum())4

65. Milestone 4 — find their coordinates

Worked example

Your turn: list the [row, col] of every red cell. Predict how many pairs you'll get (it should match your count).

Hint: np.argwhere(red) returns the coordinates; add .tolist() for a clean list of pairs.

print(np.argwhere(red).tolist())
lineprints
print(np.argwhere(red).tolist())[[0, 0], [0, 2], [1, 1], [2, 0]]

66. Full program

Worked example

Your turn: put all four milestones together into one program that reports the count and the locations. Predict the last two prints before running.

import numpy as np
grid = np.array([
  [[200,30,30],[20,20,20],[210,40,50]],
  [[10,200,10],[190,20,20],[30,30,30]],
  [[180,25,25],[15,15,15],[20,180,20]]
])
red = (grid[:,:,0] > 150) & (grid[:,:,1] < 80) & (grid[:,:,2] < 80)
print("Found", red.sum(), "red cells")
print(np.argwhere(red).tolist())
stepresult
grid.shape(3, 3, 3)
red.sum()4
first printFound 4 red cells
second print[[0, 0], [0, 2], [1, 1], [2, 0]]

If yours prints Found 4 red cells and those four coordinates - you just built color detection with a mask.

67. Fill in: result for Full program

Comparison

Comparison matrix

From Full program: refill the result column from what you know. The rest of the table is as it appeared.

stepresult
grid.shape(3, 3, 3)
red.sum()4
first printFound 4 red cells
second print[[0, 0], [0, 2], [1, 1], [2, 0]]

68. Show it off

Worked example

Explain your program out loud: point to the line that makes the mask, the line that counts, and the line that locates.

Now connect it to the real task: at camp the rover's camera will photograph a wall of post-it notes, and cv2/HSV will hand you a red mask - then this same .sum() and argwhere find each red marker so the rover can steer the maze.

You can now beat both of today's traps: use & (not and) on arrays, and always pick a channel with [:,:,0] before comparing.

69. Connect it up: Color & Seeing: Detecting a Color is a Mask

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — A Pixel is Three Numbers · Is This Pixel Red? · Check Every Pixel: a Mask · Count Them and Find Them · The Camp's Real Tools · Your Turn: Maze Rover Simulator. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

70. What you can do now

Recap

you want to...you write
pick the red channelpixels[:,:,0]
build the red mask(R>150) & (G<80) & (B<80)
count red pixelsred.sum()
find where they arenp.argwhere(red)

Next time (Lesson 6): the rover starts making decisions from what it sees - turning the mask's count and locations into movement.

Sources

  1. NumPy user guide - Broadcasting (how element-wise comparisons line up across arrays)
  2. NumPy reference - numpy.argwhere (find the indices where a condition is True)
  3. All snippets executed on CPython 3.12; output copied verbatim. Author verification run, 2026-06-24 (Pre-COSMOS Lesson 5 of 8). — Author verification run, 2026-06-24 (Pre-COSMOS Lesson 5 of 8).

Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108