Тарифы Услуги Сим-карты

Opencv распознавание элементов схемы. Детектирование объектов с помощью особенностей в OpenCV: FREAK. Детектирование множества объектов. Сопоставление с шаблоном

Важнейшими источниками информации о внешнем мире для робота являются его оптические датчики и камеры. После получения изображения необходима его обработка для анализа обстановки или принятия решения. Как я говорил ранее, компьютерное зрение объединяет множество методов работы с изображениями. При функционировании робота предполагается, что видеоинформация с камер обрабатывается какой-либо программой, запущенной на контроллере. Чтобы не писать код с нуля, можно воспользоваться готовыми программными решениями. На текущий момент существует множество готовых библиотек компьютерного зрения:

  • Matrox Imaging Library
  • Camellia Library
  • Open eVision
  • HALCON
  • libCVD
  • OpenCV
  • и т.д…
Данные SDK могут сильно различаться по функциональности, условиям лицензирования, используемым языкам программирования. Мы же подробнее остановимся на OpenCV . Она бесплатна как для учебных целей, так и для коммерческого использования. Написана на оптимизированном C/C++, поддерживает интерфейсы C, C++, Python, Java и включает в себя реализации свыше 2500 алгоритмов. Помимо стандартных функций обработки изображений (фильтрация, размытие, геометрические преобразования и т.д…) данный SDK позволяет решать более сложные задачи, к которым относятся обнаружение объекта на фотографии и его «узнавание». Следует понимать, что задачи обнаружения и распознавания могут быть совершенно различными:
  • поиск и распознавание конкретного объекта,
  • поиск объектов одной категории (без распознавания),
  • только распознавание объекта (уже готовое изображение с ним).
Для обнаружения признаков на изображении и проверки на совпадение в OpenCV имеются следующие методы:
  • Гистограмма направленных градиентов HOG(Histogram of Oriented Gradients) — может применяться для обнаружения пешеходов
  • Алгоритм Виолы-Джонса — применяется для поиска лиц
  • Алгоритм обнаружения признаков SIFT (Scale Invariant Feature Transform)
  • Алгоритм обнаружения признаков SURF (Speeded Up Robust Features)
Например, SIFT обнаруживает наборы точек, которые можно использовать для идентификации объекта. Помимо приведенных методик в OpenCV имеются и другие алгоритмы для детектирования и распознавания, а также набор алгоритмов, относящихся к машинному обучению, таких как метод k ближайших соседей, нейронные сети, метод опорных векторов и т.д… В целом OpenCV предоставляет инструментарий, достаточный для решения подавляющего большинства задач компьютерного зрения. Если алгоритм не имеется в составе SDK, то, как правило, он может быть без проблем запрограммирован. Кроме того, существует множество авторских версий алгоритмов, написанных пользователями на основе OpenCV. Также следует отметить, что за последние годы OpenCV сильно расширилась и стала в некоторой степени «тяжеловесной». В связи с этим, разными группами энтузиастов создаются «облегченные» библиотеки, основанные на OpenCV. Примеры: SimpleCV, liuliu ccv, tinycv… Полезные сайты
  1. http://opencv.org/ — Основной сайт проекта
  2. http://opencv.willowgarage.com/wiki/ — Старый сайт проекта с документацией по старым версиям

В этой статье использован C++ интерфейс, FREAK и детектирование множества объектов. Надежность детектирования объектов с помощью FREAK ниже, чем SURF, однако его работа намного быстрее, что позволяет использовать алгоритм на мобильных и встроенных системах. Пример работы представлен на рисунке:

