Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Opencv increasing accuracy of threshold

Tags:

android

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;

}

the method responsible for Hue:

    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:[Input Image1]Output Image

Input Image 2 and Output: enter image description here enter image description here

Input Image 3 and Output: enter image description here enter image description here

like image 401
life evader Avatar asked Nov 28 '15 22:11

life evader


1 Answers

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.

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.

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.

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.

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.

like image 78
alexisrozhkov Avatar answered Nov 03 '22 09:11

alexisrozhkov