Camera setDisplayOrientation () в портретном режиме Разрывы Соотношение сторон

Я пытаюсь, чтобы предварительный просмотр камеры работал правильно в портретном режиме, где самой деятельности разрешено изменять ориентацию в обычном режиме (т. Е. Не быть заблокированным для пейзажа).

Однако использование setDisplayOrientation() серьезно нарушает поведение превью.

Это может продемонстрировать собственный ApiDemos Google. Первое изображение основано на CameraPreview из версии android-17 образца ApiDemos , где единственным изменением, которое я сделал, было удаление android:orientation="landscape" из записи этого действия в манифесте. Следующее изображение представляет собой скриншот того, что появляется на дисплее Nexus 4, работающего под управлением Android 4.2, при этом камера указала на 8,5-дюймовый квадрат бумаги:

Предварительный просмотр Nexus 4 ApiDemos, портретный режим

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

Решением для этого, по крайней мере для ландшафтного режима, является использование setDisplayOrientation() . Но если вы измените внутренний класс Preview CameraPreview на mCamera.setDisplayOrientation(90); , Вы получите следующее:

Предварительный просмотр Nexus 4 ApiDemos, портретный режим, с setDisplayOrientation (90)

Примечательно, что квадрат больше не квадратный.

Это использует тот же размер SurfaceView что и раньше. Я воспроизвел этот сценарий с каким-то другим кодом, вне ApiDemos , и я получаю такое же поведение с TextureView в этом коде.

Я кратко подумал, что проблема может заключаться в том, что getSupportedPreviewSizes() может возвращать разные значения на основе setDisplayOrientation() , но быстрый тест показывает, что это не так.

Кто-нибудь знает, как заставить setDisplayOrientation() работать в портретном режиме, не разрушая пропорции изображений, перешедших на предварительный просмотр?

Благодаря!

Хорошо, я думаю, что понял это. Было несколько штук к пресловутой загадке, и я благодарю @kcoppock за понимание, которое помогло мне решить часть этого.

Во-первых, вам нужно учитывать ориентацию при определении размера предварительного просмотра. Например, getOptimalPreviewSize() из CameraPreview ApiDemos не обращает внимания на ориентацию, просто потому, что их версия этого приложения имеет ориентацию, привязанную к ландшафту. Если вы хотите, чтобы ориентация плавала, вам нужно отменить соотношение сторон для соответствия. Итак, где getOptimalPreviewSize() имеет:

 double targetRatio=(double)width / height; 

Вам также необходимо:

 if (displayOrientation == 90 || displayOrientation == 270) { targetRatio=(double)height / width; } 

Где displayOrientation – это значение от 0 до 360, что я определяю примерно из 100 строк какого-то серьезного уродливого кода, поэтому я обертываю все это в повторно используемый компонент, который я опубликую в ближайшее время. Этот код основан на setDisplayOrientation() и на этом ответе StackOverflow .

(BTW, если вы читаете это после 1 июля 2013 года, и вы не видите ссылку в этом ответе на этот компонент, напишите комментарий, чтобы напомнить мне, как я забыл)

Во-вторых, вы должны учитывать эту ориентацию дисплея при управлении соотношением сторон SurfaceView / TextureView которое вы используете. Операция CameraPreview от ApiDemos имеет собственную Preview ViewGroup которая обрабатывает соотношение сторон, и там вам нужно отменить соотношение сторон для использования в портрете:

  if (displayOrientation == 90 || displayOrientation == 270) { previewWidth=mPreviewSize.height; previewHeight=mPreviewSize.width; } else { previewWidth=mPreviewSize.width; previewHeight=mPreviewSize.height; } 

Где displayOrientation – это то же значение ( 90 и 270 – портретный и обратный портрет соответственно, и обратите внимание, что я не пытался работать с обратным портретом или обратным пейзажем, поэтому может потребоваться больше настройки).

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

Поэтому у меня был код, похожий на следующий:

 Camera.Parameters parameters=camera.getParameters(); Camera.Size pictureSize=getHost().getPictureSize(parameters); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); parameters.setPictureSize(pictureSize.width, pictureSize.height); parameters.setPictureFormat(ImageFormat.JPEG); camera.setParameters(parameters); camera.startPreview(); 

И это неправильно. Что вам действительно нужно:

 Camera.Parameters parameters=camera.getParameters(); Camera.Size pictureSize=getHost().getPictureSize(parameters); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); camera.setParameters(parameters); camera.startPreview(); parameters=camera.getParameters(); parameters.setPictureSize(pictureSize.width, pictureSize.height); parameters.setPictureFormat(ImageFormat.JPEG); camera.setParameters(parameters); 

