План розпізнавання об'єктів з зображень opencv python. Комп'ютерний зір на openCV

План розпізнавання об'єктів з зображень opencv python. Комп'ютерний зір на openCV

Ну, в основному, вам потрібно виявити кола. Ви бачили cvHoughCircles() ? Чи дозволено це використовувати?

На цій сторінці є гарна інформаціяпро те, як виявити речі за допомогою OpenCV. Вас може зацікавити розділ 2.5.

Це невелика демонстрація, яку я щойно написав, щоб знайти монети на цій картинці. Сподіваюся, ви можете використовувати частину коду у своїх інтересах.

Вхід :

Виходи :

// compiled with: g++ circles.cpp -o circles `pkg-config --cflags --libs opencv` #include #include #include #include int main(int argc, char** argv) ( IplImage* img = NULL; if ((img = cvLoadImage(argv))== 0) ( printf("cvLoadImage failed\n"); ) IplImage* gray = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1), CvMemStorage* storage = cvCreateMemStorage(0); , CV_GAUSSIAN, 7, 7), IplImage* canny = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U,1); 3);CvSeq* circles = cvHoughCircles(gray, storage;< circles->total; i++) (// float float int float* p = (float*)cvGetSeqElem( circles, i); cv::Point center(cvRound(p), cvRound(p)); int radius = cvRound(p) // draw the circle center cvCircle(rgbcanny, center, 3, CV_RGB(0,255,0), -1, 8, 0); // draw the circle outline cvCircle(rgbcanny, center, radius+1, CV_RGB(0, 0,255), 2, 8, 0); cvShowImage("cyrcles", rgbcanny); cvSaveImage("out.png", rgbcanny); cvWaitKey(0); return 0; )

Виявлення кіл сильно залежить від параметрів cvHoughCircles() . Зверніть увагу, що у цій демонстрації я також використав Canny.

Я повинен закодувати детектор об'єкта (у даному випадкукулька) за допомогою OpenCV. Проблема в тому, що кожен пошук у Google повертає мені щось з FACE DETECTION. Тому мені потрібна допомога в тому, з чого почати, що використовувати і т.д.

Певна інформація:

  • М'яч не має фіксованого кольору, він, ймовірно, буде білим, але може змінитися.
  • Я повинен використовувати машинне навчання, не обов'язково бути складним і надійним, пропозиція KNN (це простіше та простіше).
  • Після всього мого пошуку я виявив, що обчислення гістограми зразків тільки для куль і навчання його ML може бути корисним, але моя головна турбота тут у тому, що розмір кулі може і зміниться (ближче і далі від камери) і я не маю поняття, що передати ML, щоб класифікувати для мене, я маю на увазі.. я не можу (або я можу?) просто перевірити кожен піксель зображення для кожного можливого розміру (від, скажімо, від 5x5 до WxH) та сподіваємося знайти позитивний результат.
  • Там може бути нерівномірне тло, наприклад, люди, тканина за м'ячем тощо.
  • Як я вже сказав, я повинен використовувати алгоритм ML, що означає відсутність алгоритмів Хаара або Віоли.
  • Крім того, я думав про використання контурів, щоб знайти кола на зображенні Canny"ed, просто потрібно знайти спосіб перетворення контуру в рядок даних для навчання KNN.

    Отже... речення?

    Заздалегідь дякую. ;)

Найважливішими джерелами інформації про зовнішній світ для робота є його оптичні датчики та камери. Після отримання зображення необхідна його обробка для аналізу ситуації або прийняття рішення. Як я казав раніше, комп'ютерний зірпоєднує безліч методів роботи із зображеннями. При функціонуванні робота передбачається, що відеоінформація з камер обробляється програмою, запущеною на контролері. Щоб не писати код із нуля, можна скористатися готовими програмними рішеннями. На даний момент існує безліч готових бібліотек комп'ютерного зору:

  • 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 найближчих сусідів, нейронні мережі, метод опорних векторів і т.д. вирішення переважної більшості завдань комп'ютерного зору. Якщо алгоритм немає у складі 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::vector Scene; 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 (maped object in the scene - image_2) line(imgMatch, scene_corners + Point2f(ImageTemple.cols, 0), scene_corners + Point2f(ImageTemple.cols, 0), Scalar(0 , 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. Положення та поворот об'єкта обчислюється за допомогою функції перспективногоперетворення. Ну а потім висновок на зображення.
Еталонне зображення:

Зображення сцени:


Результат представлений на початку.
Однак тут виникає питання, як розраховувати оптимальний поріг особливостей: 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::vector Obj; std::vector Scene; 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::vector Scene_corners(4); perspectiveTransform(obj_corners, scene_corners, H); //-- Draw lines between the corners (maped 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; )

Результат роботи наступний:


Очевидно, що всі об'єкти детектовані. Причому деякі двічі (через те, що потрапили до двох блоків).

З цієї статті ви дізнаєтесь, як створити Python-скрипт для підрахунку кількості книг на зображенні за допомогою OpenCV.

Що ми будемо робити?

Погляньмо на зображення, на якому шукатимемо книги:

Ми бачимо, що на зображенні знаходяться чотири книги, а також речі, що відволікають, такі як кухоль кави, чашка Starbucks, кілька магнітів і цукерка.

Наша мета полягає в тому, щоб знайти чотири книги на зображенні, при цьому не визначивши жодного іншого предмета як книгу.

