Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
204 views
in Technique[技术] by (71.8m points)

android - Trying to detect blue color from image using opencv, and getting unexpected result

I am new to OpenCV4Android. Here is some code I wrote to detect blue colored blob in an image. Among the following images, image 1 was in my laptop. I run the application and the frame captured by the OpenCV camera is image 2. You can look at the code to see what the rest of the images are. (As you can see in the code, all the images are saved in the SD card.)

I have the following questions:.

  • Why hass the color of the light-blue blob turned out to be light-yellow in the rgba frame captured by the camera (shown in image 2).

  • I created a boundingRect around the largest blue colored blob, but then ROI by doing rgbaFrame.submat(detectedBlobRoi). But you can see in the last image, it just looks like a couple of grey pixels. I was expecting the blue colored sphere separated from the rest of the image.

What am I missing or doing wrong?

CODE:

private void detectColoredBlob () { 
        Highgui.imwrite("/mnt/sdcard/DCIM/rgbaFrame.jpg", rgbaFrame);//check
        Mat hsvImage = new Mat(); 
        Imgproc.cvtColor(rgbaFrame, hsvImage, Imgproc.COLOR_RGB2HSV_FULL);
        Highgui.imwrite("/mnt/sdcard/DCIM/hsvImage.jpg", hsvImage);//check

        Mat maskedImage = new Mat(); 
        Scalar lowerThreshold = new Scalar(170, 0, 0); 
        Scalar upperThreshold = new Scalar(270, 255, 255); 
        Core.inRange(hsvImage, lowerThreshold, upperThreshold, maskedImage);
        Highgui.imwrite("/mnt/sdcard/DCIM/maskedImage.jpg", maskedImage);//check

        Mat dilatedMat= new Mat(); 
        Imgproc.dilate(maskedImage, dilatedMat, new Mat() ); 
        Highgui.imwrite("/mnt/sdcard/DCIM/dilatedMat.jpg", dilatedMat);//check
        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
        Imgproc.findContours(dilatedMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
        //Use only the largest contour. Other contours (any other possible blobs of this color range) will be ignored.
        MatOfPoint largestContour = contours.get(0);
        double largestContourArea = Imgproc.contourArea(largestContour);
        for ( int i=1; i<contours.size(); ++i) {//NB Notice the prefix increment.
            MatOfPoint currentContour = contours.get(0);
            double currentContourArea = Imgproc.contourArea(currentContour);
            if (currentContourArea > largestContourArea) {
                largestContourArea = currentContourArea;
                largestContour = currentContour;
            }
        }

        Rect detectedBlobRoi = Imgproc.boundingRect(largestContour);
        Mat detectedBlobRgba = rgbaFrame.submat(detectedBlobRoi);
        Highgui.imwrite("/mnt/sdcard/DCIM/detectedBlobRgba.jpg", detectedBlobRgba);//check
     }
  1. Original Image in computer, This was captured by placing phone's camera infront of laptop screen.

enter image description here

  1. rgbaFrame.jpg

enter image description here

  1. hsvImage.jpg

enter image description here

  1. dilatedImage.jpg

enter image description here

  1. maskedMat.jpg

enter image description here

  1. detectedBlobRgba.jpg

enter image description here


EDIT:

I just used Core.inRange(hsvImage, new Scalar(0,50,40), new Scalar(10,255,255), maskedImage);//3, 217, 225 --- 6, 85.09, 88.24 ...... 3 219 255, and I captured a screeshot of the website colorizer.org by giving it a custom HSV values for red color, i.e. for the OpenCV red Scalar(3, 217, 255) (which falls in the range set in the given inRange function, I scaled the channel values to the scale of colorizer.org, which is H=0-360, S=0-100, V=0-100, by multiplying H value by 2, and dividing both the S and V values by 255 and multiplying by 100. This gave me 6, 85.09, 88.24 which I set on the website, and took a screenshot (the first in the following images).

  1. Original screenshot, I captured this frame.

enter image description here

  1. rgbaFrame.jpg

enter image description here

  1. hsvImage.jpg

enter image description here

  1. maskedImage.jpg

enter image description here

  1. dilatedMat.jpg

enter image description here

  1. detectedBlobRgba.jpg

enter image description here


IMPORTANT:

The method given is actually invoked in my test application when I touch inside the rgbaFrame (i.e. it is invoked inside onTouch method). I am also using the following code to print to a TextView the Hue, Saturation, and Value values of the colored blob that I have touched. When I run this application, I touched the red colored blob, and got the following values: Hue:3, Saturation:219, Value:255.

public boolean onTouch(View v, MotionEvent motionEvent) { detectColoredBlob(); int cols = rgbaFrame.cols(); int rows = rgbaFrame.rows();

int xOffset = (openCvCameraBridge.getWidth() - cols) / 2;
int yOffset = (openCvCameraBridge.getHeight() - rows) / 2;

int x = (int) motionEvent.getX() - xOffset;
int y = (int) motionEvent.getY() - yOffset;

Log.i(TAG, "Touch image coordinates: (" + x + ", " + y + ")");//check

if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) { return false; }

Rect touchedRect = new Rect();
touchedRect.x = (x > 4) ? x - 4 : 0;
touchedRect.y = (y > 4) ? y - 4 : 0;
touchedRect.width = (x + 4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x;
touchedRect.height = (y + 4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y;
Mat touchedRegionRgba = rgbaFrame.submat(touchedRect);

Mat touchedRegionHsv = new Mat();
Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv, Imgproc.COLOR_RGB2HSV_FULL);

double[] channelsDoubleArray = touchedRegionHsv.get(0, 0);//**********
float[] channelsFloatArrayScaled = new float[3];
for (int i = 0; i < channelsDoubleArray.length; i++) {
    if (i == 0) {
        channelsFloatArrayScaled[i] = ((float) channelsDoubleArray[i]) * 2;// TODO Wrap an ArrayIndexOutOfBoundsException wrapper
    } else if (i == 1 || i == 2) {
        channelsFloatArrayScaled[i] = ((float) channelsDoubleArray[i]) / 255;// TODO Wrap an ArrayIndexOutOfBoundsException wrapper
    }
}

int androidColor = Color.HSVToColor(channelsFloatArrayScaled);

view.setBackgroundColor(androidColor);
textView.setText("Hue : " + channelsDoubleArray[0] + "
Saturation : " + channelsDoubleArray[1] + "
Value : "
        + channelsDoubleArray[2]);

touchedRegionHsv.release();
return false; // don't need subsequent touch events 

}

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Probably the range you are using is wrong for blue, In OpenCV the hue range is from 0-180 and you have given it's 170-270. Find the correct hue value for blue and use in inRange.

  1. http://answers.opencv.org/question/30547/need-to-know-the-hsv-value/#30564
  2. http://answers.opencv.org/question/28899/correct-hsv-inrange-values-for-red-objects/#28901

You can refer the answer here for choosing correct hsv value.

Below is the code for segmenting red color, check it with your code, and make sure it segmenting red object.

    Imgproc.cvtColor(rgbaFrame, hsv, Imgproc.COLOR_RGB2HSV,4); // Convert to hsv for color segmentation.      
    Core.inRange(hsv,new Scalar(0,50,40,0), new Scalar(10,255,255,0),thr);//upper red range of hue cylinder 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...