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

My question is very simple:

How to get Android android.hardware.Camera2 with a ratio of 1: 1 and without deformation, for example Instagram?

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

enter image description here

+12
android android-camera2 ratio
source share
4 answers

Thanks @CommonsWare.

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

To do this, I simply update the AutoFitTextureView of the GoogeSamples android-Camera2Basic project as follows:

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

enter image description here

+6
source share

Create a custom texture view as follows:

 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 kind of texture 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(); 
+1
source share

I did this with a layout, so the google code can be saved as it is and automatically set the preview to 1: 1 based on the user interface.

  <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 an AutoFitTextureView inside a ConstraintLayout and then

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

+1
source share

For those looking for this, I tried the answer above . Adding a field to hide part of the texture so that it looks square looks good in the preview. but when saving the image, you must also remove the hidden areas from the output image.

A simpler solution is to show the full texture look and put other layouts on it so that it looks square. You can easily crop the output image.

You can find sample code here.

0
source share

All Articles