Android Camera2 API, как получить изображение предварительного просмотра, чтобы покрыть весь экран, используя матрицы преобразования? - PullRequest
0 голосов
/ 06 февраля 2020

Рассмотрим следующий код:

        @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        if (rotation != prevRotation) {
            int angle = ORIENTATIONS_REAL.get(rotation);
            Log.d(LOGTAG, "Rotation = " + angle);
            if (angle == 90 || angle == 270) {
                angle = ORIENTATIONS.get(rotation);
                float centerX = cameraRectF.centerX();
                float centerY = cameraRectF.centerY();
                int rotAngle = (angle + sensorOrientation + 180) % 360; // Formula correct
                transformMatrix.postRotate(rotAngle, centerX, centerY);
            } else {
                transformMatrix.reset();
            }
            textureView.setTransform(transformMatrix);
            prevRotation = rotation;
        }
    }

Вот некоторые определения:

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private static final SparseIntArray ORIENTATIONS_REAL = new SparseIntArray();
static {
    ORIENTATIONS_REAL.append(Surface.ROTATION_0, 0);
    ORIENTATIONS_REAL.append(Surface.ROTATION_90, 90);
    ORIENTATIONS_REAL.append(Surface.ROTATION_180, 180);
    ORIENTATIONS_REAL.append(Surface.ROTATION_270, 270);
}

private Size imageDimensions = null;
private Matrix transformMatrix = new Matrix();
private RectF previewRectF = null;
private RectF cameraRectF = null;
private int sensorOrientation = 0;

private TextureView textureView = null;
private SurfaceTexture surfaceTexture = null;
private Button buttonTakePicture = null;

// Some misc stuff
private int prevRotation = 0;

Обратите внимание, что sensorOrientation устанавливается при использовании CameraCharacteristics в процедуре открытия.

У меня были приступы, когда я пытался добиться правильного отображения ориентации на Samsung Galaxy S7. Оказывается, датчик повернут на 90 ° от трубки. Приведенный выше код учитывает указанную ориентацию датчика и вычисляет необходимый угол поворота. Теперь проблема в том, что когда телефон поворачивается из портретного в альбомный режим, изображение предварительного просмотра камеры не покрывает весь экран. Смотрите скриншоты ниже.

Portrait Orientation

Landscape Orientation

Так что теперь вопрос в том, как мне получить предварительный просмотр камеры, чтобы покрыть экран вместо одного его сегмента? Приведенный выше код был адаптирован из ответа здесь:

Android Предварительный просмотр Camera2 иногда поворачивается на 90 градусов

Однако у меня нет второго набора ширины и высота, чтобы использовать, и я не уверен, где их взять. Что я пытался сделать, так это взять разрешение камеры и отобразить его, но это не получилось довольно эффектно. Отчасти проблема в том, что я не очень хорошо знаком с преобразованиями и их работой. Я понимаю, что они обычно обрабатываются матрицами с большим количеством математических матриц, но я не видел и даже не играл с таким кодом в течение некоторого времени (последний раз был в классе 2 года go.) Мысли? Посоветуйте?

Спасибо.

РЕДАКТИРОВАТЬ: Я ожидаю, что приложение будет использоваться более или менее в портретном режиме, но я должен обрабатывать ландшафтный режим в случае, если пользователь поворачивает устройство.

1 Ответ

0 голосов
/ 07 февраля 2020

Ну, похоже, я ответила на свой вопрос. Первоначально я начал с ответа из Android предварительного просмотра Camera2, иногда поворачиваемого на 90 градусов . В этом коде есть ошибка. Конкретно масштабирование. С этим и несколькими другими изменениями я смог заставить его работать. В связанном ответе сказано поставить звонок прямо перед тем, как открыть камеру, что я и сделал изначально. Итак, что произойдет, когда система изменит ориентацию с портретной на ландшафтную (или наоборот), система перезапустит действие. Однако, если вы быстро перевернули устройство, чтобы оно по-прежнему находилось в альбомной ориентации, но перевернулось, то изображение, по-видимому, будет повернуто на 180 °, поскольку окончательное вращение все еще в альбомной ориентации, и система не перезапускает действие. Таким образом, размещение кода внутри подпрограммы обратного вызова с измененной поверхностью гарантирует, что код будет вызываться при каждом изменении ориентации, независимо от того, перезапущена ли активность.

Вот пересмотренный код:

        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            if (rotation != prevRotation) {
            prevRotation = rotation;
            RectF cameraRectF = new RectF(0, 0, imageDimensions.getWidth(), imageDimensions.getHeight());
            RectF textureRectF = new RectF(0, 0, textureView.getHeight(), textureView.getWidth());
            float centerX = cameraRectF.centerX();
            float centerY = cameraRectF.centerY();
            if (rotation != Surface.ROTATION_0) {
                textureRectF.offset(centerX - textureRectF.centerX(), centerY - textureRectF.centerY());
                transformMatrix.setRectToRect(cameraRectF, textureRectF, Matrix.ScaleToFit.FILL);
                float scale = Math.max(
                        (float) textureRectF.right / cameraRectF.right,
                        (float) textureRectF.bottom / cameraRectF.bottom);
                transformMatrix.postScale(scale, scale, centerX, centerY);
                if (rotation != Surface.ROTATION_180) {
                    int angle = (ORIENTATIONS_REAL.get(rotation) + sensorOrientation +
                            ORIENTATIONS_SENSOR.get(sensorOrientation)) % 360;
                    transformMatrix.postRotate(angle, centerX, centerY);
                } else {
                    transformMatrix.postRotate(180, centerX, centerY);
                }
            } else {
                transformMatrix.reset();
            }
        }
        textureView.setTransform(transformMatrix);
    }

Мой код также учитывает ориентацию датчика, поэтому он должен работать на любом устройстве. У меня есть три разных устройства, два из которых сообщают, что ориентация датчика составляет 90 °. Третье устройство сообщает, что ориентация датчика равна 0 °. Поэтому, немного поэкспериментировав, я придумал таблицу, которая будет добавлять константу к конечному углу поворота, основываясь на том, что представляет собой угол поворота датчика. Ниже приведены определения для ориентации. ORIENTATIONS_SENSOR является вышеупомянутой таблицей.

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private static final SparseIntArray ORIENTATIONS_REAL = new SparseIntArray();
static {
    ORIENTATIONS_REAL.append(Surface.ROTATION_0, 0);
    ORIENTATIONS_REAL.append(Surface.ROTATION_90, 90);
    ORIENTATIONS_REAL.append(Surface.ROTATION_180, 180);
    ORIENTATIONS_REAL.append(Surface.ROTATION_270, 270);
}
private static final SparseIntArray ORIENTATIONS_SENSOR = new SparseIntArray();
static {
    ORIENTATIONS_SENSOR.append(0, 180);
    ORIENTATIONS_SENSOR.append(90, 90);
    ORIENTATIONS_SENSOR.append(180, 0);
    ORIENTATIONS_SENSOR.append(270, -90);
}

Хотя я смог проверить только 0 ° и 90 ° от таблицы, я приглашаю других, у кого есть устройства с указанными датчиками ориентации датчика 180 ° и 270 °, проверить что мои расчеты верны.

...