Рассмотрим исходный код, который позволяет этого достигнуть. Код приведен полностью для желающих быстро вставить его в свой проект.
#include #include #include #include #include #include #include #include keypointsImageTemple, keypointsImage; Mat descriptorsImageTemple, descriptorsImage; std::vector matches; // Инициалищация класса детектора особенностей, 1000 - пороговое значение для отсеивания // малозначимых особенностей SurfFeatureDetector detector(1000); // Класс для FREAK особенностей. Можно настраивать режимы сравнения особенностей: // FREAK extractor(true, true, 22, 4, std::vector matcher; // Детектирование double t = (double)getTickCount(); detector.detect(ImageTemple, keypointsImageTemple); detector.detect(Image, keypointsImage); t = ((double)getTickCount() - t)/getTickFrequency(); std::cout << "detection time [s]: " << t/1.0 << std::endl; // Извлечение особенностей t = (double)getTickCount(); extractor.compute(ImageTemple, keypointsImageTemple, descriptorsImageTemple); extractor.compute(Image, keypointsImage, descriptorsImage); t = ((double)getTickCount() - t)/getTickFrequency(); std::cout << "extraction time [s]: " << t << std::endl; // Сравнение t = (double)getTickCount(); matcher.match(descriptorsImageTemple, descriptorsImage, matches); t = ((double)getTickCount() - t)/getTickFrequency(); std::cout << "matching time [s]: " << t << std::endl; // Отобразить на изображении Mat imgMatch; drawMatches(ImageTemple, keypointsImageTemple, Image, keypointsImage, matches, imgMatch); imwrite("matches.jpeg", imgMatch); std::vectorObj; std::vectorScene; for(int i = 0; i < matches.size(); i++) { obj.push_back(keypointsImageTemple[ matches[i].queryIdx ].pt); scene.push_back(keypointsImage[ matches[i].trainIdx ].pt); } Mat H = findHomography(obj, scene, CV_RANSAC); std::vector Scene_corners(4); perspectiveTransform(obj_corners, scene_corners, H); //-- Draw lines between the corners (the mapped object in the scene - image_2) line(imgMatch, scene_corners + Point2f(ImageTemple.cols, 0), scene_corners + Point2f(ImageTemple.cols, 0), Scalar(0, 255, 0), 4); line(imgMatch, scene_corners + Point2f(ImageTemple.cols, 0), scene_corners + Point2f(ImageTemple.cols, 0), Scalar(0, 255, 0), 4); line(imgMatch, scene_corners + Point2f(ImageTemple.cols, 0), scene_corners + Point2f(ImageTemple.cols, 0), Scalar(0, 255, 0), 4); line(imgMatch, scene_corners + Point2f(ImageTemple.cols, 0), scene_corners + Point2f(ImageTemple.cols, 0), Scalar(0, 255, 0), 4); imwrite("matches3.jpeg", imgMatch); return 0; }

Для любых особенностей в OpenCV необходимо инициализировать класс SurfFeatureDetector. Первое действие после различных инициализаций – это детектирование особенностей detector.detect для эталонного изображения и изображения сцены. После чего для каждого изображения по результатам работы детектора вычисляются FREAK особенности: extractor.compute.
Сравнение схожести особенностей осуществляется с помощью matcher.match.
Далее присутствует цикл с формированием точек из особенностей для обоих изображений. На основании точек вычисляется гомография изображений findHomography. Положение и поворот объекта вычисляется с помощью функции perspectiveTransform. Ну а затем – вывод на изображение.
Эталонное изображение:

Изображение сцены:


Результат представлен вначале.
Однако здесь возникает вопрос, как рассчитывать оптимальный порог особенностей: SurfFeatureDetector detector(1000);. Ответ – экспериментально. Некоторую информацию по данному вопросу вы можете получить .
Предположим, что у нас на изображении несколько объектов:


Результат работы программы будет следующий:


Естественно, что такая ситуация не устраивает. Для того, чтобы детектировать все объекты, необходимо разделить изображение на несколько частей. Однако здесь следует помнить, что если изображение разделить на непересекающиеся блоки (пример изображение 100x100 разделить на 4 блока по 50x50), то может возникнуть ситуация, когда объект будет частично находиться в нескольких блоках и не будет детектирован. Для избегания этого необходимо делать пересекающиеся блоки, что несколько замедлит работу, но улучшит качество (пример изображение 100x100 разделить на 9 блоков по 50x50 так, как показано в примере). Пример программы детектирующий множество объектов ниже:
#include #include #include #include #include #include #include #include using namespace cv; int main(int argc, char** argv) { if(argc != 3) return 1; Mat ImageTemple = imread(argv, CV_LOAD_IMAGE_GRAYSCALE); if(!ImageTemple.data) return 2; // Ошибка Mat Image = imread(argv, CV_LOAD_IMAGE_GRAYSCALE); if(!Image.data) return 3; // Ошибка std::vector keypointsImageTemple; Mat descriptorsImageTemple; std::vector matches; // Инициалищация класса детектора особенностей, 1000 - пороговое значение для отсеивания // малозначимых особенностей SurfFeatureDetector detector(1000); detector.detect(ImageTemple, keypointsImageTemple); int maxy = 3; int maxx = 3; Mat Draw_mat = imread(argv, 1); for(int y = 0; y < maxy; y++) for(int x = 0; x < maxx; x++) { // Класс для FREAK особенностей. Можно настраивать режимы сравнения особенностей: // FREAK extractor(true, true, 22, 4, std::vector()); FREAK extractor; // Используется для определение совпадений особенностей - мера Хемминга BruteForceMatcher matcher; std::vector keypointsImage; Mat descriptorsImage; CvRect Rect = cvRect(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1)), 2 * (Image.cols / (maxx + 1)), 2 * (Image.rows / (maxy + 1))); Mat ImageROI(Image, Rect); detector.detect(ImageROI, keypointsImage); extractor.compute(ImageTemple, keypointsImageTemple, descriptorsImageTemple); extractor.compute(ImageROI, keypointsImage, descriptorsImage); matcher.match(descriptorsImageTemple, descriptorsImage, matches); // Отброс слишком расходящихся значений for (int i = 0; i < matches.size(); i++) { if(matches[i].distance > 150) { matches.erase (matches.begin() + i); } } std::vectorObj; std::vectorScene; for(int i = 0; i < matches.size(); i++) { obj.push_back(keypointsImageTemple[ matches[i].queryIdx ].pt); scene.push_back(keypointsImage[ matches[i].trainIdx ].pt); } Mat H = findHomography(obj, scene, CV_RANSAC); std::vectorObj_corners(4); obj_corners = cvPoint(0,0); obj_corners = cvPoint(ImageTemple.cols, 0); obj_corners = cvPoint(ImageTemple.cols, ImageTemple.rows); obj_corners = cvPoint(0, ImageTemple.rows); std::vectorScene_corners(4); perspectiveTransform(obj_corners, scene_corners, H); //-- Draw lines between the corners (the mapped object in the scene - image_2) line(Draw_mat, scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), Scalar(0, 255, 0), 4); line(Draw_mat, scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), Scalar(0, 255, 0), 4); line(Draw_mat, scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), Scalar(0, 255, 0), 4); line(Draw_mat, scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), scene_corners + Point2f(x * (Image.cols / (maxx + 1)), y * (Image.rows / (maxy + 1))), Scalar(0, 255, 0), 4); } imwrite("draw_mat.jpeg", Draw_mat); return 0; }

