Why isn't inRange function detecting blue color when I have given it the entire possible Hue range for the blue color? - android

On the website colorizer.org, they have an HSV range of H=0-360, S=0-100, V=0-100. We are also aware that the HSV range in OpenCV is H=0-180, S=0-255, V=0-255.
I wanted to select a range for any shade of (what we perceive as) blue color, so I looked at colorizer.org, and saw that blue Hue ranges roughly from 170 to 270. So I scaled this Hue range to OpenCV by dividing by 2, which gives 85-135.
Now, I took the following screenshot of color [H=216, S=96, V=67] from the preview at the website
Then I run the app on my phone and captured the following camera frame from the laptop screen. I understand that the HSV channel values will differ from those in website to some extent because there are other conditions like additional light (V in HSV) in the room when I captured the camera frame, etc.
Then I converted this Mat to HSV color space by Imgproc.cvtColor(rgbaFrame, hsvImage, Imgproc.COLOR_RGB2HSV_FULL);, which resulted in the following image.
Then I called the inRange function:
Core.inRange(hsvImage, new Scalar(85, 50, 40), new Scalar(135, 255, 255), maskedImage);
which resulted in the following maskedImage.
The question is that why isn't it detecting the blue color when I have included all the Hue Range possible for blue color really?
IMPORTANT: Except the first original image, all the images were stored in sdcard using Highgui.imwrite function, so that I could move them to my computer in order to upload them on Stackoverflow. You must have noticed that the blue color in the first original screenshot is converted to red color in the second image. The reason is that the frame captured by the camera (that is the photo/frame of the first screenshot captured by the mobile phone camera) is an RGBA image. But OpenCV converts all images to BRG by default when it saves them to sdcard of something. So be assured that the original image is RGBA, and it is only converted to BGR internally by OpenCV for saving into sdcard. That's why red appears blue.

