Androidカメラプレビューストレッチ


136

Androidでカスタムカメラアクティビティの作成に取り組んでいますが、カメラを回転させると、サーフェスビューのアスペクト比がめちゃくちゃになります。

アクティビティのoncreateで、カメラのパラメーターを表示するサーフェスビューを保持するframelayoutを設定しました。

//FrameLayout that will hold the camera preview
        FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview);

        //Setting camera's preview size to the best preview size
        Size optimalSize = null;
        camera = getCameraInstance();
        double aspectRatio = 0;
        if(camera != null){
            //Setting the camera's aspect ratio
            Camera.Parameters parameters = camera.getParameters();
            List<Size> sizes = parameters.getSupportedPreviewSizes();
            optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
            aspectRatio = (float)optimalSize.width/optimalSize.height;
        }

        if(optimalSize!= null){
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
            previewHolder.setLayoutParams(params);
            LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
            cameraPreview.setLayoutParams(surfaceParams);

        }

        cameraPreview.setCamera(camera);

        //Adding the preview to the holder
        previewHolder.addView(cameraPreview);

次に、サーフェスビューで、表示するカメラのパラメーターを設定します

public void setCamera(Camera camera) {
        if (mCamera == camera) { return; }

        mCamera = camera;

        if (mCamera != null) {
            requestLayout();

            try {
                mCamera.setPreviewDisplay(mHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }


            if(mCamera != null){
                //Setting the camera's aspect ratio
                Camera.Parameters parameters = mCamera.getParameters();
                List<Size> sizes = parameters.getSupportedPreviewSizes();
                Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);

                parameters.setPreviewSize(optimalSize.width, optimalSize.height);
                mCamera.setParameters(parameters);
            }

            /*
              Important: Call startPreview() to start updating the preview surface. Preview must 
              be started before you can take a picture.
              */
            mCamera.startPreview();
        }

    }

ここに画像の説明を入力してください

LEGOの男性は、電話を回転させると背が高くなり、細くなります。

カメラビューのアスペクト比が正しいことをどのように確認できますか?


@科学はあなたが私を助けることができますか?
user3233280 2014年

私は同様の問題を取得するように見えたが、それは以下呼んでいたものを修正:カメラのための私のSurfaceViewのsurfaceCreated()メソッドでmCamera.setDisplayOrientation(90)
グレッグ・

回答:


163

私はこの方法を使用しています-> APIデモに基づいてプレビューサイズを取得しています:

private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio=(double)h / w;

        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

ご覧のとおり、画面の幅と高さを入力する必要があります。このメソッドは、これらの値に基づいて画面の比率を計算し、supportedPreviewSizesのリストから、利用可能なものから最適なものを選択します。次のコマンドを使用して、Cameraオブジェクトがnullではない場所にsupportedPreviewSizeリストを取得します。

mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();

その後、onMeasureで次のように最適なpreviewSizeを取得できます。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes != null) {
           mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }
    }

そして、(私がCameraActivityコードのAPIデモ構造を使用していると言ったように、surfaceChangedメソッドの私のコードでは、Eclipseでそれを生成することができます):

Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();

そして、私はあなたとほとんど同じアプリをやったので、あなたのための一つのヒント。カメラアクティビティの良い習慣は、StatusBarを非表示にすることです。Instagramのようなアプリケーションがそれをやっています。画面の高さの値を減らし、比率の値を変更します。一部のデバイスで奇妙なプレビューサイズが表示される可能性があります(SurfaceViewは少しカットされます)


そして、あなたの質問に答えるために、プレビュー率が正しいかどうかを確認する方法は?次に、設定したパラメータの高さと幅を取得します。

mCamera.setParameters(parameters);

設定した比率は高さ/幅に等しくなります。カメラを画面上で見栄えよくしたい場合、カメラに設定するパラメーターの高さ/幅の比率は、画面の高さ(マイナスのステータスバー)/幅の比率と同じでなければなりません。


2
ただ1つの質問:あなたは2つの異なる計算された比率を使用しているようです:double targetRatio =(double)h / wおよびdouble ratio =(double)size.width / size.height。1つ目は、高さを幅で割ったもので、もう1つは、幅を高さで割ったものです。それらは同じ計算ではないのですか?
phyzalis 2014年

