2017-11-02 4 views
-1

Ich sende Android Kamera Vorschau Unity3D mit Textur und es ist gestreckt. Die Vorschau wird nicht in Android zu jeder Ansicht gerendert, sondern direkt auf eine Textur mit:Android Kamera Vorschau auf eine Textur gerendert ist gestreckt

setPreviewTexture(texture); 

Dann werden die Bytes Unity3d gesendet und auf dem Bildschirm mit jedem onPreviewFrame gezogen.

Die Vorschaugröße der Kamera und die Texturgröße sind auf 1024x768 eingestellt und der Texturcontainer in Unity3d hat dieselbe Größe. Die Auflösung des Geräts ist 960x540, also ist es ein anderes Verhältnis. Ich wähle eine unterstützte Auflösung für die Kamera, die native Größe hat ein Verhältnis von 4: 3, also sollte es keine Dehnung geben. Es sieht so aus, als würde Android nur den Teil rendern, der tatsächlich gerendert werden kann - es benötigt ein 16: 9 Bild und rendert es zu einer 4: 3 Textur. Wenn ich falsch liege, bitte korrigieren Sie mich aber, wie es in diesem Beispiel zu funktionieren scheint. Hier ist ein Stück Code:

public int startCamera(int idx, int width, int height) { 
    nativeTexturePointer = createExternalTexture(); 
    texture = new SurfaceTexture(nativeTexturePointer); 

    mCamera = Camera.open(idx); 
    setupCamera(width, height); 

    try { 
     mCamera.setPreviewTexture(texture); 
     mCamera.setPreviewCallback(this); 
     mCamera.startPreview(); 
     Log.i("Unity", "JAVA: camera started"); 
    } catch (IOException ioe) { 
     Log.w("Unity", "JAVA: CAM LAUNCH FAILED"); 
    } 
    return nativeTexturePointer; 
} 

public void stopCamera() { 
    mCamera.setPreviewCallback(null); 
    mCamera.stopPreview(); 
    mCamera.release(); 
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); 
    Log.i("Unity", "JAVA: Camera stopped"); 
} 

private int createExternalTexture() { 
    int[] textureIdContainer = new int[1]; 
    GLES20.glGenTextures(1, textureIdContainer, 0); 
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIdContainer[0]); 
    return textureIdContainer[0]; 
} 

@SuppressLint("NewApi") 
private void setupCamera(int width, int height) { 
    Camera.Parameters params = mCamera.getParameters(); 
    params.setRecordingHint(true); 
    params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 
    params.setPreviewFormat(17); 
    params.setZoom(0); 
     // 16 ~ NV16 ~ YCbCr 
     // 17 ~ NV21 ~ YCbCr ~ DEFAULT * 
     // 4 ~ RGB_565 
     // 256~ JPEG 
     // 20 ~ YUY2 ~ YcbCr ... 
     // 842094169 ~ YV12 ~ 4:2:0 YCrCb comprised of WXH Y plane, W/2xH/2 Cr & Cb. see documentation * 
    params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 
    Camera.Size previewSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPreviewSizes()); 
    Camera.Size picSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPictureSizes()); 

    params.setPictureSize(picSize.width, picSize.height); 
    params.setPreviewSize(previewSize.width, previewSize.height); 
    params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO); 
    params.setExposureCompensation(0); 

    try{ 
     mCamera.setParameters(params); 
    } catch (Exception e){ 
     Log.i("Unity", "ERROR: " + e.getMessage()); 
    } 

    Camera.Size mCameraPreviewSize = params.getPreviewSize(); 
    prevWidth = mCameraPreviewSize.width; 
    prevHeight = mCameraPreviewSize.height; 

    int[] fpsRange = new int[2]; 
    params.getPreviewFpsRange(fpsRange); 
} 

private Camera.Size getOptimalSize(int width, int height, List<Camera.Size> sizes) { 
    if(mCamera == null) 
     return null; 

    final double ASPECT_TOLERANCE = 0.1; 
    double targetRatio=(double)width/height; 

    if (sizes == null) 
     return null; 

    Camera.Size optimalSize = null; 
    double minDiff = Double.MAX_VALUE; 
    int targetWidth = width; 

    for (Camera.Size size : sizes) { 
     double ratio = (double) size.width/size.height; 
     Log.i("Unity", "RES: size=" + size.width + "/" + size.height + "/ Aspect Ratio: " + ratio + "target width: " + width + "target height: " + height); 

     if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; 
     if (Math.abs(size.width - targetWidth) < minDiff) { 
      optimalSize = size; 
      minDiff = Math.abs(size.width - targetWidth); 
     } 
    } 

    if (optimalSize == null) { 
     minDiff = Double.MAX_VALUE; 
     for (Camera.Size size : sizes) { 
      if (Math.abs(size.width - targetWidth) < minDiff) { 
       optimalSize = size; 
       minDiff = Math.abs(size.width - targetWidth); 
      } 
     } 
    } 
    Log.i("Unity", "optimal size=" + optimalSize.width + "/" + optimalSize.height + "/ Aspect Ratio: " + (double) optimalSize.width/optimalSize.height); 
    return optimalSize; 
} 

public int getPreviewSizeWidth() { 
    return prevWidth; 
} 

