2013-06-10 3 views
19

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

public class MyActivity extends Activity implements TextureView.SurfaceTextureListener 
{ 

    private Camera mCamera; 
    private TextureView mTextureView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_options); 

    mTextureView = (TextureView) findViewById(R.id.camera_preview); 
    mTextureView.setSurfaceTextureListener(this); 
} 

@Override 
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 
    mCamera = Camera.open(); 

    try 
    { 
     mCamera.setPreviewTexture(surface); 
     mCamera.startPreview(); 
    } catch (IOException ioe) { 
     // Something bad happened 
    } 
} 

@Override 
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 
    mCamera.stopPreview(); 
    mCamera.release(); 
    return true; 
} 

@Override 
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 
} 

@Override 
public void onSurfaceTextureUpdated(SurfaceTexture surface) 
{ 
    // Invoked every time there's a new Camera preview frame 
} 
} 

Кроме того, после выполнения предварительного просмотра правильно, мне нужно, чтобы иметь возможность читать в режиме реального времени пикселей, найденные в центре кадра изображения.

+0

Я не уверен, что это поможет вам, вы можете попробовать с меньшим размером предварительного просмотра. В основном камера дает размер предварительного просмотра по умолчанию более крупный .. – Amrendra

+1

Я знаю о размерах предварительного просмотра. Это не помогает мне, потому что представление, показывающее предварительный просмотр камеры, имеет размеры, отличные от доступных в виде размеров предварительного просмотра. –

+0

Вы можете попробовать с фрагментом на экране. – Amrendra

ответ

2

Вы можете манипулировать byte[] data от onPreview().

Я думаю, что вы должны будете:

  • положить его в Bitmap
  • делать обрезку в Bitmap
  • сделать немного растяжения/изменение размеров
  • и передать Bitmap к вашему SurfaceView

Это не очень удачный способ. Возможно, вы можете напрямую манипулировать byte[], но вам придется иметь дело с форматами изображений, такими как NV21.

1

Ответ, данный @SatteliteSD, является наиболее подходящим. Каждая камера поддерживает только определенные размеры предварительного просмотра, которые установлены в HAL. Следовательно, если доступных размеров предварительного просмотра недостаточно, вам необходимо извлечь данные из onPreview

0

Для обработки изображений в режиме реального времени камеры OpenCV для Android отлично адаптируется. Вы найдете каждый образец, необходимый здесь: http://opencv.org/platforms/android/opencv4android-samples.html, и, как вы увидите, он работает хорошо в режиме реального времени.

Отказ от ответственности: настройка opencv lib на Android может быть довольно головной болью в зависимости от того, как вы экспериментируете с C++/opencv/ndk. Во всех случаях написание кода opencv никогда не бывает простым, но, с другой стороны, это очень мощная библиотека.

+0

Я сам смотрел на JavaCameraView, и, похоже, он делает yuv -> rgb-преобразование в собственном коде один процессор, а не на gpu ... Я думаю, что это приводит к реальному замедлению кадров камеры, которые доставляются на экран (задержка и плохие fps) – Sam

11

Просто рассчитайте соотношение сторон, создайте матрицу масштабирования и примените ее к текстуру. Основываясь на соотношении сторон поверхности и соотношении сторон изображения предварительного просмотра, изображение предварительного просмотра обрезается сверху, снизу или слева и справа. Другое решение, которое я выяснил, заключается в том, что если вы открываете камеру до, то SurfaceTexture доступен, предварительный просмотр уже масштабируется автоматически. Просто попробуйте переместить mCamera = Camera.open(); к вашей функции onCreate после установки SurfaceTextureListener. Это сработало для меня на N4. С помощью этого решения вы, вероятно, столкнетесь с проблемами при повороте с портрета на пейзаж. Если вам нужна портретная и ландшафтная поддержка, тогда возьмите решение с матрицей масштаба!

