Adjust color levels with a color matrix in Android
Using a color matrix to correct levels in images
Recently we have been developing the WytePad Scan App. You can use it to take pictures of WytePads and it will automatically crop and transform the picture so that looks right and is easy to use in other applications such as the Marvel App. While mobile cameras are rapidly getting better they are still not great at taking pictures of black and white surfaces and preserving the right white balance and exposure. Our WytePads are mostly white with doodles on, so we wanted to find a way to automatically correct potentially bad images.
One simple way to correct exposure issues in images is to adjust the image levels. This is quickly done in Photoshop by using a “Level adjustment layer”. There you can see the histogram of the image and transform it by moving its lower and upper bounds. When you click confirm, the histogram is transformed so the selected upper and lower bounds become the start and end of the histogram. Read more about levels on Cambridge in colour.
We are using BoofCV for image recognition in the WytePad app, so we started by using the BoofCV class
EnhanceImageOps for correcting the levels. However the process of transforming a histogram is not very well documented and it took multiple seconds to correct the color in a bitmap, which is not a great user experience.
In an attempt to optimize the speed we tried using RenderScript. To our luck there are some examples that demonstrates how to change levels in RenderScript like this blog post. This works quite well, however at least one allocation with the same size as the image is needed during processing which is taxing on the memory usage. As we do not want to split images into parts and would like as large an image as possible to be handled, we needed another solution.
The color matrix documentation describes how a color matrix can be used to transform the value of each pixel in an image. It can then be converted into a color filter and applied to a
Paint object or an
The process of applying a color matrix is hardware accelerated and highly optimized. Our experience was that this was way faster than using RenderScript.
As an example we described here how we are using the color matrix to change levels on a picture in the WytePad Scan App:
When used, the ColorCorrectionHelper takes an interval as a parameter. This interval describes the index of the new lower and upper bounds of the histogram. In a 8 bit bitmap as the one we use, the histogram has the length = 255. Each row in the matrix describes how one color should be transformed. To adjust the levels we create a color matrix that scales and offsets the value of each color.
The static method below describes how we find the histogram and retrieve the lower and upper bounds that should be used for the level correction. If you are processing large bitmaps it is advisable to first resize the bitmap before passing it to this method.
ImageStatistics.histogram are part of the BoofCV library we use.
The following short snippet shows how we apply the
ColorFilter to a Paint object that is then used to draw a Bitmap onto a canvas.
The resulting color level correction can be seen in the image below - original to the left and the greatly improved result to the right.