为啥 OpenCV4Android 的 pointPolygonTest() 方法为每个像素返回-1?
Posted
技术标签:
【中文标题】为啥 OpenCV4Android 的 pointPolygonTest() 方法为每个像素返回-1?【英文标题】:Why is pointPolygonTest() method of OpenCV4Android returning -1 for every pixel?为什么 OpenCV4Android 的 pointPolygonTest() 方法为每个像素返回-1? 【发布时间】:2016-01-13 19:44:10 【问题描述】:在以下代码中,我执行了以下步骤:
-
从 sdcard 加载了一张图片。
将其转换为 HSV 格式。
使用inRange
函数来掩盖红色。
使用findContours
查找轮廓。
从这些轮廓中找到最大的轮廓。
使用boundingRect
和submat
函数围绕最大轮廓创建了一个ROI。
-
将此 ROI Mat 转换为 HSV 格式。
遍历 ROI Mat,并检查每个像素是否位于最大轮廓内。 我使用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://***.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;
所以这里有人(可能是你?)曾建议我一种方法来排除我轮廓之外的像素。我会这样实现:
//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
s 列表的掩码,其中仅包含 最大 轮廓。当我把它写到 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
【问题讨论】:
为什么需要这样做?一旦你有了轮廓,或者面具,你现在已经知道了哪些点在里面。 @Miki 请查看问题中的编辑,我在那里说明了。 我几乎不推荐使用pointPolygonTest
:D。查看答案...
【参考方案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 ;
【讨论】:
“因为你已经有了面具” 和 “if( /* value of mask(r,c) > 0 */)...
” - 我们说的是哪个面具?你的意思是我应该通过Imgproc.drawContours(anEmptyMatForMask, aListHavingLargestContour, 0, new Scalar(255), -1);
来从detectedBlobHsv
创建一个掩码?
1) 是的! 2) 为什么你不能将 double 与 > 0
进行比较?
anEmptyMatForMask.get(r,c)[0]
应该得到正确的值
掩码只有一个通道
使用蒙版(单通道),您只能选择像素位置(r,c)
是否应该用于计算平均值。然后你在位置(r,c)
的 3 通道 hsv 图像上计算它以上是关于为啥 OpenCV4Android 的 pointPolygonTest() 方法为每个像素返回-1?的主要内容,如果未能解决你的问题,请参考以下文章