Реализация распознавания лиц на неподвижных изображениях на основе OpenCV4.1.0

компьютерное зрение

Аннотация: Обнаружение лиц на неподвижных изображениях заняло один день.Это первый интуитивный опыт компьютерного зрения, и это большое достижение. В этой статье подробно объясняется роль каждой строки оператора в исходной программе, в основном для того, чтобы прояснить мысли вашего собственного мозга. Без дальнейших церемоний, давайте начнем прямо!

исходный код

Примечание. В этой статье используется среда разработки VS2019 Community + OpenCV4.1.0. Среду необходимо настроить заранее. Если вам нужна помощь, обратите внимание на публичный аккаунт [Deer Talk] и посмотрите мой первый твит.

//FaceRec.cpp

#include<opencv2/opencv.hpp>
//#include<opencv2/objdetect/objdetect.hpp>
//#include<opencv2/highgui/highgui.hpp>
//#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2\imgproc\types_c.h>

using namespace std;
using namespace cv;

CascadeClassifier faceCascade;                  //人脸检测的类

int main()
{     faceCascade.load("D:/openCV/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml");   //加载分类器,注意文件路径

	Mat img = imread("3ming.jpg");    //图片放在与FaceRec.cpp同级目录中
	Mat imgGray;
	vector<Rect> faces;

	if (img.empty())
	{
		return 1;
	}

	if (img.channels() == 3)
	{
		cvtColor(img, imgGray, CV_RGB2GRAY);
	}
	else
	{
		imgGray = img;
	}

	faceCascade.detectMultiScale(imgGray, faces, 1.2, 6, 0, Size(0, 0));   //检测人脸

	if (faces.size() > 0)
	{
		for (int i = 0; i < faces.size(); i++)
		{
			rectangle(img, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
				Scalar(0, 0, 255), 4, 8);    //框出人脸位置
		}
	}

	imshow("FacesOf3ming", img);

	waitKey(0);
	return 0;
}

Примечание: в этом примере использованы фотографии трех братьев семьи Мин. Лучше всего найти фото лицевой стороны, поместить его под тот же уровень FaceRec.cpp, а название картинки в коде заменить на свое.

Детальный анализ кода

раздел заголовочного файла

#include<opencv2/opencv.hpp>
//#include<opencv2/objdetect/objdetect.hpp>
//#include<opencv2/highgui/highgui.hpp>
//#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2\imgproc\types_c.h>

Найдите каталог установки OpenCV и откройте его.D:\openCV\opencv\sources\include\opencv2,увидимopencv.hppфайл, мы открыли его с помощью Блокнота и обнаружили, что он содержит множество файлов заголовков OpenCV, а также включены три закомментированных нами файла заголовков. Таким образом, мы можем использовать#include<opencv2/opencv.hpp>Одно предложение заменяет трехстрочный файл заголовка, закомментированный ниже, что эффективно упрощает код. И последнее#include <opencv2\imgproc\types_c.h>родыD:\openCV\opencv\build\include\opencv2\imgprocвнутри, не входит вopencv.hppвнутри, поэтому это предложение#include <opencv2\imgproc\types_c.h>добавить. Так почему же используются эти заголовочные файлы?

1. положить#include <opencv2\imgproc\types_c.h>После комментирования будет предложеноНеопределенный идентификатор "CV_RGB2GRAY", считается, что идентификатор находится в<opencv2\imgproc\types_c.h>этот заголовочный файл.

2. Так же, как и 1,cvtColor()функция иRectangl()функция должна быть в<opencv2/imgproc/imgproc.hpp>.

3. Там же,imread(),imshow(),waitkey()роды<opencv2/highgui/highgui.hpp>.

4. Там же,CascadeClassifierкласс должен находиться в<opencv2/objdetect/objdetect.hpp>.

часть пространства имен

using namespace std;
using namespace cv;

