Writing code to access one pixel at a time, e.g. pixel at (0, 0), then the pixel at (1, 0), etc. is way too tedious and inefficient. We'd like to say something like "for exery pixel do this", and let the computer fiddle with the details of going through all the (x, y) values to look at each pixel once.
The very powerful "for loop" structure we'll learn here provides an this "for every pixel do this" feature. The loop takes a few lines of our code, and runs those lines again and again, once for each pixel in the image. As a first example, the code below sets the red value of very pixel in the image to 0:
|
The "for loop" structure is extremely powerful, running a few lines of your code again and again. The main feature for the for-loop, is that lines inside and outside the loop are very different. Here are the three main parts of the for-loop:
// 1. Before the loop -- the lines here run // once from top to bottom, in the usual way. image = newimpleImage("flowers.jpg"); print("Here we go"); for (pixel: image) { // 2. Inside the loop { ... } // the "body" lines here within the { ... } run again and again, // once for each pixel. The body lines are indented. pixel.setRed(0); } // 3. After the loop -- the lines here // run once from top to bottom, after the many runs // of the loop body. print(image); print("All done);
The loop begins with the syntax for (pixel: image) {
which says that we want to use a variable named "pixel" to look, in turn, at every pixel in the image. The curly brace section "{ ... }", also known as the "body" of the loop is the most important part. The loop structure runs the code in the body again and again .. once for each pixel in the image, which can easily be over 100,000 times. For each run, or "iteration" of the loop body, the pixel variable will point to the next pixel in the image, with its own values for pixel.getGreen() etc. Note how the strings "Here we go" and "All done" are just printed once -- only the lines inside the loop {...} body are run many times. The lines inside the loop is usually indented 2 or more spaces, to emphasize visually which lines are inside and outside the loop.
Loops are common fixture in many programming languages. This for (pixel: image) {
form is not actually standard part of Javascript, it's something I added for this course, although many other computer langauges have loops that look just like this (including Java, the language of the CS AP course).
|
To see how the for loop works, try adding and running run code above for each of the problems below (solutions available).
Example Problems | Solution |
---|---|
Temporarily change the code to load "flowers-small.jpg" which is much smaller (width 100, height 78, so how many pixels is that?)
Add the line print("Here we go"); before the loop, then moved inside the loop, then after the loop
to see the inside/outside loop differences. |
|
For every pixel, set the green value to 0. | |
How to show the "red channel" of the image? For every pixel, set the red and blue values to 0. | |
Back in black - set every pixel in the image to be perfectly black. |
Write code to set the blue value of every pixel to 200, giving the whole image a sort of blue light.
|
Write code to show the blue channel of the image (as shown at the top of this doc).
|
The brown problem. What combination of red, green, and blue values makes brown? Experiment to find values that work, and set every pixel in this image to use those values. Effectively, this writes over the original flowers image to create a uniform brown rectangle.
|
Many times, we have seen function calls where we pass in a value within the parenthesis. The value supplied to the function in this way is called an "argument" to the function, such as the 0 and 255 if this code snippet:
pixel = image.getPixel(0, 0); pixel.setRed(255);
In place of a plain number like 255, the code also supports expressions which compute a value to use. For example you could write something like this:
pixel.setRed(22 * 4);
When that line runs, it first computes 22 * 4, yielding 88. Then in effect it calls setRed(88), passing in the computed value. The "22 * 4" is called an "expression", a combination of values that the computer can evaluate to yield a single value. Any place where we have used a fixed number like 0 or 255 up to now, we could instead write an expression, letting the computer figure out a specific value when it runs that line.
We have not used them until now, but pixels support: pixel.getRed()
, pixel.getGreen()
, pixel.getBlue()
functions which retrieve the color value 0..255 from a pixel. Here is a code snippet which uses getRed() and setRed() to increase the red value of a pixel by 100:
red = pixel.getRed(); pixel.setRed(red + 100);
In fact, the "red" variable is not necessary, and the snippet can be reduced to a single line:
pixel.setRed(pixel.getRed() + 100);
The "pixel.getRed() + 100" is an expression, which is whatever the old red value was plus 100. This expression is evaluated first, resulting a number such as 150. Then in effect pixel.setRed(150); is called. The setRed() etc. functions automatically limit the value set to the range 0..255. If setRed() is called with a value a greater than 255, it just uses 255, and likewise if a value less than 0 is passed in, it just uses the value 0.
Before we could only express ideas like "set the red value to 200". Now we can express what new value we want in terms of the old value, like "set the red value to be 100 more than it was", or "double the red value".
|
Try adding and running run code above for each of the problems below (solutions available).
Example Problems | Solution |
---|---|
For every pixel, decrease the green value by 100. How does this change the flowers? | |
Similar to the above, divide all the green values by 2. | |
Multiply the red values by 2. | |
Divide all three color values by 2 or a larger value like 3 or 5. What happens to the whole image? | |
Multiply all three color values by 2. What happens to the flowers? |
I must say, I really like this code puzzle as a way of getting a computing principles. This is a puzzle. You are given an image of something famous. However the image has been messed up: the real data is in the red channel, however the red channel values have all been divided by 10, so they are too small by a factor of 10. The blue and green channels are just meaningless random values ("noise") added to obscure the real image. You must undo all these steps to recover the real image. (Solutions at end of page).
|
This is an image puzzle -- an image of something famous is hidden in the image "copper-puzzle.png". The real image is in the blue and green channels, however the blue and green values have all been divided by 20, so the values are very small. The red channel is just noise added on top to obscure things. Undo these obscuring factors to recover the original image.
|
It is useful in a few situations to compute the "average" value for a pixel across the red/green/blue values. So for example, if a pixel has red=120, green=80, blue=100 ... in some sense the "average" value for that pixel is 100. The average is in effect a measure of how light/dark the pixel is (0 being dark, 255 being light), ignoring the pixel's color. Here is code to compute the average value for a pixel and store it in an "avg" variable:
sum = pixel.getRed() + pixel.getGreen() + pixel.getBlue(); avg = sum / 3;
Using the above code, write code below to compute the average for each pixel, and then set the three red/green/blue values to be that average. What is the visual effect?
|
Discussion question: why does the "sum = ... " code need to be inside the loop? It seems like it could be left on the outside of the loop.
There is an image hidden in cold-puzzle.png -- modify the pixels to recover it.
|
Iron puzzle solution:
Copper puzzle solution:
Average solution:
(goal count 7)
This retrieves the saved code (saved by the Run/Save button)
and displays it below for copy/paste. Copy the code text,
fill in your name (and your parter's) and any README notes
for the grader. Paste it into a file named like "homework6.txt"
and submit that file. You can also use this feature to get your code
back if you mess it up (but have not clicked the Run/Save button).
Restores each code text area above to contain the saved code (you can preview the saved code with the Create Turn-In Text button above).