Надежная сегментация изображений в OpenCV

#c #opencv #image-processing #adaptive-threshold

#c #opencv #обработка изображений #адаптивный-пороговый

Вопрос:

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

Проблема, с которой я сталкиваюсь, заключается в том, что условия освещения могут сильно различаться, поэтому даже при моих адаптивных пороговых значениях точность алгоритма также сильно варьируется. Если на изображении есть градиент яркости, кажется, что это работает особенно плохо. Иногда объекты очень яркие на фоне, а в других случаях они имеют почти одинаковую яркость. Существуют ли какие-либо особенно эффективные способы поиска объектов в условиях различной освещенности?

Примеры изображений: img
gif

Комментарии:

1. en.wikipedia.org/wiki/Histogram_equalization на уровне патчей (разделите изображение, например, на 5×5 = 25 патчей, чтобы вы могли оценить другую статистику при разном освещении)

2. Я чувствую, что это будет иметь последствия для исправлений, которые перекрывают разные яйца и регионы без яиц. Я использовал adaptiveThreshold() с большими размерами блоков, и он всегда обнаруживал посторонние объекты, хотя и помогал с градиентным освещением.

Ответ №1:

Поскольку все, что больше 100 пикселей, не имеет отношения к вашему изображению, я бы создал полосовой фильтр Фурье для удаления этих структур.

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

 static void GenerateBandFilter(thrust::host_vector<float>amp; filter, const BandPassSettingsamp; band, const FrameSizeamp; frame)
    {
        //From https://imagej.nih.gov/ij/plugins/fft-filter.html
        if (band.do_band_pass == false)
        {
            return;
        }
        if (frame.width != frame.height)
        {
            throw std::runtime_error("Frame height and width should be the same");
        }
        auto maxN = static_cast<int>(std::max(frame.width, frame.height));//todo make sure they are the same

        auto filterLargeC = 2.0f*band.max_dx / maxN;
        auto filterSmallC = 2.0f*band.min_dx / maxN;
        auto scaleLargeC = filterLargeC*filterLargeC;
        auto scaleSmallC = filterSmallC*filterSmallC;

        auto filterLargeR = 2.0f*band.max_dy / maxN;
        auto filterSmallR = 2.0f*band.min_dy / maxN;
        auto scaleLargeR = filterLargeR*filterLargeR;
        auto scaleSmallR = filterSmallR*filterSmallR;

        // loop over rows
        for (auto j = 1; j < maxN / 2; j  )
        {
            auto row = j * maxN;
            auto backrow = (maxN - j)*maxN;
            auto rowFactLarge = exp(-(j*j) * scaleLargeR);
            auto rowFactSmall = exp(-(j*j) * scaleSmallR);
            // loop over columns
            for (auto col = 1; col < maxN / 2; col  )
            {
                auto backcol = maxN - col;
                auto colFactLarge = exp(-(col*col) * scaleLargeC);
                auto colFactSmall = exp(-(col*col) * scaleSmallC);
                auto factor = (((1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall));
                filter[col   row] *= factor;
                filter[col   backrow] *= factor;
                filter[backcol   row] *= factor;
                filter[backcol   backrow] *= factor;
            }
        }
        auto fixy = [amp;](float t){return isinf(t) ? 0 : t; };
        auto rowmid = maxN * (maxN / 2);
        auto rowFactLarge = fixy(exp(-(maxN / 2)*(maxN / 2) * scaleLargeR));
        auto rowFactSmall = fixy(exp(-(maxN / 2)*(maxN / 2) *scaleSmallR));
        filter[maxN / 2] *= ((1 - rowFactLarge) * rowFactSmall);
        filter[rowmid] *= ((1 - rowFactLarge) * rowFactSmall);
        filter[maxN / 2   rowmid] *= ((1 - rowFactLarge*rowFactLarge) * rowFactSmall*rowFactSmall); //
        rowFactLarge = fixy(exp(-(maxN / 2)*(maxN / 2) *scaleLargeR));
        rowFactSmall = fixy(exp(-(maxN / 2)*(maxN / 2) *scaleSmallR));
        for (auto col = 1; col < maxN / 2; col  ){
            auto backcol = maxN - col;
            auto colFactLarge = exp(-(col*col) * scaleLargeC);
            auto colFactSmall = exp(-(col*col) * scaleSmallC);
            filter[col] *= ((1 - colFactLarge) * colFactSmall);
            filter[backcol] *= ((1 - colFactLarge) * colFactSmall);
            filter[col   rowmid] *= ((1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall);
            filter[backcol   rowmid] *= ((1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall);
        }
        // loop along column 0 and expanded_width/2
        auto colFactLarge = fixy(exp(-(maxN / 2)*(maxN / 2) * scaleLargeC));
        auto colFactSmall = fixy(exp(-(maxN / 2)*(maxN / 2) * scaleSmallC));
        for (auto j = 1; j < maxN / 2; j  ) {
            auto row = j * maxN;
            auto backrow = (maxN - j)*maxN;
            rowFactLarge = exp(-(j*j) * scaleLargeC);
            rowFactSmall = exp(-(j*j) * scaleSmallC);
            filter[row] *= ((1 - rowFactLarge) * rowFactSmall);
            filter[backrow] *= ((1 - rowFactLarge) * rowFactSmall);
            filter[row   maxN / 2] *= ((1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall);
            filter[backrow   maxN / 2] *= ((1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall);
        }
        filter[0] = (band.remove_dc) ? 0 : filter[0];
    }
  

введите описание изображения здесь

Вы можете просмотреть мой код, который использует его здесь: https://github.com/kandel3/DPM_PhaseRetrieval

Комментарии:

1. Хотя я еще не проверил, что это помогает в данном случае (поскольку внутренняя часть яиц, вероятно, значительно видна при такой форме определения границ), это, безусловно, интересно и привело к кроличьей норе обработки изображений с помощью полосовых фильтров.

Ответ №2:

Вычисление альфа- и бета-значений изображения image = cv::imread(«F:Dilated.jpg «); int x,y; int a= 0; // переменные, которые будут использоваться в цикле int count=0; // переменные, которые будут использоваться в цикле

   for( int y = 0; y < image.rows; y   )
  { for( int x = 0; x < image.cols; x   )
      { for( int c = 0; c < 3; c   )
          {
                   image.at<Vec3b>(y,x)[c] =
            saturate_cast<uchar>( alpha*(     image.at<Vec3b>(y,x)[c] )   beta );
           }
       }
  }
  

Комментарии:

1. Как вычислить альфа- и бета-версии? Похоже, вы выполняете какую-то линейную регрессию, но я не понимаю, к чему вы подходите.