TL; DR: камера OpenCV по умолчанию плохо справляется с портретным режимом мобильного устройства. Возьмите приведенный ниже код и вставьте его в CameraBridgeViewBase
, чтобы использовать заднюю и переднюю камеры OpenCV в полноэкранной портретной ориентации.
Перейти к:
- Матрица Android
- Когда обновлять
- Получение размеров
- Ориентация по передней или задней камере
- Масштабирование
- Зеркальное отображение
- "Полноэкранный"
- Полный код
Проблема с модулем камеры 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 для получения ресурсов, событий и гораздо больше, что поможет вам быстрее и лучше строить лучшие модели машинного обучения.