2013-06-10 127 views
19

固定幅と高さを持つ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

私はあなたが小さいプレビューで試してみることができますbtを確認していません。最初のカメラは、デフォルトのプレビューサイズに大きなものを与えます.. – Amrendra

+1

私はプレビューサイズについて知っています。カメラのプレビューを表示するビューのサイズがプレビューのサイズと異なるため、これは役に立ちません。 –

+0

画面上でフラグメントを試すことができます。 – Amrendra

答えて

2

byte[] dataonPreview()から操作できます。私はあなたが持っていると思う

  • をリサイズ/ストレッチほとんどないし、あなたにBitmapを渡すBitmap
  • でトリミングを行うビットマップに入れますSurfaceView

これはあまり効果がありません。おそらくbyte[]を直接操作することはできますが、NV21のような画像フォーマットに対処する必要があります。

1

@SatteliteSDの回答が最も適しています。すべてのカメラは、HALで設定された特定のプレビューサイズのみをサポートしています。したがって、使用可能なプレビューサイズで十分でない場合は、onPreviewからデータを抽出する必要があります。

0

カメラのプレビュー画像をリアルタイムで操作するために、Android用のOpenCVは完全に適合しています。あなたはここに必要なすべてのサンプル:http://opencv.org/platforms/android/opencv4android-samples.htmlを見つけるでしょう、そして、それはリアルタイムでうまく働くことがわかります。

免責事項:アンドロイドでopencv libを設定することは、どのようにしてC++/opencv/ndkを使って実験したかによってかなり頭痛になる可能性があります。 いずれの場合でも、opencvコードの作成は決して簡単ではありませんが、それは非常に強力なライブラリです。

+0

私は自分自身でJavaCameraViewを見てきました。それはyuvをやっているようです - > rgbのネイティブコードでの変換ではなく、GPU上のCPU ...私はこれが実際にカメラのフレーム(遅延と不良なfps)に配信されているフレームの減速につながっていると思う – Sam

11

アスペクト比を計算し、スケーリングマトリクスを生成してTextureViewに適用するだけです。プレビュー画像の面のアスペクト比とアスペクト比に基づいて、プレビュー画像は上下左右にトリミングされる。 もう一つの解決策は、の前にカメラを開くと、SurfaceTextureが利用可能であることです。プレビューは自動的に自動的に拡大/縮小されます。 mCamera = Camera.open(); SurfaceTextureListenerを設定した後にonCreate関数に渡します。これは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の内部に収められます。変換行列を使用すると、歪みを補正するようにスケーリングを設定できます。 – Romanski

29

@Romanskiの以前の解決方法は問題なく動作しますが、トリミングで拡大縮小されます。フィットするようにスケールする必要がある場合は、次のソリューションを使用します。サーフェイスビューが変更されるたびに、つまりonSurfaceTextureAvailableメソッドとonSurfaceTextureSizeChangedメソッドでupdateTextureMatrixを呼び出します。また、このソリューションは、アクティビティが設定の変更を無視していることにも注意してください。アンドロイド: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; 

はupdateTextureMatrixを呼び出す前にonSurfaceTextureAvailable mathodでそれを初期化します。

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

最後に、カメラの回転を補正する必要があります。だから、活動onConfigurationChanged方法でsetCameraDisplayOrientationメソッドを呼び出し(ともonSurfaceTextureAvailable方法で最初の呼び出しします):私はちょうど私がピクセル化を得ることなく、実際の入力のX2とのプレビューを表示するために必要な作業のアプリを作っている

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は、これを約1週間ほど表面観察、サーフェイステクスチャ、その他のもので攻撃しようとしています。私はスケーリングマトリックスを設定することができるいくつかの種類のビューがなければならないことを知っていましたが、私はそれを見つけることができませんでした。これは完璧です。私はそこにあるカメラのプレビューのクロッピングに関する他の多くの質問からこの答えへのリンクがあるはずだと思います。 – Sam

+0

編集:romanskiが解決したように見えるこのコードにはわずかな論理問題があります。問題がどこで発生したら解決してください。 – Sam

+0

これをSurfaceTextureとGLESで直接行う例については、 Grafika(https://github.com/google/grafika)の「カメラからのテクスチャ」アクティビティ「ズーム」アクションは、テクスチャ座標を操作することによって中央のクロップおよびスケールを行います。 TextureViewは同様のことをしています。 TextureViewの利点は、使用するのがずっと簡単だということです。 「生のGLES」アプローチははるかに複雑ですが、はるかに柔軟です。 – fadden

1

を見てください。つまり私は1280x720のライブプレビューの中央部分を640x360のTextureViewに表示する必要がありました。

ここに私がしたことがあります。

は私が必要なもののx2の解像度にカメラのプレビュー設定します。

params.setPreviewSize(1280, 720); 

をそれに応じてテクスチャビューをスケーリング:

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

これは、小型のデバイス上の任意のしゃっくりなしで実行されます。

+0

ありがとうalot.You私を保存します。 – YeeKhin

関連する問題