Результат работы следующий:


Видно, что все объекты детектированы. Причем некоторые дважды (из-за того, что попали в два блока).

При решении задач компьютерного зрения не обойтись без использования специализированного софта. Хочу познакомить вас с таким - OpenCV - библиотека с открытым исходном кодом на C++. Обладает набором инструментов для оцифровки изображений, последующей обработки через численные алгоритмы или нейросеть.

Базовые алгоритмы обработки изображений: интерпретации изображений, калибровки камеры по эталону, устранение оптических искажений, определение сходства, анализ перемещения объекта, определение формы объекта и слежение за объектом, 3D-реконструкция, сегментация объекта, распознавание жестов.

Скачать библиотеку можно на официальном сайте http://sourceforge.net/projects/opencvlibrary/

Структура библиотеки OpenCV

cxcore - ядро
* содержит базовые структуры данных и алгоритмы:
- базовые операции над многомерными числовыми массивами
- матричная алгебра, математические ф-ции, генераторы случайных чисел
- Запись/восстановление структур данных в/из XML
- базовые функции 2D графики

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

Highgui - модуль для ввода/вывода изображений и видео, создания пользовательского интерфейса
- захват видео с камер и из видео файлов, чтение/запись статических изображений.
- функции для организации простого UI (все демо приложения используют HighGUI)

Cvaux - экспериментальные и устаревшие функции
- пространств. зрение: стерео калибрация, само калибрация
- поиск стерео-соответствия, клики в графах
- нахождение и описание черт лица

CvCam - захват видео
- позволяет осуществлять захват видео с цифровых видео-камер (поддержка прекращена и в последних версиях этот модуль отсутствует)


Установка OpenCV под Linux

После скачивания последней версии OpenCV с сайта разработчика http://sourceforge.net/projects/opencvlibrary/ нужно распаковать архив и выполнить сборку через CMake версии 2.6 или выше.

Установка CMake выполняется стандартно:

Sudo apt-get install cmake

Для отображения окон OpenCV потребуется установить библиотеки GTK+ 2.x и libgtk2.0-dev

Apt-get install libgtk2.0-dev

Собираем библиотеку:

Tar -xjf OpenCV-2.2.0.tar.bz2 cd OpenCV-2.2.0 cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ./ make make install

Для тестирования установленной библиотеки можно собрать примеры и что-нибудь запустить:

Cd samples/c/ chmod +x build_all.sh ./build_all.sh ./delaunay

Если вместо тестовой картинки вы увидите ошибку "error while loading shared libraries: libopencv_core.so.2.2: cannot open shared object file: No such file or directory", то это значит, что программа не может найти библиотеки. Нужно явно указать путь до них:

$ export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

Если после этого опять ошибка:
OpenCV Error: Unspecified error (The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Carbon support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script) in cvNamedWindow, file /usr/src/OpenCV-2.2.0/modules/highgui/src/window.cpp, line 274 terminate called after throwing an instance of "cv::Exception" what(): /usr/src/OpenCV-2.2.0/modules/highgui/src/window.cpp:274: error: (-2) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Carbon support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function cvNamedWindow
Значит вы забыли установить GTK+ 2.x: libgtk2.0-dev. Запустите установку (см. выше).

Когда установка завершена заголовочные файлы будут доступны в дирректории /usr/local/include/opencv , а библиотечные файлы лежат в /usr/local/lib

Соберем программу с OpenCV:

test.cpp

