OpenCV. Инструмент для «прозрения» машины

Библиотека компьютерного зрения и машинного обучения с открытым исходным кодом. В неё входят более 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-рендере, продвинутом редактировании изображений и видео, отслеживании и идентификации объектов и людей на видео, поиске идентичных изображений из набора и для много-много чего ещё.

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

Основная идея заключается в учете статистических связей между расположением антропометрических точек лица . На каждом изображении лица точки пронумерованы в одинаковом порядке. По их взаимному расположению осуществляется сравнение лиц.

Для сравнения лиц можно использовать одного и того же положения лица относительно камеры. Более предпочтителен для этого .

Захват видеопотока с камеры и выделение лица

#include using namespace cv; int main() { // Load Face cascade (.xml file) CascadeClassifier face_cascade; face_cascade.load("haarcascade_frontalface_alt2.xml"); Mat img; VideoCapture cap(0); while (true) { cap >> img; //cvtColor(img, img, CV_BGR2GRAY); // Detect faces std::vector faces; face_cascade.detectMultiScale(img, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30)); // Draw circles on the detected faces for (int i = 0; i < faces.size(); i++) { Point center(faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5); ellipse(img, center, Size(faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar(255, 0, 255), 4, 8, 0); } imshow("Detected Face", img); waitKey(1); } return 0; }

Файлы каскадов находятся в директории c:\opencv\build\etc\… Нужный каскад размещаете в директории проекта, там же, где и исходный файл main.cpp.

Выделение особых точек лица

Приложение создано на основе C++ code for OpenCV Facemark

#include #include #include #include #include #include #include "drawLandmarks.hpp" using namespace std; using namespace cv; using namespace cv::face; int main(int argc, char** argv) { // Load Face Detector CascadeClassifier faceDetector("haarcascade_frontalface_alt2.xml"); // Create an instance of Facemark Ptr facemark = FacemarkLBF::create(); // Load landmark detector facemark->loadModel("lbfmodel.yaml"); // Set up webcam for video capture VideoCapture cam(0); // Variable to store a video frame and its grayscale Mat frame, gray; // Read a frame while (cam.read(frame)) { // Find face vector faces; // Convert frame to grayscale because // faceDetector requires grayscale image. cvtColor(frame, gray, COLOR_BGR2GRAY); // Detect faces faceDetector.detectMultiScale(gray, faces); // Variable for landmarks. // Landmarks for one face is a vector of points // There can be more than one face in the image. Hence, we // use a vector of vector of points. vector< vector > landmarks; // Run landmark detector bool success = facemark-> < faces.size(); i++) { cv::rectangle(frame, faces[i], Scalar(0, 255, 0), 3); } for (int i = 0; i < landmarks.size(); i++) { drawLandmarks(frame, landmarks[i]); /*for (size_t j = 0; j < landmarks[i].size(); j++) circle(frame, Point(landmarks[i][j].x, landmarks[i][j].y), 1, Scalar(255, 0, 0), 2);*/ } } // Display results imshow("Facial Landmark Detection", frame); // Exit loop if ESC is pressed if (waitKey(1) == 27) break; } return 0; }

В проекте приложения, там же где и файл main.cpp, разместил файлы haarcascade_frontalface_alt2.xml , drawLandmarks.hpp и lbfmodel.yaml , на которые есть ссылки в коде. Файлы каскадов находятся в директории c:\opencv\build\etc\… Файлы drawLandmarks.hpp и lbfmodel.yaml есть в архиве Facemark_LBF.rar .

После вставки кода появились ошибки из-за того, что в OpenCV 3.4.3-vc14-vc15 отсутствуют ряд библиотек, необходимых для запуска приложения. Скомпоновал свою библиотеку (скачать opencv_new.zip) и установил ее в корень диска C (C:\opencv-new).

Теперь, все настройки, которые выполнялись , необходимо выполнить и для opencv-new:

