0

I'm trying to filter images by the dominant color its appears in it. Thats is done, now I want to know if the obtained color is in range from a defined range of colors. In this particular case I want to know if the is inside orange range color.

I saw function 'inrange' but i don't think this case help me. By this I did next code:

Scalar ORANGE_MIN = Scalar(18, 40, 90);
Scalar ORANGE_MAX = Scalar(27, 255, 255);

bool in_range(Scalar color, Scalar orange_min, Scalar orange_max)
{
bool result = false;
if ((color.val[0] > orange_min.val[0] && color.val[0] < orange_max.val[0]) &&
    (color.val[1] > orange_min.val[1] && color.val[1] < orange_max.val[1]) &&
    (color.val[2] > orange_min.val[2] && color.val[2] < orange_max.val[2]))
{
    result = true;
}
return result;
}

Something its wrong because not filter like I expected. Can anyone help me? Thanks!

  • 1
    Since you are allowing any combination of RGB values in the intervals [18,27], [40,255] and [90, 255] lots of colors are actually possible. I guess you would need to check much more specifically. – Alexander Büse Apr 06 '16 at 07:59
  • 2
    _Something its wrong because not filter like I expected_ : How can we know what you expect ? – Jabberwocky Apr 06 '16 at 08:00
  • 3
    If you really want something that is roughly “orange”, consider switching to an [HSL or HSV color space](https://en.wikipedia.org/wiki/HSL_and_HSV). – mindriot Apr 06 '16 at 08:00
  • you might want to choose a different metric, you are allowing all values inside a box defined by two edges, i.e. you allow points in this color space that are far away from the line connecting those two edges. If this is mumbo jumbo for you, then just switch to a different color space as suggested by mindriot – 463035818_is_not_an_ai Apr 06 '16 at 08:06

2 Answers2

1

I'll follow a slightly different approach. Instead of defining the range intervals for all predefined colors, you can:

  1. Define your predefined colors. This would be the palette of your possible colors
  2. Given the color you want to check, compute the distance from all colors in your palette, and keep the nearest palette color.
  3. Check if the found palette color is near enough.

In this small sample I used BGR color space, since the euclidean distance (norm of the two colors) is well behaved.

You can use another color space, like HSV, but you need to find a suitable distance. You can't use just the H value, since you'll miss black / white / gray colors (as mentioned by @MSalters).

So, given for example your color almost orange 20, 130, 250, with an appropriate palette you'll get something like:

[20, 130, 250] is similar to orange
Distance with nearest color [0, 127, 255] is 20.8327

Code:

#include <opencv2/opencv.hpp>
#include <vector>
#include <map>
#include <string>
using namespace cv;
using namespace std;

// Needed to put Vec3b into a std::map
struct lessVec3b
{
    bool operator()(const Vec3b& lhs, const Vec3b& rhs) {
        return (lhs[0] != rhs[0]) ? (lhs[0] < rhs[0]) : ((lhs[1] != rhs[1]) ? (lhs[1] < rhs[1]) : (lhs[2] < rhs[2]));
    }
};


int main()
{
    // Define a set of predefined BGR colors
    map<Vec3b, string, lessVec3b> palette;
    palette[Vec3b(0, 0, 0)] = "black";
    palette[Vec3b(0, 0, 255)] = "red";
    palette[Vec3b(0, 255, 0)] = "green";
    palette[Vec3b(255, 0, 0)] = "blue";
    palette[Vec3b(0, 127, 255)] = "orange";

    // Your color
    Vec3b my_color(20, 130, 250); // almost orange

    // Look for nearest color in palette
    Vec3b nearest_color;
    string color_name;
    float min_distance = FLT_MAX;
    for (const auto& pal : palette)
    {
        float dist = norm(pal.first, my_color);
        if (dist < min_distance)
        {
            nearest_color = pal.first;
            color_name = pal.second;
            min_distance = dist;
        }
    }

    // Define a distance. This will behave like your ranges
    float th_distance = 1000.f;

    if (min_distance < th_distance)
    {
        cout << my_color << " is similar to " << color_name <<  endl;
    }
    else
    {
        cout << my_color << " is not in the palette" << endl;
    }
    cout << "Distance with nearest color " << nearest_color << " is " << min_distance << endl;

    return 0;
}
Miki
  • 40,887
  • 13
  • 123
  • 202
0

Convert ORANGE_MIN, ORANGE_MAX, and color to HSL (HSV) color models and check that the hue is within the required range. See, e.g., http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/ for the math.

UPDATE

Saturation and lightness should be checked against some ranges as well, see the comments below. Thanks MSalters for pointing this out.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • 1
    Except that you must check the other 2 variables to see if you're dealing with white, grey or black. – MSalters Apr 06 '16 at 08:36
  • You're right, forget about this. Likely to check lightness and saturation against some allowed ranges. – Daniel Langr Apr 06 '16 at 08:58
  • I don't know why is calculating the saturation value if then It's not used. What is the importance of that? – Jaime Menendez Llana Apr 06 '16 at 10:23
  • Saturation effectively means how much hue is applied within a given color. Saturation 0 turns colors into greyscale, independently of their hue. Similarly, lightness 0 and 1 results in black and white color, respectively, again independently of hue. That's why you likely want to limit saturation and lightness to some ranges (e.g., saturation from 0.25 to 1 and lightness from 0.25 to 0.75); particular values depend on your problem. – Daniel Langr Apr 06 '16 at 12:00