// // for testing // // robocraft.ru // #include #include #include #include int main(int argc, char* argv) { IplImage* image=0, *dst=0; // имя картинки char filename = "Image0.jpg"; // получаем картинку image = cvLoadImage(filename, 1); printf("[i] image: %s\n", filename); assert(image != 0); // покажем изображение cvNamedWindow("image"); cvShowImage("image", image); // ждём нажатия клавиши cvWaitKey(0); // освобождаем ресурсы cvReleaseImage(& image); cvReleaseImage(&dst); // удаляем окна cvDestroyAllWindows(); return 0; }

Makefile

CC:= g++ CFLAGS:= -I/usr/local/include/opencv -L/usr/local/lib OBJECTS:= LIBRARIES:= -lopencv_core -lopencv_imgproc -lopencv_highgui .PHONY: all clean all: test test: $(CC) $(CFLAGS) -o test test.cpp $(LIBRARIES) clean: rm -f *.o

Запуск сборки коммандой make.


Hello World!

OpenCV установлен и готов к работе. Напишем свое первое приложение Hello World!

#include #include int main(int argc, char** argv) { // задаём высоту и ширину картинки int height = 620; int width = 440; // задаём точку для вывода текста CvPoint pt = cvPoint(height/4, width/2); // Создаёи 8-битную, 3-канальную картинку IplImage* hw = cvCreateImage(cvSize(height, width), 8, 3); // заливаем картинку чёрным цветом cvSet(hw,cvScalar(0,0,0)); // инициализация шрифта CvFont font; cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX,1.0, 1.0, 0, 1, CV_AA); // используя шрифт выводим на картинку текст cvPutText(hw, "OpenCV Step By Step", pt, &font, CV_RGB(150, 0, 150)); // создаём окошко cvNamedWindow("Hello World", 0); // показываем картинку в созданном окне cvShowImage("Hello World", hw); // ждём нажатия клавиши cvWaitKey(0); // освобождаем ресурсы cvReleaseImage(&hw); cvDestroyWindow("Hello World"); return 0; }

Загрузка изображения

Данный пример будет основой всех ваших программ на OpenCV. Мы загрузим в среду изображение из файла Image0.jpg

#include #include #include #include IplImage* image = 0; IplImage* src = 0; int main(int argc, char* argv) { // имя картинки задаётся первым параметром char* filename = argc == 2 ? argv : "Image0.jpg"; // получаем картинку image = cvLoadImage(filename,1); // клонируем картинку src = cvCloneImage(image); printf("[i] image: %s\n", filename); assert(src != 0); // окно для отображения картинки cvNamedWindow("original",CV_WINDOW_AUTOSIZE); // показываем картинку cvShowImage("original",image); // выводим в консоль информацию о картинке printf("[i] channels: %d\n", image->nChannels); printf("[i] pixel depth: %d bits\n", image->depth); printf("[i] width: %d pixels\n", image->width); printf("[i] height: %d pixels\n", image->height); printf("[i] image size: %d bytes\n", image->imageSize); printf("[i] width step: %d bytes\n", image->widthStep); // ждём нажатия клавиши cvWaitKey(0); // освобождаем ресурсы cvReleaseImage(& image); cvReleaseImage(&src); // удаляем окно cvDestroyWindow("original"); return 0; }

Поддерживаемые типы форматов изображений:

  • Windows bitmaps - BMP, DIB
  • JPEG files - JPEG, JPG, JPE
  • Portable Network Graphics - PNG
  • Portable image format - PBM, PGM, PPM
  • Sun rasters - SR, RAS
  • TIFF files - TIFF, TIF

Для обращения к изображению можно делать такие вызовы:

Image->nChannels // число каналов картинки (RGB, хотя в OpenCV - BGR) (1-4) image->depth // глубина в битах image->width // ширина картинки в пикселях image->height // высота картинки в пикселях image->imageSize // память занимаемая картинкой (==image->height*image->widthStep) image->widthStep // расстояние между соседними по вертикали точками изображения (число байт в одной строчке картинки - может потребоваться для самостоятельного обхода всех пикселей изображения)

Загрузка видео

Загрузка видео не на много сложнее, чем загрузка изображения за тем исключением, что будет цикл, который перебирает кадры.
Задержка между кадрами задана в 33 миллисекунды т.к. такая задержка позволяет обрабатывать видеопоток с стандартной частотой 30 кадров в секунду.

#include #include #include #include IplImage* frame =0; int main(int argc, char* argv) { // имя файла задаётся первым параметром char* filename = argc == 2 ? argv : "test.avi"; printf("[i] file: %s\n", filename); // окно для отображения картинки cvNamedWindow("original",CV_WINDOW_AUTOSIZE); // получаем информацию о видео-файле CvCapture* capture = cvCreateFileCapture(filename); while(1){ // получаем следующий кадр frame = cvQueryFrame(capture); if(!frame) { break; } // здесь можно вставить // процедуру обработки // показываем кадр cvShowImage("original", frame); char c = cvWaitKey(33); if (c == 27) { // если нажата ESC - выходим break; } } // освобождаем ресурсы cvReleaseCapture(&capture); // удаляем окно cvDestroyWindow("original"); return 0; }