Выполняю настройки в Windows . Выхожу на окно «Изменить переменную среды» (кнопки Windows->Служебные->Панель управления -> Система и безопасность -> Система -> Дополнительные параметры системы -> Переменные среды -> Path ->Изменить). В этом окне создаю переменную C:\opencv-new \x64\vc14\bin. Перезагружаю Windows .

В свойствах проекта также ссылаюсь на библиотеку opencv_new (вместо opencv). В окне «Property Pages» выполняю действия:

  • C/C++ -> General -> Additional Include Directories -> C:\opencv-new \include
  • Linker -> General -> Additional Library Directories -> C:\opencv-new \x64\vc14\lib
  • Linker -> Input -> Additional Dependencies -> opencv_core400.lib; opencv_face400.lib; opencv_videoio400.lib; opencv_objdetect400.lib; opencv_imgproc400.lib; opencv_highgui400.lib

При запуске программа выдает ошибку, если в установках проекта Debug. Для Release, запуск успешный.


Выбор признаков для фильтрации изображений и распознавания лиц

Точечный каркас лица отображается по разному в зависимости от объективных и субъективных факторов.

Объективные факторы — положение лица относительно камеры.

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

При видеозахвате иногда проскакивают и такие изображения. Их нужно отфильтровывать — как при обучении так и распознавании.

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

Для сравнения лиц можно использовать точечный 2D каркас одного и того же положения лица. Какое положение лица относительно камеры есть наиболее информативным? Очевидно, что фронтальное. Не зря в криминалистике делают фото в анфас и профиль. Пока ограничимся анфасом.

Все признаки (расстояния) должны быть безразмерные (нормализованые), т.е., соотнесены к какому-то размеру(расстоянию). Предполагаю, что наиболее подходящий для этого размер — расстояние между серединами угловых точек глаз. А почему, например, не внешними угловыми точками глаз, которые реально определены в массиве landmarks? Дело в том, что угловые точки глаз раздвигаются (сближаются) при реагировании на изменение цвета, выражении удивления, моргании и т.п. Расстояние между серединами глаз нивелирует эти колебания и поэтому более предпочтительно.

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

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

Оставляем только те точечные каркасы, которые проходят по следующим признакам:

  • Прямая, которая проходит через крайние точки глаз (линия глаз), перпендикулярна прямой, которая проходит через крайние точки носа (линия носа).
  • Линия глаз параллельна прямой, которая проходит через точки уголков рта (линия рта).
  • Соблюдается симметрия указанных выше точек относительно линии носа.
  • Угловые точки глаз (внешние и внутренние) находятся на одной прямой.

Пример фронтальных изображений, которые проходят по всем признакам:

Пример изображений, которые отфильтровываются:

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

Как формализуются признаки, которые обеспечивают фильтрацию и распознавание лиц? В основном они построены на условиях определении расстояний между точками, условий параллельности и перпендикулярности. Задача формализации таких признаков рассмотрена в теме .

Алгоритм распознавания лиц по 2D-каркасу точек

Координаты точек каркаса лица изначально задаются в системе координат, которая привязана к верхней левой точке окна. При этом ось Y направлена вниз.

Для удобства определения признаков используем пользовательскую систему координат (ПСК), ось X которой проходит через отрезок между серединами глаз, а ось Y — перпендикулярно этому отрезку через его середину в направлении вверх. Координаты ПСК (от -1 до +1) нормализованы — соотнесены с расстоянием между средними точками глаз.

ПСК обеспечивает удобство и простоту определения признаков. Например, положение лица в анфас определяется признаком симметрии соответствующих точек глаз относительно линии носа. Этот признак формализуется совпадением линии носа с осью Y, т.е X1=X2=0, где X1 и X2 — координаты крайних точек носа (27 и 30) в ПСК.

Определяем относительно оконной СК

Координаты средних точек левого и правого глаз (Left и Right):

XL = (X45 + X42) /2 ; YL = (Y45 + Y42) /2 ; XR = (X39 + X 36) /2; YR = (Y39 + Y 36) /2;

Начало ПСК:

X0 =(XL + XR)/2; Y0 =(YL + YR)/2;

Расстояния между средними точками глаз вдоль осей Х и Y:

