Перейти к основному содержимому

Введение в OpenCV с Java

· 6 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

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 осуществляется с помощью каскадных классификаторов на основе функций Хаара.

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

./17c72963c8e0eebd1b2a361782e8958b.jpg

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

6. Распознавание лиц

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

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

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

./cb2b496696bd111899397cd5e0cbb867.jpg

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

Для начала нам нужно загрузить изображение в формате 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);

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

./e56c621ea94797b320d15f52ef5c82a4.jpg

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 .