After reading these tutorials (this & this), I can figure out how to extract any color range including Red, Green, Blue and Yellow, using the Hue value of an HSV image or OpenCV Mat. However this value doesn't have the ability to describe the Gray color that I need to select. So is there any way to extract the Gray pixels of an image using OpenCV?
In HSV/HSL colourspace, the grey pixels are characterised by having Saturation of zero or very close to zero, so that test will discriminate black through grey to white pixels. Then the Value/Lightness will tell you how far along the scale from black to white they actually are, low Lightness/Value is dark grey whereas high Lightness/Value means light grey.
In RGB colourspace, grey pixels are characterised by having all three colour components equal, i.e. R=G=B.
After several notes, and knowing that:
To get have a white-to-dark-gray color out of an HSV image channels you should have the following values together Hue 0 to 360, Sat 0 to 10 & Val 50 to 100 (Check this page to test your own color range).
The OpenCV inRange function doesn't support handling HSV color combinations like this.
I decided to apply the range on each channel alone then getting the intersection of the 3 masks into a final mask that will represent my target white-to-dark-gray color.
Note that in OpenCV H has values from 0 to 180, S and V from 0 to 255. so you need to map your ranges into these limits.
Here is my code:
Mat hueMask1 = new Mat();
Mat satMask2 = new Mat();
Mat valMask3 = new Mat();
Mat white2GrayMask = new Mat();
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2HSV);
List<Mat> channels_HSV = new ArrayList<>();
Core.split(image,channels_HSV);
Core.inRange(channels_HSV.get(0),new Scalar(0),new Scalar(180),hueMask1);
Core.inRange(channels_HSV.get(1),new Scalar(0),new Scalar(20),satMask2);
Core.inRange(channels_HSV.get(2),new Scalar(70),new Scalar(255),valMask3);
Core.bitwise_and(hueMask1,satMask2,white2GrayMask);
Core.bitwise_and(white2GrayMask,valMask3,white2GrayMask);
Related
I have following imaged, preprocessed with certain OpenCV functions:
Now I'm trying to get the number of bricks of each color (in this case 1 for every color). These are all colors I have.
My plan was to loop through all colors, use the OpenCV function "inRange" and get the area size. My problem is to recognize the different colors. I tried the red color and struggled on finding a good range. After asking google I understood, that I have to use HSV colors, to get a good range with the hue value.
So I wrote this code (in Java for some reason):
Mat img; //img is given from previos code
Mat hsv = img.clone();
Imgproc.cvtColor(img, hsv, Imgproc.COLOR_BGR2HSV);
After this I expected to have an image, with which I can get the different colors. In fact I got this:
In this image the colors are almost destroyed...red and light green look the same and other colors are completely different too.
Now my question:
Do I something wrong? Is it possible to distinguish the Lego brick colors? What can I do to achieve this? I'm using OpenCV 3.1.0 for Java.
Thanks alot,
Dennis
I found my mistake and I'm not proud of it. It was a simple typo. With fixing this and the help of #Miki I'm able to find my colors.
My mistake:
Mat img; //img is given from previos code
Mat hsv = img.clone();
Imgproc.cvtColor(img, hsv, Imgproc.COLOR_BGR2HSV);
Core.inRange(img, lowerBlue, upperBlue, hsv); //img
instead of
Mat img; //img is given from previos code
Mat hsv = img.clone();
Imgproc.cvtColor(img, hsv, Imgproc.COLOR_BGR2HSV);
Core.inRange(hsv, lowerBlue, upperBlue, hsv); //hsv
In fact...I translated the image to HSV and took the RGB picture for inRange().
Thanks
I'm working an image analyzation project that checks the rgb values at set locations in a host of images, and need to be able to know if a certain area is green or blue. Originally I thought I could do this by testing if g>b in the rgb, but I've come to realize that often there can be more blue than green in a green image, due to the mixture with red. How can I tell- possibly a formula or an algorithm, what a color visibly appears to be based on the rgb?
You can convert RGB values to HSB values using the Color classes RGBtoHSB method. The resulting hue value falls between 0-1, with the hue value of green (0,255,0) at 0.33 and blue (0,0,255) at 0.66
float[] hsb = Color.RGBtoHSB(0, 255, 0, null);//green
System.out.println(hsb[0]);
hsb = Color.RGBtoHSB(0, 0, 255, null);//blue
System.out.println(hsb[0]);
From this you could create a metric for hue values 'closer' to green, for instance any hue value < 0.5 is more green than blue.
Below is an image depicting how the colors change in this color space, with hue on the X axis (note in this picture hue varies from 0-360 degrees, whereas the RGBtoHSB returns values 0-1)
Would it be possible to use over 255 of red, green, and blue for colors in java? If so, how would those colors be used? I was wondering because I am making a program that produces rectangles with random colors, but most of the colors are similar. Is there any difference with using colors with values over 255 and colors with values under 255? Jpeg image formats use color values over 255 rgb, so would java be able to too? If I used a jpeg image, would the color still retain it's over 255 rgb color value?
This is my code for generating colors and drawing the shape to the screen:
Random r = new Random();
for(int i = 0;i<=shapes.size()-1;i++){
Shape s = shapes.get(i);
int red = r.nextInt(256);
int green = r.nextInt(256);
int blue = r.nextInt(256);
this.colors.add(new Color(red, green, blue));
//Draw the rectangle
g.setPaint(this.colors.get(i));
g.fill(s);
//Draw the outline
g.setPaint(Color.black);
g.draw(s);
}
I added the shape corresponding to the color in previous code.
"shapes" is my arraylist of shapes. "colors" is my arraylist of colors corresponding to each shape.
It depends on the encoding of color you use, but when it comes down to intensity no. 24-bit means that each color is communicated by - as it says - sending 24 bits: a zero or one. 24 means 3 (the number of color channels) times 8. Thus 8 bit or a byte per channel (red, green and blue). You can only represent 256 possibilities with 8 bit since it is 2^8=2*2*2*2*2*2*2*2=256. So no. For 24-bit colors. 255 is thus simply defined as the maximum intensity of red/green/blue a monitor renders. If you use default Java packages, I think they will (almost) always use 24 bit colors.
Now if you look into image formats, you will see some formats support higher bit rates. This only means that you define colors more precise. In other words if you would use a 48-bit color representation, each color channel will have 16 bit. In that case 65 535 will be the maximum value, but that's (more or less) equivalent to the 255 for a 24-bit color.
Most monitors only support 24-bit colors. More bits are in general only used during rendering (to perform computations on an image). For instance if you implement a raytracer, you must add all kinds of origins of light together. Or if you implement a Window environment where different windows can have different transparency level, you could calculate intermediate results with higher precision to increase the quality of the result.
Some camera's capture color more precisely. This can be useful because industrial printers sometimes make their own ink and one can thus make colors more accurately. That's one of the reasons magazine pictures are typically better than the pictures one prints at home.
A final note is that some renderers or (computer graphics) camera's even use more color channels. A friend implemented a raytracer that rendered rainbows. In order to do that, he implemented a definition of color with seven color channels. You don't need them to display the image on the screen, but you need them to do the calculations on how light interacts with water.
EDIT
About your code:
The problem lies in your code:
The most important problem is probably that:
Random r = new Random();
is called inside the loop. This is a bit inefficient.
Next, it seems that you have precoded the list of colors. In that case, no random generator will alter the colors. Mind that .add simply appends your new Color to the end of the list.
Next you call:
g.setPaint(Color.black);
g.draw(s);
g.setPaint(this.colors.get(i));
g.fill(s);
So you first paint the shape in black and then repaint it in the color you've picked. Why? You can replace it by:
g.setPaint(this.colors.get(i));
g.fill(s);
g.setPaint(Color.black);
g.draw(s);
Finally you can make the construction of a color more efficient by calling:
Color c = new Color(r.nextInt(16777216));
So the resulting program will be something like:
Random r = new Random();
for(int i = 0;i<=shapes.size()-1;i++){
Shape s = shapes.get(i);
Color c = new Color(r.nextInt(16777216));
g.setPaint(c);
g.fill(s);
g.setPaint(Color.black);
g.draw(s);
}
I have an Image with some rectangles inside it .. what i need is to crop only rectangles that have a red border and white background using (Java) or JavaCV.
for example i have an car image with license plate .. each letter at the license plate have red bordered rectangle around it and a white background.
what i am looking for is to crop each letter in a single image .. letters are identified by red bordered rectangle around each one and a white background.
Any suggestions?
Thanks
Change color space to HSV
IplImage* imgHSV = cvCreateImage(cvGetSize(img), 8, 3);
cvCvtColor(img, imgHSV, CV_BGR2HSV);
Get only hue channel:
cvSplit( imgHSV, h_plane, s_plane, v_plane, 0 );
Do the thresholding to find red color:
cvInRangeS(h_plane, cvScalar(x, x, x), cvScalar(x, x, x), imgThreshed);
x - range of red in HSV color model.
After this you will have white and black image, where white color is a red color on your original image (they should be of rectangle shape, as you said).
Then use cvFindContours function.
int contoursCont = cvFindContours( imgThreshed, storage,&contours,sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0));
To bound box (rectangle) use (for every contour):
CvBox2D box = cvMinAreaRect2( #current_contour#,
CvMemStorage* storage CV_DEFAULT(NULL))
To check the color of background calculate its histogram and check if values of bins are only 255 and 0 (they are values of white and black colors).
Hope, that will be useful!
You might try this:
Find a group of red pixels close to one another
Find all red pixels connected to these, bucket-fill style
Compute the bounding box of all the pixels found
Perhaps check whether the red pixels are all close to the rim of the bounding box
Check whether the interior of the box is mostly white
This should work as long as your boxes are not interrupted and do not overlap.
I want to ignore all other colours. I just want to count the colours between white and yellow(Bright yellow, Light yellow.. all the way to white). and then give a rating of how yellow a certain image is. is that possible?
I have been playing with Bitmap.getPixel() but I can't figure out how to ignore other colours.
In this example, image 1 would be the one select because it has more colour between bright yellow and white.
How can I detect yellowish colours only?
Well I would focus on the HUE of the pixel. Using this code:
// get HSV values for the pixel's colour
float[] pixelColorHsv = new float[3];
Color.colorToHSV(Bitmap.getPixel(x,y), pixelColorHsv);
What is Yellow might be up to you but a range could be between 72 and 49 (you can play with this tool to get an idea) then you can quantify where it is in this range or how high or low the brightness and saturation are.
The Bitmap.getPixel(int x, int y) method returns a Color object with the RGB values for that pixel. Yellow is a combination of red and green, so a straight yellow RGB triple would be (255, 255, 0), right? If you get darker, you lower both of the red and green values. If you get brighter, you bring up the blue value. So basically, you need to find a way to detect how "close" any given pixel's RGB value comes to (255, 255, 0) and then accumulate those closeness values for the entire image. Do the same for the second image, then compare the 2 results.