Для захвата видео с камеры нужно немного модифицировать код - вместо функции cvCreateFileCapture() будет использоваться cvCreateCameraCapture(). При нажатии ESC воспроизведение прервется и окно закроется, а при нажатии Enter текущий кадр сохранится в jpg файл.

#include #include #include #include int main(int argc, char* argv) { // получаем любую подключённую камеру CvCapture* capture = cvCreateCameraCapture(CV_CAP_ANY); //cvCaptureFromCAM(0); assert(capture); //cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 640);//1280); //cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 480);//960); // узнаем ширину и высоту кадра double width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH); double height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT); printf("[i] %.0f x %.0f\n", width, height); IplImage* frame=0; cvNamedWindow("capture", CV_WINDOW_AUTOSIZE); printf("[i] press Enter for capture image and Esc for quit!\n\n"); int counter=0; char filename; while(true){ // получаем кадр frame = cvQueryFrame(capture); // показываем cvShowImage("capture", frame); char c = cvWaitKey(33); if (c == 27) { // нажата ESC break; } else if(c == 13) { // Enter // сохраняем кадр в файл sprintf(filename, "Image%d.jpg", counter); printf("[i] capture... %s\n", filename); cvSaveImage(filename, frame); counter++; } } // освобождаем ресурсы cvReleaseCapture(&capture); cvDestroyWindow("capture"); return 0; }

OpenCV v1.0 показывает и сохраняет картинку минимального разрешения камеры 320x240.


Распознавание объектов по шаблону

Для распознавание областей на исходном изображении по шаблону существует функция cvMatchTemplate(). Функция накладывает шаблон изображения на текущее изображение и согласно выбранному алгоритму выполняет поиск корреляции между ними. Определение границ найденного шаблона на исходном изображении выполняется функцией cvMinMaxLoc, а для нормализации алгоритма поиска cvNormalize().

// // пример cvMatchTemplate() // сравнение изображение с шаблоном // #include #include #include #include IplImage* image = 0; IplImage* templ = 0; int main(int argc, char* argv) { // имя картинки задаётся первым параметром char* filename = argc >= 2 ? argv : "Image0.jpg"; // получаем картинку image = cvLoadImage(filename,1); printf("[i] image: %s\n", filename); assert(image != 0); // шаблон char* filename2 = argc >= 3 ? argv : "eye.jpg"; printf("[i] template: %s\n", filename2); templ = cvLoadImage(filename2,1); assert(templ != 0); cvNamedWindow("origianl", CV_WINDOW_AUTOSIZE); cvNamedWindow("template", CV_WINDOW_AUTOSIZE); cvNamedWindow("Match", CV_WINDOW_AUTOSIZE); cvNamedWindow("res", CV_WINDOW_AUTOSIZE); // размер шаблона int width = templ->width; int height = templ->height; // оригинал и шаблон cvShowImage("origianl", image); cvShowImage("template", templ); // изображение для хранения результата сравнения // размер результата: если image WxH и templ wxh, то result = (W-w+1)x(H-h+1) IplImage *res = cvCreateImage(cvSize((image->width-templ->width+1), (image->height-templ->height+1)), IPL_DEPTH_32F, 1); // сравнение изображения с шаблоном cvMatchTemplate(image, templ, res, CV_TM_SQDIFF); // покажем что получили cvShowImage("res", res); // определение лучшее положение для сравнения // (поиск минимумов и максимумов на изображении) double minval, maxval; CvPoint minloc, maxloc; cvMinMaxLoc(res, &minval, &maxval, &minloc, &maxloc, 0); // нормализуем cvNormalize(res,res,1,0,CV_MINMAX); cvNamedWindow("res norm", CV_WINDOW_AUTOSIZE); cvShowImage("res norm", res); // выделим область прямоугольником cvRectangle(image, cvPoint(minloc.x, minloc.y), cvPoint(minloc.x+templ->width-1, minloc.y+templ->height-1), CV_RGB(255, 0, 0), 1, 8); // показываем изображение cvShowImage("Match", image); // ждём нажатия клавиши cvWaitKey(0); // освобождаем ресурсы cvReleaseImage(&image); cvReleaseImage(&templ); cvReleaseImage(&res); cvDestroyAllWindows(); return 0; }

Библиотека компьютерного зрения и машинного обучения с открытым исходным кодом. В неё входят более 2500 алгоритмов, в которых есть как классические, так и современные алгоритмы для компьютерного зрения и машинного обучения. Эта библиотека имеет интерфейсы на различных языках, среди которых есть Python (в этой статье используем его), Java, C++ и Matlab.

Установка

Инструкцию по установке на Windows можно посмотреть , а на Linux - .

