Work with your assigned group partner from the GPU mini-lab.
In this lab, you will implement parallel patterns on the GPU: map, reduce (optional), and stencil. Your program will load an image, reduce the number of colors in the image to a good representative subset (more on this later), and then add outlines between regions of solid colors. The effect of this transformation, which you can see below, is similar to that of the “posterize” filter in some popular image editing programs.
The three images above show the original, a version with a reduced color palette, and finally a version with outlines. The difference between the first two images is somewhat subtle, but the example below may be clearer.
At the core of this process is an algorithm known as k-means clustering. This algorithm takes a set of values (color pixels in this case) and assigns them to a fixed number of clusters. We will discuss k-means clustering in class, but here is a rough outline of the algorithm:
The above images were produced by assigning pixels to one of 16 clusters using k-means. Then, the program replaces the color at each pixel with its cluster center’s color. Next, the program uses k-means to assign colors to just three clusters, but does not change the color of each pixel. Using this small set of clusters, the program then looks for pixels at the boundary of two clusters and changes them to black. A boundary is a pixel with at least one neighbor in a different cluster, including diagonal neighbors.
The starter code for this lab is available on GitHub at grinnell-cs/213-cartoonify. Use the usual fork & collaborate model for this lab.
Use the following commands to run the starter code:
$ make
... make output will appear here ...
$ ./cartoonify images/airplane.bmp
The provided code loads a bitmap image (two compatible images are provided), then removes all of the blue color from the image.
The output of the program is written to output.bmp in the current directory.
While this program is written in a .cu file, it does not use the GPU to perform any tasks. You are responsible for copying the image to the GPU, writing kernels to run kmeans clustering, and copying image data back from the GPU. You should refer back to our GPU mini-lab for examples of how to allocate memory on the GPU, how to move data back and forth from the CPU to the GPU, and how to invoke kernels.
While some newer GPUs do support kernels invoking other kernels, ours do not; The repeated process of reassigning points clusters and computing new cluster centers with k-means will require multiple kernel calls from the CPU. While you are calling the kernel multiple times, you do not need to transfer the image data back and forth each time.
There are many pieces of this program that can be run in parallel on the GPU. You are required to perform these two tasks on the GPU:
For an added challenge, use the reduce pattern on the GPU to compute new cluster centers. You can also use a map pattern to swap the original image colors with cluster center colors.
While this filter works fairly well, it is far from the best method for a general image processing technique known as image segmentation. Image segmentation is the task of breaking an image into its distinct visible objects. Many other techniques for image segmentation consider the image distance between pixels before deciding whether or not to assign them to the same group. You can make changes to the distance computation for k-means clustering to improve the output of this tool, or you could switch to a different clustering technique like the parallel region labeling example we ran on the board with a stencil computation.