BlogRuby on RailsSoftware Development

How to detect colour brightness in Ruby

By April 27, 2018 No Comments

The first attempt

Working on a project where users can customise background colour for their profile, I needed a way to test if a background colour is light or dark in order to choose an appropriate text colour – black on light colours and white on dark ones. My search for a solution began with me calculating the greyscale of colour, but any algorithm I tried produced mixed results that didn’t satisfy my needs.

Then I changed my approach and started to consider RGB colours as points in 3D space where r, g, n are the axes. In this case, black is a point represented by the coordinate (0, 0, 0). A colour is darker the closer it is to this “black” point. So, calculating the brightness of the colour became a question of calculating distance in 3D space, which could be answered with the formula:

sqrt(dx^2+dy^2+dz^2)

or in our case simply,

sqrt(x^2+y^2+z^2)

because black is (0, 0, 0).

Still not right

This solution, however, was not perfect. One of the problems that arose was related to the fact that red, green and blue have different wavelengths and consequently they have three different contributions in the formation of the final colour. The colour red has the longest wavelength of the three colours, while green has a shorter wavelength than red. Not only this, but green is also the colour that produces a more soothing effect on the eyes. This means we have to decrease the contribution of the red colour and increase the contribution of the green colour. In other words, we needed to find a way to adjust colour weight.

The final solution

There is no unique way to choose colour weight, so we decided to use the same algorithm for converting RGB to YIQ colour space used in standard colour TV and video systems in North America. Weights for red, green and blue are 0.299, 0.587 and 0.114. Knowing how to choose the right weight for each colour component required knowledge of photometry, however, this is beyond the scope of this blog.

Combining the colour weights and the distance formula presented earlier, we came up with a final solution. Below is the solution we implemented in ruby:

Because brightness can be considered as a grayscale, we can use the same formula for converting coloured images into black-and-white. We have to replace each r, g, b component in every pixel with pixel brightness. Below is a ruby implementation and what we got as a result.

For reading image from file and saving to file solution using ImageMagick image processing library (check it out here) and RMagick gem (check it out here)