Импорт и просмотр изображения

import cv2 image = cv2.imread("./путь/к/изображению.расширение") cv2.imshow("Image", image) cv2.waitKey(0) cv2.destroyAllWindows()

Примечание При чтении способом выше изображение находится в цветовом пространстве не RGB (как все привыкли), а BGR. Возможно, в начале это не так важно, но как только вы начнёте работать с цветом - стоит знать об этой особенности. Есть 2 пути решения:

  1. Поменять местами 1-й канал (R - красный) с 3-м каналом (B - синий), и тогда красный цвет будет (0,0,255) , а не (255,0,0) .
  2. Поменять цветовое пространство на RGB: rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

    И тогда в коде работать уже не с image , а с rgb_image .

Примечание Чтобы закрыть окно, в котором отображается изображение, нажмите любую клавишу. Если использовать кнопку закрытия окна, можно наткнуться на подвисания.

На протяжении статьи для вывода изображений будет использоваться следующий код:

Import cv2 def viewImage(image, name_of_window): cv2.namedWindow(name_of_window, cv2.WINDOW_NORMAL) cv2.imshow(name_of_window, image) cv2.waitKey(0) cv2.destroyAllWindows()

Кадрирование

Пёсик после кадрирования

Import cv2 cropped = image viewImage(cropped, "Пёсик после кадрирования")

Где image - это image .

Изменение размера

После изменения размера на 20 %

Import cv2 scale_percent = 20 # Процент от изначального размера width = int(img.shape * scale_percent / 100) height = int(img.shape * scale_percent / 100) dim = (width, height) resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) viewImage(resized, "После изменения размера на 20 %")

Эта функция учитывает соотношение сторон оригинального изображения. Другие функции изменения размера изображений можно увидеть .

Поворот

Пёсик после поворота на 180 градусов

Import cv2 (h, w, d) = image.shape center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, 180, 1.0) rotated = cv2.warpAffine(image, M, (w, h)) viewImage(rotated, "Пёсик после поворота на 180 градусов")

image.shape возвращает высоту, ширину и каналы. M - матрица поворота - поворачивает изображение на 180 градусов вокруг центра. -ve - это угол поворота изображения по часовой стрелке, а +ve , соответственно, против часовой.

Перевод в градации серого и в чёрно-белое изображение по порогу

Пёсик в градациях серого

Чёрно-белый пёсик

Import cv2 gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) ret, threshold_image = cv2.threshold(im, 127, 255, 0) viewImage(gray_image, "Пёсик в градациях серого") viewImage(threshold_image, "Чёрно-белый пёсик")

gray_image - это одноканальная версия изображения.

Функция threshold возвращает изображение, в котором все пиксели, которые темнее (меньше) 127 заменены на 0, а все, которые ярче (больше) 127, - на 255.

Для ясности другой пример:

Ret, threshold = cv2.threshold(im, 150, 200, 10)

Здесь всё, что темнее, чем 150, заменяется на 10, а всё, что ярче, - на 200.

Остальные threshold-функции описаны .

Размытие/сглаживание

Размытый пёсик

Import cv2 blurred = cv2.GaussianBlur(image, (51, 51), 0) viewImage(blurred, "Размытый пёсик")

Функция GaussianBlur (размытие по Гауссу) принимает 3 параметра:

  1. Исходное изображение.
  2. Кортеж из 2 положительных нечётных чисел. Чем больше числа, тем больше сила сглаживания.
  3. sigmaX и sigmaY . Если эти параметры оставить равными 0, то их значение будет рассчитано автоматически.

Рисование прямоугольников

Обводим прямоугольником мордочку пёсика

Import cv2 output = image.copy() cv2.rectangle(output, (2600, 800), (4100, 2400), (0, 255, 255), 10) viewImage(output, "Обводим прямоугольником лицо пёсика")

Эта функция принимает 5 параметров:

  1. Само изображение.
  2. Координата верхнего левого угла (x1, y1) .
  3. Координата нижнего правого угла (x2, y2) .
  4. Цвет прямоугольника (GBR/RGB в зависимости от выбранной цветовой модели).
  5. Толщина линии прямоугольника.

Рисование линий

2 пёсика, разделённые линией

Import cv2 output = image.copy() cv2.line(output, (60, 20), (400, 200), (0, 0, 255), 5) viewImage(output, "2 пёсика, разделённые линией")

Функция line принимает 5 параметров:

  1. Само изображение, на котором рисуется линия.
  2. Координата первой точки (x1, y1) .
  3. Координата второй точки (x2, y2) .
  4. Цвет линии (GBR/RGB в зависимости от выбранной цветовой модели).
  5. Толщина линии.

Текст на изображении

Изображение с текстом

Import cv2 output = image.copy() cv2.putText(output, "We <3 Dogs", (1500, 3600),cv2.FONT_HERSHEY_SIMPLEX, 15, (30, 105, 210), 40) viewImage(output, "Изображение с текстом")

