☰ Contents Nested and 2D Structures

Index Loops with Rasters

Student: So what’s something we need Raster for?
Prof: One of the main things is manipulating images. For example, programs on my computer can crop images, or shrink or grow them, or change their colors.
Student: And it does that with Raster?
Prof: More or less. Enough so that we’ll learn something about both arrays and images. First I want to show you that we can load interesting images from files, so that we are transforming something more interesting than rectangles and squares.
Student: Oh that’s useful. So we can load real pictures, not just make shapes.
Prof: Exactly. So, there are a lot of things we can do with this image! For instance, we could ask what the color is at various spots. There is a method on Raster called colorAt that can tell us the color at a particular place. In a Raster, the top row is at index 0 (much like our theater picture). In this picture of the Salk Institute, what color do you think is at 0, 0?
Student: Well, there’s blue sky, so something with a large b field?
Prof: Let’s see! The top left is a little dark, so let’s also try something a little more inset into the picture where it gets lighter.
Prof: One thing we can do with rasters is calculate interesting image properties. For example, we can get the average color of an image.
Student: The average color?
Prof: Right, we can add up all the red, blue, and green channels independently, then divide by the number of pixels. A very blue picture should have a mostly-blue result, a very purple picture would show blue and red, and so on.
Student: OK, I guess that’s interesting.
Prof: It will be a good start for other raster methods. We are going to need something new and useful: an indexing for loop. We’ve seen for-each loops (what we just called a for loop before) that work over the elements of an array, but it turns out Raster only has colorAt, no way to use a for-each loop that goes element by element.
Student: So what does an indexing for loop look like?

Prof: The idea is that the loop variable tracks an index into an array or other structure (like a Raster).

for(int index = init; index < maxIndex; index += increment) { ... }

The name index is your choice, people use i and j or counter, things like that, as well. The value init is some starting point; 0 is common for starting at the beginning of an iteration. maxIndex is often the length of an array or structure, but some iterations might stop early. Then increment is how many indexes to “move ahead” by, which is often 1 but could be different values as well.

Enough talking, let’s see it in action. This will be an indexed, nested, running-sum loop that tracks a sum for each of red, blue, and green:

Prof: Whew, that was a lot. What questions do you have?
Student: Have we seen getRed and getBlue before?
Prof: No, I just know those are methods on Color that get back those components.
Student: Why is it colIndex, rowIndex and not the other way around?
Prof: That’s just the way Raster.colorAt is defined; it takes an “x” and then a “y” coordinate.
Student: And unlike picts, the 0 value for y is at the top?
Prof: That’s right, to match Java’s underlying image library more closely.
Student: So the Salk picture is very slightly more blue than anything else?
Prof: Yup.
Student: Could I have nested the loops the other way?
Prof: For this loop, yes. We’re only adding things up, so it doesn’t matter whether we go across each row, one row at a time, or down each column, one column at a time. Either way every pixel gets added in exactly once.
Student: OK! Indexing for loops and rasters. Cool stuff.

Practice

Here is a past builder, rewritten with an indexing for loop. The loop variable i does the job the separate index counter used to do. Pick each part of the loop header so that halfOff builds every sale price.
int[] halfOff(int[] pricesCents) {
int[] result = new int[pricesCents.length];
for(; ; ) {
result[i] = pricesCents[i] / 2;
}
return result;
}
int[] prices = { 500, 1250, 99 };
int[] half = halfOff(prices);
Write the body of the inner loop so that hasPureBlue returns true as soon as it finds a pixel that is exactly blue (red 0, green 0, blue 255), and false if there isn’t one anywhere. It’s a search, like rowHasSeats.
import java.awt.Color;
boolean hasPureBlue(Raster raster) {
for(int rowIndex = 0; rowIndex < raster.height(); rowIndex += 1) {
for(int colIndex = 0; colIndex < raster.width(); colIndex += 1) {

}
}
return false;
}
Color[][] lastBlue = { { Color.RED, Color.RED }, { Color.RED, new Color(0, 0, 255) } };
Color[][] noBlue = { { Color.RED, Color.WHITE }, { new Color(0, 0, 254), Color.RED } };
boolean salkHasBlue = hasPureBlue(ImageIO_.load("images/salk.png"));
boolean lastHasBlue = hasPureBlue(new Raster(lastBlue));
boolean noneHasBlue = hasPureBlue(new Raster(noBlue));