DX = XR — XL; DY = YR — YL;

Действительное расстояние L между средними точками глаз (по теореме Пифагора):

L = sqrt (DX** 2 + DY**2)

Тригонометрические функции угла поворота ПСК:

Переходим от координат в оконной СК к координатам в ПСК , используя параметры X0,Y0, L, sin AL, cos AL:

X_User_0 = 2 (X_Window — X0) / L;

Y_User_0 = — 2 (Y_Window — Y0) / L ;

X_User = X_User_0 * cos_AL — Y_User_0 * sin_AL;

Y_User = X_User_0 * sin_AL + Y_User_0 * cos_AL;

Реализуем фильтрацию изображений последовательно проверяя признаки:

1.Признак перпендикулярности линий носа и глаз, а также симметрии угловых точек глаз . Линия носа определяется точками 27 и 30 (см. рисунок во ). Оба признака выполняются, если в ПСК координаты этих точек X1 = X2= 0 (т.е., линия носа совпадает с осью Y).

2.Признак параллельности линии глаз и линии рта . Линия рта определяется точками 48 и 54 (см. рисунок во ). Признак выполняется, если в ПСК Y1-Y2=0.

3. Признак симметрии угловых точек рта . Линия рта определяется точками 48 и 54 (см. рисунок во ). Признак выполняется, если в ПСК X1+X2 =0

4. Признак «Угловые точки глаз находятся на одной прямой» . Прямые определяются парами точек: (36 и 45), а также (39 и 42). Поскольку тест по признаку 1 уже пройден, достаточно лишь определить в ПСК признак Y2-Y1 =0 лишь для точек 36 и 39.

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

Программа сравнения лиц по одному признаку

В качестве признака берется расстояние между точками переносицы и подбородка (Landmarks точками 27 и 8, см. рисунок во ). Признак, нормализован, определяется в ПСК отношением: (Y1 — Y2)/L , где L — расстояние между центрами глаз. При обучении программы признак для конкретного лица определяется числом, которое высвечивается рядом с отслеживаемым лицом (эта часть кода в программе закомментирована). При распознавании значение признака сравнивается с введенным в программу конкретным признаком для каждого лица. При положительном результате сравнения рядом с лицом появляется его идентификатор.

Программа распознает и по фото, на котором я на 15 лет моложе, да и при том, еще с усами. Различие на фото существенное, не каждый человек уловит. Но компьютерную программу не обманешь.

Контрольные задания:

  1. Ознакомиться с программой.
  2. Определить значение признака для своего лица и нескольких своих коллег.
  3. Протестировать программу на предмет идентификации лиц (своего и коллег).