Без этих изменений я бы получил растянутое соотношение сторон, даже в ландшафте. Это не было проблемой для CameraPreview ApiDemos , поскольку они не фотографировались, поэтому они никогда не называли setPictureSize() .

Но пока, на Nexus 4, теперь у меня есть квадратные пропорции для портрета и пейзажа с плавающей ориентацией (т. Е. Не привязанной к ландшафту).

Я попытаюсь не забыть изменить этот вопрос, если я столкнулся с другими хаками, требуемыми другими устройствами, а также для ссылки на мои компоненты CameraView / CameraFragment когда они будут выпущены.


ОБНОВЛЕНИЕ # 1

Ну, конечно, это не могло быть почти таким простым. 🙂

Исправление проблемы, когда setPictureSize() закручивает пропорции, работает … пока вы не сделаете снимок. В этот момент предварительный просмотр переключается на неправильное соотношение сторон.

Одной из возможностей было бы ограничить изображения изображениями с одним и тем же (или очень близким) соотношением сторон к просмотру, поэтому икота не существует. Мне не нравится этот ответ, так как пользователь должен иметь возможность получать любой размер изображения, предлагаемый камерой.

Вы можете ограничить объем ущерба:

  • Только обновление размера изображения и т. Д. Camera.Parameters перед съемкой
  • Возврат к предустановленным Camera.Parameters после съемки

Это все еще отражает неправильное соотношение сторон в моменты, когда изображение выполняется. Долгое решение для этого – я думаю, – будет временно заменять предварительный просмотр камеры неподвижным изображением (последний кадр предварительного просмотра) во время съемки. Я попробую это в конце концов.


UPDATE # 2 : v0.0.1 моей библиотеки CWAC-Camera теперь доступна, включая вышеупомянутый код.

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

  • Иногда вы просто получаете черные границы , если на некоторых устройствах видны панели действий или программные кнопки, я могу только на некоторых устройствах посоветую : * не волнует *, или вы сойдете с ума или прервите превью, оба не хороши …

Изменения на @CommonsWare просто идеальны, но не могут быть применены непосредственно к CameraPreview из образцов уровня API 8 (ссылка ниже), так что вот различие :

Для правильного соотношения сторон в портрете:
Методы одинаковы, просто примените diff

 public void setCamera(Camera camera, boolean portrait) { mCamera = camera; this.portrait = portrait; if (mCamera != null) { mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); requestLayout(); } } public void switchCamera(Camera camera, boolean portrait) throws IOException { setCamera(camera, portrait); camera.setPreviewDisplay(mHolder); Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); requestLayout(); camera.setParameters(parameters); } 

в:

 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {…} 

изменение:

 int previewWidth = width; int previewHeight = height; if (mPreviewSize != null) { previewWidth = mPreviewSize.width; previewHeight = mPreviewSize.height; } 

чтобы:

 if (portrait && mPreviewSize != null) { previewWidth = mPreviewSize.height; previewHeight = mPreviewSize.width; } else if (mPreviewSize != null){ previewWidth = mPreviewSize.width; previewHeight = mPreviewSize.height; } 

в:

 private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {…} 

изменение:

 double targetRatio = (double) w / h; 

чтобы:

 double targetRatio = 1; if (portrait) { targetRatio= (double) w / h; } 

Наконец, запуск камеры для портретного режима с чем-то вроде:

 try { mCamera = openFrontFacingCameraGingerbread(); if (portraitMode) { mCamera.setDisplayOrientation(90); } preview.setCamera(mCamera, portraitMode); } catch (Exception e) { e.printStackTrace(); handleCameraUnavailable(); } 

WUUHUUUU! Для меня, наконец, не проблема в предварительном просмотре, и никаких проблем с сохранением изображений. Поскольку мое местоположение активности заблокировано, я получаю ориентацию дисплея через OrientationChangedListener для сохранения изображений с правильной ориентацией.

Используя что-то вроде:
http://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.html

Но они правильно просматриваются и сохраняются.

Надеюсь, что я могу помочь многим людям с этим, есть исходный код из уровня API 8 :
https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java, где были применены изменения.

Измените ориентацию камеры на своем коде, и вы должны setDisplayorientation() не getDisplayOrientation()

 if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { camera.setDisplayOrientation(0); }