Функция putText принимает 7 параметров:

  1. Непосредственно изображение.
  2. Текст для изображения.
  3. Координата нижнего левого угла начала текста (x, y) .
  4. Лиц обнаружено: 2

    Import cv2 image_path = "./путь/к/фото.расширение" face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") image = cv2.imread(image_path) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor= 1.1, minNeighbors= 5, minSize=(10, 10)) faces_detected = "Лиц обнаружено: " + format(len(faces)) print(faces_detected) # Рисуем квадраты вокруг лиц for (x, y, w, h) in faces: cv2.rectangle(image, (x, y), (x+w, y+h), (255, 255, 0), 2) viewImage(image,faces_detected)

    detectMultiScale - общая функция для распознавания как лиц, так и объектов. Чтобы функция искала именно лица, мы передаём ей соответствующий каскад.

    Функция detectMultiScale принимает 4 параметра:

    1. Обрабатываемое изображение в градации серого.
    2. Параметр scaleFactor . Некоторые лица могут быть больше других, поскольку находятся ближе, чем остальные. Этот параметр компенсирует перспективу.
    3. Алгоритм распознавания использует скользящее окно во время распознавания объектов. Параметр minNeighbors определяет количество объектов вокруг лица. То есть чем больше значение этого параметра, тем больше аналогичных объектов необходимо алгоритму, чтобы он определил текущий объект, как лицо. Слишком маленькое значение увеличит количество ложных срабатываний, а слишком большое сделает алгоритм более требовательным.
    4. minSize - непосредственно размер этих областей.

    Contours - распознавание объектов

    Распознавание объектов производится с помощью цветовой сегментации изображения . Для этого есть две функции: cv2.findContours и cv2.drawContours .

    В этой статье детально описано обнаружение объектов с помощью цветовой сегментации. Всё, что вам нужно для неё, находится там.

    Сохранение изображения

    import cv2 image = cv2.imread("./импорт/путь.расширение") cv2.imwrite("./экспорт/путь.расширение", image)

    Заключение

    OpenCV - отличная библиотека с лёгкими алгоритмами, которые могут использоваться в 3D-рендере, продвинутом редактировании изображений и видео, отслеживании и идентификации объектов и людей на видео, поиске идентичных изображений из набора и для много-много чего ещё.

    Эта библиотека очень важна для тех, кто разрабатывает проекты, связанные с машинным обучением в области изображений.

Евгений Борисов понедельник, 24 июля 2017 г.

В этой статье привёден обзор методов поиска объекта на изображении.

1. Введение

Множество практических задач от автоматизации контроля на производстве до конструирования роботизированных автомобилей непосредственно связаны с задачей поиска объектов на изображении. Для её решения можно применять две разные стратегии, которые зависят от условий съёмки - моделирование фона и моделирование объекта.
  1. Моделирование фона - этот подход можно применять если камера неподвижна, т.е. мы имеем фон, который мало изменяется, и таким образом можно построить его модель. Все точки изображения, которые существенно отклоняются от модели фона, считаем объектами переднего плана. Таким образом можно решать задачи обнаружения и сопровождения объекта.
  2. Моделирование объекта - этот подход более общий, применяеться в случаях когда фон постоянно и существенным образом изменяется. В отличии от предыдущего случая, здесь нам необходимо знать что именно мы хотим найти, т.е. необходимо построить модель объекта, а затем проверить точки картинки на соответствие этой модели.
Иногда условия задачи позволяют комбинировать оба подхода, это может существенно улучшить результаты. Решение задачи с моделированием фона для неподвижной камерой можно найти в . Далее будем рассматривать применение второй стратегии, т.е. моделирование объекта поиска.

2. Обзор методов

В этом разделе мы приведём список подходов, с помощью которых можно успешно решать задачу поиска объекта на изображении, в порядке возрастания сложности.
  1. Цветовые фильтры - если объект существенно выделяется на фоне по цвету, то можно подобрать соответствующий фильтр.
  2. Выделение и анализ контуров - если мы знаем, что объект имеет форму, например, круга, то можно поискать окружности на изображении.
  3. Сопоставление с шаблоном - у нас есть изображение объекта, ищем в другом изображении области совпадающие с этим изображением объекта.
  4. Работа с особыми точками - на картинке с объектом ищем особенности (например углы), которые пытаемся сопоставить с такими особенностями на другом изображении.
  5. Методы машинного обучения - обучаем классификатор на картинках с объектом, некоторым способом разделяем изображение на части, проверяем классификатором каждую часть на наличие объекта.
Далее мы рассмотрим эти методы подробней.

3. Цветовые фильтры

Метод цветовых фильтров можно применять в случаях, когда объект существенно отличаться от фона по цвету и освещение равномерно и не изменяется. Подробней о методе цветовых фильтров можно почитать в .

