2016-01-13 82 views
0

在下面的代碼中,我執行了以下步驟:爲什麼OpenCV4Android的pointPolygonTest()方法爲每個像素返回-1?

  1. 從sdcard加載圖像。

enter image description here

  • 它轉換爲HSV格式。

  • 使用inRange功能可以遮蔽紅色。

  • enter image description here

  • 用於findContours找到輪廓。

  • 從這些輪廓中找出最大的輪廓。

  • 使用boundingRectsubmat函數創建圍繞最大輪廓的ROI。

  • enter image description here

  • 轉換此ROI墊到HSV格式。
  • enter image description here

  • 通過ROI墊迭代,並檢查對於每個像素,如果它位於最大輪廓內。 我使用方法pointPolygonTest來找出這個問題,但是它會爲每個像素返回-1,從Log.i輸出I have pasted here可以看出。問題是爲什麼?我該如何糾正這一點。

    private Scalar detectColoredBlob() { 
        rgbaFrame = Highgui.imread("/mnt/sdcard/DCIM/rgbaMat4Mask.bmp"); 
    
        Mat hsvImage = new Mat(); 
        Imgproc.cvtColor(rgbaFrame, hsvImage, Imgproc.COLOR_BGR2HSV); 
        Highgui.imwrite("/mnt/sdcard/DCIM/hsvImage.bmp", hsvImage);// check 
    
        Mat maskedImage = new Mat(); 
        Core.inRange(hsvImage, new Scalar(0, 100, 100), new Scalar(10, 255, 255), maskedImage); 
        Highgui.imwrite("/mnt/sdcard/DCIM/maskedImage.bmp", maskedImage);// check 
    
        List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); 
        Imgproc.findContours(maskedImage, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); 
    
        // \/ We will 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(i); 
         double currentContourArea = Imgproc.contourArea(currentContour); 
         if (currentContourArea > largestContourArea) { 
          largestContourArea = currentContourArea; 
          largestContour = currentContour; 
         } 
        } 
        MatOfPoint2f largestContour2f = new MatOfPoint2f(largestContour.toArray());// Required on Line 289. See http://stackoverflow.com/questions/11273588/how-to-convert-matofpoint-to-matofpoint2f-in-opencv-java-api 
    
        Rect detectedBlobRoi = Imgproc.boundingRect(largestContour); 
        Mat detectedBlobRgba = rgbaFrame.submat(detectedBlobRoi); 
        Highgui.imwrite("/mnt/sdcard/DCIM/detectedBlobRgba.bmp", detectedBlobRgba);// check 
    
        Mat detectedBlobHsv = new Mat(); 
        Imgproc.cvtColor(detectedBlobRgba, detectedBlobHsv, Imgproc.COLOR_BGR2HSV); 
        Highgui.imwrite("/mnt/sdcard/DCIM/roiHsv.bmp", detectedBlobHsv);// check 
    
        for (int firstCoordinate = 0; firstCoordinate < detectedBlobHsv.rows(); firstCoordinate++) { 
         for (int secondCoordinate = 0; secondCoordinate < detectedBlobHsv.cols(); secondCoordinate++) { 
          Log.i(TAG, "HAPPY " + Arrays.toString(detectedBlobHsv.get(firstCoordinate, secondCoordinate))); 
          if (Imgproc.pointPolygonTest(largestContour2f, new Point(firstCoordinate, secondCoordinate), false) == -1) { 
           Log.i(TAG, "HAPPY ....................... OUTSIDE"); 
          } 
         } 
        } 
        Highgui.imwrite("/mnt/sdcard/DCIM/processedcontoured.bmp", detectedBlobHsv);// check 
    

  • 編輯:

    我這樣做,因爲我需要計算趴在輪廓內像素的平均HSV顏色(即平均HSV顏色最大的紅色斑點)。如果我通過正常的公式計算的ROI detectedBlobHsv的平均顏色,我會做類似

    Scalar averageHsvColor= new Scalar(256); 
    Scalar sumHsvOfPixels = new Scalar(256); 
    sumHsvOfPixels = Core.sumElems(detectedBlobHsv); 
    int numOfPixels = detectedBlobHsv.width() * detectedBlobHsv.height(); 
    for (int channel=0; channel<sumHsvOfPixels.val.length; channel++) { 
        averageHsvColor = sumHsvOfPixels.val[channel]/numOfPixels; 
    } 
    

    因此就有人在這裏SO(可能嗎?)曾建議我的方式來排除我的輪廓,而在外面的像素背部。我想實現像:

    //Giving pixels outside contour of interest an HSV value of `double[]{0,0,0}`, so that they don't affect the computation of `sumHsvOfPixels` while computing average, 
    //and while keeping track of the number of pixels removed from computation this way, so we can subtract that number from the `$numOfPixels` during computation of average. 
    int pixelsRemoved = 0; 
    for (int row=0; row<detectedBlobHsv.rows(); row++) { 
        for (int col=0; col<detectedBlobHsv.cols(); col++) { 
        if (Imgproc.pointPolygonTest(largestContour2f, new Point(row, col), false) == -1) { 
         detectedBlobHsv.put(row, col, new double[]{0,0,0}); 
         pixelsRemoved++; 
        } 
        } 
    } 
    

    然後計算像

    Scalar averageHsvColor= new Scalar(256); 
    Scalar sumHsvOfPixels = new Scalar(256); 
    sumHsvOfPixels = Core.sumElems(detectedBlobHsv); //This will now exclude pixels outside the contour 
    int numOfPixels = ( detectedBlobHsv.width()*detectedBlobHsv.height() )-pixelsRemoved; 
    for (int channel=0; channel<sumHsvOfPixels.val.length; channel++) { 
        averageHsvColor = sumHsvOfPixels.val[channel]/numOfPixels; 
    } 
    

    EDIT 1平均:

    往下面的方法結束時,我有創建了一個列表MatOfPoint s的面具,其中包含最大的只有輪廓。當我把它寫的SD卡,我得到了

    enter image description here

    我不知道我搞砸了!

    private Scalar detectColoredBlob() { 
         //Highgui.imwrite("/mnt/sdcard/DCIM/rgbaFrame.jpg", rgbaFrame);// check 
         rgbaFrame = Highgui.imread("/mnt/sdcard/DCIM/rgbaMat4Mask.bmp"); 
    
    
         //GIVING A UNIFORM VALUE OF 255 TO THE V CHANNEL OF EACH PIXEL (255 IS THE MAXIMUM VALUE OF V ALLOWED - Simulating a maximum light condition) 
         for (int firstCoordinate = 0; firstCoordinate < rgbaFrame.rows(); firstCoordinate++) { 
          for (int secondCoordinate = 0; secondCoordinate < rgbaFrame.cols(); secondCoordinate++) { 
           double[] pixelChannels = rgbaFrame.get(firstCoordinate, secondCoordinate); 
           pixelChannels[2] = 255; 
           rgbaFrame.put(firstCoordinate, secondCoordinate, pixelChannels); 
          } 
         } 
    
    
         Mat hsvImage = new Mat(); 
         Imgproc.cvtColor(rgbaFrame, hsvImage, Imgproc.COLOR_BGR2HSV); 
         Highgui.imwrite("/mnt/sdcard/DCIM/hsvImage.bmp", hsvImage);// check 
    
    
         Mat maskedImage = new Mat(); 
         Core.inRange(hsvImage, new Scalar(0, 100, 100), new Scalar(10, 255, 255), maskedImage); 
         Highgui.imwrite("/mnt/sdcard/DCIM/maskedImage.bmp", 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(maskedImage, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); 
         //FINDING THE BIGGEST CONTOUR 
         // \/ We will 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(i); 
          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.bmp", detectedBlobRgba);// check 
    
    
         Mat detectedBlobHsv = new Mat(); 
         Imgproc.cvtColor(detectedBlobRgba, detectedBlobHsv, Imgproc.COLOR_BGR2HSV); 
         Highgui.imwrite("/mnt/sdcard/DCIM/roiHsv.bmp", detectedBlobHsv);// check 
    
         List<MatOfPoint> largestContourList = new ArrayList<>(); 
         largestContourList.add(largestContour); 
    
         Mat roiWithMask = new Mat(detectedBlobHsv.rows(), detectedBlobHsv.cols(), CvType.CV_8UC3); 
         roiWithMask.setTo(new Scalar(0,0,0)); 
         Imgproc.drawContours(roiWithMask, largestContourList, 0, new Scalar(0, 255, 255), -1);//TODO Using -1 instead of CV_FILLED. 
         Highgui.imwrite("/mnt/sdcard/DCIM/roiWithMask.bmp", roiWithMask);// check 
    
    
         // CALCULATING THE AVERAGE COLOR OF THE DETECTED BLOB 
         // STEP 1: 
         double [] averageHsvColor = new double[]{0,0,0}; 
         int numOfPixels = 0; 
         for (int firstCoordinate = 0; firstCoordinate < detectedBlobHsv.rows(); ++firstCoordinate) { 
          for (int secondCoordinate = 0; secondCoordinate < detectedBlobHsv.cols(); ++secondCoordinate) { 
    
           double hue = roiWithMask.get(firstCoordinate, secondCoordinate)[0]; 
           double saturation = roiWithMask.get(firstCoordinate, secondCoordinate)[1]; 
           double value = roiWithMask.get(firstCoordinate, secondCoordinate)[2]; 
    
           averageHsvColor[0] += hue; 
           averageHsvColor[1] += saturation; 
           averageHsvColor[2] += value; 
    
           numOfPixels++; 
          } 
         } 
         averageHsvColor[0] /= numOfPixels; 
         averageHsvColor[1] /= numOfPixels; 
         averageHsvColor[1] /= numOfPixels; 
    
    
    
         return new Scalar(averageHsvColor); 
        } 
    

    編輯2:

    我糾正我的3信道掩模和由單一信道掩模

    Mat roiMask = new Mat(rgbaFrame.rows(), rgbaFrame.cols(), CvType.CV_8UC1); 
         roiMask.setTo(new Scalar(0)); 
         Imgproc.drawContours(roiMask, largestContourList, 0, new Scalar(255), -1); 
    

    ,這導致在正確roiMask

    enter image description here

    然後,評論// CALCULATING THE AVERAGE COLOR OF THE DETECTED BLOB之前,我補充說:

    Mat newImageWithRoi = new Mat(rgbaFrame.rows(), rgbaFrame.cols(), CvType.CV_8UC3); 
    newImageWithRoi.setTo(new Scalar(0, 0, 0)); 
    rgbaFrame.copyTo(newImageWithRoi, roiMask); 
    Highgui.imwrite("/mnt/sdcard/DCIM/newImageWithRoi.bmp", newImageWithRoi);//check 
    

    這導致:

    enter image description here

    現在又不知如何着手。:s

    +0

    爲什麼你需要這樣做?一旦你有輪廓或面具,你現在已經有哪些點在裏面了。 – Miki

    +0

    @Miki請參閱問題中的編輯,我在那裏說明。 – Solace

    +0

    我_hardly_建議使用'pointPolygonTest':D。看到答案... – Miki

    回答

    1

    您不需要使用pointPolygonTest,因爲您已經擁有該面具。

    您可以簡單地總結掩碼上的值。一些沿線(不能測試這個):

    // Initialize at 0!!! 
    Scalar averageHsvColor= new Scalar(0,0,0); 
    
    int numOfPixels = 0; 
    
    for(int r=0; r<detectedBlobHsv.height(); ++r) 
    { 
        for(int c=0; c<detectedBlobHsv.width(); ++c) 
        { 
         if(/* value of mask(r,c) > 0 */) 
         { 
          int H = // get H value of pixel at (r, c) 
          int S = // get S value of pixel at (r, c) 
          int V = // get V value of pixel at (r, c) 
    
          // Sum values 
          averageHsvColor[0] += H; 
          averageHsvColor[1] += S; 
          averageHsvColor[2] += V; 
    
          // Increment number of pixels inside mask 
          numOfPixels ++; 
         } 
        } 
    } 
    
    // Compute average 
    averageHsvColor[0] /= numOfPixels ; 
    averageHsvColor[1] /= numOfPixels ; 
    averageHsvColor[2] /= numOfPixels ; 
    
    +0

    _「因爲你已經擁有了掩碼」_和_「'if(/ *掩碼值(r,c)> 0 * /)...'」_ - 我們在說什麼掩碼關於?你的意思是我應該通過'imgproc.drawContours(anEmptyMatForMask,aListHavingLargestContour,0,new Scalar(255),-1)從'detectedBlobHsv'創建一個掩碼;'? – Solace

    +0

    在這種情況下,我不能有像'if(anEmptyMatForMask.get(r,c)> 0)'因爲'anEmptyMatForMask.get(r,c)'會返回一個double,所以無法比較與'0':s – Solace

    +1

    1)是的! 2)爲什麼你不能用'> 0'比較一個double? – Miki