Race Condition Image Batch Assignment
Contents
Motivation
Race conditions can lead to difficult bugs to find and fix. We gain experience finding them and fixing them.
Background
Pixels
The word pixel stands for Picture Element. A pixel is a small part of a picture. Pixels are the little dots that make up computer screens. When you see a low-resolution picture that is grainy, the grains you’re seeing are the pixels.
Color Models
There are many different representations of Color. These representations are called color models.
Subtractive Color Models
Some color models are subtractive. These models are useful for printing. They are subtractive because they model the subtraction of light as each color absorbs light (before what remains to bounce off into your eyes). The most common of these models is CMYK which represents Cyan, Magenta, Yellow, blacK. If you have ever changed the ink in a color printer, you have encountered CMYK cartridges. Truth be told, you don’t need the black. Essentially all of the visible spectrum of light would be absorbed printing layers of cyan, magenta, and yellow. A lot of black tends to be printed, so it is more cost effective to add to add black into the model.
Additive Color Models
Some color models are additive. These models are useful for display devices. They are additive because they model the addition of light which is thrown at your eyes.
RGB
The most common of these models is RGB which repesents Red, Green, Blue. If you have ever plugged an old device into a TV with a component video cable, that is what the separate cables are carrying: analog signal for red, green, and blue.
HSV
We will use the HSV color model which represents Hue, Saturation, Value.
This model benefits from often being easier for humans to think about and will enable us to create some compelling image filters with minimal effort. Evidence for it being easier for humans to think about can be found in most defaults for color pickers, including the one which can be found by searching on Google for color picker.
Play around with the color picker for a while and watch the hue, saturation, and value change as you interact with it. Try to isolate the saturation by dragging left to right in the rectangle component of the color picker. Isolate changes to the value by dragging up and down. Control the hue with the slider below.
Hue
Hue is cyclical. Red is usually at 0.0 and the hue changes as you go around the ring… returning back to red at 1.0.
Saturation
Full saturation is a completely “vivid” form of the color. No saturation is “dull” gray.
Value
This component is somewhat uninspiringly named: “value”. It is called brightness or lightness in similar color models. Value can be somewhat confusing since one might expect a full (1.0) "brightness" value to always equate to white, which it does not.
Code To Investigate
HsvColor
class HsvColor
HsvImage
class HsvImage
- int width()
- int height()
- Optional<HsvColor> colorAtPixel(int x, int y)
- setColorAtPixel(int x, int y, HsvColor color)
- HsvColor[][] rows()
- HsvColor[] rowAt(int y)
SequentialImageFilter
class SequentialImageFilter |
---|
public class SequentialImageFilter implements ImageFilter { @Override public HsvImage apply(HsvImage src, PixelFilter pixelFilter) { HsvImage dst = new HsvImage(src.width(), src.height()); for (int y = 0; y < src.height(); ++y) { for (int x = 0; x < src.width(); ++x) { Optional<HsvColor> srcPixelOptional = src.colorAtPixel(x, y); if (srcPixelOptional.isPresent()) { HsvColor srcPixel = srcPixelOptional.get(); HsvColor dstPixel = pixelFilter.apply(srcPixel); dst.setColorAtPixel(x, y, dstPixel); } else { throw new Error(); } } } return dst; } } |
DesaturateAllPixelFilter
class DesaturateAllPixelFilter |
---|
public class DesaturateAllPixelFilter implements PixelFilter { @Override public HsvColor apply(HsvColor src) { return new HsvColor(src.hue(), 0.0, src.value()); } } |
DesaturateNonSkinTonePixelFilter
class DesaturateNonSkinTonePixelFilter |
---|
public class DesaturateNonSkinTonePixelFilter implements PixelFilter { private static boolean isInRange(double min, double x, double max) { return min < x && x < max; } private static boolean isSkin(HsvColor src) { return (src.hue() < 0.1 || src.hue() > 0.9) && isInRange(0.1, src.saturation(), 0.7); } @Override public HsvColor apply(HsvColor src) { return new HsvColor(src.hue(), isSkin(src) ? src.saturation() : 0.0, src.value()); } } |
Code To Debug
ImageBatch
applyToAll
class: | ParallelImageBatch.java | |
methods: | filterAllImages | |
package: | racecondition.image.exercise | |
source folder: | student/src/main/java |
method: ImmutableHsvImage[] filterAllImages(ImmutableHsvImage[] images, ImageFilter imageFilter, PixelFilter pixelFilter)
(parallel implementation required)
If one had a filter that could be applied to an image independently, it is reasonable to apply the filter to each image in an array of images in parallel. Unfortunately, the code you must debug has a data race. Fix the data race in ImageBatch's applyToAll method.
public class ParallelImageBatch implements ImageBatch {
@Override
public ImmutableHsvImage[] filterAllImages(ImmutableHsvImage[] images, ImageFilter imageFilter,
PixelFilter pixelFilter) throws InterruptedException, ExecutionException {
@SuppressWarnings("unchecked")
Future<Void>[] futures = new Future[images.length];
ImmutableHsvImage[] filteredImages = new ImmutableHsvImage[images.length];
int[] array = { 0 };
for (array[0] = 0; array[0] < images.length; ++array[0]) {
futures[array[0]] = void_fork(() -> {
filteredImages[array[0]] = imageFilter.apply(images[array[0]], pixelFilter);
});
}
join(futures);
return filteredImages;
}
}
Client
class: | ImageBatchApp.java | VIZ |
package: | racecondition.image.viz | |
source folder: | student/src/main/java |
Testing Your Solution
Correctness
class: | _ImageBatchTestSuite.java | |
package: | racecondition.image.exercise | |
source folder: | testing/src/test/java |
Pledge, Acknowledgments, Citations
file: | race-condition-image-batch-pledge-acknowledgments-citations.txt |
More info about the Honor Pledge