Pull to refresh

Comments 43

Неплохо. Несколько замечаний.

1). Не надо усложнять задачу нейронной сетью. Сейчас анализ изображений и нейронная сеть стали синонимами, но в узких задачах это не нужно. Не стоит заморачиваться отсеиванием пейзажа, который может давать мешающие контуры. Пользователь вашей программы заинтересован сосканировать этикетку, и у него есть несколько попыток, если что-то не получилось. Другое дело, если бы вы работали с единственной фотографией, которую невозможно переснять.

2). Я вижу гораздо больше проблем для этикеток вида 1.allegroimg.com/s1440/01c82d/96505f864bb09b4139713eeb3cb1, где присутствуют прямоугольники или же прямые линии в рисунке на самой этикетке.

3). Я бы предложила иной алгоритм для вашей задачи. Попросить пользователя снять видео или 2-3 фото, проворачивая бутылку. Далее вы находите ключевые точки, и зная, что значительная их часть (кроме фоновой обстановки) находится на цилиндре, вычисляете координатную сетку. Такому алгоритму будет всё равно, какой формы этикетка, и что на ней нарисовано.
Как постоянный пользователь vivino, покупающий вино каждый день, могу отметить, что ваш п.3 не очень удобен. Предложение снимать бутылку вина с разных ракурсов приводит к необходимости брать бутылку с полки. Обычно в процессе обсуждения с кавистом отбираю для себя несколько вариантов и сразу снимаю их на полке/в шкафу. Как вы представляете себе отбор из 10 бутылок, когда каждую необходимо снять, потом одной рукой держать, а второй снимать?
Поддерживаю, особенно если учесть что съемка зачастую происходит после употребления содержимого бутылки
Я бы не сказал, что здесь будет усложнение нейросетью — как раз наобарот, если задача включает слишком много дополнительных условий и исключений, то это подходящая задача для нейронки. А с точки зрения пользователя, он не станет постоянно переснимать, а просто проигнорирует фичу, либо заминусует приложение.
2). Я вижу гораздо больше проблем для этикеток вида 1.allegroimg.com/s1440/01c82d/96505f864bb09b4139713eeb3cb1, где присутствуют прямоугольники или же прямые линии в рисунке на самой этикетке.

Обычно более длинная линия определяется лучше, чем более контрастная, но короткая. Тем не менее, такие этикетки действительно определяются не так хорошо. Еще есть очень распространенная ситуация, когда этикетка наклеена не по всей ширине бутылки, и тогда края ее растягиваются слишком сильно (хотя трасформированая этикетка все-равно лучше читается, чем оригинальная).
Преобразование Хафа ничего не знает про длину линий. Большая яркость линии для Хафа всё равно что большая длина.
  1. Во главе всех задач стоит пользователь. И если вы создаете продукт который должен быть лучшим, чтобы быть успешным (а агрегаторы и сервисы для обмена опытом, которым относится Вивино относятся именно к таким продуктам), то ваш совет очень вреден. Именно заморочиться стоит, потому что несколько попыток — плохой ux, особенно учитывая, что есть еще миллион причин, по которым что то может пойти не так, например плохая связь в магазине или слишком широкий асортимент.
  2. А можно предложить пользователю написать все данные руками и отправить по электронной почте — тогда даже приложение делать не придется
    К слову, вивино очень хорошо и очень быстро распознает этикетки в последнее время, чему я несказанно радуюсь, будучи в магазине.
Во главе всех задач стоит пользователь. И если вы создаете продукт который должен быть лучшим, чтобы быть успешным (а агрегаторы и сервисы для обмена опытом, которым относится Вивино относятся именно к таким продуктам), то ваш совет очень вреден. Именно заморочиться стоит, потому что несколько попыток — плохой ux, особенно учитывая, что есть еще миллион причин, по которым что то может пойти не так, например плохая связь в магазине или слишком широкий асортимент.

