Image processing

# Image processing

In this project we will do a bit of image processing. You can find photos to experiment with by going to Flickr and searching for a useful keyword (for instance, search for KAIST to find photos of the KAIST campus). Or use photos from Facebook.

#### Sepia

Sepia is a color-toning technique that makes a photo look as if it was taken in the early twentieth century. Here is a comparison of black-and-white versus sepia versions of the same photo:

Write a function sepia that will convert a photo to sepia and returns a new image (the function does not modify the input image):

fun sepia(img: BufferedImage): BufferedImage {
// ...
}
For each pixel of the photo,
1. obtain the luminance using the formula
val v = (0.299 * red + 0.587 * green + 0.114 * blue).toInt()

2. set red, green, and blue all to v
3. if v is at most 62, increase red by a factor of 1.1 and decrease blue by a factor of 0.9
4. if v is between 63 and 191, increase red by a factor of 1.15 and decrease blue by a factor of 0.85
5. if v is at least 192, then increase red by a factor of 1.08, and decrease blue by a factor of 0.93. (If red becomes larger than 255, set it to 255)
6. set the pixel to the new red, green, blue values.

#### Posterize

Posterizing means to replace the colors in a photo with a rather small set of colors (maybe ten). To posterize a photo, you first make a list of allowed colors. Then you look at every pixel of the photo, and replace it by the color from the list that is most similar. Write a function posterize that performs the second step:

fun posterize(img: BufferedImage, colors: List<Int>): BufferedImage {
// ...
}
(Again, your function returns a new image and does not modify the input image.)

To do this, you will need to measure the distance between two colors $$(r_1, g_1, b_1)$$ and $$(r_2, g_2, b_2)$$. The natural way to do this is to measure the distance like the (squared) Euclidean distance of three-dimensional vectors:

$d = (r_1 - r_2)^2 + (g_1 - g_2)^{2} + (b_1 - b_2)^{2}$

Write a function to compute the distance between two colors!

To test your function, you'll need to create a list of colors. Test first with a fixed list of colors like this:

val colorList = listOf(0xff0000, 0x00ff00, 0x0000ff, 0x000000, 0xffffff)

The result should like somewhat like this:

Then try other ways to select a color list. One possibility is to simple use a few random colors, for instance using this code:

val random = java.util.Random()

fun randomColor(): Int = random.nextInt(0x1000000)

Write a function that generates $$k$$ random colors:

fun randomColors(k: Int): List<Int> {
// ...
}

Here is what I got using 10 random colors (of course it will look different every time you run it):

#### Selecting colors

Let's now consider the problem of selecting colors that are adapted to the photo you want to posterize. Here is a method for doing this ($$k$$ is the number of colors we want to choose):

1. Create a set $$S$$ of all the color values in the photo.
2. Pick $$k$$ random elements of $$S$$, and call them $$c_1, c_2, \ldots, c_k$$.
3. For each element $$e$$ of $$S$$, find the closest color in the set $$\{c_1,\ldots,c_k\}$$. Define sets $$S_1, S_2, \ldots, S_k$$ as all follows: $$S_i$$ contains all the elements $$e$$ in $$S$$ such that $$e$$ is closer to $$c_i$$ than to any $$c_j$$, for $$j \neq i$$.
4. For each $$i$$, replace $$c_i$$ by the average value of the colors in $$S_i$$.
5. Repeat from Step 3. You can end the loop when the colors $$\{c_1, c_2,\ldots, c_k\}$$ do not change in one iteration, or when you have reached a certain number of iterations (such as 10).

You can now use $$c_1, c_2, \ldots, c_k$$ to posterize the photo. Write a function

fun selectColors(img: BufferedImage, k: Int): List<Int> {
// ...
}

#### Printing photos

How do newspapers print photos? Ink is black, paper is white—there is no "gray ink". And still newspapers can print photos.

Can you create a version of a photo that only uses black and white pixels, and still creates the impression of gray levels?

One idea would be to represent each pixel of the photo using a small block of pixels. Depending on how many pixels in this block you turn black, you can achieve the impression of different gray levels.

Here is an example of how I implemented this.

 Image processing