たとえば、サイズのリストではあらゆる可能性が考慮されるため、問題ではありません。たとえば、480x680のようなものが見つかりますが、遅かれ早かれ680x480(横向き)になるため、そのリストを調べて、そのリストを見つける必要があります。あなたのニーズに一致します。あなたはそれを遅かれ早かれ見つけるでしょう。
F1sher 2014年

さらに、画像をこのサイズにしたい場合は(おそらくそうなるはずです)、で画像サイズを設定しましたparameters.setPictureSize(mPreviewSize.width, mPreviewSize.height)
Matt Logan、

2
@ F1sher、私は常にnullポインター例外パラメーターを取得します。setPreviewSize(mPreviewSize.width、mPreviewSize.height); 解決策が見つかりません。
FIXI

3
@phyzalis同意します。実際、コードを期待どおりに機能させるには、その修正を行う必要がありました。
fernio 2015年

149

F1Sherのソリューションは優れていますが、機能しない場合があります。特に、surfaceViewが画面全体をカバーしていない場合。この場合、onMeasure()メソッドをオーバーライドする必要があります。参照用にコードをここにコピーしました。

幅に基づいてsurfaceViewを測定したので、画面の端に少し白い隙間があり、これを設計で埋めました。高さを維持し、比率に乗算して幅を増やすと、この問題を修正できます。ただし、surfaceViewをわずかに押しつぶします。

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";

    private Context mContext;
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mContext = context;
        mCamera = camera;

        // supported preview sizes
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for(Camera.Size str: mSupportedPreviewSizes)
                Log.e(TAG, str.width + "/" + str.height);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // empty. surfaceChanged will take care of stuff
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + h);
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or reformatting changes here
        // start preview with new settings
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }

        if (mPreviewSize!=null) {
            float ratio;
            if(mPreviewSize.height >= mPreviewSize.width)
                ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
            else
                ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

            // One of these methods should be used, second method squishes preview slightly
            setMeasuredDimension(width, (int) (width * ratio));
  //        setMeasuredDimension((int) (width * ratio), height);
        }
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.height / size.width;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;

            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }

        return optimalSize;
    }
}

11
これは正解です。カメラのプレビューが画面全体をカバーしていない場合に対応します。+1。
Houcine 14

1
アクティビティアプリケーションで膨らませると、アプリケーションが閉じられました<com.app.camera.CameraPreview android:id = "@ + id / camera_preview" android:layout_width = "match_parent" android:layout_height = "match_parent" />
fish40

いくつかの変更を加える必要がありますが、私はこれを解決策として受け入れます。賛成票。
JaydeepW 2014

2
こんにちは@ adnan9011、このチュートリアルはおそらく役立つでしょう。airpair.com/android/android-camera-surface-view-fragment
Hesam 2015

4
これを機能させるには、高さと幅を切り替える必要がありましたgetOptimalPreviewSize。これは、表示方向が90に設定されたためですか?そうでなければ、比率の計算はあまり近づきませんでした(ターゲットは1.7で、カメラの比率は1未満でした)
小野2015年

23

注:私のソリューションはヘサムのソリューションの続きです:https ://stackoverflow.com/a/22758359/1718734

私が対処すること:Hesam氏は、次のように、一部の電話に表示される可能性がある小さな空白があると述べています

注:アスペクト比は正しいですが、カメラは画面全体を埋めません。

Hesamは2番目の解決策を提案しましたが、それはプレビューを圧縮します。一部のデバイスでは、ひどく歪んでいます。

では、この問題をどのように修正すればよいでしょうか。画面いっぱいになるまで縦横比を掛けることで簡単です。私は、Snapchat、WhatsAppなどのいくつかの人気のあるアプリが同じように機能することに気づきました。

これをonMeasureメソッドに追加するだけです。

float camHeight = (int) (width * ratio);
    float newCamHeight;
    float newHeightRatio;

    if (camHeight < height) {
        newHeightRatio = (float) height / (float) mPreviewSize.height;
        newCamHeight = (newHeightRatio * camHeight);
        Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight);
        setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);
        Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight);
    } else {
        newCamHeight = camHeight;
        setMeasuredDimension(width, (int) newCamHeight);
        Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight);
    }

