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 осуществляется с помощью каскадных классификаторов на основе функций Хаара.
Функции Хаара — это фильтры, которые используются для обнаружения краев и линий на изображении. Фильтры выглядят как квадраты с черным и белым цветами:
Эти фильтры применяются к изображению несколько раз, пиксель за пикселем, и результат собирается как одно значение. Это значение представляет собой разницу между суммой пикселей под черным квадратом и суммой пикселей под белым квадратом.
6. Распознавание лиц
Как правило, каскадный классификатор должен быть предварительно обучен, чтобы вообще что-либо обнаруживать.
Поскольку процесс обучения может быть длительным и потребует большого набора данных, мы собираемся использовать одну из предварительно обученных моделей, предлагаемых OpenCV . Мы поместим этот файл XML в нашу папку ресурсов
для быстрого доступа.
Давайте пройдемся по процессу обнаружения лица:
Мы попытаемся обнаружить лицо, обведя его красным прямоугольником.
Для начала нам нужно загрузить изображение в формате 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);
Когда мы вводим наше исходное изображение, теперь мы должны получить выходное изображение со всеми лицами, отмеченными красным прямоугольником:
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
— это идентификатор камеры, которую мы хотим использовать. Нам также нужно создать AnimationTimer
для настройки изображения:
new AnimationTimer() {
@Override public void handle(long l) {
imageView.setImage(getCapture());
}
}.start();
Наконец, наш метод getCapture
обрабатывает преобразование Mat
в Image
:
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 .