using namespace std;Излишне говорить, что все идентификаторы стандартной библиотеки C++ объявлены вstdВ пространстве имен часто используетсяcin,cout,endlи другие идентификаторы, поэтому практически все программы на C++ имеют эту команду. В этой функцииvectorидентификатор принадлежитstdnamespace, поэтому есть эта директива.

заusing namespace cv;,Официальная документация OpenCVКак сказано: все классы и функции OpenCV помещаются вcv namespace. Therefore, to access this functionality from your code, use the cv:: specifier or using namespace cv;(Перевод: все классы и функции OpenCV помещаются вcvв пространстве имен. Итак, чтобы получить доступ к этой функции из кода, используйте инструкцииcv::знак илиusing namespace cv;команда), так что есть эта команда.

Класс каскадного классификатора CascadeClassifier

CascadeClassifier faceCascade;  //定义一个CascadeClassifier类的对象faceCascade

CascadeClassifier: класс каскадного классификатора для обнаружения объектов. То есть CascadeClassifier являетсясвоего рода. Так что жеКласс каскадного классификатораШерстяная ткань?

Понимание каскадных классификаторов

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

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

(цитата из сообщения в блоге АрвикаПростой анализ класса opencv: CascadeClassifier)

основная функциональная часть

faceCascade.load("D:/openCV/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml");   //加载分类器,注意文件路径

Mat img = imread("3ming.jpg");    //图片放在与FaceRec.cpp同级目录中
Mat imgGray;
vector<Rect> faces;
  • faceCascade.load("D:/openCV/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml");

    вызывающий объектfaceCascadeизload()функция, параметры которой.xmlАбсолютный путь к файлу.

    load()Функция функции заключается в инициализации объекта путем загрузки файла xml.faceCascade..xmlэто формат некоторых классификаторов, которые мы используем здесьhaarcascade_frontalface_alt2.xmlЭто один из многих обученных классификаторов, созданных в OpenCV.Особенности ХаараРаспознавание лиц.

    Путь к .xml должен быть написан правильно и строго соответствовать указанному выше формату. Путь копирования на компьютере — правая косая черта «\», но в программе используется левая косая черта «/».

  • Mat img = imread("3ming.jpg");

    определитьMatобъект классаimgи использоватьimread()Функция загружает указанный файл3ming.jpgприсвоить объектуimg.

    MatЯвляетсясвоего рода: основной класс реализации C++ библиотеки OpenCV.

    MatКласс состоит из двух частей данных: заголовка матрицы (содержащего такую ​​информацию, как размер матрицы, способ хранения, адрес хранения и т. д.) и указателя на матрицу, где хранятся все значения пикселей (разные матрицы могут быть разной размерности в зависимости от выбранного способа хранения).

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

    imread()Функция функции — загрузить изображение, а параметр — путь к изображению.

  • Mat imgGray;

    определитьMatобъект классаimgGray.

  • vector<Rect> faces;

    использоватьvectorОпределите тип элемента какRectдинамический массив классовfaces.

    vectorпредставляет собой инкапсулированный динамический массив, предоставляемый стандартной библиотекой C++, это не класс, ашаблон класса. использоватьvectorВсе элементы определенного объекта массива инициализируются. Если элемент массива является примитивным типом, он инициализируется;Если элемент массива имеет тип класса, вызовите конструктор по умолчанию для инициализации. Здесь элементы массиваПрямоугольный классtype, поэтому для инициализации массива будет вызываться конструктор по умолчанию класса Rect.

    Прямоугольный класс, класс прямоугольника. Он имеет четыре общественных объекта,Rect(int_x,int_y,int_width,int_height), (x, y) — координаты вершины левого верхнего угла, width — ширина, а height — высота.

if (img.empty())   {return 1;}

перечислитьMatобъект классаimgчленская функцияempty()Проверять3ming.jpgПрошла ли загрузка успешно, в случае успеха вернуть 1.

if (img.channels() == 3)  {cvtColor(img, imgGray, CV_RGB2GRAY);}
else  {imgGray = img;}