画面の高さを計算し、画面の高さとmPreviewSizeの高さの比率を取得します。次に、カメラの幅と高さに新しい高さの比率を掛け、それに応じて測定された寸法を設定します。

ここに画像の説明を入力してください

そして、あなたが次に知っていること、あなたはこれで終わる:D

ここに画像の説明を入力してください

これは彼のフロントカメラでもうまくいきます。これはこれを行うための最良の方法だと思います。アプリに残された唯一のことは、「キャプチャ」をクリックしたときにプレビュー自体を保存することです。しかし、そうです、これです。


これはとてもうまくいきます!しかし、Landscapeでは正しく動作しません:/
Dahan

あなたのコードは私の2か月の空の黒いスペースの問題を解決しました:)
karthik kolanji

1
それは機能しますが、私の場合は..私は上部にアクションバーを持っているので白いバーが小さくなりましたが、コードを追加した後、私のカメラは40-50%ズームしたように見えます。前面カメラに切り替えると、ズームが大きいため、基本的に顔がまっすぐに見える(髪の毛が少しだけ)のがわかりません。
Makalele

@Yoosuf私があなたのコードをフォローしたとき、私は白い画面になっています。でonMeasure(int widthMeasureSpec, int heightMeasureSpec)方法I常にGET幅= 0、高さ= 0;
Kush Patel 2017

10

わかりました。それで、一般的なカメラプレビューストレッチング問題に対する十分な答えはないと思います。または、少なくとも1つは見つかりませんでした。私のアプリもこのストレッチ症候群に悩まされており、このポータルとインターネット上のすべてのユーザーの回答からの解決策をまとめるのにしばらく時間がかかりました。

私は@Hesamの解決策を試しましたが、うまくいかず、カメラのプレビューが大幅に歪んでしまいました。

最初にソリューションのコード(コードの重要な部分)を示し、次にこれらのステップを実行した理由を説明します。パフォーマンスを変更する余地があります。

メインアクティビティのXMLレイアウト:

<RelativeLayout 
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
</RelativeLayout>

カメラのプレビュー:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder prHolder;
private Camera prCamera;
public List<Camera.Size> prSupportedPreviewSizes;
private Camera.Size prPreviewSize;

@SuppressWarnings("deprecation")
public YoCameraPreview(Context context, Camera camera) {
    super(context);
    prCamera = camera;

    prSupportedPreviewSizes = prCamera.getParameters().getSupportedPreviewSizes();

    prHolder = getHolder();
    prHolder.addCallback(this);
    prHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder) {
    try {
        prCamera.setPreviewDisplay(holder);
        prCamera.startPreview();
    } catch (IOException e) {
        Log.d("Yologram", "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    if (prHolder.getSurface() == null){
      return;
    }

    try {
        prCamera.stopPreview();
    } catch (Exception e){
    }

    try {
        Camera.Parameters parameters = prCamera.getParameters();
        List<String> focusModes = parameters.getSupportedFocusModes();
        if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        parameters.setPreviewSize(prPreviewSize.width, prPreviewSize.height);

        prCamera.setParameters(parameters);
        prCamera.setPreviewDisplay(prHolder);
        prCamera.startPreview();

    } catch (Exception e){
        Log.d("Yologram", "Error starting camera preview: " + e.getMessage());
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    setMeasuredDimension(width, height);

    if (prSupportedPreviewSizes != null) {
        prPreviewSize = 
            getOptimalPreviewSize(prSupportedPreviewSizes, width, height);
    }    
}

public Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) h / w;

    if (sizes == null)
        return null;

    Camera.Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    for (Camera.Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
            continue;

        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Camera.Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }

    return optimalSize;
}
}

主な活動:

