为什么 OpenCV4Android 的 pointPolygonTest() 方法为每个像素返回 -1?

Why is pointPolygonTest() method of OpenCV4Android returning -1 for every pixel?

在下面的代码中,我执行了以下步骤:

  1. 已从 SD 卡加载图像。

  1. 已将其转换为 HSV 格式。

  2. 使用 inRange 函数遮盖了红色。

  1. 使用findContours找到轮廓。

  2. 从那些轮廓中找出最大的轮廓。

  3. 使用 boundingRectsubmat 函数围绕最大轮廓创建了 ROI。

  1. 已将此 ROI Mat 转换为 HSV 格式。

  1. 遍历 ROI Mat,并检查每个像素是否位于最大轮廓内。 我使用方法 pointPolygonTest 找到了这个,但是它 returns -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 
    
        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; 
}

编辑 1:

在以下方法的末尾,我创建了一个包含 MatOfPoint 列表的蒙版,其中仅包含 最大的 轮廓。当我把它写到SDCard时,我得到了

不知道哪里搞错了!

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:

然后,在评论// 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

这导致:

现在我又不知道如何进行了。 :s

你不需要使用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 ;