Android Opencv提高阈值的准确性

Android Opencv提高阈值的准确性,android,opencv,Android,Opencv,我正在开发一个应用程序,预计将使用opencv删除图像背景,起初我尝试使用grabcut,但速度太慢,结果并不总是准确,然后我尝试使用threshold,虽然结果还不接近grabcut,但速度非常快,看起来更好,因此,我的代码首先查看图像色调并分析其中哪个部分显示得更多,该部分作为背景,问题是有时将前景作为背景,下面是我的代码: private Bitmap backGrndErase() { Bitmap bitmap = BitmapFactory.decodeResource(g

我正在开发一个应用程序,预计将使用opencv删除图像背景,起初我尝试使用grabcut,但速度太慢,结果并不总是准确,然后我尝试使用threshold,虽然结果还不接近grabcut,但速度非常快,看起来更好,因此,我的代码首先查看图像色调并分析其中哪个部分显示得更多,该部分作为背景,问题是有时将前景作为背景,下面是我的代码:

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;

}
私有位图备份数据库()
{
位图Bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.skirt);
Log.d(标记“bitmap:+bitmap.getWidth()+“x”+bitmap.getHeight());
位图=ResizeImage.getResizedBitmap(位图,calculatePercentage(40,bitmap.getWidth()),calculatePercentage(40,bitmap.getHeight());
垫架=新垫();
bitmapToMat(位图,帧);
Mat hsvImg=新Mat();
List hsvPlanes=new ArrayList();
Mat阈值img=新Mat();
//int thresh_type=Imgproc.thresh_BINARY_INV;
//if(this.inverse.isSelected())
int thresh_type=Imgproc.thresh_二进制;
//使用平均色调值设置图像阈值
创建(frame.size(),CvType.CV_8U);
Imgproc.cvt颜色(帧、hsvImg、Imgproc.COLOR\u BGR2HSV);
堆芯分裂(hsvImg、hsvPlanes);
//获取图像的平均色调值
double threshValue=this.getHistAverage(hsvImg,hsvPlanes.get(0));
Imgproc.threshold(hsvPlanes.get(0)、thresholdImg、thresholdValue、mThresholdValue、threshold_类型);
//Imgproc.adaptiveThreshold(hsvPlanes.get(0),thresholdImg,255,Imgproc.ADAPTIVE_THRESH_MEAN_C,Imgproc.THRESH_BINARY,11,2);
Imgproc.blur(阈值img,阈值img,新大小(5,5));
//扩张以填充间隙,侵蚀以平滑边缘
扩张(阈值img,阈值img,新Mat(),新点(-1,-1),1);
侵蚀(阈值img,阈值img,新垫(),新点(-1,-1),3);
Imgproc.threshold(thresholdImg,thresholdImg,thresholdValue,mThresholdValue,Imgproc.threshold_二进制);
//Imgproc.adaptiveThreshold(thresholdImg,thresholdImg,255,Imgproc.ADAPTIVE_THRESH_MEAN_C,Imgproc.THRESH_BINARY,11,2);
//创建新图像
Mat前台=新Mat(frame.size(),CvType.CV_8UC3,新标量(255,255,255));
frame.copyTo(前景,阈值img);
Utils.matToBitmap(前景、位图);
//返回前景;
alreadyRun=true;
返回位图;
}
负责色调的方法:

    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;
}
private double getHistAverage(Mat hsvImg,Mat hueValues)
{
//初始化
双平均=0.0;
Mat hist_色调=新Mat();
//0-180:色调值的范围
MatOfInt histSize=新MatOfInt(180);
列表色调=新的ArrayList();
色调。添加(色调值);
//计算直方图
Imgproc.calcHist(色调,新MatOfInt(0),新Mat(),hist_色调,histSize,新Matofloat(0179));
//获取图像的平均色调值
//(和(箱(高)*h))/(图像高度*图像宽度)
// -----------------
//相当于获取图像中每个像素的色调,添加它们,然后
//除以图像大小(高度和宽度)
对于(int h=0;h<180;h++)
{
//对于每个箱子,获取其值并将其乘以相应的
//色调
平均+=(历史色调获取(h,0)[0]*h);
}
//返回图像的平均色调
平均=平均/hsvImg.size().高度/hsvImg.size().宽度;
收益率平均值;
}
输入和输出示例:[

输入图像2并输出:

输入图像3并输出:
我会再试一次Grabcut,它是可用的最好的分割方法之一。这是我得到的


Grabcut的唯一问题是,您必须输入一个包含要提取的对象的矩形作为输入。除此之外,它工作得非常好。

事实上,正如其他人所说,仅使用色调阈值不太可能获得好的结果。您可以使用类似的方法,但速度更快

在引擎盖下,GrabCut计算前景和背景直方图,然后根据这些直方图计算每个像素成为FG/BG的概率,然后使用优化得到的概率图来获得分割

最后一步是最昂贵的,可能会被忽略,具体取决于应用程序。相反,您可以将阈值应用于概率图以获得分割。它可能(也将)比GrabCut更差,但比您当前的方法更好

在这种方法中,直方图模型的选择是非常重要的。在YUV或HSV等空间中,可以考虑2个通道,考虑3个RGB通道,或者考虑2个归一化RGB通道。你还必须为这些直方图选择合适的bin大小。o“过度训练”,但过大会降低精度。这两者之间的权衡是单独讨论的主题,简言之,我建议在开始时使用每个通道64个存储箱的RGB,然后看看哪些更改更适合您的数据

另外,如果你使用插值来获得数据块之间的值,你可以得到更好的粗分块结果。在过去,我使用了三线性插值,与根本没有插值相比,这是一种很好的方法


但是请记住,如果没有关于对象形状的先验知识,无论是抓取切割、阈值分割还是这种方法,都无法保证分割是正确的。

您查找平均色调的方法是错误的!您很可能知道,色调表示为角度,并在[0360]中取值因此,色调为360的像素基本上与色调为0的像素具有相同的颜色(两者均为纯红色)。同样,色调为350的像素实际上比色调为300的像素更接近色调为10的像素

对于opencv,
cvtColorcv::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