public class MainActivity extends Activity {

...

@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);     
    setContentView(R.layout.activity_main);

        maCamera = getCameraInstance();

        maLayoutPreview = (FrameLayout) findViewById(R.id.camera_preview);

        maPreview = new CameraPreview(this, maCamera);

        Point displayDim = getDisplayWH();
        Point layoutPreviewDim = calcCamPrevDimensions(displayDim, 
                maPreview.getOptimalPreviewSize(maPreview.prSupportedPreviewSizes, 
                    displayDim.x, displayDim.y));
        if (layoutPreviewDim != null) {
            RelativeLayout.LayoutParams layoutPreviewParams = 
                (RelativeLayout.LayoutParams) maLayoutPreview.getLayoutParams();
            layoutPreviewParams.width = layoutPreviewDim.x;
            layoutPreviewParams.height = layoutPreviewDim.y;
            layoutPreviewParams.addRule(RelativeLayout.CENTER_IN_PARENT);
            maLayoutPreview.setLayoutParams(layoutPreviewParams);
        }
        maLayoutPreview.addView(maPreview);
}

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private Point getDisplayWH() {

    Display display = this.getWindowManager().getDefaultDisplay();
    Point displayWH = new Point();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        display.getSize(displayWH);
        return displayWH;
    }
    displayWH.set(display.getWidth(), display.getHeight());
    return displayWH;
}

private Point calcCamPrevDimensions(Point disDim, Camera.Size camDim) {

    Point displayDim = disDim;
    Camera.Size cameraDim = camDim;

    double widthRatio = (double) displayDim.x / cameraDim.width;
    double heightRatio = (double) displayDim.y / cameraDim.height;

    // use ">" to zoom preview full screen
    if (widthRatio < heightRatio) {
        Point calcDimensions = new Point();
        calcDimensions.x = displayDim.x;
        calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width;
        return calcDimensions;
    }
    // use "<" to zoom preview full screen
    if (widthRatio > heightRatio) { 
        Point calcDimensions = new Point();
        calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height;
        calcDimensions.y = displayDim.y;
        return calcDimensions;
    }
    return null;
}   
}

私のコメント:

このすべてのポイントは、最適なカメラサイズを計算するgetOptimalPreviewSize()のに、画面に合う最も近い比率を選択するだけであるということです。したがって、比率が正確に同じでない限り、プレビューは拡大します。

なぜ伸びるの?FrameLayoutカメラのプレビューが、layout.xmlで幅と高さのmatch_parentに設定されているためです。そのため、プレビューが全画面に拡大されます。

行う必要があるのは、カメラプレビューレイアウトの幅と高さを、選択したカメラサイズ比と一致するように設定して、プレビューがアスペクト比を維持し、歪まないようにすることです。

CameraPreviewクラスを使用してすべての計算とレイアウト変更を実行しようとしましたが、理解できませんでした。このソリューションを適用しようとしましたSurfaceViewが、getChildCount ()またはを認識しませんgetChildAt (int index)。私は最終的にはを参照して動作するようになったと思いますが、これは動作に問題がありmaLayoutPreview、設定した比率をアプリ全体に適用しました。だから私はそれを手放し、レイアウト変更をに移動しましたMainActivity

CameraPreview変更prSupportedPreviewSizesgetOptimalPreviewSize()公開したので、で使用できますMainActivity。次に、ディスプレイの寸法(ナビゲーション/ステータスバーがある場合はマイナス)を必要とし、最適なカメラサイズを選択しました。表示サイズではなくRelativeLayout(またはFrameLayout)のサイズを取得しようとしましたが、ゼロの値を返していました。この解決策は私にはうまくいきませんでした。レイアウトは後で値を取得しましたonWindowFocusChanged(ログで確認)。

そのため、選択したカメラサイズのアスペクト比に合わせてレイアウトの寸法を計算する方法があります。次にLayoutParams、カメラのプレビューレイアウトを設定するだけです。幅、高さを変更し、親の中央に配置します。

プレビューの寸法を計算する方法は2つあります。画面の両端または上部/下部に黒いバー(windowBackgroundがnullに設定されている場合)を合わせて表示します。または、プレビューを全画面にズームしたい。詳細については、にコメントを残しましたcalcCamPrevDimensions()


6

こんにちは、ここではgetOptimalPreview()が機能しなかったので、バージョンを共有したいと思います。

private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

    if (sizes==null) return null;

    Camera.Size optimalSize = null;
    double ratio = (double)h/w;
    double minDiff = Double.MAX_VALUE;
    double newDiff;
    for (Camera.Size size : sizes) {
        newDiff = Math.abs((double)size.width/size.height - ratio);
        if (newDiff < minDiff) {
            optimalSize = size;
            minDiff = newDiff;
        }
    }
    return optimalSize;
}