Да, в идеале сканирование этикеток должно быть таким же, как и баркодов — держишь камеру, а когда продукт распознан, то экран сам перключит на результат. На практике, можно использовать сервис распознавания картинок vuforia — он примерно так и работает — шлет поток изображений на сервер в real-time, пока не распознает продукт с нужной степенью вероятности. Но мы от vuforia отказались из-за кривого REST API на добавление таргетов в базу и немилосердного ценника при большой базе продуктов.
Сейчас используем tineye, но он, к сожалению, работает по принципу запрос-ответ, поэтому realtime распознавания не поддерживается (если только не слать пачками запросы на сервис, но т.к. биллинг идет на каждый запрос, то получится весьма дорого). Так что функциональностью пришлось пожертвовать в угоду ценника — сейчас получается 1 цент на запрос, при этом база продуктов практически неограничена (на практике — это около 300к вин).
держишь камеру, а когда продукт распознан, то экран сам перключит на результат


Я это и имела ввиду. Просьба пользователя повернуть бутылку при этом не такая уж и сложная.

А попробуйте прямо на клиенте распознавать. Тот алгоритм, который сейчас у вас, мне кажется, должен неплохо шевелиться и на смартфоне.
Губораскаточная тогда уже.
Что если применить поиск ключевых точек, например FAST найдет углы, скорее всего это будут края этикетки. Еще как-то отсеять точки, найденные в других местах, параметры покрутить…
Нужен метод, как распознать среди всего многообразия нужные фичи, я не сильно представляю, какие методики для этого применять.
Мы решали похожу задачу — поиск картины по фото пользователя (250к картин, время обработки запроса ~ 1 сек), думаю, метод к вам вполне применим, вот даже пару статей написали: habr.com/company/singularis/blog/421187
Вам добавить поверх распознавание текста, которое у вас уже есть, и вообще конфетка.

алгоритм не учитывает искажение перспективы самого эллипса

Может не понял, а в чем проблема взять точки ACFD, сказать, что они прямоугольник (для большинства этикеток это так), и сделать обратное перспективное преобразование на основе этого, эллипсы волшебным образом разве не исправятся тоже?
Да, если бы мы делали свой собственный поиск картинки вместо tineye, то возможно смогли бы сделать процесс распознавания куда более интерактивным. Но как обычно — пока мажорными фичами остаются закругленные кнопочки в корзине товаров (грубо говоря), такие фичи лежат на полке и ждут своего времени :)
На счет обратного перспективного преобразования — он работает для прямоугольника, а здесь целая сетка прямоугольников, и у каждой ячейки этикетки — свои параметры этого преобразования.
А вы случайно не знаете где достать базу винных этикеток? :)

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

Просто вы там писали про угол наклона камеры, а если исправить общее искажение (бутылка была не под 90 градусов к камере), то его влияние нивелируется.
Перспективное искажение эллипсов будет в любом случае, т.к. разные части этикетки имет разное расстояние — центральная часть чуть ближе. На практике, точки A, C, D, F — это не крайние точки, а лучи по касательной от глаза наблюдателя, но по большому счету, этими искажениями можно пренебречь :)
Базу этикеток (примерно 50к продуктов) можно достать, к примеру, здесь — api.wine.com/wiki
Качество многих картинок, правда, не очень, но для распознавания хватает :)
А что за приложение-то? Самое главное и не написали.

vivino
вторая строчка

Нет, не vivino, написал двусмысленно. Приложение — интернет магазин для продажи вин в США. Название не пишу, так как приложения нацелены на локального пользователя.
Приложение сильно нацелено на локального пользователя, поэтому не пишу название. А так — интернет магазин для продажи вин.
UFO just landed and posted this here

А что такое разверстка бутылки? Это как этикетка, только вся поверхность, включая стекло?

UFO just landed and posted this here
Речь идет о том, чтобы натянуть текстуру на 3д модель? Или получить текстуру с бутылки?
Если натянуть текстуру — то это суперстандартная задача из 3д моделирования.
А чтобы получить текстуру — то можете сделать интерфейс, в котором указать 6 ключевых точек руками, а затем вызвать функцию ниже:

unwrapper = LabelUnwrapper(src_image=imcv, percent_points=points)
dst_image = unwrapper.unwrap()