перечислитьMatобъект классаimgчленская функцияchannels(), который возвращает количество каналов матрицы. Если число каналов матрицы равно 3, вызовитеcvtColor()функция, которая войдет вimgПреобразовать из вimgGrayвыход, режим преобразованияCV_RGB2GRAY, то есть преобразовать 3-канальное цветное изображение в вывод в градациях серого; если матричный канал не равен 3, напрямую преобразоватьimgназначатьimgGray.

cvtColor()Функция: функция преобразования цветового пространства в Opencv может реализовать преобразование цвета RGB в HSV, HSI и другие цветовые пространства, а также может быть преобразована в оттенки серого. Первый параметр — вход, второй параметр — выход, а третий параметр — режим преобразования. Так зачем преобразовывать изображения RGB в оттенки серого?

1. После преобразования трехканального в одноканальный объем вычислений значительно сокращается.

2. В природе на сам цвет очень легко влияет свет, и RGB сильно меняется, но информация о градиенте может предоставить более важную информацию.

3. Многие функции Opencv поддерживают только один канал.

	faceCascade.detectMultiScale(imgGray, faces, 1.2, 6, 0, Size(0, 0));

перечислитьCascadeClassifierобъект классаfaceCascadeчленская функцияdetectMultiScale(), для входного изображения в градациях серогоimgGrayВыполните обнаружение и сохраните прямоугольную группу векторов кадра обнаруженного лица вfaces, параметр масштаба1.2, каждая цель должна быть обнаружена не менее6Второй раз лицо, а флаг0, наименьший возможный размер объектаSize(0,0), объекты меньше этого значения будут игнорироваться.

detectMultiScale()Функция: обнаружение объектов разных размеров на входном изображении и возврат обнаруженных объектов в виде списка прямоугольников.

if (faces.size() > 0){
		for (int i = 0; i < faces.size(); i++){
			rectangle(img, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
				Scalar(0, 0, 255), 4, 8);    //框出人脸位置
		}
}

Вызывает динамический массив, элементы которого имеют тип Rect.facesчленская функцияsize(),еслиface.size()Если он больше 0, то есть матричный массив не пуст, то есть если цель обнаружена, выполняется цикл for для кадрирования обнаруженной цели.

rectangle()Функция: Нарисуйте простой прямоугольник заданной толщины или заполненный прямоугольник.

Rectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, int shift=0 );

1. Первым параметром является img, который указывает, на какой картинке рисовать рамку.Для этого примера этоimg.

2. Второй параметр — это координаты левого верхнего угла прямоугольника, соответствующие этому примеру:Point(faces[i].x, faces[i].y).

3. Третий параметр — еще одна вершина на диагонали прямоугольника, соответствующая этому примеру:Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height).

4. Четвертый параметр — цвет линии. Скаляр (B, G, R) имеет три параметра, соответствующие синему, зеленому и красному соответственно. В этом примере поле установлено на красный цвет, то есть скаляр (0, 0,255).

5. Пятый параметр — толщина линии, чем больше число, тем толще линия, в данном примере установлено значение 4.

6. Шестой параметр, похоже, это тип линии. Подлежит проверке.

imshow("FacesOf3ming", img);
waitKey(0);
return 0;

После того, как цикл будет выполнен, все обнаруженные цели будут обрамлены, вызовитеimshow()Функция кадрирования изображения лицаimgпоказывать.

imshow()Функция принимает два параметра, первый — это имя окна для отображения изображения, а второй — объект, в котором хранятся данные изображения.

waitkey(0)Функция имеет функцию задержки при отображении изображения, единицей измерения являются миллисекунды. Если параметр равен 0, изображение не будет автоматически закрываться, оно будет ждать бесконечно, пока не будет нажата кнопка.

Программа закончена,return 0.

Результат работы программы

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

Суммировать

Я много хотел написать, но не мог. Я просто надеюсь, что смогу усвоить то, что хочу, и Oxygen будет счастлив.