4. Выделение и анализ контуров

Если объект на фоне по цвету существенным образом не выделяется и/или имеет сложную раскраску, то применение метода цветовых фильтров не даст хороших результатов. В этом случае можно попробовать применить метод выделения и анализа контуров. Для этого мы выделяем границы на изображении. Границы это места резкого изменения градиента яркости, их можно найти с помощью метода Canny . Далее мы можем проверить выделенные линии-границы на соответствие геометрическим контурам объекта, это можно сделать применив метод Хафа (Hough Transform) , например мы можем поискать в границах окружности.





Рис.4: поиск окружностей

Этот метод также можно применять совместно с цветовыми фильтрами. Подробней о выделении и анализе контуров можно почитать в . Исходный код примера c поиском окружностей можно скачать .

5. Сопоставление с шаблоном

Если изображение имеет множество мелких деталей то анализ контуров может быть затруднён. В этом случае можно применить метод сопоставления с шаблоном (template matching). Он заключается в следующем - берем картинку с объектом (рис.5) и ищем на большом изображении области совпадающие с изображением объекта (рис.6,7).


Рис 5: объект для поиска

Подробней о методе сопоставления с шаблоном можно послушать лекцию . Исходный код примера можно скачать .

6. Работа с особыми точками

Метод сопоставления с шаблоном, описанный в предыдущем разделе, ищет точные совпадения точек шаблона с точками изображения. Если изображение повёрнуто или масштабировано относительно параметров шаблона то этот метод работает плохо. Для преодоления этих ограничений применяют методы основанные на т.н. особых точках, их мы рассмотрим далее. Особая точка (key point) это небольшая область, которая существенным образом выделяется на изображении. Существует несколько методов определения таких точек, это могут быть углы (Harris corner detector) или блобы (blob, капля) , т.е. небольшие области одинаковой яркости, достаточно чёткой границей, выделяющиеся на общем фоне. Для особой точки вычисляют т.н. дескриптор - характеристику особой точки. Дескриптор вычисляют по заданной окрестности особой точки, как направления градиентов яркости разных частей этой окрестности. Существует несколько методов расчёта дескрипторов для особых точек: SIFT, SURF, ORB и др. Надо отметить, что некоторые методы расчёта дескрипторов являются запатентованными (например SIFT) и их коммерческое использование ограничено. Подробней про особые точки на изображениях и методах работы с ними можно послушать лекцию . Особые точки можно применять для поиска объекта на изображении. Для этого нам необходимо иметь изображение искомого объекта и далее выполнить следующие действия.
  1. На картинке с объектом ищем особые точки объекта и вычисляем их дескрипторы.
  2. На анализируемом изображении тоже ищем особые точки и вычисляем для них дескрипторы.
  3. Сравниваем дескрипторы особых точек объекта и дескрипторы особых точек, найденных на изображении.
  4. Если найдено достаточное количество соответствий то помечаем область с соответствующими точками.
Ниже на рис.8 представлены результаты работы метода поиска объекта по особым точкам.


Рис 8: детектор объектов по особым точкам

Исходный код примера можно скачать .

7. Методы машинного обучения

Метод поиска объектов путём сравнение наборов особых точек имеет свои недостатки, один из них это плохая обобщающая способность. Если у нас стоит задача, например, выделения лиц людей на фото, то по особым точкам наш метод будет искать одну конкретную фотографию. Ту фотографию, на которой были выделены особые точки, остальные лица будут выделятся хуже, потому как им, скорее всего, соответствуют другие наборы особых точек. Результаты могут быть ещё хуже если изменить ракурс съемки. Для решения этих проблем нам уже необходимы методы машинного обучения и не одна картинка с объектом но целые учебные наборы из сотен (а в некоторых случаях - сотен тысяч) разных картинок с изображением объекта в разных условиях. Применение методов машинного обучения для поиска объектов на изображении мы рассмотрим во второй части этой статьи.

Литература

  1. Е.С.Борисов Детектор объектов для неподвижных камер.
    - http://сайт/cv-backgr.html
  2. Е.С.Борисов Обработка видео: детектор объектов на основе цветовых фильтров.
    - http://сайт/cv-detector-color.html
  3. Е.С.Борисов Базовые методы обработки изображений.
    - http://сайт/cv-base.html
  4. Антон Конушин Компьютерное зрение (2011). Лекция 3. Простые методы анализа изображений. Сопоставление шаблонов.
    - http://www.youtube.com/watch?v=TE99wDbRrUI
  5. OpenCV documentation: Harris Corner Detection
    - http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_features_harris/py_features_harris.html
  6. Wikipedia: Blob_detection
    - http://en.wikipedia.org/wiki/Blob_detection
  7. Антон Конушин Компьютерное зрение (2011). Лекция 5. Локальные особенности
    - http://www.youtube.com/watch?v=vFseUICis-s