Подтвердить что ты не робот

Opencv увеличивает точность порога

Я работаю над приложением, которое, как ожидается, удалит фоны изображений с помощью opencv, сначала я попытался использовать grabcut, но он был слишком медленным, и результаты не всегда были точными, тогда я попытался использовать порог, хотя результаты еще не достигнуты закрыть th 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;

}

метод, отвечающий за 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;
}

Образец ввода и вывода: [Input Image1]Выходное изображение

Входное изображение 2 и выход: введите описание изображения здесь введите описание изображения здесь

Входное изображение 3 и выход: введите описание изображения здесь введите описание изображения здесь

4b9b3361

Ответ 1

Действительно, как говорили другие, вы вряд ли получите хорошие результаты только с порогом на оттенке. Вы можете использовать что-то похожее на GrabCut, но быстрее.

Под капотом GrabCut вычисляет гистограммы переднего плана и фона, затем вычисляет вероятность того, что каждый пиксель будет FG/BG на основе этих гистограмм, а затем оптимизирует полученную карту вероятности с помощью graph для получения сегментации.

Последний шаг является самым дорогим, и его можно игнорировать в зависимости от приложения. Вместо этого вы можете применить порог к карте вероятности для получения сегментации. Он может (и будет) хуже, чем GrabCut, но будет лучше, чем ваш текущий подход.

Есть несколько моментов для рассмотрения этого подхода. Выбор гистограммы был бы очень важен. Вы можете рассмотреть два канала в каком-то пространстве, например, YUV или HSV, рассмотреть 3 канала RGB или рассмотреть 2 канала нормализованного RGB. Вы также должны выбрать соответствующий размер бункера для этих гистограмм. Слишком маленькие бункеры приведут к "перетренированности", в то время как слишком большие уменьшат точность. Компромиссы между ними - тема для отдельного обсуждения, вкратце - я бы посоветовал использовать RGB с 64 бункерами на канал для начала, а затем посмотреть, какие изменения лучше для ваших данных.

Кроме того, вы можете получить лучшие результаты для грубого биннинга, если вы используете интерполяцию для получения значений между бункерами. В прошлом я использовал трилинейную интерполяцию, и это было хорошо, по сравнению с отсутствием интерполяции.

Но помните, что нет никаких гарантий того, что ваша сегментация будет правильной без предварительного знания формы объекта, либо с помощью GrabCut, либо с помощью порога или такого подхода.

Ответ 2

Я бы попробовал снова 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 заключается в том, что вы должны указывать в качестве прямоугольника прямоугольник, содержащий объект, который вы хотите извлечь. Кроме того, он работает очень хорошо.

Ответ 3

Ваш метод поиска среднего оттенка НЕПРАВИЛЬНО! Как вы, скорее всего, знаете, оттенок выражается как угол и принимает значение в диапазоне [0,360]. Следовательно, пиксель с оттенком 360 по существу имеет тот же цвет, что и пиксель с оттенком 0 (оба являются чистыми красными). Точно так же пиксель с оттенком 350 на самом деле ближе к пикселю с оттенком 10, чем пиксель с оттенком, например, 300.

Что касается opencv, функция cvtColor фактически делит рассчитанное значение оттенка на 2, чтобы поместить его в 8-битное целое число. Таким образом, в opencv значения оттенка обертываются после 180. Теперь рассмотрим, что у нас есть два красных (иш) пикселя с оттенками 10 и 170. Если мы возьмем их среднее значение, мы получим 90 — оттенок чистого голубого, полная противоположность красного цвета; который не является нашим желаемым значением.

Поэтому, чтобы правильно найти средний оттенок, вам нужно сначала найти среднее значение пикселя в цветовом пространстве RGB, а затем вычислить оттенок из этого значения RGB. Вы можете создать матрицу 1x1 со средним пикселем RGB и преобразовать ее в HSV/HSL.

Следуя тем же соображениям, применение threshold к оттенку изображения не работает безупречно. Он не учитывает обертывание значений оттенков.

Если я правильно понял, вы хотите найти пиксели с похожим оттенком в качестве фона. Предполагая, что мы знаем цвет фона, я бы сделал эту сегментацию в пространстве RGB. Я бы ввел некоторую переменную tolerance. Я бы использовал значение фонового пикселя как центр и этот допуск как радиус и тем самым определял сферу в цветовом пространстве RGB. Теперь rest проверяет каждое значение пикселя, если оно попадает внутрь этой сферы, затем классифицируется как фон; в противном случае рассматривайте его как пиксель переднего плана.