public int getPreviewSizeHeight() { return prevHeight; } 

public byte[] bytes; 
@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
    bytes = data; 

    //Log.i("Unity", "JAVA: " + Integer.toString(data.length)); 
    UnityPlayer.UnitySendMessage(gameObjectTargetName, "GetBuffer", ""); 
} 

Es ist erforderlich, dass die Textur zu Unity3d 1024x768 ist ohne Zuschneiden gesendet. Hat irgendjemand eine Lösung dafür?

UPDATE (C# -Code hinzugefügt)

Dieser Code Vorschau von Java wird:

private RectTransform previewRect { 
    get { 
     if (!_previewRect) 
      _previewRect = cameraPreview.GetComponent<RectTransform>(); 
     return _previewRect; 
    } 
} 

private void GetPreviewFromCamera() { 
    NativeCamera.OnCameraTextureCreated += (Texture2D cameraTex) => { 
     if (cameraPreview != null && cameraPreview.texture != null) 
      DestroyImmediate(cameraPreview.texture); 

     previewRect.sizeDelta = new Vector2(1024, 768); 

     cameraPreview.texture = cameraTex; 
     cameraPreview.enabled = true; 
    }; 
} 

UPDATE: Added

void CreateCameraTexture() { 
    _texWidth = nativeCameraObject.Call<int>("getPreviewSizeWidth"); 
    _texHeight = nativeCameraObject.Call<int>("getPreviewSizeHeight"); 
    _cameraPreview = new Texture2D(_texWidth, _texHeight, TextureFormat.Alpha8, false); 
    _converter = new YUVDecode(_texWidth, _texHeight); 
    shaderMat.SetFloat("_Width", _texWidth); 

    if (OnCameraTextureCreated != null) 
     OnCameraTextureCreated(_cameraPreview); 
} 

private byte[] _bytes; 
public void GetBuffer(string str) { 
    _bytes = nativeCameraObject.Get<byte[]>("bytes"); 
    SetColor(_bytes); 
    _cameraPreview.LoadRawTextureData(_bytes); 
    _cameraPreview.Apply(); 

    UpdateLibFrame(); 
} 

void SetColor(byte[] bytes) { 
    _converter.SetBytes(bytes); 
    shaderMat.SetTexture("_YUVTex", _converter.yuvtexture); 
} 

void UpdateLibFrame() { 
    if (OnFrameUpdate != null) OnFrameUpdate(_cameraPreview.GetRawTextureData()); 
} 

Dieser Code in einer anderen Klasse, die Daten zeigen, Link zu einem Beispielprojekt

Das Projekt ist hier: https://www.dropbox.com/s/wid1qa9cmq3ck6w/CameraPreview.zip?dl=0

CameraJava ein gradle Projekt. Die Ausgabe ist eine .AAR-Datei, die Sie in Assets/Plugins kopieren müssen.

+0

wo ist der Einheitscode? – andruido

+0

Ich habe den Beitrag mit C# -Code aktualisiert – krzychawe

+0

Ich konnte Ihren Code nicht vollständig replizieren, texture2d bleibt schwarz auf Einheit. vielleicht, weil ich YUY2 Format verwende? Das würde deinen Shader-Code erklären. Kannst du den ganzen Code hochladen? – andruido

Antwort

0

Entschuldigung für die Verzögerung. Ich hatte endlich Zeit, Ihr Projekt zu testen. es sieht gut aus für mich. Hier ist, was Sie in Unity tun können, um ein gutes Ergebnis zu erzielen (obwohl nur für die Landschaft getestet, da Sie keine Rotationsbehandlung in Android implementiert haben): Gehen Sie zu den Player-Einstellungen und setzen Sie die Standardausrichtung auf Landschaft links. Setzen Sie den Canvas Scaler auf die gleiche Referenzauflösung wie Ihr Kamerabild (1024x768). Setze schließlich die Z-Drehung von RawImage auf 0. Aufgrund des 4: 3-Verhältnisses werden oben und unten abgeschnitten, aber das ist normal. Wenn Sie das vollständige Bild haben möchten, stellen Sie den Cavas Scaler auf Höhe statt Breite ein.

+0

Danke für die Antwort. Ich fürchte, Sie haben nicht verstanden, was das Problem ist, und ich mache Ihnen keine Vorwürfe, weil ich Schwierigkeiten gehabt hätte, es richtig zu erklären.Das Problem ist, dass die Bytes von Android gestreckt sind. Ich habe versucht, die Bytes in eine Datei zu speichern, um meinen Punkt zu beweisen, und die Bilder wurden tatsächlich gestreckt. Kein Problem auf Unity Seite hier. – krzychawe

+0

Wenn ich den Fix vergleiche, habe ich oben beschrieben, mit dem Bild von einer Kamera-App mit der gleichen Auflösung, bekomme ich genau das gleiche Bild. keine visuelle Dehnung dort. – andruido

+0

Wie ich schon sagte, das Problem ist nicht auf Unity-Seite für mich, ich speichere die Bytes als JPG-Datei und das Bild ist gestreckt. Wie auch immer, ich könnte das Thema schließen, weil ich mich entschieden habe, Camera2 API zu verwenden und das Problem verschwand. – krzychawe

Verwandte Themen