private void initPreview(SurfaceTexture surface, int width, int height) { 
    try { 
     camera.setPreviewTexture(surface); 
    } catch (Throwable t) { 
     Log.e("CameraManager", "Exception in setPreviewTexture()", t); 
    } 

    Camera.Parameters parameters = camera.getParameters(); 
    previewSize = parameters.getSupportedPreviewSizes().get(0); 

    float ratioSurface = width > height ? (float) width/height : (float) height/width; 
    float ratioPreview = (float) previewSize.width/previewSize.height; 

    int scaledHeight = 0; 
    int scaledWidth = 0; 
    float scaleX = 1f; 
    float scaleY = 1f; 

    boolean isPortrait = false; 

    if (previewSize != null) { 
     parameters.setPreviewSize(previewSize.width, previewSize.height); 
     if (display.getRotation() == Surface.ROTATION_0 || display.getRotation() == Surface.ROTATION_180) { 
      camera.setDisplayOrientation(display.getRotation() == Surface.ROTATION_0 ? 90 : 270); 
      isPortrait = true; 
     } else if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) { 
      camera.setDisplayOrientation(display.getRotation() == Surface.ROTATION_90 ? 0 : 180); 
      isPortrait = false; 
     } 
     if (isPortrait && ratioPreview > ratioSurface) { 
      scaledWidth = width; 
      scaledHeight = (int) (((float) previewSize.width/previewSize.height) * width); 
      scaleX = 1f; 
      scaleY = (float) scaledHeight/height; 
     } else if (isPortrait && ratioPreview < ratioSurface) { 
      scaledWidth = (int) (height/((float) previewSize.width/previewSize.height)); 
      scaledHeight = height; 
      scaleX = (float) scaledWidth/width; 
      scaleY = 1f; 
     } else if (!isPortrait && ratioPreview < ratioSurface) { 
      scaledWidth = width; 
      scaledHeight = (int) (width/((float) previewSize.width/previewSize.height)); 
      scaleX = 1f; 
      scaleY = (float) scaledHeight/height; 
     } else if (!isPortrait && ratioPreview > ratioSurface) { 
      scaledWidth = (int) (((float) previewSize.width/previewSize.height) * width); 
      scaledHeight = height; 
      scaleX = (float) scaledWidth/width; 
      scaleY = 1f; 
     }   
     camera.setParameters(parameters); 
    } 

    // calculate transformation matrix 
    Matrix matrix = new Matrix(); 

    matrix.setScale(scaleX, scaleY); 
    textureView.setTransform(matrix); 
} 
+0

Я не могу получить это обрезать предварительный просмотр. Вы уверены, что можно обрезать предварительный просмотр внутри TextureView? –

+0

Это работает для меня именно так. В основном это не обрезка, а масштабирование. Предварительный просмотр камеры всегда устанавливается внутри TextureView - даже если отношение TextureView и отношение предварительного просмотра камеры не совпадают. С матрицей преобразования вы можете установить масштабирование для компенсации искажений. – Romanski

29

Приведенное ранее решение от @Romanski работает нормально, но оно масштабируется с обрезкой. Если вам нужно масштабировать, чтобы соответствовать, используйте следующее решение. Вызывайте updateTextureMatrix каждый раз при изменении вида поверхности: т. Е. В onSurfaceTextureAvailable и в методах onSurfaceTextureSizeChanged. Также обратите внимание, что это решение полагает, что активность игнорирует изменения конфигурации (т.андроид: configChanges = "ориентация | Размер экрана | keyboardHidden" или что-то в этом роде):

private void updateTextureMatrix(int width, int height) 
{ 
    boolean isPortrait = false; 

    Display display = getWindowManager().getDefaultDisplay(); 
    if (display.getRotation() == Surface.ROTATION_0 || display.getRotation() == Surface.ROTATION_180) isPortrait = true; 
    else if (display.getRotation() == Surface.ROTATION_90 || display.getRotation() == Surface.ROTATION_270) isPortrait = false; 

    int previewWidth = orgPreviewWidth; 
    int previewHeight = orgPreviewHeight; 

    if (isPortrait) 
    { 
     previewWidth = orgPreviewHeight; 
     previewHeight = orgPreviewWidth; 
    } 

    float ratioSurface = (float) width/height; 
    float ratioPreview = (float) previewWidth/previewHeight; 

    float scaleX; 
    float scaleY; 

    if (ratioSurface > ratioPreview) 
    { 
     scaleX = (float) height/previewHeight; 
     scaleY = 1; 
    } 
    else 
    { 
     scaleX = 1; 
     scaleY = (float) width/previewWidth; 
    } 

    Matrix matrix = new Matrix(); 

    matrix.setScale(scaleX, scaleY); 
    textureView.setTransform(matrix); 

    float scaledWidth = width * scaleX; 
    float scaledHeight = height * scaleY; 

    float dx = (width - scaledWidth)/2; 
    float dy = (height - scaledHeight)/2; 
    textureView.setTranslationX(dx); 
    textureView.setTranslationY(dy); 
} 

