尝试使用opencv从图像中检测蓝色,并获得意外结果

时间:2015-12-09 04:40:29

标签: android opencv hsv opencv4android opencv-drawcontour

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

我有以下问题:

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

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

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

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. 计算机中的原始图像,这是通过将手机的相机放在笔记本电脑屏幕前面拍摄的。
  2. enter image description here

    1. rgbaFrame.jpg
    2. enter image description here

      1. hsvImage.jpg
      2. enter image description here

        1. dilatedImage.jpg
        2. enter image description here

          1. maskedMat.jpg
          2. enter image description here

            1. detectedBlobRgba.jpg
            2. enter image description here

              编辑:

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

              1. 原始屏幕截图,我抓住了这个框架。
              2. enter image description here

                1. rgbaFrame.jpg
                2. enter image description here

                  1. hsvImage.jpg
                  2. enter image description here

                    1. maskedImage.jpg
                    2. enter image description here

                      1. dilatedMat.jpg
                      2. enter image description here

                        1. detectedBlobRgba.jpg
                        2. enter image description here

                          重要:

                          当我触摸rgbaFrame(即在onTouch方法中调用)时,实际上在我的测试应用程序中调用了给定的方法。我还使用以下代码打印到TextView我所触摸的彩色blob的HueSaturationValue值。 当我运行此应用程序时,我触摸了红色斑点,并获得了以下值: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] + "\nSaturation : " + channelsDoubleArray[1] + "\nValue : "
                                  + channelsDoubleArray[2]);
                          
                          touchedRegionHsv.release();
                          return false; // don't need subsequent touch events 
                          

                          }

2 个答案:

答案 0 :(得分: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
  3. 您可以在此处参考答案来选择正确的hsv值。

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

    $customClaims = [
            'sub' => $user->id,
            'iat' => time(),
            'exp' => time() + (2 * 7 * 24 * 60 * 60)
        ];
    
     $payload = app('tymon.jwt.payload.factory')->make($customClaims);
    

答案 1 :(得分:3)

将图像转换为HSV色彩空间并使用HSV色彩空间时有多个陷阱。

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

  2. 色调告诉您色调,但饱和度和值对于降低噪点仍然很重要,因此将阈值设置为某个最小饱和度和值

  3. 非常重要:OpenCV使用BGR内存排序进行渲染和图像保存。这意味着如果您的图像具有RGB(a)排序并且您保存它而没有颜色转换,则您交换R和B通道,因此假设红色将变为蓝色等。不幸的是,通常您无法从图像数据本身读取,它是RGB或BGR有序的,所以你应该尝试从图像源找到它。 OpenCV允许几个标志从RGB(A)转换为HSV和/或从BGR(A)转换为HSV,和/或从RGB转换为BGR等,这样就没问题,只要你知道你的图像是哪种内存格式使用。但是,显示和保存始终采用BGR排序,因此如果要显示或保存图像,请将其转换为BGR!但是,无论您是使用BGR2HSV转换BGR图像还是使用RGB2HSV转换RGB图像,HSV值都是相同的。但是如果你使用RGB2HSV或带有BGR2HSV的RGB图像转换BGR图像,它将会有错误的值...我不是100%肯定openCV的Java / Python / Android API,但你的图像看起来真的像B和R通道被交换或误解(但是因为你使用RGBA2HSV转换对hsv颜色没有问题)。

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

    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);