#include #include #include #include #include #include #include "drawLandmarks.hpp" using namespace std; using namespace cv; using namespace cv::face; int main(int argc, char** argv) { // Load Face Detector CascadeClassifier faceDetector("haarcascade_frontalface_alt2.xml"); // Create an instance of Facemark Ptr facemark = FacemarkLBF::create(); // Load landmark detector facemark->loadModel("lbfmodel.yaml"); // Set up webcam for video capture VideoCapture cam(0); // Variable to store a video frame and its grayscale Mat frame, gray; // Read a frame while (cam.read(frame)) { // Find face vector faces; // Convert frame to grayscale because // faceDetector requires grayscale image. cvtColor(frame, gray, COLOR_BGR2GRAY); // Detect faces faceDetector.detectMultiScale(gray, faces); // Variable for landmarks. // Landmarks for one face is a vector of points // There can be more than one face in the image. Hence, we // use a vector of vector of points. vector< vector > landmarks; // Run landmark detector bool success = facemark->fit(frame, faces, landmarks); if (success) { // If successful, render the landmarks on the face for (size_t i = 0; i < faces.size(); i++) { cv::rectangle(frame, faces[i], Scalar(0, 255, 0), 3); } for (int i = 0; i < landmarks.size(); i++) { //if((i >=30)&&(i <= 35)) drawLandmarks(frame, landmarks[i]); for (size_t j = 0; j < landmarks[i].size(); j++) { circle(frame, Point(landmarks[i][j].x, landmarks[i][j].y), 1, Scalar(255, 0, 0), 2); } line(frame, Point(landmarks[i].x, landmarks[i].y), Point(landmarks[i].x, landmarks[i].y), Scalar(0, 0, 255), 2); float XL = (landmarks[i].x + landmarks[i].x) / 2; float YL = (landmarks[i].y + landmarks[i].y) / 2; float XR = (landmarks[i].x + landmarks[i].x) / 2; float YR = (landmarks[i].y + landmarks[i].y) / 2; line(frame, Point(XL, YL), Point(XR, YR), Scalar(0, 0, 255), 2); float DX = XR - XL; float DY = YR - YL; float L = sqrt(DX * DX + DY * DY); float X1 = (landmarks[i].x); float Y1 = (landmarks[i].y); float X2 = (landmarks[i].x); float Y2 = (landmarks[i].y); float DX1 = abs(X1 - X2); float DY1 = abs(Y1 - Y2); float L1 = sqrt(DX1 * DX1 + DY1 * DY1); float X0 = (XL + XR) / 2; float Y0 = (YL + YR) / 2; float sin_AL = DY / L; float cos_AL = DX / L; float X_User_0 = (landmarks[i].x - X0) / L; float Y_User_0 = -(landmarks[i].y - Y0) / L; float X_User27 = X_User_0 * cos_AL - Y_User_0 * sin_AL; float Y_User27 = X_User_0 * sin_AL + Y_User_0 * cos_AL; X_User_0 = (landmarks[i].x - X0) / L; Y_User_0 = -(landmarks[i].y - Y0) / L; float X_User30 = X_User_0 * cos_AL - Y_User_0 * sin_AL; float Y_User30 = X_User_0 * sin_AL + Y_User_0 * cos_AL; if (abs(X_User27 - X_User30) <= 0.1) { //putText(frame, std::to_string(abs(L1 / L)), Point(landmarks[i].x, landmarks[i].y), 1, 2, Scalar(0, 0, 255), 2); if (abs((L1 / L) - 1.6) < 0.1) { putText(frame, "Roman", Point(landmarks[i].x, landmarks[i].y), 1, 2, Scalar(0, 0, 255), 2); } if (abs((L1 / L) - 1.9) < 0.1) { putText(frame, "Pasha", Point(landmarks[i].x, landmarks[i].y), 1, 2, Scalar(0, 0, 255), 2); } if (abs((L1 / L) - 2.1) < 0.1) { putText(frame, "Svirnesvkiy", Point(landmarks[i].x, landmarks[i].y), 1, 2, Scalar(0, 0, 255), 2); } } putText(frame, "Incorrect", Point(landmarks[i].x, landmarks[i].y), 1, 2, Scalar(0, 0, 255), 2); } } // Display results imshow("Facial Landmark Detection", frame); // Exit loop if ESC is pressed if (waitKey(1) == 27) break; } return 0; }

Евгений Борисов понедельник, 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

Привет! Передо мной встала задача реализовать распознавание дорожных знаков с видео потока. Так как с задачами подобного рода я раньше не сталкивался, то процесс реализации само собой предполагает предварительное долгое «курение» форумов и безжалостные издевательства над чужими примерами. Поэтому решил собрать всё прочитанное в одном месте для будущих поколений, а так же, в ходе повествования, задать Хабру несколько вопросов.

Прелюдии.

Итак, после изучения всех средств, которые возможно использовать для реализации поставленной задачи, я остановился на среде разработки Microsoft Visual Studio© 2010 , с использованием чудесной библиотеки OpenCV .

Сам процесс работы с OpenCV предполагает предварительные танцы с бубном, о которых есть достаточно подробных описаний:

Второй акт танцев с бубном.

