Рассмотрим эти 10 изображений различного количества сырых зерен белого риса.
ЭТО ТОЛЬКО ПУСТОЙ. Нажмите на изображение, чтобы просмотреть его в полном размере.
Количество зерен: A: 3, B: 5, C: 12, D: 25, E: 50, F: 83, G: 120, H:150, I: 151, J: 200
Заметить, что...
- Зерна могут касаться друг друга, но они никогда не пересекаются. Расположение зерен никогда не превышает одного зерна.
- Изображения имеют разные размеры, но масштаб риса во всех них одинаков, потому что камера и фон были неподвижны.
- Зерна никогда не выходят за границы и не касаются границ изображения.
- Фон всегда имеет одинаковый постоянный оттенок желтовато-белого цвета.
- Мелкие и крупные зерна считаются одинаковыми как одно зерно каждое.
Эти 5 баллов являются гарантией для всех изображений такого рода.
Вызов
Напишите программу, которая принимает такие изображения и максимально точно подсчитывает количество рисовых зерен.
Ваша программа должна взять имя файла изображения и распечатать количество зерен, которые оно рассчитывает. Ваша программа должна работать как минимум с одним из следующих форматов графических файлов: JPEG, Bitmap, PNG, GIF, TIFF (сейчас все изображения - JPEG).
Вы можете использовать библиотеки обработки изображений и компьютерного зрения.
Вы не можете жестко закодировать выходные данные 10 примеров изображений. Ваш алгоритм должен быть применим ко всем подобным изображениям рисового зерна. Он должен работать менее чем за 5 минут на приличном современном компьютере, если область изображения меньше 2000 * 2000 пикселей и в нем меньше 300 зерен риса.
счет
Для каждого из 10 изображений возьмите абсолютное значение фактического количества зерен минус количество зерен, предсказанное вашей программой. Суммируйте эти абсолютные значения, чтобы получить ваш счет. Самый низкий балл побеждает. Оценка 0 идеально.
В случае ничьих победит ответ, получивший наибольшее количество голосов. Я могу проверить вашу программу на дополнительных изображениях, чтобы проверить ее достоверность и точность.
источник
Ответы:
Математика, оценка: 7
Я думаю, что имена функций достаточно наглядны:
Обработка всех фотографий одновременно:
Оценка:
Здесь вы можете увидеть чувствительность счета к используемому размеру зерна:
источник
EdgeDetect[]
,DeleteSmallComponents[]
иDilation[]
реализуются в другом месте)Python, оценка:
2416Это решение, как и решение Фалько, основано на измерении площади «переднего плана» и делении ее на среднюю площадь зерна.
Фактически, эта программа пытается обнаружить фон, а не передний план. Используя тот факт, что рисовые зерна никогда не касаются границы изображения, программа начинается с заливки белым цветом в верхнем левом углу. Алгоритм заливки закрашивает смежные пиксели, если разница между их и яркостью текущего пикселя находится в пределах определенного порога, таким образом приспосабливаясь к постепенному изменению цвета фона. В конце этого этапа изображение может выглядеть примерно так:
Как вы можете видеть, он неплохо справляется с обнаружением фона, но пропускает все области, которые «зажаты» между зернами. Мы обрабатываем эти области, оценивая яркость фона в каждом пикселе и размещая все пиксели, равные или более яркие. Эта оценка работает следующим образом: на этапе заливки мы рассчитываем среднюю яркость фона для каждой строки и каждого столбца. Расчетная яркость фона в каждом пикселе является средним значением яркости строк и столбцов в этом пикселе. Это производит что-то вроде этого:
РЕДАКТИРОВАТЬ: Наконец, площадь каждой непрерывной области переднего плана (то есть, не белого) делится на среднюю, предварительно рассчитанную, площадь зерна, что дает нам оценку количества зерна в указанной области. Сумма этих величин является результатом. Первоначально мы делали то же самое для всей области переднего плана в целом, но этот подход, в буквальном смысле, более мелкозернистый.
Принимает имя входного файла через командную строку.
Результаты
источник
avg_grain_area = 3038.38;
взялась?hardcoding the result
?The images have different dimensions but the scale of the rice in all of them is consistent because the camera and background were stationary.
Это просто значение, которое представляет это правило. Результат, однако, меняется в зависимости от ввода. Если вы измените правило, то это значение изменится, но результат будет таким же - в зависимости от ввода.Python + OpenCV: оценка 27
Горизонтальное линейное сканирование
Идея: отсканируйте изображение, по одной строке за раз. Для каждой строки подсчитайте количество встречающихся рисовых зерен (проверив, становится ли пиксель черным или белым или наоборот). Если количество зерен для линии увеличивается (по сравнению с предыдущей строкой), это означает, что мы столкнулись с новым зерном. Если это число уменьшается, это означает, что мы прошли через зерно. В этом случае добавьте +1 к общему результату.
Из-за того, как работает алгоритм, важно иметь чистое ч / б изображение. Много шума дают плохие результаты. Сначала основной фон очищается с помощью заливки (решение, аналогичное ответу Ell), затем применяется порог для получения черно-белого результата.
Это далеко от совершенства, но это дает хорошие результаты в отношении простоты. Вероятно, есть много способов улучшить его (путем предоставления более качественного черно-белого изображения, сканирования в других направлениях (например, по вертикали, диагонали), принимая среднее значение и т. Д. ...)
Ошибки на изображение: 0, 0, 0, 3, 0, 12, 4, 0, 7, 1
источник
Python + OpenCV: оценка 84
Вот первая наивная попытка. Он применяет адаптивный порог с настраиваемыми вручную параметрами, закрывает некоторые отверстия с последующей эрозией и разбавлением и выводит количество зерен из области переднего плана.
Здесь вы можете увидеть промежуточные двоичные изображения (черный цвет переднего плана):
Ошибки на изображение составляют 0, 0, 2, 2, 4, 0, 27, 42, 0 и 7 зерен.
источник
C # + OpenCvSharp, Оценка: 2
Это моя вторая попытка. Это сильно отличается от моей первой попытки , которая намного проще, поэтому я публикую ее как отдельное решение.
Основная идея состоит в том, чтобы идентифицировать и маркировать каждое отдельное зерно с помощью итеративной подгонки эллипса. Затем удалите пиксели для этого зерна из источника и попытайтесь найти следующее зерно, пока каждый пиксель не будет помечен.
Это не самое симпатичное решение. Это гигантский боров с 600 строками кода. Для самого большого изображения нужно 1,5 минуты. И я действительно извиняюсь за грязный код.
В этом есть так много параметров и способов мыслить, что я очень боюсь подгонять свою программу к 10 образцам изображений. Окончательный результат 2 почти наверняка является случаем переобучения: у меня есть два параметра,
average grain size in pixel
иminimum ratio of pixel / elipse_area
, в конце, я просто исчерпал все комбинации этих двух параметров, пока не получил наименьшее количество очков. Я не уверен, что это все, что кошерно с правилами этого вызова.average_grain_size_in_pixel = 2530
pixel / elipse_area >= 0.73
Но даже без этих переоснащенных муфт результаты довольно хороши. Без фиксированного размера зерна или соотношения пикселей, просто путем оценки среднего размера зерна по тренировочным изображениям, оценка по-прежнему составляет 27.
И я получаю в качестве вывода не только число, но и фактическое положение, ориентацию и форму каждого зерна. существует небольшое количество неправильно маркированных зерен, но в целом большинство этикеток точно соответствуют реальным зернам:
A B C D E
F G H I J
(нажмите на каждое изображение для полноразмерной версии)
После этого шага маркировки моя программа просматривает каждое отдельное зерно и оценивает его на основе количества пикселей и отношения пикселей к площади эллипса.
Оценка ошибок для каждого изображения
A:0; B:0; C:0; D:0; E:2; F:0; G:0 ; H:0; I:0, J:0
Однако фактическая ошибка, вероятно, немного выше. Некоторые ошибки в одном и том же изображении отменяют друг друга. На изображении H, в частности, имеется несколько плохо маркированных зерен, тогда как на изображении E метки в основном правильные
Концепция немного надумана:
Сначала передний план отделяется через отсу-порог на канале насыщения (подробности см. В моем предыдущем ответе)
повторять до тех пор, пока не останется больше пикселей:
выберите 10 случайных краевых пикселей на этом блобе в качестве начальной позиции для зерна
для каждой отправной точки
предположим, что зерно с высотой и шириной 10 пикселей в этой позиции.
повторить до схождения
идите радиально наружу от этой точки, под разными углами, пока не встретите крайний пиксель (от белого к черному)
найденные пиксели должны быть крайними пикселями одного зерна. Попытайтесь отделить выбросы от выбросов, отбросив пиксели, которые находятся на более отдаленном расстоянии от предполагаемого эллипса, чем другие.
неоднократно пытаться подогнать эллипс через подмножество вкладышей, сохраняя лучший эллипс (RANSACK)
обновить положение зерна, ориентацию, ширину и высоту с помощью найденного эллипса
если положение зерна существенно не меняется, остановитесь
среди 10 подходящих зерен выберите лучшее зерно в соответствии с формой, количеством краевых пикселей. Откажитесь от других
удалите все пиксели для этого зерна из исходного изображения, затем повторите
наконец, просмотрите список найденных зерен и посчитайте каждое зерно либо 1 зерно, 0 зерен (слишком мало), либо 2 зерна (слишком много)
Одна из моих основных проблем заключалась в том, что я не хотел реализовывать полную метрику расстояния между точками эллипса, поскольку ее вычисление само по себе является сложным итеративным процессом. Поэтому я использовал различные обходные пути, используя функции OpenCV Ellipse2Poly и FitEllipse, и результаты не слишком привлекательны.
Видимо, я также нарушил ограничение по размеру для Codegolf.
Ответ ограничен 30000 символами, в настоящее время я нахожусь на 34000. Поэтому мне придется несколько сократить код ниже.
Полный код можно увидеть на http://pastebin.com/RgM7hMxq
Извините за это, я не знал, что был предел размера.
Я немного смущен этим решением, потому что: а) я не уверен, соответствует ли оно духу этой задачи, и б) оно слишком велико для ответа Codegolf и не обладает элегантностью других решений.
С другой стороны, я очень доволен прогрессом, которого я достиг в маркировке зерен, а не просто в подсчете, так что это так.
источник
C ++, OpenCV, оценка: 9
Основная идея моего метода довольно проста - попробуйте стереть одиночные зерна (и «двойные зерна» - 2 (но не больше!) Зерна, близкие друг к другу) из изображения, а затем сосчитать остальные, используя метод, основанный на области (например, Falko, Элл и Велисарий). Использование этого подхода немного лучше, чем стандартный «метод области», потому что легче найти хорошее среднее значениеPixelsPerObject.
(1-й шаг) Прежде всего нам нужно использовать бинаризацию Оцу на S-канале изображения в HSV. Следующим шагом является использование оператора расширения для улучшения качества извлекаемого переднего плана. Чем нам нужно найти контуры. Конечно, некоторые контуры не являются рисовыми зернами - нам нужно удалить слишком маленькие контуры (с площадью, меньшей, чем AveragePixelsPerObject / 4. В моем случае, AveragePixelsPerObject равен 2855). Теперь, наконец, мы можем начать считать зерна :) (2-й шаг) Найти одинарные и двойные зерна довольно просто - просто посмотрите в списке контуров контуры с областью в определенных диапазонах - если область контура находится в диапазоне, удалите его из списка и добавьте 1 (или 2, если это было «двойное» зерно) к счетчику зерна. (3-ий шаг) Последний шаг - это, конечно, деление площади оставшихся контуров на значение AveragePixelsPerObject и добавление результата в счетчик зерен.
Изображения (для изображения F.jpg) должны показать эту идею лучше, чем слова:
1-й шаг (без маленьких контуров (шум)):
2-й шаг - только простые контуры:
3-й шаг - остальные контуры:
Вот код, он довольно уродливый, но должен работать без проблем. Конечно OpenCV требуется.
Если вы хотите увидеть результаты всех шагов, раскомментируйте все вызовы функций imshow (.., ..) и установите для переменной fastProcessing значение false. Изображения (A.jpg, B.jpg, ...) должны находиться в каталоге изображений. В качестве альтернативы вы можете указать имя одного изображения в качестве параметра из командной строки.
Конечно, если что-то неясно, я могу объяснить это и / или предоставить некоторые изображения / информацию.
источник
C # + OpenCvSharp, оценка: 71
Это очень неприятно, я пытался найти решение, которое фактически идентифицирует каждое зерно, используя водораздел , но я просто. не может. получить. Это. к. работай.
Я остановился на решении, которое, по крайней мере, отделяет некоторые отдельные зерна, а затем использует эти зерна для оценки среднего размера зерна. Однако до сих пор я не могу превзойти решения с жестко закодированным размером зерна.
Итак, главная изюминка этого решения: оно не предполагает фиксированный размер пикселя для зерен и должно работать, даже если камера перемещается или тип риса изменяется.
Мое решение работает так:
Разделите передний план, преобразовав изображение в HSV и применив пороговое значение Otsu к каналу насыщения. Это очень просто, работает очень хорошо, и я бы порекомендовал это всем, кто хочет попробовать эту задачу:
->
Это будет чисто удалить фон.
Затем я дополнительно удалил тени зерен с переднего плана, применив фиксированный порог к каналу значений. (Не уверен, что это действительно очень помогает, но это было достаточно просто добавить)
Затем я применяю преобразование расстояния на переднем плане изображения.
и найти все локальные максимумы в этом преобразовании расстояния.
Вот где моя идея рушится. чтобы избежать получения нескольких локальных максимумов в пределах одного зерна, мне приходится много фильтровать. В настоящее время я держу только самый сильный максимум в радиусе 45 пикселей, что означает, что не у каждого зерна есть локальный максимум. И у меня нет никакого оправдания для радиуса 45 пикселей, это было просто значение, которое сработало.
(как видите, этих семян недостаточно для учета каждого зерна)
Затем я использую эти максимумы в качестве семян для алгоритма водораздела:
Результаты Мех . Я надеялся в основном на отдельные зерна, но сгустки все еще слишком велики.
Теперь я идентифицирую самые маленькие капли, подсчитываю их средний размер в пикселях, а затем оцениваю количество зерен из этого. Это не то, что я планировал сделать в начале, но это был единственный способ спасти это.
Небольшой тест с использованием жестко закодированного размера пикселя на зерно 2544,4 показал общую ошибку 36, которая все еще больше, чем у большинства других решений.
источник
HTML + Javascript: оценка 39
Точные значения:
Разбивается (не точно) на большие значения.
Объяснение: По сути, подсчитывает количество рисовых пикселей и делит его на среднее количество пикселей на зерно.
источник
Попытка с php, не самый низкий ответ, но довольно простой код
Оценка: 31
Самостоятельная оценка
95 - синее значение, которое, похоже, работает при тестировании с GIMP 2966 - средний размер зерна
источник