それは正方形のカメラに変換します。与えられた入力が(1920x1080)で与えられた幅と高さ(1088x1088)を最適化した後、私のテストデバイスはSamsung S6です。その理由を教えてください。この問題についてご不明な点がありましたらお知らせください。
MohanRaj S 2018

2

このスレッドをより完全にするために、私のバージョンの回答を追加しています:

私が達成したかったこと:表面ビューは引き伸ばされてはならず、画面全体をカバーする必要があります。さらに、私のアプリには横長モードしかありませんでした。

解決:

このソリューションは、F1sherのソリューションの非常に小さな拡張です。

=>最初のステップは、F1sherのソリューションを統合することです。

=>さて、サーフェスビューが画面全体をカバーしない場合、F1sherのソリューションでシナリオが発生する可能性があります。解決策は、そのためにサーフェスビューを画面の寸法より大きくして、画面全体をカバーすることです。

    size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight);

    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(size.width, size.height);


    mCamera.setParameters(parameters);      

    double screenRatio = (double) screenHeight / screenWidth;
    double previewRatio = (double) size.height / size.width;

    if (previewRatio > screenRatio)     /*if preview ratio is greater than screen ratio then we will have to recalculate the surface height while keeping the surface width equal to the screen width*/
    {
        RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
        params1.addRule(RelativeLayout.CENTER_IN_PARENT);
        flPreview.setLayoutParams(params1);

        flPreview.setClipChildren(false);

        LayoutParams surfaceParams = new LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
        surfaceParams.gravity = Gravity.CENTER;
        mPreview.setLayoutParams(surfaceParams);        
    }
    else     /*if preview ratio is smaller than screen ratio then we will have to recalculate the surface width while keeping the surface height equal to the screen height*/
    {
        RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
        params1.addRule(RelativeLayout.CENTER_IN_PARENT);
        flPreview.setLayoutParams(params1);
        flPreview.setClipChildren(false);

        LayoutParams surfaceParams = new LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
        surfaceParams.gravity = Gravity.CENTER;
        mPreview.setLayoutParams(surfaceParams);

    }       

    flPreview.addView(mPreview); 

  /*  The TopMost layout used is the RelativeLayout, flPreview is the FrameLayout in which Surface View is added, mPreview is an instance of a class which extends SurfaceView  */

1

私は問題が何であるかを理解しました-それは向きの変更です。カメラの向きを90度または270度に変更する場合は、サポートされているサイズの幅と高さ交換する必要があり、すべて問題ありません。

また、サーフェスビューはフレームレイアウトにあり、重心を持つ必要があります。

これは、C#(Xamarin)の例です。

public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
    _camera.StopPreview();

    // find best supported preview size

    var parameters = _camera.GetParameters();
    var supportedSizes = parameters.SupportedPreviewSizes;
    var bestPreviewSize = supportedSizes
        .Select(x => new { Width = x.Height, Height = x.Width, Original = x }) // HACK swap height and width because of changed orientation to 90 degrees
        .OrderBy(x => Math.Pow(Math.Abs(x.Width - width), 3) + Math.Pow(Math.Abs(x.Height - height), 2))
        .First();

    if (height == bestPreviewSize.Height && width == bestPreviewSize.Width)
    {
        // start preview if best supported preview size equals current surface view size

        parameters.SetPreviewSize(bestPreviewSize.Original.Width, bestPreviewSize.Original.Height);
        _camera.SetParameters(parameters);
        _camera.StartPreview();
    }
    else
    {
        // if not than change surface view size to best supported (SurfaceChanged will be called once again)

        var layoutParameters = _surfaceView.LayoutParameters;
        layoutParameters.Width = bestPreviewSize.Width;
        layoutParameters.Height = bestPreviewSize.Height;
        _surfaceView.LayoutParameters = layoutParameters;
    }
}

カメラパラメータは元のサイズ(入れ替えではなく)に設定し、サーフェスビューのサイズは入れ替える必要があることに注意してください。


1

上記の解決策をすべて試しましたが、どれもうまくいきませんでした。最後に私は自分で解決しましたが、実際にはとても簡単です。注意すべき点が2つあります。

parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);

このpreviewSizeは、カメラがサポートする解像度のいずれかである必要があります。これは、以下のように取得できます。

