Две недели назад мы предложили читателям Хабры задать свои вопросы создателям библиотеки компьютерного зрения OpenCV. Вопросов было задано много, причем, интересных — значит, эта тема интересует не только компанию Intel, но и широкие массы разработчиков. Без лишних слов переходим к публикации ответов и приглашаем к их обсуждению. А также объявляем авторов лучших вопросов! В самом конце поста.
Вопрос noonv
Каким вы видите будущее компьютерного зрения? Наблюдая за развитием машинного обучения, какие перспективы вы видите?
Анатолий Бакшеев. Несколько моих мыслей:
- everything learnable, minimum handcrafting
- новое – это хорошо забытое старое.
Думаю через какое-то время произойдет некоторый возврат к традиционному компьютерному зрению (когда будут сорваны все низковисящие фрукты по DL), но немного на ином уровне развития и возможностей.
Вадим Писаревский. Мое видение достаточно стандартно. Ближайшее будущее (вообще-то уже настоящее) за глубоким/глубинным обучением, причем оно будет применяться все более изощренным и нетривиальным образом, что доказала прошедшая конференция CVPR 2017. 6 лет назад deep learning появился, точнее, возродился после статьи Крыжевского (Alexnet), и тогда он хорошо решал только одну задачу – распознавание класса объекта при условии нахождения одного доминирующего объекта в кадре, без определения его положения. 2 года назад про него уже говорили почти все в нашей области. Придумали первые сетки для детектирования объектов и сетки для семантической сегментации. До этого задача семантической сегментации считалась безнадежной, нерешаемой задачей, как доказательство теоремы Ферма.
Имелась большая проблема со скоростью – все работало очень медленно. Сейчас сетки сжали, реализации оптимизировали, переложили на GPU, специализированное железо на подходе и вопрос скорости по большей части отпал и полностью отпадет в ближайшие пару лет – сетки уже работают в целом так же быстро, как и традиционные подходы, и при этом существенно лучше. Сейчас основные области для исследования:
- попытаться применять deep learning для новых задач, все более и более сложных,
- в-частности, применить к задачам, где трудно собрать огромные тренировочные базы.
Первая общая задача будет основным трендом ближайшего десятилетия по крайней мере, вторая задача, я думаю, будет решена в ближайшие несколько лет, в частных случаях она уже решается. Перспективы такие, что компьютерное зрение из узкоспециальной области с кучей кустарных методов превратится в промышленную область и сильно повлияет на жизнь многих людей. Собственно, этот процесс уже происходит с большой скоростью, и мы уже скорее говорим не просто о зрении, но об искусственном интеллекте.
Вопрос IliaSafonov
Нет ли планов добавить в OpenCV возможность обрабатывать 3D (volumetric) изображения? Работаю с томографическими изображениями размера порядка 4000х4000х4000. Существующие open-source библиотеки для 3D, мягко говоря, бедноваты и медленноваты по сравнению с OpenCV.
Вадим. Базовые поэлементные функции уже могут работать с такими данными. 3х-мерной фильтрации и каких-то других более сложных алгоритмов пока нет. Но, правда, есть глубокие сетки, которые могут делать некоторые преобразования над 3D массивами данных. Если есть список нужных операций, приглашаю подать запрос на расширение функциональности. Если будет хороший детальный запрос с описанием задачи, со ссылками, то, вполне возможно, что это станет одним из наших проектов для следующего Google Summer of Code (лето 2018).
Вопрос MaximKucherenko
Видеокамера висит на мосту, под которым идет поток автомобилей. Для видеокамеры установлено отличное освещение, в нормальную погоду ночью можно даже лица водителей рассматривать. Когда же начинается метель, снимки получаются почти белыми (из-за большого количества мелких движущихся объектов, снежинок). Можете подсказать, как побороть такой «шум»?
Анатолий. Сложно ответить, не имея самих изображений. Можно попробовать сделать CNN которая бы как-то восстанавливала картинку. Посмотрите работы по CNN impainting, где сетка «додумывает» испорченные части изображения. Или CNN debluring, где сетка, по сути, пытается выучить классический Debluring алгоритм. То же можно попробовать сделать и вам.
В вашем случае сетка может быть в каком-то месте рекуррентной, чтобы учитывать предыдущие кадры для синтеза «чистого» изображения.
Вадим. Нужен какой-то вариант temporal filtering с учетом движения машин и камеры – т.е. надо собирать кадры из нескольких, мы говорим про некую вариацию на тему video superresolution, но без повышения разрешения. Берется временная окрестность каждого кадра, вычисляется плотный оптический поток между центральным кадром и соседними по времени, составляется некая функция штрафа для результирующего «улучшенного» изображения – оно одновременно должно быть гладким и похожим на все изображения из окрестности с учетом скомпенсированного движения. Потом запускается итеративный процесс оптимизации. Не уверен что такой алгоритм будет производить чудеса, особенно в экстремальных условиях (метель), но в ситуациях умеренной сложности, возможно, получится улучшить изображение на выходе. Но в самом начале, без такого алгоритма, можно попробовать функцию cv::equalizeHist(), может она что-то даст.
Вопрос uzh13
Какой язык лучше всего подходит для экспериментов с CV? Стоит ли разбираться с Erlang для этого?
Есть ли канонический набор книг или цикл статей, для быстрого старта с техническим зрением? Есть ли с кем пообщаться?
Вадим. В данный момент наиболее предпочтительные варианты:
- C++ с хорошей средой разработки,
- Python
В зависимости от обстоятельств Питон может быть предпочтительнее, чем C++ или наоборот. Erlang слишком экзотичен для этой области. Возможно, вы на нем что-то и напишете, но потом трудно будет найти единомышленников, чтобы этот код обсуждать и развивать вместе. Из книжек по классическому компьютерному зрению можно порекомендовать книгу R. Szeliski. Computer Vision, драфт которой доступен здесь. Есть множество книг по OpenCV, как правило, тоже на английском. С deep learning можно начать знакомство, используя следующий учебник. Насчет общения вопрос более сложный. Ну, собственно, интернет сейчас есть у всех, можно присоединиться к какому-нибудь проекту.
Анатолий. С++ и Python – это, по-моему классика для быстрого прототипирования и для серьезных решений никуда от этого не деться.
В дополнение к ответу Вадима рекомендую awesome репозитории на github:
- awesome-rnn
- awesome-deep-vision
- awesome-computer-vision
- awesome-random-forest
- neural-network-papers
- awesome-tensorflow
Вообще видел awesome репозитории поддерживаемые энтузиастами для многих областей.
Вопрос ChaikaBogdan
Как начать свою карьеру в области CV, если ты не имеешь в нем опыта? Где набраться опыта решения реальных задач и стажа в этой области, который так любят HRы?
Немного TL;DR который является предысторией возникновения вопросаЯ отучился в ВУЗе, где не было такого направления и, естественно, пошел работать в другую область (программная инженерия, автоматизация). Будь у меня возможность и понимание, насколько потенциально круто работать в области CV, я бы поступил на него пусть даже в другой ВУЗ, но, увы, я узнал о нем слишком поздно. Переучиваться на второе высшее уже как-то непозволительно долго.По долгу службы попалась задача детектирования с помощью Python+OpenCV, решил кое-как через template match (благо предметная область позволяла). Было весело, ново и вообще всем понравилось, особенно мне.
Начал изучать возможность самообучения, прошел курс Introduction to Computer Vision (Udacity-Georgia Tech) и начал практические от PyImageSearch.
Параллельно смотрел вакансии на Upwork и PyImageSearch Jobs, Fiverr и расстраивался, так как знаний явно не хватает для решения реальных задач (например, почти везде мешают light/shadow/angle conditions). Не уверен, что даже полное прохождение, скажем, Guru курса от PyImageSearch поможет найти достойную работу, ибо примеры уж очень «идеальные» и редко работают, как задумывались в реальных условиях.На биржах типа Fiverr, Upwork, PyImageJobs большая конкуренция и требуется выполнение задач очень быстро. А хочется чего-то с небольшим порогом вхождения и взлетной learning-curve. Про удаленную работу вообще молчу. Плюс, везде еще хотят deep/machine learning вдогонку.
Бросать основную работу ради того, чтобы не найти себе работу в CV, как-то не хочется. Но и опускать руки тоже. Это же крутая и интересная область, чтобы развиваться профессионально, как ни смотри).
Анатолий. Я думаю, вы на правильном пути.
Вообще, если человек хочет работать в данной области, думаю, любой нормальный руководитель возьмет его к себе даже без навыков. Главное продемострировать желание работать, выраженное в конкретных действиях: показать алгоритмы, которые вы сделали для OpenCV, для caffe/tf/torch, показать ваши проекты на github, показать ваш рейтинг на Kaggle. У меня есть инженер, который ушел с предыдущей скучной не-CV работы, уехал в Тайланд и год нигде не работал. Через полгода ему там стало скучно, и он начал участвовать в Kaggle соревнованиях. Потом когда он пришел ко мне, его хороший рейтинг на Kaggle тоже сыграл свою роль даже без опыта в CV. Сейчас это один из сильнейших моих инженеров.
Вадим. У меня есть для вас «история успеха». Одно время в OpenCV была подана серия патчей с добавлением функциональности по распознаванию лиц. Конечно, сейчас задачу распознавания лиц решают с помощью deep learning, а тогда это были достаточно простые алгоритмы, но не суть. Автор кода был некий человек из Германии по имени Филипп. Он тогда на основной работе занимался скучными проектами, по его собственным словам, программировал DSP. Нашел время после работы заняться распознаванием лиц, подготовил патчи, мы их приняли. Естественно, он там был указан как автор. Через некоторое время он мне написал радостное письмо что в том числе благодаря такому наглядному «резюме» нашел работу, связанную со компьютерным зрением.
Конечно, это далеко не единственный путь. Просто если вам действительно нравится компьютерное зрение, приготовьтесь заниматься им сверхурочно на добровольных началах, нарабатывайте практический опыт. А насчет образования – как вы думаете, сколько человек из команды OpenCV получили образование в этой области? Ноль. Все мы по образованию математики, физики, инженеры. Важны общие навыки (которые развиваются практикой) изучать новый материал, в-основном на английском, программировать, общаться, решать математические и инженерные задачи. А конкретные знания – это преходящее. С появлением deep learning несколько лет назад большая часть наших знаний стала устаревшей, а через несколько лет может и deep learning станет устаревшей технологией.
Вопрос aslepov78
Не кажется ли вам что вы поддались массовой истерии по поводу нейронных сетей, глубинного обучения?
Вадим. Перефразируя Уинстона Черчилля, возможно, [современное] глубинное обучение — это плохой способ решать задачи компьютерного зрения, но все остальные известные нам – еще хуже. Но монополии на исследования нет ни у кого, слава богу, придумывайте свое. И на самом деле люди придумывают. Я сам был большим скептиком этого подхода несколько лет назад, но, во-первых, результаты налицо, а во-вторых, оказалось, что глубинное обучение можно применять не тупо (взял первую попавшуюся архитектуру, набрал миллион тренировочных примеров, запустил кластер и через неделю получил модель или не получил), а можно применять творчески. И тогда это становится поистине волшебной технологией, и начинают решаться задачи, к которым до этого вообще непонятно было, как подступиться. Например определение 3D поз игроков на поле с одной камеры.
Вопрос aslepov78
OpenCV превратился в склад алгоритмов из разных областей (вычислительная геометрия, обработка сигнала, machine learning и т.д.). А между тем, есть более продвинутые библиотеки по той же вычислительной геометрии (не говоря о нейросетях). Выходит, смысл OpenCV только в одном — все зависимости в одном флаконе?
Вадим. Мы делаем инструмент прежде всего для себя и наших коллег, а также интегрируем патчи от сообщества пользователей (не все, правда, но большую часть), т.е. то, что пользователи считают полезным для себя и других. Было бы хорошо, конечно, если бы в C++ была некая общая модель – как писать библиотеки так, чтобы они были друг с другом совместимыми, и их можно было бы легко использовать совместно и не было бы проблем с построением и конвертированием структур данных. Тогда, возможно, OpenCV могла бы быть безболезненно заменена серией более специальных библиотек. Но такой модели пока нет, и, может, и не будет. В Питоне есть подобная модель, построенная вокруг numpy и системы модулей и расширений, и питоновские обертки для OpenCV, как мне кажется, довольно органично туда встроены. Я думаю, если вы практически поработаете в области CV несколько лет, то придет понимание, зачем нужна OpenCV и почему она устроена так, как устроена. Или не придет.
Вопрос aslepov78
Почему так мало готовых решений? Например, если я новичок в CV, и хочу искать черный квадрат на белом фоне, то открыв доку OpenCV, я утону в ней. Вместо этого я бы хотел полистать список наиболее типичных и простых задач и выбрать, или скомбинировать. Т.е. в OpenCV практически нет декларативного подхода.
Вадим. По правде говоря, в OpenCV вообще нет готовых решений. Готовые решения в компьютерном зрении стоят больших денег и пишутся под конкретного заказчика для решения конкретных, очень четко поставленных задач. Процесс создания таких решений отличается от комбинирования блоков примерно так же, как процесс проектирования, строительства и обустройства индивидуального дома под заказ отличается от сборки игрушечного домика из кубиков лего.
Вопрос vlasenkofedor
Расскажите, пожалуйста, об наиболее интересных проектах с оригинальным решением — OpenCV с микрокомпьютерами (Raspberry, ASUS ...)
Анатолий. У нас мало опыта работы с данными устройствами
Вопрос killla
Есть ли небольшие платы (уровня Raspberry Pi с процессором, заточенным под видеообработку OpenCV) и видеокамерой, подключенной напрямую к микропроцессору (микроконтроллеру) без всяких посредников в виде USB и его больших задержек? Чтобы можно было бы взять его и на коленке быстро сделать устройство для подсчета ворон на грядке или устройство для слежения за объектом (простейшая обработка изображения + реакция с минимальными задержками на раздражители).
Мой собственный опытКрайний раз пытался решать подобную задачу года 4 назад. 1) Все популярные доступные платы разработки не тянули обработку хорошего видео потока быстрее чем 1-2 раза в секунду, задействовать DSP без программирования на низких уровнях было нереально, да и достать контроллер с мощным хорошим документированным DSP и софтом у нему было непросто 2) все камеры во всех примерах цепляются по USB, соответственно на пустом месте огромные задержки + софтовая обработка камеры маломощным основным процессором. На распознавание процессорного времени уже почти не остается.
Вадим. Raspberry Pi, начиная со второго поколения, содержит ARM CPU с векторными инструкциями NEON. OpenCV довольно шустро должна работать на такой железке. Касательно скорости захвата видео – мы как-то из USB 2 выжимали 20-30 кадров/сек, не очень понятно о чем речь.
Вопрос killla
Есть ли готовые дистрибутивы и софт «из коробки» под такие железки, с которым можно сразу начать работать, не допиливая неделями?
Вадим. OpenCV собирается под любой ARM Linux и в значительной степени оптимизирована с использованием NEON. Думаю, на Raspberry Pi стоит смотреть в первую очередь, например, вот опыт энтузиаста.
Вопрос killla
Обобщая, задам вопрос так: можно ли в 2017-2018 году студенту 2-3 курса IT-специальности с базовыми навыками программирования, уложившись в 10000 руб., достать железку уровня 2-3 летнего телефона, на которой за 2-4 недели изучения OpenCV и написания кода создать простейшее устройство: камеру на мотоподвесе с парой осей движения, которая будет висеть на балконе и следить за движением любимой собаки во дворе?
Вадим. По железной части ответа не дам, исследуйте. Насчет слежения за собакой. Радиомаячок решит эту задачу проще, дешевле, надежнее. Если не стоит цель решить задачу, а хочется потренироваться в компьютерном зрении, то пожалуйста. За 2-4 недели можно побаловаться и заодно начать задумываться над вопросами типа:
- как обрабатывать движения/колыхания самой камеры, какое поведение ожидается в темное время суток, туман, дождь, снег, как обрабатывать разные времена года,
- как обрабатывать разные условия освещения – пасмурно, солнце в зените, солнце на восходе-закате с большими тенями,
- как система должна обрабатывать появление другого объекта в области видимости (машины, человека, кошки, другой собаки, другой собаки такой же породы),
- какое качество считается допустимым (система выдает вам ложные сообщения о пропаже собаки каждые 5 минут, система сообщает о пропаже собаки через сутки после ее пропажи)
- и т.д.
По моим скромным прикидкам, если заниматься этой задачей серьезно, на год-два себя точно можно занять. Узнаете про компьютерное зрение больше, чем преподают где-либо.
Вопрос almator
Не работает функция model = cv2.ANN_MLP() на питоне.
Код функцииimport cv2 import numpy as np import math class NeuralNetwork(object): def __init__(self): self.model = cv2.ANN_MLP() def create(self): layer_size = np.int32([38400, 32, 4]) self.model.create(layer_size) self.model.load('mlp_xml/mlp.xml') def predict(self, samples): ret, resp = self.model.predict(samples) return resp.argmax(-1) model = NeuralNetwork() model.create()
Ошибка AttributeError: 'module' object has no attribute 'ANN_MLP'
Вадим. Посмотрите пример letter_recog.py из поставки OpenCV.
Вопрос almator
Как OpenCV будет развиваться в сторону нейросетей, машинного обучения? Где есть простые примеры для начинающих по машинному обучению? Желательно на русском.
Анатолий. В OpenCV не планируется тренировка сетей, только быстрый оптимизированный inference. У нас уже есть CNN Face Detector который может работать больше 100fps на современном Core i5 (правда выложить мы его в публичный доступ не можем). Думаю многие текущие алгоритмы будут постепенно инструментированы небольшими (>5000fps) вспомогательными сеточками, будь то featues или optical flow, или RANSAC, или любой другой алгоритм.
Вадим. OpenCV будет развиваться в сторону глубинного обучения. Обычные нейронные сети являются частным случаем и сейчас нас мало интересуют. На русском ничего посоветовать не могу, но буду признателен, если найдете и сообщите. На английском есть онлайн курсы и книжки в сети, тот же учебник по deep learning, упомянутый выше.
Вопрос almator
Каким алгоритмом лучше искать относительно сложные логотипы на фотографии — например, логотипы различных маркировок, где обычно присутствует и текст, и рисунки, и все вписано в форму? Пробовал через Haar Cascade — этот алгоритм хорошо ищет цельные куски, а такой сложный многосоставной объект, как логотип, не находит. Попробовал MatchTemplate — не ищет, если происходит минимальное несовпадение — уменьшение, поворот относительно исходной картинки. Не подскажете, в каком направлении искать?
Вадим. Глубокие сетки + аугментация тренировочной базы. То есть, вам нужно собрать базу изображений этого логотипа, а потом искусственно расширить ее во много раз. Вот, например, сходу находится через Гугл.
Вопрос WEBMETRICA
Можно ли с помощью компьютерного зрения смоделировать аналоги зрения различных биологических организмов, например, животных и насекомых и создать приложение, дающее возможность увидеть мир глазами других существ?
Анатолий. Я думаю, это произойдет еще не скоро. Более того, не существует метода достоверно сказать, как видят мир другие существа.
Вадим. На CVPR 2017 была интересная статья об использовании считанных с человека сигналов для распознавания образов. Авторы пообещали интересное продолжение. Возможно, скоро и до братьев наших меньших доберутся.
Вопрос WEBMETRICA
Если пойти еще дальше, то можно создать множество моделей зрения различных живых существ, пропустить всё это многообразие через нейросеть и создать нечто новое? Возможен ли синтез зрения различных биологических систем?
Вадим. Возможно все. Нужно идти от конкретной задачи, мне кажется.
Вопрос barabanus
Почему нельзя умножить матрицу на вектор (cv::Vec_) в OpenCV, но при этом можно умножить на точку? (cv::Point_) Получается, что проще манипулировать с точками тогда, когда математически это не точки, а вектора. Например, направление линии легче хранить как точку, а не как вектор — меньше преобразований типов в цепочке операций.
Анатолий. Мне известна эта проблема уже лет 8. Насколько я помню, это невозможно реализовать — можете сами попробовать. Там получается что-то вроде неоднозначности вызова конструктора для служебного промежуточного типа — компилятор не может сам решить, какой конструктор вызывать и выдает ошибку. Вам вручную придется преобразовать в точку через cv::Mat * Point_<...>(Vec_<...>).
Вадим. Предлагаю подать запрос. Возможно, что в данном конкретном случае просто пропустили эту функцию, или намеренно ее отключили, чтобы не запутать C++ компилятор во всем множестве перекрытых операторов ‘*’ – иногда это случается.
Вопрос barabanus
Почему до сих пор в OpenCV нет ни одной реализации Hough Transform, которая возвращала бы аккумулятор. Ведь иногда надо найти, скажем, единственный максимум! Разрешат ли держатели проекта добавить новую реализацию, которая возвращала бы аккумулятор?
Вадим. Да, это было бы полезно. После рецензирования и необходимой доработки, если она понадобится, такой патч можно принять.
Вопрос perfect_genius
Пробует ли Intel создавать аппаратные нейросети для обработки изображений и есть ли результаты?
Анатолий. Аппаратные сети имеют мало смысла, потому что прогресс очень быстро двигается вперед, и такая железка будет устаревать прежде, чем выйдет в продажу. А вот создание ускоряющих инструкций для сетей (а-ля MMX/SSE/AVX) или даже сопроцессоров, по-моему, очень даже логичный шаг. Но мы информацией не владеем.
Вадим. На данном этапе нам известны попытки, и наши коллеги в них принимают активное участие, задействовать имеющееся железо (CPU, GPU) для ускорения выполнения сеток. Попытки довольно успешные. Ускоренные решения для CPU (библиотека MKL-dnn, и скомпилированный с ней Intel Caffe) и для GPU (clDNN) позволяют запускать большое количество популярный сетей, таких как AlexNet, GooLeNet/Inception, Resnet-50 и т.д. в реальном времени на обычном компьютере без мощной дискретной карты, на обычном ноутбуке. Даже OpenCV, хоть она пока и не использует эти оптимизированные библиотеки, позволяет запускать некоторые сетки для классификации, детектирования и семантической сегментации в реальном времени на ноутбуке без дискретной графики. Попробуйте наши примеры и убедитесь в этом сами. Эффективная работа сетей ближе, чем многим кажется.
Вопрос Mikhail063
Пользуюсь OpenCV не первый год, но столкнулся с такой интересной штуковиной. Есть камера, которая передает сигнал, и есть телеметрия, которая принимает сигнал, а еще есть тюнер, который декодирует сигнал в видео на компьютер. Так вот программы по захвату изображения работают на ура, но библиотека OpenCV при попытке вывести на экран изображение выдает черный экран, а при попытке выйти из программы, вываливается синий экран) ВОПРОС Почему это происходит?
Характеристики устройств: TV-тюнер EasyCap USB 2.0, Приемник FPV видеосигнала 5.8ГГц RC832, FPV камера с передатчиком 5.8ГГц 1000TVL.
Вадим. Потому что где-то какая-то ошибка, очевидно :) Надо начать с локализации.
Вопрос KOLANICH
Есть ли смысл в современных реалиях в программах видеоанализа, основанных на фичах, спроектированных человеком, или лучше не морочиться и сразу тренировать нейронную сеть?
Анатолий. Иногда классические фичи могут быть быстрым решением.
Вадим. Для анализа лучше тренировать. Для более простых задач, типа склейки панорам, классические фичи, такие как SIFT, пока конкурентоспособны.
Вопрос vishnerevsky
Я использовал версию OpenCV 3.1.0, пользовался cv2.HOGDescriptor() и .setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector()), остался под хорошим впечатлением. Но я хочу уменьшить количество ложных срабатываний и поэтому хочу узнать, какой набор данных использовался для обучения SVM классификатора и могу ли я получить доступ к этому набору? Также хотелось бы узнать, планируется ли создание модулей OpenCV для распознавания различных объектов на базе YOLO или Semantic Segmentation?
Вадим. Базы брали стандартные, находящиеся в открытом доступе. Сейчас конкретные конфигурационные файлы со списками файлов утеряны, много лет прошло. Патч с добавлением YOLO v.2 висит, к моменту публикации этих ответов, думаю, его уже зальем. Пример с MobileNet_SSD уже есть. Там же можно найти примеры и с сегментацией.
Вопрос iv_kovalyov
«Посоветуйте, как можно распознать штамп слева в изображении справа? Штампы не идентичны, но есть общие элементы.
Пробовал find_obj.py из примеров opencv, но в данной ситуации этот пример не помогает.
Вадим. См. совет выше по поиску логотипов. Только тут, скорее всего, понадобятся две сетки – детектирование и последующее распознавание.
Эксперты Intel признали лучшими вопросы IliaSafonov об использовании OpenCV для 3D объектов и ChaikaBogdan о построении карьеры в области компьютерного зрения для новичка. Авторам этих вопросов достаются призы от Intel. Поздравляем победителей и благодарим Анатолия и Вадима за познавательные ответы!
Автор: saul