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
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.
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:
[R, G, B], each 0..255.&.red.sum() and locate them with np.argwhere(red).and vs &, and forgetting to pick a channel.cv2) and HSV - and say why they exist.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.
Concept
Five stops, each one a step toward seeing a color:
[R, G, B]..sum() and argwhere.Matching
Match the pairs
From Today's roadmap — match each one to what it actually does. The descriptions have been shuffled.
[R, G, B].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.
Section
Section 1
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.
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.
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.
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?').
Matching
Match the pairs
Match each term to the definition this lesson gave it — not the one you would guess from the word.
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.
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].
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.
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.
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.
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.
Picking a channel with pixels[:,:,0] is like pulling out just the red sheet - one number per pixel instead of three.
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.
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).
| expression | means | value |
|---|---|---|
| rows (H) | how many rows of pixels | 3 |
| columns (W) | how many pixels per row | 3 |
| last 3 | numbers in each pixel [R,G,B] | 3 |
| pixels.shape | the whole shape | (3, 3, 3) |
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.
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'.
| row | red values printed |
|---|---|
| row 0 | [200 20 210] |
| row 1 | [ 10 190 30] |
| row 2 | [180 15 20] |
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.
| row | red values printed |
|---|---|
| row 0 | [200 20 210] |
| row 1 | [ 10 190 30] |
| row 2 | [180 15 20] |
Section
Section 2
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.
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.
| part | test | result |
|---|---|---|
| R > 150 | 200 > 150 | True |
| G < 80 | 30 < 80 | True |
| B < 80 | 30 < 80 | True |
| is_red | all three True | True |
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.
| part | test | result |
|---|---|---|
| R > 150 | 200 > 150 | True |
| G < 80 | 30 < 80 | True |
| B < 80 | 30 < 80 | True |
| is_red | all three True | True |
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'.
Section
Section 3
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.
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.
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.
| row | mask printed | why |
|---|---|---|
| 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 |
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.
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.
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 >.
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.
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.
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.
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.
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.
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.and, like you did for one pixel.; You compare the whole pixel to one number instead of picking a channel.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.
pixels[:,:,0] (R), [:,:,1] (G), [:,:,2] (B).().& (AND) - or | (OR) - to get the mask.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.
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:
pixels[:,:,0] (R), [:,:,1] (G), [:,:,2] (B).().& (AND) - or | (OR) - to get the mask.mask.sum(); locate with np.argwhere(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.
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.
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.
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?
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.
and would try to make (and it crashes). & keeps the answer per-pixel, so you get a whole grid of True/False, not one.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.
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?
and needs one True/False, but each side is a grid of many (correct)and is not a real Python keywordAnswer: 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.
and, not the comparison.and on a multi-element array.and is a real keyword; it just works on single True/False values, not on whole arrays. For arrays you need the & operator.Section
Section 4
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.
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().
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.
| row | True cells in row | running total |
|---|---|---|
| row 0 | 2 | 2 |
| row 1 | 1 | 3 |
| row 2 | 1 | 4 |
| red.sum() | - | prints 4 |
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.
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.
| coordinate | pixel there | red? |
|---|---|---|
| [0, 0] | [200, 30, 30] | True |
| [0, 2] | [210, 40, 50] | True |
| [1, 1] | [190, 20, 20] | True |
| [2, 0] | [180, 25, 25] | True |
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.
| coordinate | pixel there | red? |
|---|---|---|
| [0, 0] | [200, 30, 30] | True |
| [0, 2] | [210, 40, 50] | True |
| [1, 1] | [190, 20, 20] | True |
| [2, 0] | [180, 25, 25] | True |
Check
The mask has True at exactly four cells.
Check your understanding
Given the red mask above, what does print(red.sum()) show?
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.
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.
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?
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.
Section
Section 5 · preview
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.
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.
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.
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.
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.
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.
Check
Think about today's job versus the camp's real setup.
Check your understanding
What are OpenCV (cv2) and HSV in this lesson?
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.
Section
Section 6 · build it yourself
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 this | tool you'll use |
|---|---|---|
| 1 | build the pixel grid | np.array(...) |
| 2 | make the red mask | [:,:,0], &, parentheses |
| 3 | count the red cells | .sum() |
| 4 | find their coordinates | np.argwhere(...) |
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)| line | prints |
|---|---|
| print(grid.shape) | (3, 3, 3) |
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)| row | mask printed |
|---|---|
| row 0 | [ True False True] |
| row 1 | [False True False] |
| row 2 | [ True False False] |
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.
| row | mask printed |
|---|---|
| row 0 | [ True False True] |
| row 1 | [False True False] |
| row 2 | [ True False False] |
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())| line | prints |
|---|---|
| print(red.sum()) | 4 |
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())| line | prints |
|---|---|
| print(np.argwhere(red).tolist()) | [[0, 0], [0, 2], [1, 1], [2, 0]] |
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())| step | result |
|---|---|
| grid.shape | (3, 3, 3) |
| red.sum() | 4 |
| first print | Found 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.
Comparison
Comparison matrix
From Full program: refill the result column from what you know. The rest of the table is as it appeared.
| step | result |
|---|---|
| grid.shape | (3, 3, 3) |
| red.sum() | 4 |
| first print | Found 4 red cells |
| second print | [[0, 0], [0, 2], [1, 1], [2, 0]] |
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.
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.
Recap
[R, G, B], and an image as a grid of shape (H, W, 3).&.red.sum() and locate them with np.argwhere(red).cv2) and HSV as the camp's real tools - and why HSV is better for color.| you want to... | you write |
|---|---|
| pick the red channel | pixels[:,:,0] |
| build the red mask | (R>150) & (G<80) & (B<80) |
| count red pixels | red.sum() |
| find where they are | np.argwhere(red) |
Next time (Lesson 6): the rover starts making decisions from what it sees - turning the mask's count and locations into movement.
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.