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!
I'll follow a slightly different approach. Instead of defining the range intervals for all predefined colors, you can:
- Define your predefined colors. This would be the palette of your possible colors
- Given the color you want to check, compute the distance from all colors in your palette, and keep the nearest palette color.
- 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;
}
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.