Opencv提高阈值的准确性

我正在一个应用程序,预计将删除使用opencv图像背景,起初我尝试使用抓取,但它太慢,结果并不总是准确的,然后我尝试使用阈值,虽然结果还没有closuresth截取,它的速度非常快,看起来像一个更好的,所以我的代码首先看图像色调,并分析哪部分看起来更多,这部分被作为背景,有时它的前景作为背景下面是我的代码:

private Bitmap backGrndErase() { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.skirt); Log.d(TAG, "bitmap: " + bitmap.getWidth() + "x" + bitmap.getHeight()); bitmap = ResizeImage.getResizedBitmap(bitmap, calculatePercentage(40, bitmap.getWidth()), calculatePercentage(40, bitmap.getHeight())); Mat frame = new Mat(); Utils.bitmapToMat(bitmap, frame); Mat hsvImg = new Mat(); List<Mat> hsvPlanes = new ArrayList<>(); Mat thresholdImg = new Mat(); // int thresh_type = Imgproc.THRESH_BINARY_INV; //if (this.inverse.isSelected()) int thresh_type = Imgproc.THRESH_BINARY; // threshold the image with the average hue value hsvImg.create(frame.size(), CvType.CV_8U); Imgproc.cvtColor(frame, hsvImg, Imgproc.COLOR_BGR2HSV); Core.split(hsvImg, hsvPlanes); // get the average hue value of the image double threshValue = this.getHistAverage(hsvImg, hsvPlanes.get(0)); Imgproc.threshold(hsvPlanes.get(0), thresholdImg, threshValue, mThresholdValue, thresh_type); // Imgproc.adaptiveThreshold(hsvPlanes.get(0), thresholdImg, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 11, 2); Imgproc.blur(thresholdImg, thresholdImg, new Size(5, 5)); // dilate to fill gaps, erode to smooth edges Imgproc.dilate(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 1); Imgproc.erode(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 3); Imgproc.threshold(thresholdImg, thresholdImg, threshValue, mThresholdValue, Imgproc.THRESH_BINARY); //Imgproc.adaptiveThreshold(thresholdImg, thresholdImg, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 11, 2); // create the new image Mat foreground = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(255, 255, 255)); frame.copyTo(foreground, thresholdImg); Utils.matToBitmap(foreground,bitmap); //return foreground; alreadyRun = true; return bitmap; } 

负责色相的方法:

  private double getHistAverage(Mat hsvImg, Mat hueValues) { // init double average = 0.0; Mat hist_hue = new Mat(); // 0-180: range of Hue values MatOfInt histSize = new MatOfInt(180); List<Mat> hue = new ArrayList<>(); hue.add(hueValues); // compute the histogram Imgproc.calcHist(hue, new MatOfInt(0), new Mat(), hist_hue, histSize, new MatOfFloat(0, 179)); // get the average Hue value of the image // (sum(bin(h)*h))/(image-height*image-width) // ----------------- // equivalent to get the hue of each pixel in the image, add them, and // divide for the image size (height and width) for (int h = 0; h < 180; h++) { // for each bin, get its value and multiply it for the corresponding // hue average += (hist_hue.get(h, 0)[0] * h); } // return the average hue of the image average = average / hsvImg.size().height / hsvImg.size().width; return average; } 

input和输出的示例:[ 输入图像1] 输出图像

input图像2和输出: 在这里输入图像说明 在这里输入图像说明

input图像3和输出: 在这里输入图像说明 在这里输入图像说明

Solutions Collecting From Web of "Opencv提高阈值的准确性"

事实上,正如其他人所说的,你只是在色调的门槛上不可能得到好的结果。 你可以使用类似于GrabCut的东西,但速度更快。

在GrabCut的基础上,GrabCut计算前景和背景直方图,然后基于这些直方图计算每个像素为FG / BG的概率,然后使用graphics剪切优化得到的概率图以获得分割。

最后一步是最昂贵的,根据应用可能会被忽略。 相反,您可以将阈值应用于概率图以获得分割。 它可能(也将会)比GrabCut更差,但会比现在的方法更好。

这种方法有几点需要考虑。 直方图模型的select在这里非常重要。 你可以在YUV或HSV等一些空间中考虑2个通道,考虑3个RGB通道,或者考虑2个归一化RGB通道。 您还必须为这些直方图select适当的bin大小。 太小的箱子会导致“过度训练”,而太大的箱子会降低精度。 这些之间的权衡是一个单独的讨论主题,简而言之 – 我会build议使用RGB每个通道64个仓位开始,然后看看哪些更改是更好的数据。

此外,如果使用插值来获取垃圾箱之间的值,则可以获得更好的粗分级结果。 在过去我已经使用三线性插值,这是一种很好,相比之下根本没有插值。

但请记住,如果没有对物体形状的先验知识,无法使用GrabCut,阈值或这种方法保证分割是正确的。

我会再试一次Grabcut,它是可用的最好的分割方法之一。 这是我得到的结果

 cv::Mat bgModel,fgModel; // the models (internally used) cv::grabCut(image,// input image object_mask,// segmentation result rectang,// rectangle containing foreground bgModel,fgModel, // models 5,// number of iterations cv::GC_INIT_WITH_RECT); // use rectangle // Get the pixels marked as likely foreground cv::compare(object_mask,cv::GC_PR_FGD,object_mask,cv::CMP_EQ); cv::threshold(object_mask, object_mask, 0,255, CV_THRESH_BINARY); //ensure the mask is binary 

Grabcut唯一的问题是你必须给一个包含你想要提取的对象的矩形作为input。 除此之外,它工作得很好。

你find平均色调的方法是错误的! 正如你可能知道的那样,色调被表示为angular度并且在[0,360]范围内取值。 因此,具有色调360的像素基本上与具有色调0的像素(都是纯红色)具有相同的颜色。 以相同的方式,具有色相350的像素实际上比具有色调的像素(例如300)更接近具有色相10的像素。

至于opencv, cvtColor函数实际上是将计算出来的色调值除以2来适应8位整数。 因此,在opencv中,色调值在180以后换行。现在考虑我们有两个红色(ish)像素,色调为10和170.如果我们取平均值,我们将得到纯青色的90-色调,这不是我们期望的价值。

因此,要正确地find平均色调,首先需要在RGB色彩空间中find平均像素值,然后从该RGB值计算色调。 您可以创build具有平均RGB像素的1x1matrix,并将其转换为HSV / HSL。

按照相同的推理,对色调图像应用threshold并不能完美地工作。 它不考虑包装的色调值。

如果我理解正确,你想find与背景色相似的像素。 假设我们知道背景的颜色,我会在RGB空间中做这个分割。 我会介绍一些tolerancevariables。 我将使用背景像素值作为中心,并将此容差作为半径,从而在RGB颜色空间中定义一个球体。 现在,rest是检查每个像素值,如果它落在这个领域,然后分类为背景; 否则,将其视为前景像素。