it-swarm.com.de

Crop-Kamera-Vorschau für TextureView

Ich habe eine TextureView mit fester Breite und Höhe und möchte eine Kameravorschau darin zeigen. Ich muss die Kameravorschau so beschneiden, dass sie in meiner TextureView nicht gestreckt erscheint. Wie erfolgt das Zuschneiden? Wenn ich OpenGL verwenden muss, wie binde ich die Oberflächentextur an OpenGL und wie beschneide ich sie mit OpenGL?

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
 }
}

Nachdem ich die Vorschau korrekt ausgeführt habe, muss ich in der Lage sein, die in der Mitte des zugeschnittenen Bildes gefundenen Pixel in Echtzeit zu lesen.

22
Catalin Morosan

Die frühere Lösung von @Romanski funktioniert gut, skaliert jedoch beim Zuschneiden. Wenn Sie die Größe anpassen müssen, verwenden Sie die folgende Lösung. Rufen Sie updateTextureMatrix jedes Mal auf, wenn die Oberflächenansicht geändert wird: in onSurfaceTextureAvailable und in onSurfaceTextureSizeChanged-Methoden. Beachten Sie auch, dass bei dieser Lösung die Aktivität Konfigurationsänderungen ignoriert (d. H. Android: configChanges = "direction | screenSize | keyboardHidden" oder etwas Ähnliches):

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);
}

Außerdem benötigen Sie folgende Felder:

private int orgPreviewWidth;
private int orgPreviewHeight;

initialisieren Sie ihn in onSurfaceTextureAvailable mathod, bevor Sie updateTextureMatrix aufrufen:

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-Methode:

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);
}

Und als letztes müssen Sie die Kameradrehung korrigieren. Rufen Sie daher die setCameraDisplayOrientation-Methode in der Activity onConfigurationChanged-Methode auf (und rufen Sie sie auch in der onSurfaceTextureAvailable-Methode auf)

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);
}
31

Berechnen Sie einfach das Seitenverhältnis, generieren Sie eine Skalierungsmatrix und wenden Sie diese auf die TextureView an. Basierend auf dem Seitenverhältnis der Oberfläche und dem Seitenverhältnis des Vorschaubildes wird das Vorschaubild oben und unten oder links und rechts abgeschnitten. Eine andere Lösung, die ich gefunden habe, ist die, dass Sie beim Öffnen der Kamera Bevor die SurfaceTexture verfügbar ist, wird die Vorschau bereits automatisch skaliert. Versuchen Sie einfach, mCamera = Camera.open () zu verschieben. auf Ihre onCreate-Funktion, nachdem Sie den SurfaceTextureListener festgelegt haben. Das hat für mich auf der N4 funktioniert. Mit dieser Lösung werden Sie wahrscheinlich Probleme bekommen, wenn Sie vom Hochformat in das Querformat drehen. Wenn Sie Unterstützung im Hoch- und Querformat benötigen, dann nehmen Sie die Lösung mit der Skalenmatrix!

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);
}
11
Romanski

sie könnten den byte[] data von onPreview() manipulieren.

Ich denke, du musst:

  • lege es in eine Bitmap
  • beschneiden Sie in der Bitmap
  • ein wenig dehnen/größen ändern
  • und übergeben Sie die Bitmap an Ihre SurfaceView

Dies ist kein sehr performanter Weg. Möglicherweise können Sie den byte[] direkt bearbeiten, müssen jedoch mit Bildformaten wie NV21 umgehen.

2
sschrass

Ich habe gerade eine funktionierende App erstellt, bei der ich eine Vorschau mit x2 der tatsächlichen Eingabe anzeigen musste, ohne einen pixelierten Look zu erhalten. Ie. Ich musste den Mittelteil einer 1280x720-Live-Vorschau in einer 640x360-TextureView anzeigen.

Hier ist was ich getan habe.

Stellen Sie die Kameravorschau auf die x2-Auflösung des benötigten Bedarfs ein:

params.setPreviewSize(1280, 720);

Und skaliere dann die Texturansicht entsprechend:

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

Dies funktioniert auf kleinen Geräten problemlos.

1
slott

Die Antwort von @SatteliteSD ist am besten geeignet. Jede Kamera unterstützt nur bestimmte Vorschaugrößen, die bei HAL eingestellt sind. Wenn also die verfügbaren Vorschaugrößen nicht ausreichen, müssen Sie Daten aus onPreview extrahieren

1
blganesh101

Für die Echtzeit-Bearbeitung von Vorschaubildern der Kamera ist OpenCV für Android perfekt geeignet. Sie finden jedes benötigte Beispiel hier: http://opencv.org/platforms/Android/opencv4Android-samples.html und wie Sie sehen, funktioniert es verdammt gut in Echtzeit.

Haftungsausschluss: Das Einrichten einer opencv-lib auf Android kann ziemlich Kopfschmerzen verursachen, abhängig davon, wie Sie mit c ++/opencv/the ndk experimentieren. In allen Fällen ist das Schreiben von opencv-Code nie einfach, aber auf der anderen Seite ist es eine sehr mächtige Bibliothek.

0
C4stor