где imcv — это numpy массив изображения, а percent_points — координаты точек в процентах от размера изображения (если удобнее, то можно использовать pixel_points, чтобы указывать координаты в пикселях, см github.com/Nepherhotep/unwrap_labels/blob/master/unwrap.py#L17)
UFO just landed and posted this here
Почему нельзя считывать информацию с акцизной марки вместо этикетки?
Машинное обучение не нужно там, где можно написать алгоритм. Вы же не будете писать нейронку, которая сортирует массив?

Получение градиента изображения — хорошо. Нахождение линий цилиндра — тоже, хотя надо написать свое преобразование Хафа, поскольку в имплементации OpenCV есть два дефекта: он не взвешивает точки по силе градиента (т.е. для него пиксели бинарны — или ноль, или не ноль) и не возвращает аккумулятор. Имея аккумулятор можно, например, брать в анализ только самые сильные линии. Можно размывать (не забывая про wrap around) по rho, чтобы получить самые сильные толстые линии. Правда, на Питоне его не напишешь, слишком медленный язык, разве что на Cython.

Как альтернатива, можно использовать LSD детектор (в том же OpenCV), он быстрый и хорошо находит фрагменты линии. Далее их можно соединять и получать длинные отрезки.

Следующий момент — поскольку у нас уже есть две линии, то можно их преобразовать, чтобы сделать их параллельными. Это в том числе упростит поиск эллипсов.

Еще вариант — собрать точки градиента и рассмотреть задачу как задачу оптимизации модели цилиндра под перспективными искажениями, как это делается в фотограмметрии.

А вообще классная задача, такие приятно решать.
Машинное обучение не нужно там, где можно написать алгоритм

Везде можно написать алгоритм => машинное обучение не нужно?
Видите ли, мне кажется, что у вас не совсем правильное понимание машинного обучения.
Вот, например:
Получение градиента изображения — хорошо. Нахождение линий цилиндра — тоже, хотя надо написать свое преобразование Хафа

Свертки в процессе обучения построят свое преобразование, которое намного лучше будет решать конкретно эту задачу. Все аналитические преобразования по сути частные случаи того, что сеть может построить сама (за редкими исключениями). И как правило, качество будет лучше.
Еще вы почему-то не замечаете, что основную работу делают Google и Tineye, вы думаете, они работают на аналитических алгоритмах?
Машинное обучение на текущем этапе лучший способ решить подобные задачи, смиритесь и используйте это, а не противьтесь. Аналитика — это подбор методов и параметров силами человека, ML — силами машины.
Правда, на Питоне его не напишешь, слишком медленный язык, разве что на Cython.

Не уверен, но думаю тот же Numpy не сильно медленней будет, в задачах CV слухи о низкой производительности питона в большинстве случаев слухи, так как очень много самых разных производительных библиотек, где питон дергает только методы, которые чаще всего на C/C++.
Дело в том, что здесь у нас строгая математическая модель цилиндра и строгая математическая модель проекции цилиндра на плоскость. Зачем обучать нейронную сеть, тратить ресурсы, память, если вся необходимая информация у нас уже есть? Грубо говоря, массив надо просто отсортировать. А для машинного обучения, чтобы модель обнаружила нужные свертки, надо еще предоставить миллион изображений под разными углами, и все ради чего, ради нескольких формул, которым она обучится, но которые, при этом, были известны априори?

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

Что касается производительности Python в задачах CV, то если дергать только стандартные методы, то все ок. В компьютерном зрении приходится много экспериментировать, поэтому Питон здесь хорошо работает. Но проблема Питона в том, что он осуществляет постоянную аллокацию/деаллокацию маленьких объектов, поэтому если надо делать какие-то низкоуровневые микрооперации, то Питон сильно уступает C/C++. Иногда в сотни раз! Попробуйте реализовать преобразование Хафа, чтобы это было быстро на Питоне.
Дело в том, что здесь у нас строгая математическая модель цилиндра и строгая математическая модель проекции цилиндра на плоскость

Тогда я вас не так понял, я про весь комплекс задач: нахождение нужных точек на этикетке, распознавание текста и самой этикетки. Исключительно для выравнивания цилиндра вопросов нет :)
то он осуществляет постоянную аллокацию/деаллокацию маленьких объектов

