2

我是 OpenCV4Android 的新手。这是我编写的一些代码,用于检测图像中的蓝色斑点。在以下图片中,图片 1 在我的笔记本电脑中。我运行应用程序,OpenCV 相机捕获的帧是图像 2。您可以查看代码以查看其余图像是什么。(正如您在代码中看到的,所有图像都保存在 SD 卡中。)

我有以下问题:

  • 为什么在相机捕获的 rgba 帧中,浅蓝色斑点的颜色变成了浅黄色(如图 2 所示)。

  • boundingRect我围绕最大的蓝色 blob创建了一个,但随后ROI通过rgbaFrame.submat(detectedBlobRoi). 但你可以在最后一张图片中看到,它看起来就像几个灰色像素。我期待蓝色球体与图像的其余部分分开。

我错过了什么或做错了什么?

代码:

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. 电脑中的原始图像,这是通过将手机的摄像头放在笔记本电脑屏幕前拍摄的。

在此处输入图像描述

  1. rgbaFrame.jpg

在此处输入图像描述

  1. hsvImage.jpg

在此处输入图像描述

  1. 扩张图像.jpg

在此处输入图像描述

  1. 蒙面垫.jpg

在此处输入图像描述

  1. 检测到的BlobRgba.jpg

在此处输入图像描述


编辑:

我刚刚使用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了,我通过给它一个自定义的红色 HSV 值来捕获网站 colorizer.org 的截图,即对于 OpenCV 红色Scalar(3, 217, 255)(它落在给定inRange函数中设置的范围内,我将通道值缩放到colorizer.org 的比例,即 H=0-360,S=0-100,V=0-100,通过将 H 值乘以 2,并将 S 和 V 值除以 255 并乘以 100。这给出了我6, 85.09, 88.24在网站上设置,并截取了屏幕截图(下图中的第一个)。

  1. 原始截图,我捕捉到了这一帧。

在此处输入图像描述

  1. rgbaFrame.jpg

在此处输入图像描述

  1. hsvImage.jpg

在此处输入图像描述

  1. 蒙面图像.jpg

在此处输入图像描述

  1. 膨胀垫.jpg

在此处输入图像描述

  1. 检测到的BlobRgba.jpg

在此处输入图像描述


重要的:

当我触摸 rgbaFrame 内部时(即在onTouch方法内部调用它),给定的方法实际上是在我的测试应用程序中调用的。我还使用以下代码打印到我触摸过的彩色 blobTextViewHueSaturation和值。当我运行这个应用程序时,我触摸了红色的 blob,并得到了以下值:.ValueHue:3, Saturation:219, Value:255

public boolean onTouch(View v, MotionEvent motionEvent) {detectColoredBlob(); int cols = rgbaFrame.cols(); int 行 = 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] + "\nSaturation : " + channelsDoubleArray[1] + "\nValue : "
        + channelsDoubleArray[2]);

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

}

4

2 回答 2

4

可能您使用的蓝色范围是错误的,在 OpenCV 中,色调范围是 0-180,而您给出的范围是 170-270。找到正确的蓝色色调值并在 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

您可以在此处参考答案以选择正确的 hsv 值。

下面是分割红色的代码,用你的代码检查它,并确保它分割红色对象。

    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 
于 2015-12-09T06:34:01.977 回答
3

将图像转换为 HSV 颜色空间和使用 HSV 颜色空间有多个陷阱。

  1. OpenCV 使用压缩的色调范围,因为原始色调范围从 0 到 360,这意味着值不能适合 1 个字节(值 0 到 255),而饱和度和值通道恰好被 1 个字节覆盖。因此,OpenCV 使用色调值除以 2。因此色调通道将被 0 到 180 之间的矩阵条目覆盖。关于这一点,您的色调范围从 170 到 270 应该除以 2 = OpenCV 中的范围 65 到 135。

  2. hue tells you about the color tone, but saturation and value are still important to reduce noise, so set your threshold to some minimum saturation and value, too

  3. very important: OpenCV uses BGR memory ordering for rendering and image saving. This means that if your image has RGB(a) ordering and you save it without color conversion, you swap R and B channels, so assumed red color will become blue etc. Unfortunately normally you can't read from the image data itself, whether it is RGB or BGR ordered, so you should try to find it out from the image source. OpenCV allows several flags to convert either from RGB(A) to HSV and/or from BGR(A) to HSV, and/or from RGB to BGR etc, so that is no problem, as long as you know which memory format your image uses. However, displaying and saving always assumes BGR ordering, so if you want to display or save the image, convert it to BGR! HSV values however will be the same, no matter whether you convert a BGR image with BGR2HSV or whether you convert a RGB image with RGB2HSV. But it will have wrong values if you convert a BGR image with RGB2HSV or a RGB image with BGR2HSV... I'm not 100% sure about Java/Python/Android APIs of openCV, but your image really looks like B and R channels are swapped or misinterpreted (but since you use RGBA2HSV conversion that's no problem for the hsv colors).

关于您的轮廓提取,您的代码中有一个很小的(复制粘贴?)错误,每个人都可能会偶尔观察到:

MatOfPoint largestContour = contours.get(0);
    double largestContourArea = Imgproc.contourArea(largestContour);
    for ( int i=1; i<contours.size(); ++i) {//NB Notice the prefix increment.
        // HERE you had MatOfPoint currentContour = contours.get(0); so you tested the first contour in each iteration
        MatOfPoint currentContour = contours.get(i);
        double currentContourArea = Imgproc.contourArea(currentContour);
        if (currentContourArea > largestContourArea) {
            largestContourArea = currentContourArea;
            largestContour = currentContour;
        }
    }

所以可能只是这必须更改为使用i而不是0在循环中

MatOfPoint currentContour = contours.get(i);
于 2015-12-09T15:49:55.547 回答