using this code does work for me (C++):
cv::Mat input = cv::imread("../inputData/HSV_RGB.jpg");
//assuming your image to be in RGB format after loading:
cv::Mat hsv;
cv::cvtColor(input,hsv,CV_RGB2HSV);
// hue range:
cv::Mat mask;
inRange(hsv, cv::Scalar(85, 50, 40), cv::Scalar(135, 255, 255), mask);
cv::imshow("blue mask", mask);
I used this input image (saved and loaded in BGR format although it in fact is a RGB image, that's why we have to use RGB2HSV instead of BGR2HSV):
resulting in this mask:
The difference to your code is that I used CV_RGB2HSV instead of CV_RGB2HSV_FULL. Flag CV_RGB2HSV_FULL uses the whole byte to store the hue values, so range 0 .. 360 degrees will be scaled to 0 .. 255 instead of 0 .. 180 as in CV_RGB2HSV
I could verify this by using this part of the code:
// use _FULL flag:
cv::cvtColor(input,hsv,CV_RGB2HSV_FULL);
// but scale the hue values accordingly:
double hueScale = 2.0/1.41176470588;
cv::Mat mask;
// scale hue values:
inRange(hsv, cv::Scalar(hueScale*85, 50, 40), cv::Scalar(hueScale*135, 255, 255), mask);
giving this result:
For anyone who wants to test with the "right" image:
Here's the input converted to BGR: If you want to use that directly you have to switch conversion from RGB2HSV to BGR2HSV. But I thought it would be better to show the BGR version of the input, too...

Related

OpenCV Android Green Color Detection

currently I'm making an app where user will detect green colors. I use this photo for testing:
My problem is that I can not detect any green pixel. Before I worked with blue color and everything worked fine. Now I can't detect anything though I tried different combinations of RGB. I wanted to know whether it's problem with green or my detection range, so I made an image in paint using (0, 255, 0) and it worked. Why it can't see this circle then? I use this code for detection:
Core.inRange(hsv_image, new Scalar([I change this value]), new Scalar(60, 255, 255), ultimate_blue);
It could have been that I set wrong Range, but I use Photoshop to get color of one of green pixels and convert RGB value of it into HSV. Yet it doesn't work. It don't detect even pixel that I've sampled. What's wrong? Thanks in advance.
Using Miki's answer:
Green color is HSV space has H = 120 and it's in range [0, 360].
OpenCV halves the H values to fit the range [0,255], so H value instead of being in range [0, 360], is in range [0, 180].
S and V are still in range [0, 255].
As a consequence, the value of H for green is 60 = 120 / 2.
You upper and lower bound should be:
// sensitivity is a int, typically set to 15 - 20
[60 - sensitivity, 100, 100]
[60 + sensitivity, 255, 255]
UPDATE
Since your image is quite dark, you need to use a lower bound for V. With these values:
sensitivity = 15;
[60 - sensitivity, 100, 50] // lower bound
[60 + sensitivity, 255, 255] // upper bound
the resulting mask would be like:
You can refer to this answer for the details.

How to blur some portion of Image in Android?

I am working in a project where I have to show some portion of the image clear and make rest part of the image blur. The blur should be managed by slider. Means it can be increase or decrease. The final result image should look alike below.
During my research for this I found below links useful
http://blog.neteril.org/blog/2013/08/12/blurring-images-on-android/
https://github.com/kikoso/android-stackblur
http://blog.neteril.org/blog/2013/08/12/blurring-images-on-android/
But the issue in above links is they all make complete image blur. Not some part of image.
Kindly suggest some solution to achieve this. Thanks in advance.
do a masked blur few times ....
create mask
0 means blur (black) and >=1 means not blur (white). Init this part by big enough value for example w=100 pixels
create masked blur function
just a common convolution with some matrix like
0.0 0.1 0.0
0.1 0.6 0.1
0.0 0.1 0.0
but do it only for target pixels where mask is ==0 after image is blurred blur also the mask. This should enlarge the white area a bit (by pixel per iteration but losing magnitude on borders that is why w>1).
loop bullet #2 N times
N determines blur/non-blur gradient depth the w is only to assure that burred mask will grow... Each time the blur mask will increase its white part
That should do the trick, You can also use dilatation of the mask instead of blurring it.
[edit1] implementation
Have played with this a bit today and found out that the mask is not growing enough with smooth so I change the algo a bit (here mine code C++):
picture pic0,pic1,pic2;
// pic0 - source
// pic1 - output
// pic2 - mask
int x0=400,y0=330,r0=100,dr=200;
// x0,y0,r0 - masked area
// dr - blur gradient size
int i,r;
// init output as sourceimage
pic1=pic0;
// init mask (size of source image) with gradient circles
pic2.resize(pic0.xs,pic0.ys);
pic2.clear(0);
for (i=1;i<=255;i++)
{
r=r0+dr-((dr*i)>>8);
pic2.bmp->Canvas->Brush->Color=TColor(i<<16); // shifted because GDI has inverse channel layout then direct pixel access
pic2.bmp->Canvas->Pen ->Color=TColor(i<<16);
pic2.bmp->Canvas->Ellipse(x0-r,y0-r,x0+r,y0+r);
}
for (i=1;i<255;i+=10) pic1.rgb_smooth_masked(pic2,i);
here the smooth function:
//---------------------------------------------------------------------------
void picture::rgb_smooth_masked(const picture &mask,DWORD treshold)
{
int i,x,y;
color *q0,*q1,*m0,c0,c1,c2;
if ((xs<2)||(ys<2)) return;
for (y=0;y<ys-1;y++)
{
q0=p[y ]; m0=mask.p[y];
q1=p[y+1];
for (x=0;x<xs-1;x++)
if (m0[x].dd<treshold)
{
c0=q0[x];
c1=q0[x+1];
c2=q1[x];
for (i=0;i<4;i++)
q0[x].db[i]=DWORD((DWORD(c0.db[i])+DWORD(c0.db[i])+DWORD(c1.db[i])+DWORD(c2.db[i]))>>2);
}
}
}
//---------------------------------------------------------------------------
create gradient mask with circles increasing in color from 1 to 255
rest is black the gradient width is dr and determine the smoothing sharpness.
create smooth masked with mask and threshold
smooth all pixels where mask pixel is < threshold. See the function rgb_smooth_masked. It uses 2x2 convolution matrix
0.50,0.25
0.25,0.00
loop threshold from 1 to 255 by some step
the step determines the image blur strength.
And finally here some visual results this is source image I taken with my camera:
And here the output on the left and mask on the right:
the blue color means values < 256 (B is lowest 8 bits of color)
I use my own picture class for images so some members are:
xs,ys size of image in pixels
p[y][x].dd is pixel at (x,y) position as 32 bit integer type
clear(color) - clears entire image
resize(xs,ys) - resizes image to new resolution

Gradiant and angle visualisation of an image with HOGDescriptor (OpenCV for Android)

I try to visualize the gradiants and angles of an image which computed by the HOGDescriptor of the OpenCV Lib for Android. At the begin i have an 3 channel image Mat() with 8 bit unsigned int (CV_8UC3). The result of the computation is a MAT() (CV_32FC2) of the gradiants and a Mat() (CV_8UC2) of the angles. How can i visualize this results? What represent the values? Why have the angle Mat() 2 channels? Are the 2 channels of the gradiant Mat() the x and y component of the gradiant? I cant find documentation of the computeGradiant-Method.
HOG descriptor is an histogram of oriented gradient: it is an histogram where each bin reprezent the vote for gradient in corresponding orientation.
In order to compute this descriptor, you should first convert you 3 channels color image into a grayscale image
cv::cvtColor(CV_BGR2GRAY);
The result of "ComputeGradient" method is for exemple two images (same size as the original): x-component and y-component.
You should then be able to compute for each pixel the gradient magnitude and orientation.
mag=sqrt(x*x+y*y)
alpha=atan(y/x)
Then you can fill you histogram. Note that HOG descritpor is computed by blocks and cells. See this for more detail.

opencv problems on Android

Please we need help urgently, we are using openCv in Android (Java).
We are facing a lot of problems:
convertTo() doesn't work so we can't convert 3 channel image to 1 channel without passing it on cvtColor().
grayImg.convertTo(grayImg, CvType.CV_8UC1);
cvtColor() gives a weird output:
Imgproc.cvtColor(src, grayImg, Imgproc.COLOR_RGB2GRAY);
Output of this line is the image repeated 4 times!
The only way to get rid of this repetition is to add this line and the output is a white and black image but 3 channel so it crashes any coming function because it needs 1 channel image.
Imgproc.cvtColor(grayImg, grayImg, Imgproc.COLOR_GRAY2RGB,3);
canny() for edge detection:
Imgproc.Canny(grayImg, grayImg, 10, 100,3,true);
findContours() counts a horrible number of contours while number of objects in the image is only 2 input image is 3 channel bmp image and we convert it to Mat.
output image:
https://dl.dropbox.com/u/36214963/canny.jpg
Thanks for your concern
Try BGR2GRAY rather than RGB2GRAY.I had the same problem and I solved it through this.There is also a note in the documentation about this
Converts an image from one color space to another.
The function cvtColor converts an input image from one color space to another. In case of a transformation to-from RGB color space, the order of the channels should be specified explicitly (RGB or BGR). Note that the default color format in OpenCV is often referred to as RGB but it is actually BGR (the bytes are reversed). So the first byte in a standard (24-bit) color image will be an 8-bit Blue component, the second byte will be Green, and the third byte will be Red. The fourth, fifth, and sixth bytes would then be the second pixel (Blue, then Green, then Red), and so on.
If I understand your first question correctly, you have two options to convert RGB images to grayscale ones.
Option 1: Convert the 3 channel image to 1 channel as you are trying to do.
IplImage *RGB_image = cvLoadImage("my_colored_image.jpg");
IplImage *GRAY_IMAGE = cvCreateImage(cvGetSize(RGB_image), IPL_DEPTH_8U, 1);
cvCvtColor(RGB_image, GRAY_IMAGE, CV_RGB2GRAY);
Option 2: Read the colored image as a grayscale image directly.
IplImage* GRAY_IMAGE = cvLoadImage("my_colored_image.jpg", CV_LOAD_IMAGE_GRAYSCALE);
I hope this suits you.
I haven't actually used opencv before, but I don't think convertTo is the answer your looking for.
By looking at the opencv documentation I found this:
cvtColor - Converts an image from one color space to another
Mat color; // the input image
Mat gray(color.rows, color.cols, color.depth());
cvtColor(color, gray, CV_BGR2GRAY);
Or simply (and the function cvtColor will create the image internally):
Mat color;
Mat gray;
cvtColor(color, gray, CV_BGR2GRAY);

How can I convert a short[] into a int[] to create a grayscale bitmap?

I am trying to run a denoising algorithm on a bitmap image that I have -- the function returns me a short[], so I tried simply casting it to int[] in order to generate a bitmap and I get this:
I'd like it to be in grayscale, not .. well.. pink. Any ideas?
Instead of replicating the 8-bit intensity in each of the RGB channels, you can use the intensity as the alpha channel. In this scheme, 0 corresponds to transparent (background color) and 255 corresponds to fully opaque (black, or whatever color you want--even pink). The idea is similar to Jason LeBrun's proposal: take the high-order 8 bits of each value, shift 24 bits left, then bitwise-OR with the color you want to use for full intensity (or with nothing, if you want black to represent full intensity).
The pixels of a bitmap are encoded using either ARGB_8888, RGB_565, ARGB_4444, or ALPHA_8. So, the short values that you're returning must happen to correspond to values that look slightly pink-ish in one of those formats.
If you want a grayscale bitmap, you can only have values in the range of 0-256 (For the maximum precious color component of 8 bits if you're using ARGB_8888). So, you'll need to map your short to values within that range, and then replicate that value for each of the RGB components.

Categories

Resources