TL; DR: камера OpenCV по умолчанию плохо справляется с портретным режимом мобильного устройства. Возьмите приведенный ниже код и вставьте его в CameraBridgeViewBase , чтобы использовать заднюю и переднюю камеры OpenCV в полноэкранной портретной ориентации.

Перейти к:

Проблема с модулем камеры OpenCV на мобильных устройствах

Даже с учетом всех последних разработок в Android's ARCore существует множество причин, по которым вам может понадобиться OpenCV в вашем мобильном проекте дополненной реальности. С обработкой изображений, машинным обучением, обнаружением объектов, оптическим потоком и множеством других функций - библиотека делает многое, и она не привязана только к одной платформе, а это означает, что с минимальными изменениями вы можете перенести свой код на iOS, Unity, Python и другие.

И хотя OpenCV упрощает многие вещи, я обнаружил одну особую точку преткновения после того, как потратил пару месяцев на создание приложения для Android на основе AR, ориентированного на обнаружение функций - настройку самой камеры.

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

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

Поворачивайте и масштабируйте изображения с помощью матрицы Android

Во-первых, вам нужно интегрировать OpenCV в свой проект. Мы не будем рассматривать это здесь, но есть ряд хороших руководств по ​​этому поводу, а также собственная документация OpenCV.

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

Как это исправить? Частично ответ кроется в CameraBridgeViewBase.java файле, который поставляется с OpenCV для Android. В частности, мы будем использовать функцию под названием deliverAndDrawFrame. Эта функция берет кадр камеры, преобразует его в растровое изображение и отображает это растровое изображение на холсте Android на экране.

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

Для этого нам нужно добавить код в CameraBridgeViewBase.java файл. Сделаем это чуть выше метода deliverAndDrawFrame. Сначала объявите матричную переменную:

private final Matrix mMatrix = new Matrix();

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

private void updateMatrix() { }

Когда вызывать функцию UpdateMatrix

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

Они вызывают функцию updateMatrix, когда экран размещен, и когда есть вызов для измерения изменений размеров экрана.

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

Получение размеров матрицы и окна и определение масштаба

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

Что касается scale, то, по сути, мы получаем связь между матрицей и холстом. Если отношение ширины холста к высоте матрицы больше, чем отношение высоты холста к ширине матрицы, то мы масштабируемся до первого; в противном случае последнее.

Ориентация изображения на основе передней и задней камеры

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

Если бы мы повернули прямо сейчас, OpenCV использовал бы верхний левый угол изображения в качестве точки вращения, что отправило бы изображение камеры с экрана на моем устройстве. Итак, переместим его в центр:

mMatrix.preTranslate(hw, hh);

Затем мы можем повернуть его. Но нам нужно знать, с какой камерой мы имеем дело: передней или задней.

Вверху этого файла вы увидите несколько констант:

protected int mCameraIndex = CAMERA_ID_ANY;
public static final int CAMERA_ID_ANY   = -1;
public static final int CAMERA_ID_BACK  = 99;
public static final int CAMERA_ID_FRONT = 98;

mCameraIndex - наша активная камера. По умолчанию установлено значение CAMERA_ID_ANY, которое на большинстве устройств должно по умолчанию использовать заднюю камеру 99. Если бы мы хотели отобразить переднюю камеру, мы могли бы изменить mCameraIndex = CAMERA_ID_FRONT (или 98).

Вы заметите, что если вы переключитесь на переднюю камеру и создадите приложение, камера все еще лежит на боку, но повернута в другую сторону! Поэтому нам нужно вращать по-разному в зависимости от этого:

boolean isFrontCamera = mCameraIndex == CAMERA_ID_FRONT;
if (isFrontCamera){
  mMatrix.preRotate(270); 
} else {
  mMatrix.preRotate(90);
}

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

Масштабирование изображения камеры до ширины экрана

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

mMatrix.preScale(scale,scale,hw,hh);

Зеркальное отображение изображения передней камеры

Есть еще одно необязательное преобразование, которое мы могли бы включить. Если мы используем фронтальную камеру, а не видим себя в зеркальном отражении, как мы привыкли, мы увидим изображение, как будто мы смотрим через заднюю камеру, где текст по-прежнему читается справа налево. Чтобы отразить изображение, вы можете добавить следующее:

if (isFrontCamera) {
  mMatrix.preScale(-1, 1, hw, hh);
}

Ярлык для полноэкранного изображения с камеры OpenCV

Последняя вещь. Что, если мы хотим, чтобы он заполнял и ширину, и высоту экрана? Самый быстрый способ сделать это - обратиться к другому файлу. В зависимости от возраста устройства OpenCV будет использовать файл JavaCameraView или JavaCamera2View, поэтому нам нужно будет работать с обоими.

Найдите эти строки в обоих файлах:

if ((getLayoutParams().width == LayoutParams.MATCH_PARENT) && (getLayoutParams().height == LayoutParams.MATCH_PARENT))
  mScale = Math.min(((float)height)/mFrameHeight, ((float)width)/mFrameWidth);
else
  mScale = 0;

Вместо условного, мы можем просто установить его следующим образом:

mScale = Math.max(((float)height)/mFrameHeight, ((float)width)/mFrameWidth);

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

Полный код для поворота и масштабирования камеры OpenCV на Android

Полный код, который нужно добавить в CameraBridgeViewBase, можно найти здесь (или ниже). Убедитесь, что вы импортируете все классы Android или OpenCV, необходимые для использования этих методов.

Надеюсь, это поможет вам начать работу с камерой OpenCV в Android!

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

Являясь независимой редакцией, Heartbeat спонсируется и публикуется Comet, платформой MLOps, которая позволяет специалистам по данным и группам машинного обучения отслеживать, сравнивать, объяснять и оптимизировать свои эксперименты. Мы платим участникам и не продаем рекламу.

Если вы хотите внести свой вклад, отправляйтесь на наш призыв к участникам. Вы также можете подписаться на наши еженедельные информационные бюллетени (Deep Learning Weekly и Comet Newsletter), присоединиться к нам в » «Slack и подписаться на Comet в Twitter и LinkedIn для получения ресурсов, событий и гораздо больше, что поможет вам быстрее и лучше строить лучшие модели машинного обучения.