A common problem is to determine if an image is light on dark or dark on white, as certain algorithms require this knowledge. An easy approach to this with OpenCV is to take the following steps:

  1. Convert the image to grayscale
  2. Threshold the image to make it black and white
  3. Come up with a threshold to determine whether it is mostly light or mostly dark (I use 60%)
  4. Use the countNonZero function to count the number of pixels with no color value (these are black)
  5. Divide the number found in step 4 by the total number of pixels to obtain a ratio of dark to light
  6. Apply your threshold value you came up with in step 3

For a light example, I use the following image:

And for a dark image:

Now, to setup the program, beginning with the includes & namespace:

Next, we pull in the image and perform the pre processing steps described above:

When run against the light image, we get a ratio the following values:

Non-zero points: 306126 Total points: 443400  Ratio: 69%

When run against the dark image, we get these values:

Non-zero points: 28615 Total points: 50246 Ratio: 57%


I recently had a situation where it was necessary to take a given color, and then find the closest match to it within a given set. The idea was that when processing an image, numerous colors would be returned, but reproducing a true color across the entire image could be difficult, so the decision was made to map the color found to an approved list, so that the user would see a consistent experience.

The first issue that comes to mind is that doing this in RGB is not advisable, since RGB is best intended for displaying images, and does not offer any meaningful way to compare colors against each other.

However, when switching to the LAB colorspace, we are now able to plot the point values for the color in three dimensional space. The plot below provides a visual reference on how this colorspace appears:

Rendered by QuickLaTeX.com

With this, we can then plot the points of our default colors, and then measure the distance between the different points with the objective of finding the shortest distance between two points. The original Delta E equation is used to perform this calculation, though more recent changes have been designed to provide a more accurate value. The equation used in this example can be found below:

(1)   \begin{equation*}  \Delta E_{ab}^* = \sqrt{ (L^*_2-L^*_1)^2+(a^*_2-a^*_1)^2 + (b^*_2-b^*_1)^2 } \end{equation*}

We can now move on to code, where this problem is broken down into several pieces.

Our first action will be to setup the necessary includes as well as the OpenCV namespace:

Next, we will create a struct that contains matching vectors with color values for RGB and LAB colorspaces. The objective with this is that the calculation is performed against the LAB values, and once a value is found, the corresponding RGB value is returned. You could also choose to convert the LAB value to RGB, but the approach described here ensures the value you wish to receive is returned. Another benefit to this approach is that you may wish to have multiple LAB values that all return a single RGB value, so mapping would be preferred in that scenario as well.

In this example, I am sticking to a simple color palette of black, red, green, blue and white. Feel free to add more colors. You can easily verify results and obtain the corresponding LAB values over at colorizer.

Now that we know what colors we would like to map to, the next step is to create a function that can take a given color in the LAB colorspace and map it against the provided colors we see above. The evaluateColor function will be added just after the labColors() function within the ClosestColor struct.

This function runs through the loop of your labColors vector, calculating the JND (just noticeable difference) between the two colors, searching for the one with the smallest value. For example, if I wanted to process orange (255 165 0), I will get the following values for JND:

  • 111.2 – Black
  • 60.1 – Red
  • 112.1 – Green
  • 198.7 – Blue
  • 86.4 – White

Based on those values, red is the closest color to orange, and is what will be returned from the function.

Finally, we will setup our main function for pulling in the data, preparing it to process, and then returning a match.

The user will call this program by providing 3 numbers on a scale of 0-1 representing red, green and blue. Using the original example of orange, we get the following:

Which returns the following:

Based on the results here, orange most closely maps to 255,0,0 (red).

This graph provides a visual to put the location of the color orange into some perspective:

Rendered by QuickLaTeX.com