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);
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));
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));