Image-8 If Logic 2
If With Color
- Suppose we want to change the red sign to be blue
- Idea: we can use if statements on colors instead of just x/y
- Idea: check if red value is large
- For each pixel, check if (red > 160)
- Code for that test:
(pixel.getRed() > 160)
- This is one strategy to test for red areas (needs improvement)
- The test will be true or false for each pixel
- Nothing magic about 160 -- adjust it to get the result we want
- 1. Adjust the 160 value. If we change it to 220 or 250, will the if-test select more or fewer pixels than at 160? What if we set it to 100? 50?
- With a >, the 160 is like a hurdle we raise or lower
- Restrictive: When adjusting a test, is the goal to make it more or less restrictive?
- 2. Flip try flipping < > - get opposite parts of the image
The Problem With (red > 160) Test
Trying to change the stop sign to be blue, the test (red > 160) gets both white and red areas. The problem with that approach is that the red value is high in two kinds of cases -- the red part of the sign we want, but also parts of the scene that are near white. Recall that pure white is (255, 255, 255), so in whitish areas, all three values are high, so our (red > 160) test gets those too -- the white letters inside the sign are a clear example of this problem. Next we'll improve the test so it can distinguish the red and white parts of the sign.
Suppose we have three pixels. Each pixel has the familiar red/green/blue values. Suppose these three values are graphed as bar graphs like this:
Q: What color is the strongest for each pixel?
Look at the graphs to figure which shade dominates for each pixel. Find the tallest bar relative to the others. The problem with our earlier strategy was that it just looked at absolute numbers, (red > 160), failing to take into account how red relates to the other two colors.
Recall Average of Pixel Color
Recall this code which, inside a loop, computes the average of the red, blue, and green values and stores that number in a variable "avg".
avg = (pixel.getRed() + pixel.getGreen() + pixel.getBlue())/3;
Color Bars With Average
Here are the three pixels again, but now the avg value is show as a line drawn across the bars.
The avg gives us a handy way to tell if a color is high relative to the others: A pixel is reddish if the red value is over the avg.
So here the first pixel is reddish and the other two are not. Or in terms of code, the test will look like
(pixel.getRed() > avg) -- this will add the "relative to the other colors" quality that the previous test was missing.
Color Avg Test Example - Stop
- Improve the detect-red test
(pixel.getRed() > avg * 1.1)
- This works very well, picking out pixels with red cast
* 1.1adjustment factor, more/less restrictive to get look we want
- e.g. * 1.7 makes test more restrictive
- e.g. * 0.9 makes test less restrictive
- Here 1.4 looks pretty good
- Then try the flip experiment back and forth - awesome!
This is a better way to select the red parts of the sign. For each pixel, first compute the average value for that pixel. Then compare the red value to the average to decide if the pixel is reddish. Rather than checking the literal value of the red (e.g. 160), this checks if the red is relatively large compared to the other two colors .. does the pixel lean towards red. If the test is
(pixel.getRed() > avg) we get all the areas that have every a tiny red cast, which is not restrictive enough. The fix is to multiply the avg by some factor to make it more restrictive, like this:
(pixel.getRed() > avg * 1.1). The specific value, 1.1, can be tuned by trying different values, until we get the look we want. Try the values: 0.9, 1.2, 1.4, 2, 2.5. The larger the value, the higher the bar is set to detect red pixels. By adjusting the * factor, we can zero in on the look we want. Here I think 1.4 looks pretty good.
Color Avg Test You Try It - Curb
Suppose you are visiting Stanford and you park your car here, and get a parking ticket. Philosophically, they say that you are better off taking in events as they have actually happened. Nonetheless, here we'll try to fix history in code.
Challenge: write code to detect the red curb, tuning the 1.1 factor to look the best. Then (a) change the curb to medium gray red=120 green=120 blue=120. (b) change just the curb to be grayscale, which will look more realistic. Rather than changing the whole image to grayscale, we change just the red areas.
Solution code (a):
- Tune the avg * factor to detect the red areas
- Setting to solid gray (120, 120, 120) looks a little crude
- Note that we put gray on the red plants to the right
- Our strategy selects by color, so necessarily get the red plants too
- (b) Rather than (120, 120, 120), change the red pixels to avg, making graysscale
- This looks much better, using the dark/light of the real curb
(pixel.getRed() > avg * 1.5)pattern to detect pixel by color
- The body-code can do anything we wish to the detected pixel
Preview of next time: green screen and blue screen