В итоге повернул в сторону тренировки каскадов. «Покурив» в этом направлении понял что мне нужны два инструмента createsampes и haartraining. Но их exe`шники у меня отсутствовали, а компилироваться отказывались. На тот момент версия OpenCV у меня была 2.4.4, настроенная по , во же статье я впервые прочитал про использование Cmake при установке. В итоге решил скачать версию 2.3.1 и переустановить библиотеку. После чего мне удалось запустить нужные инструменты через командную строку и встал вопрос как с ними работать. Все точки над «и» расставили статьи, в которых показаны параметры с которыми нужно запускать createsampes и haartraining с подробным описанием этих параметров.

Код с чистого листа.

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

Код 2.0

#include "stdafx.h" #include #include #include using namespace cv; int main(int argc, char** argv) { Mat frame, gray; string object_cascade = "haarustupi.xml"; CascadeClassifier haar(object_cascade); VideoCapture cap(0); namedWindow("Video", 1); vector objects; while (true) { cap >> frame; cvtColor(frame, gray, CV_BGR2GRAY); haar.detectMultiScale(gray, objects, 1.9, 10, 0,Size(50, 50)); for (vector::const_iterator r = objects.begin(); r != objects.end(); r++) rectangle(frame, r->tl(), r->br(), Scalar(0, 0, 255)); imshow("Video", frame); if (waitKey(33) >= 0) break; } return (EXIT_SUCCESS); }

Среду настраиваем точно так же как и в прошлом проекте.

ПовторениЯ - отцы учения.

Дело за «малым» обучить каскады.)
Тут начинается самое интересное. После чего я решил писать о всех этих мытарствах на хабр и просить совета.
Я заготовил 500 изображений размером 1600х1200. и одно изображение со знаком размером 80х80. Одного изображения будет достаточно, потому что мы детектируем определенный объект, а не огромное разнообразие лиц.

Итак, заготовив картинки и создав файл neg.dat со структурой

Negative/n (1).jpg negative/n (2).jpg negative/n (3).jpg negative/n (4).jpg ... negative/n (500).jpg

запускаем файл opencv_createsamples.exe через CMD со следующими параметрами

C:OpenCV2.3.1buildcommonx86opencv_createsamples.exe -vec C:OpenCV2.3.1buildcommonx86positive.vect -bg C:OpenCV2.3.1buildcommonx86neg.dat -img C:OpenCV2.3.1buildcommonx86ustupi.jpg -num 500 -w 50 -h 50 -bgcolor 0 -bgthresh 0 -show

параметр -show показывает создаваемые позитивные картинки, но они, в отличие от указанных в других статьях
картинок , получается вот такая

маленькая

Т.е утилита обрезает bg-картинку под размер позитивной картинки. Изменение параметров -w и -h результата не дают и заднего фона все равно почти не видно. Если вдруг кто знает в чем тут дело, поделитесь соображениями . Размер негативных изображений уменьшал до 800х600 - результат тот же.

C:OpenCV2.3.1buildcommonx86opencv_haartraining.exe -data C:OpenCV2.3.1buildcommonx86haarustupi -vec C:OpenCV2.3.1buildcommonx86positive.vect -bg C:OpenCV2.3.1buildcommonx86neg.dat -npos 500 -nneg 500 -nstages 6 -nsplits 2 -w 20 -h 24 -mem 1536 -mode ALL -nonsym -minhitrate 0.999 -maxfalsealarm 0.5

после чего вы получите долгожданный xml-файл, который можно подгружать в исходный код программы.
В итоге каскад слегка обучается и, с большим количеством ложных срабатываний, реагирует на, полюбившуюся мне, картинку знака уступи дорогу.
Но я не могу добиться точных срабатываний, как мне кажется, из-за того что обрезается задний фон в позитивных изображениях. И никак не получаются картинки как в мануалах. Но остается еще вариант увеличить количество этапов обучения и, нагрузив свой компьютер на весь день, дождаться пока каскад будет более «образованным». Чем я и планирую заняться до появления других идей.

Эпилог

Вот такая получилась первая HelloHabr-статья у меня. Жду ваших замечаний о стиле изложения материала. Ну и конечно советов по теме.
Надеюсь после полученных советов будет чем продолжить повествование.