Ответ 1
Ваша гауссовская фильтрация в основном прекрасна, но вы рассматриваете радиус, который больше, чем нужно для задачи. Например, рассмотрим в качестве примера ядро радиуса 15. Вот представление того, что мы получаем:
Есть две прозрачные долины (да, показаны как пики), а гистограмма отфильтрованного изображения показывает, что большая часть доступных данных теперь близка к максимально возможному значению.
Учитывая только часть гистограммы, мы можем лучше видеть часть интересующих нас данных: темные пятна.
Итак, с простым порогом в 0.5
это результат (который соответствует тому, где находятся ошибки):
В зависимости от того, как вы реализуете (или библиотеки, которые используете внедрение) связанные функции, это пороговое значение будет отличаться. Но, посмотрев на гистограмму, вы сможете обнаружить хороший порог. Теперь, если вы не хотите угадать этот порог, посмотрев на гистограмму, вам нужно предварительно обработать изображение за пределами гауссовой фильтрации. Сделав этот шаг в хорошем манере, ваше изображение станет достаточно простым, чтобы методы, подобные тому, который дал Otsu, могут автоматически найти порог, которым вы пользуетесь. Выполняя морфологическое закрытие с последующим морфологическим открытием, а затем бинаризуя Otsu, это результат:
Формы ближе к исходным, так как мы не полагались на линейный фильтр нижних частот, который в лучшем случае будет размывать контуры.
EDIT:
Поскольку вопрос теперь включает некоторый код, я почувствовал необходимость объяснить, почему неправильно использовать Otsu в качестве кода, который он делает. Метод, заданный Otsu для порогового значения, фактически ожидает, что данные будут бимодальными, но, как показывают вышеприведенные графики гистограммы, здесь это не так. Otsu предоставит порог, который слишком близок к огромному пику справа, в то время как хорошая точка в 0.5 находится очень далеко от этого. Чтобы воспроизвести первый результат, показанный в этом ответе, вот какой-то базовый код:
import sys
import numpy
from PIL import Image
from scipy.ndimage import gaussian_filter, label
img = Image.open(sys.argv[1]).convert('L')
im = numpy.array(img)
im_g = gaussian_filter(im, 3)
im_norm = (im_g - im_g.min()) / (float(im_g.max()) - im_g.min())
im_norm[im_norm < 0.5] = 0
im_norm[im_norm >= 0.5] = 1
result = 255 - (im_norm * 255).astype(numpy.uint8)
print u"Objects: %d" % label(result)[1]
Image.fromarray(result).save(sys.argv[2])
Обратите внимание, что этот код использует sigma = 3
(хотя первоначально использовался 7.5) для гауссовского ядра, из которого scipy
внутренне строит окно с радиусом, в 4 раза превышающим его. Для этого конкретного изображения большой диапазон sigma
работает так же хорошо, начиная с 2 до 10, должны давать одинаковые результаты - обнаружено 2 объекта.