Кроме того, необходимо следующие поля:

private int orgPreviewWidth; 
private int orgPreviewHeight; 

инициализировать его в onSurfaceTextureAvailable Mathod перед вызовом updateTextureMatrix:

метод
Camera.Parameters parameters = camera.getParameters(); 
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 

Pair<Integer, Integer> size = getMaxSize(parameters.getSupportedPreviewSizes()); 
parameters.setPreviewSize(size.first, size.second); 

orgPreviewWidth = size.first; 
orgPreviewHeight = size.second; 

camera.setParameters(parameters); 

getMaxSize:

private static Pair<Integer, Integer> getMaxSize(List<Camera.Size> list) 
{ 
    int width = 0; 
    int height = 0; 

    for (Camera.Size size : list) { 
     if (size.width * size.height > width * height) 
     { 
      width = size.width; 
      height = size.height; 
     } 
    } 

    return new Pair<Integer, Integer>(width, height); 
} 

И последнее: вам нужно исправить вращение камеры. Так вызовите метод setCameraDisplayOrientation в активности onConfigurationChanged методы (а также сделать первоначальный вызов в onSurfaceTextureAvailable метода):

public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) 
{ 
    Camera.CameraInfo info = new Camera.CameraInfo(); 
    Camera.getCameraInfo(cameraId, info); 
    int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 
    int degrees = 0; 
    switch (rotation) 
    { 
     case Surface.ROTATION_0: 
      degrees = 0; 
      break; 
     case Surface.ROTATION_90: 
      degrees = 90; 
      break; 
     case Surface.ROTATION_180: 
      degrees = 180; 
      break; 
     case Surface.ROTATION_270: 
      degrees = 270; 
      break; 
    } 

    int result; 
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) 
    { 
     result = (info.orientation + degrees) % 360; 
     result = (360 - result) % 360; // compensate the mirror 
    } 
    else 
    { // back-facing 
     result = (info.orientation - degrees + 360) % 360; 
    } 
    camera.setDisplayOrientation(result); 

    Camera.Parameters params = camera.getParameters(); 
    params.setRotation(result); 
    camera.setParameters(params); 
} 
+0

ДАЙ ЭТО ЧЕЛОВЕК! Большое спасибо за этого Руслана. Ive bveen пытается атаковать это с обзорами поверхности, surfacetextures и миллионами других вещей в течение приблизительно недели. Я знал, что должен быть какой-то вид, на который можно было бы установить матрицу масштабирования, но я просто не мог ее найти. это потрясающе. Я думаю, что должна быть ссылка на этот ответ из многих других вопросов об обрезке предварительного просмотра камеры, которые есть там. – Sam

+0

Редактирование: в этом коде есть небольшая логическая проблема, которая, по-видимому, решена, кажется, она решила, плохо попробуйте и обновите этот ответ, когда я выясню, где проблема – Sam

+0

Пример этого непосредственно с SurfaceTexture и GLES см. В разделе " текстура из камеры "в Grafika (https://github.com/google/grafika). Действие «масштабирования» выполняет центральную обрезку и масштаб, управляя координатами текстуры. TextureView делает что-то подобное. Преимущество TextureView заключается в том, что его намного проще в использовании. Подход «raw GLES» намного сложнее, но гораздо более гибкий. – fadden

1

Я только что сделал рабочее приложение, где мне нужно, чтобы показать предварительный просмотр с x2 фактического ввода, не получая некачественно смотреть. То есть. Мне нужно было показать центральную часть предварительного просмотра 1280x720 в 640x360 TextureView.

Вот что я сделал.

Установите камеру предварительного просмотра разрешение x2 того, что мне было нужно:

params.setPreviewSize(1280, 720); 

А затем масштабировать вид текстуры соответственно:

this.captureView.setScaleX(2f); 
this.captureView.setScaleY(2f); 

Это работает без каких-либо икота на небольших устройствах.

+0

Спасибо большое. Вы меня спасете. – YeeKhin