How to get an Android Camera2 with 1:1 ratio like Instagram?

筅森魡賤 提交于 2019-12-21 03:43:46

问题


My question is very simple:

How to get an Android android.hardware.Camera2 with 1:1 ratio and without deformation like Instagram?

I tested with the GoogeSamples project android-Camera2Basic. But when I change the preview with a ratio of 1:1 image is deformed. Does anyone have an idea on this?


回答1:


Thanks @CommonsWare.

I followed your advice using negative margin (top and bottom) and it works.

To do that, I just update AutoFitTextureView the GoogeSamples project android-Camera2Basic this way:

public class AutoFitTextureView extends TextureView {

    //...
    private boolean mWithMargin = false;

    //...

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int margin = (height - width) / 2;

        if(!mWithMargin) {
            mWithMargin = true;
            ViewGroup.MarginLayoutParams margins = ViewGroup.MarginLayoutParams.class.cast(getLayoutParams());
            margins.topMargin = -margin;
            margins.bottomMargin = -margin;
            margins.leftMargin = 0;
            margins.rightMargin = 0;
            setLayoutParams(margins);
        }

        if (0 == mRatioWidth || 0 == mRatioHeight) {
            setMeasuredDimension(width, height);
        } else {
            if (width < height) {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            } else {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }
}




回答2:


Create custom texture view like this:

public class AutoFitTextureView extends TextureView {

    private int mCameraWidth = 0;
    private int mCameraHeight = 0;
    private boolean mSquarePreview = false;

    public AutoFitTextureView(Context context) {
        this(context, null);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setAspectRatio(int width, int height, boolean squarePreview) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Size cannot be negative.");
        }
        mCameraWidth = width;
        mCameraHeight = height;
        mSquarePreview = squarePreview;
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mCameraWidth || 0 == mCameraHeight) {
            setMeasuredDimension(width, height);
        } else {
            /**
             * Vertical orientation
             */
            if (width < height) {
                if (mSquarePreview) {
                    setTransform(squareTransform(width, height));
                    setMeasuredDimension(width, width);
                } else {
                    setMeasuredDimension(width, width * mCameraHeight / mCameraWidth);
                }
            }
            /**
             * Horizontal orientation
             */
            else {
                if (mSquarePreview) {
                    setTransform(squareTransform(width, height));
                    setMeasuredDimension(height, height);
                } else {
                    setMeasuredDimension(height * mCameraWidth / mCameraHeight, height);
                }
            }
        }
    }

    private Matrix setupTransform(int sw, int sh, int dw, int dh) {
        Matrix matrix = new Matrix();
        RectF src = new RectF(0, 0, sw, sh);
        RectF dst = new RectF(0, 0, dw, dh);
        RectF screen = new RectF(0, 0, dw, dh);

        matrix.postRotate(-90, screen.centerX(), screen.centerY());
        matrix.mapRect(dst);

        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        matrix.mapRect(src);

        matrix.setRectToRect(screen, src, Matrix.ScaleToFit.FILL);
        matrix.postRotate(-90, screen.centerX(), screen.centerY());

        return matrix;
    }

    private Matrix squareTransform(int viewWidth, int viewHeight) {
        Matrix matrix = new Matrix();

        if (viewWidth < viewHeight) {
            MyLogger.log(AutoFitTextureView.class, "Horizontal");
            matrix.setScale(1, (float) mCameraHeight / (float) mCameraWidth, viewWidth / 2, viewHeight / 2);
        } else {
            MyLogger.log(AutoFitTextureView.class, "Vertical");
            matrix.setScale((float) mCameraHeight / (float) mCameraWidth, 1, viewWidth / 2, viewHeight / 2);
        }

        return matrix;
    }
}

And call setAspectRatio for your texture view in activity/fragment.

if (mVideoSize.width > mVideoSize.height) {
    mTextureView.setAspectRatio(mVideoSize.height, mVideoSize.width, true);
} else {
    mTextureView.setAspectRatio(mVideoSize.width, mVideoSize.height, true);
}
mCamera.setPreviewTexture(mTextureView.getSurfaceTexture());
mCamera.startPreview();



回答3:


I did it with the Layout, in that way, google code can be keeped as it comes and automatically set a 1:1 preview based on the UI.

    <android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@id/footer"
    android:layout_below="@id/header">

    <com.example.android.camera2video.AutoFitTextureView
        android:id="@+id/texture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />

</android.support.constraint.ConstraintLayout>

Just put the AutoFitTextureView inside a ConstraintLayout and then

previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height, videoSize); does all the magic




回答4:


For anybody looking for this, I tried the above answer. Adding a margin to hide part of textureview to make it look square looks good in preview. But when saving the image, you should remove the hidden areas from the output image also.

An Easier solution is to show a full textureview and to overlay some other layouts on it to make it look square.You can easily crop the image from output.

you can find the sample code here



来源:https://stackoverflow.com/questions/34638651/how-to-get-an-android-camera2-with-11-ratio-like-instagram

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!