List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes(); 

通常、rawSupportedSizeの1つはデバイスの解像度と同じです。

次に、SurfaceViewをFrameLayoutに配置し、上記のようにsurfaceChangedメソッドでサーフェスレイアウトの高さと幅を設定します。

FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
layoutParams.height = cameraResolution.x;
layoutParams.width = cameraResolution.y;

了解しました。これでお役に立てば幸いです。


0

私の要件は、カメラのプレビューがフルスクリーンであり、アスペクト比を維持する必要があることです。HesamとYoosufのソリューションは素晴らしかったですが、なぜか高ズームの問題が発生します。

考え方は同じで、プレビューコンテナーの中心を親に持ち、画面全体をカバーできるようになるまでアスペクト比に応じて幅または高さを増やします。

注意すべき点の1つは、表示方向を設定しているため、プレビューサイズが横長であることです。

camera.setDisplayOrientation(90);

SurfaceViewビューを追加するコンテナー:

<RelativeLayout
    android:id="@+id/camera_preview_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"/>

アクティビティの親が中央にあるコンテナにプレビューを追加します。

this.cameraPreview = new CameraPreview(this, camera);
cameraPreviewContainer.removeAllViews();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
cameraPreviewContainer.addView(cameraPreview, 0, params);

CameraPreviewクラスの内部:

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // If your preview can change or rotate, take care of those events here.
    // Make sure to stop the preview before resizing or reformatting it.

    if (holder.getSurface() == null) {
        // preview surface does not exist
        return;
    }

    stopPreview();

    // set preview size and make any resize, rotate or
    // reformatting changes here
    try {
        Camera.Size nativePictureSize = CameraUtils.getNativeCameraPictureSize(camera);
        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(optimalSize.width, optimalSize.height);
        parameters.setPictureSize(nativePictureSize.width, nativePictureSize.height);
        camera.setParameters(parameters);
        camera.setDisplayOrientation(90);
        camera.setPreviewDisplay(holder);
        camera.startPreview();

    } catch (Exception e){
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    if (supportedPreviewSizes != null && optimalSize == null) {
        optimalSize = CameraUtils.getOptimalSize(supportedPreviewSizes, width, height);
        Log.i(TAG, "optimal size: " + optimalSize.width + "w, " + optimalSize.height + "h");
    }

    float previewRatio =  (float) optimalSize.height / (float) optimalSize.width;
    // previewRatio is height/width because camera preview size are in landscape.
    float measuredSizeRatio = (float) width / (float) height;

    if (previewRatio >= measuredSizeRatio) {
        measuredHeight = height;
        measuredWidth = (int) ((float)height * previewRatio);
    } else {
        measuredWidth = width;
        measuredHeight = (int) ((float)width / previewRatio);
    }
    Log.i(TAG, "Preview size: " + width + "w, " + height + "h");
    Log.i(TAG, "Preview size calculated: " + measuredWidth + "w, " + measuredHeight + "h");

    setMeasuredDimension(measuredWidth, measuredHeight);
}

-1

必要なアスペクト比に応じて、cameraView.getLayoutParams()。heightとcameraView.getLayoutParams()。widthを設定する必要があります。


これは、カメラビューのパラメーターをこの方法で設定する(これを現在行っている)のとは異なります。mCamera.setParameters(parameters);
科学的な

-2

私は計算をあきらめ、カメラのプレビューを表示したいビューのサイズを取得し、カメラのプレビューサイズをカスタムのSurfaceView実装で同じに設定しました(回転のために幅/高さを反転させただけ):

@Override // CameraPreview extends SurfaceView implements SurfaceHolder.Callback { 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    Display display = ((WindowManager) getContext().getSystemService(
            Context.WINDOW_SERVICE)).getDefaultDisplay();

    if (display.getRotation() == Surface.ROTATION_0) {
        final Camera.Parameters params = camera.getParameters();
        // viewParams is from the view where the preview is displayed
        params.setPreviewSize(viewParams.height, viewParams.width);
        camera.setDisplayOrientation(90);
        requestLayout();
        camera.setParameters(params);
    }
    // I do not enable rotation, so this can otherwise stay as is
}

Androidのドキュメントによると:プレビューサイズを設定するとき
G.シュタイガート2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.