本文介绍了OpenCV提高阈值的准确性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个应使用opencv删除图像背景的应用程序,起初我尝试使用抓取,但速度太慢,结果并不总是准确,然后我尝试使用阈值,尽管结果还没有近距离抓取,它非常快并且看起来更好.因此,我的代码首先查看图像色调,然后分析其中的哪个部分显示更多,将该部分作为背景,问题有时是逐渐成为前台.下面的背景是我的代码:

I am working on an app that is expected to remove image backgrounds using opencv, at first I tried using grabcut but it was too slow and the results were not always accurate, then I tried using threshold, although the results are not yet close th grabcut, its very fast and looks like a better, So my code is first looking at the image hue and analying which portion of it appears more, that portion is taken in as the background, the issue is at times its getting the foreground as background below is my code:

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

输入和输出的示例:[

A sample of the input and output:[

输入图像2和输出:

Input Image 2 and Output:

输入图像3和输出:

Input Image 3 and Output:

推荐答案

的确,正如其他人所说,仅凭色相阈值就不可能获得好的结果.您可以使用与 GrabCut 类似的方法,但是速度更快.

Indeed, as others have said you are unlikely to get good results just with a threshold on hue. You can use something similar to GrabCut, but faster.

在后台,GrabCut计算前景和背景直方图,然后根据这些直方图计算每个像素为FG/BG的概率,然后使用图形切割以进行分割.

Under the hood, GrabCut calculates foreground and background histograms, then calculates the probability of each pixel being FG/BG based on these histograms, and then optimizes the resulting probability map using graph cut to obtain a segmentation.

最后一步是最昂贵的,根据应用程序,它可能会被忽略.相反,您可以将阈值应用于概率图以获得分段.它可能(并且会)比GrabCut差,但会比您当前的方法好.

Last step is most expensive, and it may be ignored depending on the application. Instead, you may apply the threshold to the probability map to obtain a segmentation. It may (and will) be worse than GrabCut, but will be better than your current approach.

此方法需要考虑一些要点.直方图模型的选择在这里非常重要.您可以在某些空间(例如YUV或HSV)中考虑2个通道,考虑3个RGB通道,或考虑2个标准化RGB通道.您还必须为这些直方图选择合适的bin大小.垃圾箱太小会导致过度训练",而太大则会降低精度.两者之间的权衡是一个单独讨论的主题,简而言之-我建议使用每通道64个bin的RGB作为开始,然后查看哪些更改更适合您的数据.

There are some points to consider for this approach. The choice of histogram model would be very important here. You can either consider 2 channels in some space like YUV or HSV, consider 3 channels of RGB, or consider 2 channels of normalized RGB. You also have to select an appropriate bin size for those histograms. Too small bins would lead to 'overtraining', while too large will reduce the precision. The tradeoffs between those are a topic for a separate discussion, in brief - I would advice using RGB with 64 bins per channel for start and then see what changes are better for your data.

此外,如果使用插值来获取仓位之间的值,则对于粗仓仓位可以获得更好的结果.过去我使用三线性插值法,与根本没有插值法相比,它是一种很好的方法.

Also, you can get better results for coarse binning if you use interpolation to get values between bins. In past I have used trilinear interpolation and it was kind of good, compared to no interpolation at all.

但是请记住,如果没有对对象形状的事先了解(使用GrabCut,阈值法或这种方法),就无法保证分割正确.

But remember that there are no guarantees that your segmentation will be correct without prior knowledge on object shape, either with GrabCut, thresholding or this approach.

这篇关于OpenCV提高阈值的准确性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 23:56