Які бібліотеки нам знадобляться?

Щоб написати систему для пошуку та виявлення книг на зображеннях, ми будемо використовувати OpenCV для роботи з комп'ютерним зором та обробки зображень. Нам також потрібно встановити NumPy для коректної роботи OpenCV. Переконайтеся, що у вас є ці бібліотеки!

Пошук книг на зображеннях за допомогою Python та OpenCV

Прим. перев. Ви можете помітити, що вихідний код нашої статті відрізняється від коду в оригіналі. Автор, ймовірно, використав встановлення необхідних бібліотек через репозиторії. Ми пропонуємо використовувати pip, що набагато простіше. Щоб уникнути появи помилок, рекомендуємо використовувати версію коду, наведену в нашій статті.

Відкрийте свій улюблений редактор коду, створіть новий файл з ім'ям find_books.py та почнемо:

# -*- coding: utf-8 -*- # імпортуйте необхідні пакети import numpy as np import cv2 # завантажте зображення, змініть колір на відтінки сірого та зменшіть різкість image = cv2.imread("example.jpg") gray = cv2. cvtColor(image, cv2.COLOR_BGR2GRAY) = cv2.GaussianBlur(gray, (3, 3), 0) cv2.imwrite("gray.jpg", gray)

Почнемо з імпорту бібліотеки OpenCV. Завантаження зображення з диска обробляється функцією cv2.imread. Тут ми просто завантажуємо його з диска, а потім перетворимо колірну гаму з RGB на відтінки сірого.

Ми також трохи розмиваємо зображення, щоб зменшити високочастотні шуми та підвищити точність нашої програми. Після виконання коду зображення має виглядати так:

Ми завантажили зображення з диска, перетворили його на відтінки сірого і трохи розмили.

Тепер давайте визначимо краї (тобто контури) об'єктів на зображенні:

# розпізнавання контурів edged = cv2.Canny(gray, 10, 250) cv2.imwrite("edged.jpg", edged)

Тепер наше зображення виглядає так:

Ми знайшли контури об'єктів зображення. Однак, як ви бачите, деякі контури не закриті - між контурами існують проміжки. Щоб усунути проміжки між білими пікселями зображення, ми застосуємо операцію «закриття»:

# створіть і застосуйте закриття kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) closed = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel) cv2.imwrite("closed.jpg", clo

Тепер прогалини в контурах закриті:

Наступним кроком є ​​фактичне виявлення контурів об'єктів зображення. Для цього ми будемо використовувати функцію cv2.findContours:

# знайдіть контури у зображенні та підрахуйте кількість книг cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) total = 0

Розглянемо геометрію книжки.

Книга є прямокутником. У прямокутника чотири вершини. Тому, якщо ми розглянемо контур і виявимо, що він має чотири вершини, ми можемо припустити, що це книга, а чи не інший предмет на зображенні.

Щоб перевірити, чи є контур книгою чи ні, нам потрібно виконати цикл за кожним контуром:

# цикл по контурах for c in cnts: # апроксимуємо (згладжуємо) контур peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * peri, True) # якщо у контуру 4 вершини, припускаємо, що це книга if len(approx) == 4: cv2.drawContours(image, , -1, (0, 255, 0), 4) total += 1

Для кожного контуру ми обчислюємо периметр, використовуючи cv2.arcLength , а потім апроксимуємо (згладжуємо) контур, використовуючи cv2.approxPolyDP .

Причина, через яку ми апроксимуємо контур, полягає в тому, що він може не бути ідеальним прямокутником. Через зашумлення і тіні на фото ймовірність того, що книга буде рівно 4 вершини, невелика. Апроксимуючи контур, ми вирішуємо цю проблему.

Нарешті, ми перевіряємо, що у апроксимованого контуру справді чотири вершини. Якщо це так, ми малюємо контур навколо книги, а потім збільшуємо лічильник загальної кількості книг.

Завершимо цей приклад, показуючи отримане зображення та кількість знайдених книг:

# показуємо результуюче зображення print("Я знайшов (0) книг на цій картинці".format(total) cv2.imwrite("output.jpg", image))

На даному етапі наше зображення виглядатиме так:

Підведемо підсумки

У цій статті ви навчилися знаходити книги на зображеннях, використовуючи прості методи обробки зображень та комп'ютерного зору за допомогою Python та OpenCV.

Наш підхід полягав у тому, щоб:

  1. Завантажити зображення з диска та перетворити його на відтінки сірого.
  2. Трохи розмити зображення.
  3. Застосувати детектор контурів Canny для виявлення об'єктів зображення.
  4. Закрити будь-які проміжки у контурах.
  5. Знайти контури об'єктів на зображенні.
  6. Застосувати контурну апроксимацію, щоб визначити, чи був контур прямокутником і, отже, книгою.

Ви можете завантажити вихідний код скрипта та зображення, яке використовується у цій статті.

Основна ідея полягає в обліку статистичних зв'язків між розташуванням антропометричних точок обличчя. На кожному зображенні обличчя точки пронумеровані однаково. За їх взаємним розташуванням здійснюється порівняння осіб.

Для порівняння осіб можна використовувати те саме положення обличчя щодо камери. Більш переважний для цього.

Захоплення відеопотоку з камери та виділення обличчя

#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 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 + X36) /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 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 на 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; }

 

 

Це цікаво: