0

There are plenty of tutorials on this, but I think I'm missing something important about the HSV realm.

I'm looking specifically for the color (RGB) 98,199,166 an off blue color.

In my code I convert my image to HSV like so.

cvtColor(OriginalImage, HSVImage, COLOR_BGR2HSV);

I then find the RBG range of colors around that blue, so I choose 68, 83, 119 and 150, 164, 194 for my lower bound and upper bound respectively.

Using this website I convert the lowerbound and upperbound to HSV to get 222, 42.9, 46.7 and 221, 22.7, 76.1

Then in my C++ code I use the inrange function

inRange(HSVImage, Scalar(222, 42.9, 46.7), Scalar(221, 22.7, 76.1), ImgColorFound);

When I display this, it has not found the specific color I am looking for.

imshow("ObjectOfSpecificColor", ImgColorFound);

I'm not sure what i am missing or not understanding, but from the tutorials I've seen, it seems like this should find my color.

I'm also using the latest OpenCV (401 I believe)

EDIT

#include <opencv2/opencv.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include <Windows.h>
#include <iostream>
#include <tchar.h>
using namespace std;
using namespace cv;

Scalar BGR2HSV(uchar B, uchar G, uchar R)
{
    Mat hsv;
    Mat bgr(1, 1, CV_8UC3, Scalar(B, G, R));
    cvtColor(bgr, hsv, COLOR_BGR2HSV);
    return Scalar(hsv.data[0], hsv.data[1], hsv.data[2]);
}

int main(int argc, char **argv)
{
    Mat Img = imread("C:\\TestImage.png");
    Mat FindBlueArea;
    Mat HSVImage;
    cvtColor(Img, HSVImage, COLOR_BGR2HSV);
    inRange(HSVImage, BGR2HSV(119, 83, 68), BGR2HSV(194, 164, 150), FindBlueArea);

    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", Img);
    namedWindow("HSVImage", WINDOW_NORMAL);
    imshow("HSVImage", HSVImage);
    namedWindow("BlueArea", WINDOW_NORMAL);
    imshow("BlueArea", FindBlueArea);
    waitKey(5000);
}

In the actual program this is read from frame of an active window, but at any rate it still doesn't behave how I expect.

Image To Find blue part

The image is a little small, but I'm trying to find only the blue blob part, and leave all the small blue circles and everything else out.

Timmy
  • 543
  • 1
  • 7
  • 19
  • OpenCV uses different limits than that service; that service specifies H in [0, 360], and S and V in [0, 100]. Instead OpenCV specifies H in [0, 180] (dividing the H by 2 so that it fits in a `uint8` type), and S and V in [0, 255] (scaled to fit the whole range of available values). Use `cvtColor()` in OpenCV to find OpenCV's HSV representation of those colors you're interested in. – alkasm Feb 21 '19 at 20:47
  • To be clear though with the current values you're using you can just divide the H by 2, and multiply the other two by 255/100, convert those all to `uint8` and it will work. But I suggest using the approach in the linked SO questions instead in general. – alkasm Feb 21 '19 at 20:50
  • @AlexanderReynolds So based off that duplicate post I would just need to implement these lines `Mat bgr(1, 1, CV_8UC3, Scalar(B, G, R));` `cvtColor(bgr, hsv, COLOR_BGR2HSV);` `return Scalar(hsv.data[0], hsv.data[1], hsv.data[2]);` Where B,G,R are my BGR values I put in my post. I attempted this, but it doesn't seem to convert them (Doesn't show the color I want) – Timmy Feb 21 '19 at 22:10
  • Can you edit your post to show your implementation and a minimal, complete, and verifiable example of the problem? Maybe an example image showing the mask you should get and the mask you're getting instead? – alkasm Feb 21 '19 at 22:21
  • @AlexanderReynolds I've added some sample code per your request. Thank you. – Timmy Feb 21 '19 at 22:43
  • Okay, your problem is that the "lower bound" color actually has a higher hue value (111) than your "upper bound" color (110). You can see this in your original question as well. There's of course no hue values above 111 and below 110, so that's why you're not getting anything. First, you *can* just use `inRange()` with your BGR image and values. But another option is to actually find the HSV color you want directly, and just check for things a little above and a little below that. Alternatively, just make it so that you take the lowest of each scalar and the highest for the inputs to `inRange` – alkasm Feb 21 '19 at 22:57
  • Oh and the S value as well has the same 'problem'. I tried it with swapping the values so the lower bound has the min for H, S, V and the upper bound has the max for them, and it seems the mask is as it should be. – alkasm Feb 21 '19 at 23:05
  • @AlexanderReynolds Thank you, using the max/min of the values fixed it for me also on HSV. Now will it always be the case that I will be able to do this? Is this a problem with my code, or is this due to how openCV converts the colors? If you want to post an official response also, I'll gladly accept it as an answer. – Timmy Feb 22 '19 at 01:21
  • Since it's been marked as a duplicate, there can be no "answer" on this post, so no worries. No, it will not always be the case that you can do this, since Hue is mapped in 360 degrees and red is at both ends. You may want say all of the red values from 350 degrees to 10 degrees. This is not possible with `inRange()` in general---you'd have to do from 175 to 180 in hue, and OR that with another call with 0 to 5. Really I just wouldn't work with RGB at all in basic color filtering if at all possible, just find the color range you want directly in HSV. – alkasm Feb 22 '19 at 01:46

0 Answers0