Ну, массивы Numpy вполне себя ведут как массивы C++ (то есть им надо заранее указать размер, их можно передавать по ссылке, переписывать значения без аллокаций и тд), так же есть все необходимые операции для работы с этими массивами. Я не спорю, что дефолтные типы данных питона (списки, словари и тд) медленные, я также не спорю, что плюсы быстрее, но при правильном использовании питона в CV он уступает в единицы раз (1,5-2), а не в сотни, а скорость разработки значительно быстрее все-таки. Поэтому для прототипирования питон лучше, в продакшене лучше переписать конечно. Если совсем заморочиться, то можно написать преобразование Хафа на PyTorch или TF и гонять их на видеокарте со скоростью звука.
А от вызова написать быстрое преобразование Хафа спешу откланяться)
NumPy удобен, но на каждую операцию он создает новый объект в памяти, даже если записать `a[:] = b + c`. Это приводит к поразительному количеству аллокаций/деаллокаций там, где на плюсах будет прямая работа со структурами в стеке. Это хорошо, если задачу можно векторизовать и привести к операциям на матрицах большого размера, и то — на С++ при грамотном использовании SIMD инструкций и грамотном использовании кэша получится быстрее. Но иногда процесс итеративный и нелинейный, для каждой строки матрицы данных может понадобится разное количество итераций, в итоге Python вязнет, как в болоте.
На практике, numpy работает достаточно быстро, а если есть бутылочное горлышко — то можно (и нужно) оптимизировать только его (как я и делал для преобразования Хафа github.com/Nepherhotep/unwrap_labels/blob/detect_ellipses/c_avg_for_ellipse.pyx)

Конечно, если весь проект посвящен только обработке изображений, то изначальное использование C++ будет более оптимальным вариантом.
Нейронная сеть нужна, чтобы найти 6 маркеров — определить их в многообразии нестандартных вариантов (бутылка среди других бутылок, нестандартные этикетки, линии на этикетке). Теоретически, конечно, это всего-лишь цилиндр, но дьявол кроется в деталях. Что касается развертывания, когда сетка уже построена — питон получается вполне быстрым, т.к. на самом деле, работают сишные модули расширения от OpenCV, а инициализация 100 небольших массивов (ячейки сетки) в самом питоне — очень быстрая операция.
На счет преобразования Хафа, я могу сказать точную разницу между Python и Cython, т.к. писал оба варианта — отличие в 200 раз.
Я так и не понял, как именно выполняется разворот изображения на плоскость?
Насчёт решения с полками. Можно попробовать YOLO(Можно и чего попроще, но реал тайм важен для юзера всё же) натренировать под это дело, а его выход: боксы с каждой бутылкой вина, просто передавать в имеющийся алгоритм для разворачивания этикеток. И учитывая то, что бутылки редко стоят не вертикально, должно работать. Точно видел на гите пару реализаций для андроида.
Сразу предполагаю здесь проблему: если бутылки на полках стоят слишком близко, окна YOLO могут несколько перекрывать друг друга. Но это легко отловить и можно некоторой эвристикой просто в нужных случаях чуточку обрезать окна по бокам.
И прям в реал тайме выводить оценку рядом с каждой бутылкой на полке! Хочу такое, очень :)
Да, если бы мы сделали свой поиск по изображениям, как вы с картинами, то можно было бы и такое сварганить :)
А пока мы используем tineye с ценником 1 цент за поиск, то поточные поисковые запросы обойдутся слишком дорого (к тому же, можем нарваться на тротлинг). Разве что, если ее детектировать, и трекать, а запрос на распознавание делать только один раз, когда мобильное приложение «захватило» ее.
Немного покопался в YOLO и сопутствующей информации.
Есть довольно много тредов на гите насчёт переобучения YOLO под свои нужды. В том числе под единственный класс. Вопрос только в переносимости на андроид, тут я не специалист, конечно.

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

2) В тестовой выборке хорошо бы поиграть с расположением бутылок относительно центра съёмки. Всё же на данный момент бутылка фотографируется без отклонений в угле, так как располагается в центре и является единственной. И здесь может быть ряд проблем, которые так просто не устраняются. Возможно будет разумно ограничить пользователя некоторой «зоной» фотографии, в которой он может рассчитывать получить результат, а зону отпозиционировать уже методом проб и ошибок исходя из средней дистанции, с которой производят фотографии.
Рекомендую еще посмотреть SSD (сильно больше вариантов реализаций, к тому же Yolo очень кастомное решение и порог входа там выше) и TensorFlow Lite
Затем нужно получить контуры бутылки с этикеткой. Для этого мы используем трансформацию sobel. Если вкратце, то этот фильтр сначала размывает изображение, а затем вычитает его из исходного. В итоге равномерные области остаются темными, а края (изменения) — светлыми.
Вы бы хоть сами свою ссылку почитали (нет, edge-detection работает не так).
Sign up to leave a comment.

Articles