Как получить пороговое значение от гистограммы?

Я пишу приложение Android в OpenCV для обнаружения капли. Одна задача - пороговое изображение, чтобы отличать объекты переднего плана от фона (см. Изображение).

Он отлично работает, пока изображение известно, и я могу вручную передать пороговое значение порогу() - в этом конкретном изображении говорят 200. Но если предположить, что изображение неизвестно с единственным знанием о том, что быть темным сплошным фоном и более светлыми объектами переднего плана, как я могу динамически определить пороговое значение?

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

enter image description here

Ответ 1

Если все ваши изображения подобны этому или могут быть перенесены в этот стиль, я думаю, что cv2.THRESHOLD_OTSU, то есть алгоритм otsu tresholding - хороший снимок.

Ниже приведен пример с использованием Python в командном терминале:

>>> import cv2
>>> import numpy as np
>>> img2 = cv2.imread('D:\Abid_Rahman_K\work_space\sofeggs.jpg',0)

>>> ret,thresh = cv2.threshold(img2,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

>>> ret
122.0

ret - это пороговое значение, которое автоматически рассчитывается. Для этого мы просто передаем "0" в качестве порогового значения.

Я получил 124 в GIMP (что сопоставимо с результатом, который мы получили). И это также снимает шум. См. Результат ниже:

enter image description here

Ответ 2

Если вы говорите, что фон темный (черный), а передний план светлее, я рекомендую использовать цветовое пространство YUV ( или любой другой YXX, например YCrCb и т.д.), поскольку первый компонент таких цветовых пространств яркость (или молния strong > ).

light channel

Итак, после выделения канала Y (через функцию extractChennel) нам необходимо проанализировать гистограмму этого канала (изображения):

histogram

Посмотрите на первый (левый) горб? Он представляет темные области (фон в вашей ситуации) на вашем изображении. Поэтому наша цель - найти сегмент (по абсциссе, красная часть в изображении), в котором содержится этот горб. Очевидно, что левая точка этого отрезка равна нулю. Правая точка - это первая точка, где:

  • (локальный) максимум гистограммы находится слева от точки
  • значение гистограммы меньше некоторого небольшого эпсилона (вы можете установить его на 10)

Я нарисовал зеленую вертикальную линию, чтобы показать расположение правой точки сегмента на этой гистограмме.

И это! Эта правая точка сегмента является необходимым порогом. Здесь результат (эпсилон равен 10, а расчетный порог равен 50):

result

Я думаю, что это не проблема для удаления шума на изображении выше.

Ответ 3

Ниже приведена реализация С++-реализации ответа Abid, который работает с OpenCV 3.x:

// Convert the source image to a 1 channel grayscale:
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
// Apply the threshold function with the CV_THRESH_OTSU setting as well
// You can skip having it return the value, but I include it for showing the
// results from OTSU
double thresholdValue = threshold(gray, gray, 0, 255, CV_THRESH_BINARY+CV_THRESH_OTSU);
// Present the threshold value
printf("Threshold value: %f\n", thresholdValue);

Запуская это на исходное изображение, я получаю следующее: введите описание изображения здесь

OpenCV рассчитал для него пороговое значение 122, близкое к значению Абида, найденному в его ответе.

Чтобы проверить, я изменил исходное изображение, как показано здесь:

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

И произвел следующее, с новым пороговым значением 178:

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