«1. Введение

В этом руководстве мы узнаем, как установить и использовать библиотеку компьютерного зрения OpenCV и применить ее для распознавания лиц в реальном времени.

2. Установка

Чтобы использовать библиотеку OpenCV в нашем проекте, нам нужно добавить зависимость opencv Maven к нашему pom.xml:

<dependency>
    <groupId>org.openpnp</groupId>
    <artifactId>opencv</artifactId>
    <version>3.4.2-0</version>
</dependency>

Для пользователей Gradle нам нужно добавить зависимость в наш файл build.gradle:

compile group: 'org.openpnp', name: 'opencv', version: '3.4.2-0'

После добавления библиотеки в наши зависимости мы можем использовать функции, предоставляемые OpenCV.

3. Использование библиотеки

Чтобы начать использовать OpenCV, нам нужно инициализировать библиотеку, что мы можем сделать в нашем основном методе:

OpenCV.loadShared();

OpenCV — это класс, который содержит методы, связанные с загрузкой собственных пакетов требуется библиотекой OpenCV для различных платформ и архитектур.

Стоит отметить, что в документации все делается немного по-другому:

System.loadLibrary(Core.NATIVE_LIBRARY_NAME)

Оба этих вызова метода фактически загружают необходимые нативные библиотеки.

Разница здесь в том, что последний требует установки собственных библиотек. Первый, однако, может установить библиотеки во временную папку, если они недоступны на данном компьютере. Из-за этой разницы обычно лучше всего использовать метод loadShared.

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

4. Загрузка изображений

Для начала давайте загрузим образец изображения с диска с помощью OpenCV:

public static Mat loadImage(String imagePath) {
    Imgcodecs imageCodecs = new Imgcodecs();
    return imageCodecs.imread(imagePath);
}

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

Чтобы сохранить ранее загруженное изображение, мы можем использовать метод imwrite() класса Imgcodecs:

public static void saveImage(Mat imageMatrix, String targetPath) {
    Imgcodecs imgcodecs = new Imgcodecs();
    imgcodecs.imwrite(targetPath, imageMatrix);
}

5. Каскадный классификатор Хаара

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

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

5.1. Функции Хаара

Обнаружение лиц в OpenCV осуществляется с помощью каскадных классификаторов на основе функций Хаара.

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

Haar Features

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

6. Обнаружение лиц

Как правило, каскадный классификатор должен быть предварительно обучен, чтобы иметь возможность обнаруживать что-либо вообще.

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

Давайте рассмотрим процесс обнаружения лица:

Face To Detect

Мы попытаемся обнаружить лицо, обведя его красным прямоугольником.

Для начала нам нужно загрузить изображение в формате Mat из нашего исходного пути:

Mat loadedImage = loadImage(sourceImagePath);

Затем мы объявим объект MatOfRect для хранения найденных лиц:

MatOfRect facesDetected = new MatOfRect();

Далее , нам нужно инициализировать CascadeClassifier, чтобы выполнить распознавание:

CascadeClassifier cascadeClassifier = new CascadeClassifier(); 
int minFaceSize = Math.round(loadedImage.rows() * 0.1f); 
cascadeClassifier.load("./src/main/resources/haarcascades/haarcascade_frontalface_alt.xml"); 
cascadeClassifier.detectMultiScale(loadedImage, 
  facesDetected, 
  1.1, 
  3, 
  Objdetect.CASCADE_SCALE_IMAGE, 
  new Size(minFaceSize, minFaceSize), 
  new Size() 
);

Выше параметр 1.1 обозначает коэффициент масштабирования, который мы хотим использовать, указывая, насколько уменьшается размер изображения при каждом масштабе изображения. Следующий параметр, 3, это minNeighbors. Это количество соседей, которое должен иметь прямоугольник-кандидат, чтобы сохранить его.

Наконец, мы перебираем лица и сохраняем результат:

Rect[] facesArray = facesDetected.toArray(); 
for(Rect face : facesArray) { 
    Imgproc.rectangle(loadedImage, face.tl(), face.br(), new Scalar(0, 0, 255), 3); 
} 
saveImage(loadedImage, targetImagePath);

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

Face Detected

7. Доступ к камере с помощью OpenCV

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

«Однако, чтобы иметь возможность показать изображение с камеры, нам нужно несколько дополнительных вещей, кроме очевидного — камеры. Чтобы показать изображения, мы будем использовать JavaFX.

Так как мы будем использовать ImageView для отображения изображений, сделанных нашей камерой, нам нужен способ перевести OpenCV Mat в изображение JavaFX:

public Image mat2Img(Mat mat) {
    MatOfByte bytes = new MatOfByte();
    Imgcodecs.imencode("img", mat, bytes);
    InputStream inputStream = new ByteArrayInputStream(bytes.toArray());
    return new Image(inputStream);
}

Здесь мы конвертируем наш Mat в байты, а затем преобразование байтов в объект изображения.

Мы начнем с потоковой передачи изображения с камеры на сцену JavaFX.

Теперь давайте инициализируем библиотеку с помощью метода loadShared:

OpenCV.loadShared();

Далее мы создадим сцену с VideoCapture и ImageView для отображения изображения:

VideoCapture capture = new VideoCapture(0); 
ImageView imageView = new ImageView(); 
HBox hbox = new HBox(imageView); 
Scene scene = new Scene(hbox);
stage.setScene(scene); 
stage.show();

Здесь 0 — это ID камеры, которую мы хотим использовать. Нам также нужно создать AnimationTimer для обработки установки изображения:

new AnimationTimer() { 
    @Override public void handle(long l) { 
        imageView.setImage(getCapture()); 
    } 
}.start();

Наконец, наш метод getCapture обрабатывает преобразование Mat в изображение:

public Image getCapture() { 
    Mat mat = new Mat(); 
    capture.read(mat); 
    return mat2Img(mat); 
}

Теперь приложение должно создать окно, а затем запустить прямую трансляцию. вид с камеры в окно imageView.

8. Обнаружение лиц в реальном времени

Наконец, мы можем соединить все точки, чтобы создать приложение, которое распознает лица в реальном времени.

Код из предыдущего раздела отвечает за захват изображения с камеры и отображение его пользователю. Теперь все, что нам нужно сделать, это обработать захваченные изображения, прежде чем показывать их на экране, используя наш класс CascadeClassifier.

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

public Image getCaptureWithFaceDetection() {
    Mat mat = new Mat();
    capture.read(mat);
    Mat haarClassifiedImg = detectFace(mat);
    return mat2Img(haarClassifiedImg);
}

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

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

9. Резюме

В этом уроке мы узнали, как использовать OpenCV в Java.

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

Как всегда, все образцы кода можно найти на GitHub.