Android Camera2, почему меняются пропорции моего предварительного просмотра после поворота с последующим поворотом назад? - PullRequest
0 голосов
/ 22 декабря 2018

В моем приложении у меня есть предварительный просмотр камеры на SurfaceView, и он работает почти так же, как я хочу, с одной странно раздражающей проблемой:

Когда я вхожу в вид камеры на моем Sony Xperia или в моем тестированииSamsung S7 (эмулятор выглядит нормально) показывает корректный предварительный просмотр.Когда я вращаю его, предварительный просмотр вращается правильно, когда я поворачиваю его назад, предварительный просмотр теряет свои пропорции, и вид искажается.После первого поворота, каждый раз, когда он поворачивается назад, у меня возникают такие же искажения.

Подход, который работает до сих пор, заключается в том, что я использую масштабирование моего SurfaceView:

    FrameLayout.LayoutParams newScale = new FrameLayout.LayoutParams(width, height, Gravity.CENTER );
    mTextureView.setLayoutParams(newScale);
    mTextureView.setScaleX( xScale);
    mTextureView.setScaleY( yScale);

Затем я создаюcaptureSession:

  mCaptureSession = cameraCaptureSession;
            try {

                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

               mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRectangle);
                mPreviewRequest = mPreviewRequestBuilder.build();
                mCaptureSession.setRepeatingRequest(mPreviewRequest, getCaptureCallback(), mBackgroundHandler);
            } catch (CameraAccessException e) { 
                // and so on.
            }

Очевидно, что к этому моменту я очень расстроился, поэтому я чертовски записывал свои вращения, и это то, где я очень запутался.

Этовыглядит хорошо:

========================
View Dimensions (720, 1184)
Sensor Dimensions: (3984, 5512)
Final size: 720, 1280
========================
View ratio: 1.7777778 previewRatio: 1.6444445 scale: 1.0
Image preview size is (720, 1184) scale is: (1.0) image size is (720, 1280)
Image scale is (1.3835341, 0.925) Max Image is (3984, 5512) cropRectangle is (441, 0 -> 3543, 5512)

Затем я поворачиваю его:

========================
View Dimensions (1184, 720)
Sensor Dimensions: (5512, 3984)
Final size: 1280, 720
========================
View ratio: 0.5625 previewRatio: 0.6081081 scale: 1.0
Image preview size is (1184, 720) scale is: (1.0) view size is (1280, 720)
Image scale is (0.925, 1.3835342) Max Image is (5512, 3984) cropRectangle is (0, 441 -> 5512, 3543)

Теперь я поворачиваю его назад:

========================
View Dimensions (720, 1184)
Sensor Dimensions: (3984, 5512)
Final size: 720, 1280
========================
View ratio: 1.7777778 previewRatio: 1.6444445 scale: 1.0
Image preview size is (720, 1184) scale is: (1.0) view size is (720, 1280)
Image scale is (1.3835341, 0.925) Max Image is (3984, 5512) cropRectangle is (441, 0 -> 3543, 5512)

Эти шкалы и этот прямоугольник кадрирования выглядят абсолютно идентичнымидля меня, но по какой-то причине последнее изображение искажено гораздо шире, как будто шкала Х была ближе к 1,8 или около того.Я могу повернуть назад в альбомную ориентацию в любом направлении, и она выглядит хорошо, затем вернуться к портрету, и все снова становится жирным.

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

Обновление: если я использую setXScale и устанавливаю его на 1.0 во втором запуске вместоЗначение, которое он имел на первом, выглядит правильно.Но когда я сравниваю матрицу преобразования SurfaceView, когда пропорции изображения плохие и когда они хорошие, они идентичны, так что это выглядит как компенсация плохого масштабирования в предварительном просмотре.Я могу изменить способ масштабирования, чтобы начальный предварительный просмотр был слишком узким, а пропорции были правильными после поворота, но опять же это не очень помогло, потому что мне нужно было переключаться между портретом и пейзажем без искажения изображенияКажется, это должно быть поведение по умолчанию.Открытие представления в альбомной ориентации ведет себя примерно так же - искажается при первом открытии, а затем нормально.

Кажется, что CaptureSession каждый раз корректно создается заново.

Второе обновление: вызовdumpsys SurfaceFlinger показывает возможную причину:

-------------------------------------------------------------------------------
Layer name
           Z |  Comp Type |   Disp Frame (LTRB) |          Source Crop (LTRB)
-------------------------------------------------------------------------------
SurfaceView - com.myapp.CameraActivity#0   4294967294 |     Device |    0    0  720 1184 |    0.0    0.0  960.0  720.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Таким образом, здесь выглядит так, как будто проблема заключается в том, что исходная обрезка слишком мала для размера рамки дисплея, отсюда растянутое изображение.Мое устройство считает, что оно получает изображение с разрешением 1280x720, но по какой-то причине Source Crop указывает на меньший размер.Может ли это быть связано с пропорциями датчика?Нужно ли выбирать только размеры, соответствующие соотношению датчиков?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Так вы можете создать AutoTextureView и вызвать его в XML-файлах

public class AutoFitTextureView extends TextureView {

    private int mRatioWidth = 0;
    private int mRatioHeight = 0;

    public AutoFitTextureView(Context context) {
        this(context, null);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setAspectRatio(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Size cannot be negative.");
        }
        mRatioWidth = width;
        mRatioHeight = height;
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mRatioWidth || 0 == mRatioHeight) {
            setMeasuredDimension(width, height);
        } else {
            if (width < height * mRatioWidth / mRatioHeight) {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            } else {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }

}

, а затем настроить прослушиватель на отслеживание вашего фактурного вида

private final TextureView.SurfaceTextureListener mSurfaceTextureListener
            = new TextureView.SurfaceTextureListener() {

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
            openCamera(width, height);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
            configureTransform(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture texture) {
        }

    };
0 голосов
/ 07 января 2019

Используйте пользовательский просмотр текстур по сравнению с FrameLayout и получите доступ к нему через Surface, это решит ваши проблемы.

 private void createCameraPreviewSession() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());


            Surface surface = new Surface(texture);


            mPreviewRequestBuilder
                    = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(surface);


            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                            // The camera is already closed
                            if (null == mCameraDevice) {
                                return;
                            }

                            mCaptureSession = cameraCaptureSession;
                            try {
                               mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

                                setAutoFlash(mPreviewRequestBuilder);
                                mPreviewRequest = mPreviewRequestBuilder.build();
                                mCaptureSession.setRepeatingRequest(mPreviewRequest,
                                        mCaptureCallback, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onConfigureFailed(
                                @NonNull CameraCaptureSession cameraCaptureSession) {
                            showToast("Failed");
                        }
                    }, null
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
}

И извлеките вращение из базы конкретного устройства на выходах камеры

 /**
     * Retrieves the JPEG orientation from the specified screen rotation.
     *
     * @param rotation The screen rotation.
     * @return The JPEG orientation (one of 0, 90, 270, and 360)
     */
    private int getOrientation(int rotation) {
        return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
}
...