How to determine if a color is on range

2019-03-05 11:20发布

问题:

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:

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;
}


回答2:

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.



标签: c++ opencv bgr