是否可以裁剪相机预览?

时间:2011-12-22 09:41:02

标签: android android-camera

我没有找到任何方法来裁剪相机预览,然后在SurfaceView上显示它。

Android - 是否可以裁剪相机预览?

9 个答案:

答案 0 :(得分:16)

您可以在没有叠加视图的情况下执行此操作(这在所有情况下均无效)。

Subclass ViewGroup,将SurfaceView添加为唯一的子项,然后:

  1. inMeasure提供您想要的裁剪尺寸。
  2. 在onLayout中,使用未裁剪的尺寸布局SurfaceView。
  3. 基本上,

    public class CroppedCameraPreview extends ViewGroup {
      private SurfaceView cameraPreview;
      public CroppedCameraPreview( Context context ) {
        super( context );
        // i'd probably create and add the SurfaceView here, but it doesn't matter
      }
      @Override
      protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
        setMeasuredDimension( croppedWidth, croppedHeight );
      }
      @Override
      protected void onLayout( boolean changed, int l, int t, int r, int b ) {
        if ( cameraPreview != null ) {
          cameraPreview.layout( 0, 0, actualPreviewWidth, actualPreviewHeight );
        }
      }
    }
    

答案 1 :(得分:9)

您可以将相机预览(SurfaceView)放在ScrollView内的LinearLayout中。当摄像机输出大于您设置的LinearLayout时,您可以通过编程方式滚动它并禁用用户滚动。这样您就可以轻松地模拟相机裁剪:

<ScrollView 
                     android:id="@+id/scrollView"
                     android:layout_width="640dip"
                     android:layout_height="282dip"
                     android:scrollbars="none"
                     android:fillViewport="true">

                        <LinearLayout
                                android:id="@+id/linearLayoutBeautyContent"
                                android:layout_width="fill_parent"
                                android:layout_height="fill_parent"
                                android:orientation="vertical">

                                <SurfaceView
                                            android:layout_width="match_parent"
                                            android:layout_height="match_parent"
                                            android:id="@+id/surfaceViewBeautyCamera"/>
                      </LinearLayout>
</ScrollView>

答案 2 :(得分:6)

不直接。 Camera API现在允许偏移,并将图像压缩到表面 持有人。但是你可以通过在其上放置叠加(其他视图)来解决。

答案 3 :(得分:0)

以编程方式滚动图像的代码将是这样的:

public void setCameraOrientationOnOpen() 
    {   
        mCamera.stopPreview();
        int rotation = getRotation();
        Camera.Parameters currentCameraParameters = mCamera.getParameters();
        List<Camera.Size> previewSizes = currentCameraParameters.getSupportedPreviewSizes();

        mOptimalCameraSize = getOptimaPreviewCameraSize(previewSizes, (double)9/16);            
        currentCameraParameters.setPreviewSize(mOptimalCameraSize.width, mOptimalCameraSize.height);
        mCamera.setParameters(currentCameraParameters);
        float ratio = 100;
        int ratio1 = (mSurfaceView.getLayoutParams().height * 100) / mOptimalCameraSize.width; //height
        int ratio2 = (mSurfaceView.getLayoutParams().width * 100) / mOptimalCameraSize.height; //width 
        ratio = Math.max(ratio1, ratio2);
        mSurfaceView.getLayoutParams().height = (int) ((mOptimalCameraSize.width * ratio) / 100);
        mSurfaceView.getLayoutParams().width = (int) ((mOptimalCameraSize.height * ratio) / 100);
        if(ratio > 100)
        {
            int offset = (mSurfaceView.getLayoutParams().height - mBoxHeight)/2;
            mScrollView.scrollTo(0, offset); //center the image
        }
        mCamera.setDisplayOrientation(rotation);
        mOptimalCameraSize = mCamera.getParameters().getPreviewSize();
    }

我从相机为我的相机内容框计算最佳预览尺寸(比例16:9),然后将计算出的比率应用于图像以保持相同的比例并最终计算所需的滚动(图像将是在中间)

答案 4 :(得分:0)

创建一个居中的Frame Layout,它将存储Camera Preview并用Views覆盖它以“裁剪”它。创建视图时,动态拉伸也居中的透明视图。

XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000" >

<FrameLayout 
    android:id="@+id/camera_preview_frame"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_centerVertical="true" />

<View
    android:id="@+id/transparent_window"
    android:layout_width="fill_parent"
    android:layout_height="50dip"
    android:layout_centerVertical="true"
    android:background="@android:color/transparent" />

<View 
    android:id="@+id/black_top_box"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_above="@id/transparent_window"
    android:background="#000"/>

<View 
    android:id="@+id/black_bottom_box"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_below="@id/transparent_window"
    android:background="#000"/>  
</RelativeLayout>

然后在您的活动类的OnCreate()方法中,您可以像这样拉伸透明视图。

CameraActivity.java

final View transView = findViewById(R.id.transparent_window);

transView.post(new Runnable() {

    @Override
    public void run() {
        RelativeLayout.LayoutParams params;
        params = (RelativeLayout.LayoutParams) transView.getLayoutParams();
        params.height = transView.getWidth();
        transView.setLayoutParams(params);
        transView.postInvalidate();
    }
});

这是我手机中的screenshot。中间的灰色斑点是通过透明视图的相机对地板的视图

答案 5 :(得分:0)

Here is a solution for orientation = landscape to complete @drees' excellent answer.

Just add layout-land folder in res folder, duplicate your entire layout xml and change the layout part of @drees' code to be like this:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/background_dark" >

    <FrameLayout
        android:id="@+id/frameSurface"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:background="@android:color/background_light"/>

    <View
        android:id="@+id/transparent_window"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerInParent="true"
        android:background="@android:color/transparent" />

    <View
        android:id="@+id/black_top_box"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toLeftOf="@id/transparent_window"
        android:background="@android:color/background_dark"/>

    <View
        android:id="@+id/black_bottom_box"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toRightOf="@id/transparent_window"
        android:background="@android:color/background_dark"/>
</RelativeLayout>

答案 6 :(得分:0)

此解决方案是针对您的情况的更多解决方案之一。某些代码已被弃用且不推荐在企业项目中使用,但如果您只需显示相机预览而不会挤压它就足够了。
如果您在预览之前需要处理图像,那么您应该看SurfaceTexture

public class CameraPreview
        extends SurfaceView
        implements SurfaceHolder.Callback, Camera.PreviewCallback {

    public static final String TAG = CameraPreview.class.getSimpleName();

    private static final int PICTURE_SIZE_MAX_WIDTH = 1280;
    private static final int PREVIEW_SIZE_MAX_WIDTH = 640;
    private static final double ASPECT_RATIO = 3.0 / 4.0;

    private Camera mCamera;
    private SurfaceHolder mHolder;
    private boolean mIsLive;
    private boolean mIsPreviewing;

    public CameraPreview(Context context) {
        super(context);
        init(context);
    }

    public CameraPreview(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = (int) (width / ASPECT_RATIO + 0.5);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        //L.g().d(TAG, "onVisibilityChanged: visibility=" + visibility);
        if (mIsLive) {
            if (visibility == VISIBLE && !mIsPreviewing) {
                startCameraPreview();
            } else {
                stopCameraPreview();
            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startCamera();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        stopCamera();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        //L.g().d(TAG, "surfaceChanged: format=" + format + ", width=" + w + ", height=" + h);
        if (mHolder.getSurface() == null || mCamera == null) return;
        mHolder = holder;
        try {
            mCamera.stopPreview();
        } catch (Exception ignored) {}
        try {
            mCamera.setPreviewDisplay(mHolder);
            if (mIsLive && mIsPreviewing) mCamera.startPreview();
        } catch (Exception ignored) {}
    }

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

        //work with camera preview

        if (mIsPreviewing) camera.setOneShotPreviewCallback(this);
    }

    private Camera.Size determineBestPreviewSize(Camera.Parameters parameters) {
        return determineBestSize(parameters.getSupportedPreviewSizes(), PREVIEW_SIZE_MAX_WIDTH);
    }

    private Camera.Size determineBestPictureSize(Camera.Parameters parameters) {
        return determineBestSize(parameters.getSupportedPictureSizes(), PICTURE_SIZE_MAX_WIDTH);
    }

    /**
     * This code I found in this repository
     * https://github.com/boxme/SquareCamera/blob/master/squarecamera/src/main/java/com/desmond/squarecamera/CameraFragment.java#L368
     */
    private Camera.Size determineBestSize(List<Camera.Size> sizes, int widthThreshold) {
        Camera.Size bestSize = null;
        Camera.Size size;
        int numOfSizes = sizes.size();
        for (int i = 0; i < numOfSizes; i++) {
            size = sizes.get(i);
            boolean isDesireRatio = (size.width / 4) == (size.height / 3);
            boolean isBetterSize = (bestSize == null) || size.width > bestSize.width;

            if (isDesireRatio && isBetterSize) {
                bestSize = size;
            }
        }
        if (bestSize == null) {
            return sizes.get(sizes.size() - 1);
        }
        return bestSize;
    }

    private void init(Context context) {
        mHolder = getHolder();
        mHolder.addCallback(this);
    }

    public void startCamera() {
        if (!mIsLive) {
            //L.g().d(TAG, "startCamera");
            mIsPreviewing = false;
            mCamera = Camera.open();
            if (mCamera != null) {
                try {
                    Camera.Parameters param = mCamera.getParameters();
                    Camera.Size bestPreviewSize = determineBestPreviewSize(param);
                    Camera.Size bestPictureSize = determineBestPictureSize(param);
                    param.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);
                    param.setPictureSize(bestPictureSize.width, bestPictureSize.height);
                    mCamera.setParameters(param);
                } catch (RuntimeException ignored) {}
                try {
                    mCamera.setDisplayOrientation(90);
                    mCamera.setPreviewCallback(this);
                    mCamera.setPreviewDisplay(mHolder);
                    mIsLive = true;
                } catch (Exception ignored) {}
            }
            //else L.g().d(TAG, "startCamera: error launching the camera");
        }
    }

    public void stopCamera() {
        if (mCamera != null && mIsLive) {
            //L.g().d(TAG, "stopCamera");
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
            mIsPreviewing = false;
            mIsLive = false;
        }
    }

    public void startCameraPreview() {
        if (mCamera != null && mIsLive && !mIsPreviewing) {
            //L.g().d(TAG, "startCameraPreview");
            mCamera.setPreviewCallback(this);
            mCamera.startPreview();
            mIsPreviewing = true;
        }
    }

    public void stopCameraPreview() {
        if (mCamera != null && mIsLive && mIsPreviewing) {
            //L.g().d("stopCameraPreview");
            mCamera.stopPreview();
            mIsPreviewing = false;
        }
    }
}

答案 7 :(得分:0)

经过大量的关注,我相信我需要在这里发布我的解决方案。

我唯一能做到的并且运作良好的是添加比例。

我想用相机输出的一部分创建一个纹理视图,但是如果不让预览得到破坏,我就无法做到这一点。

所以在我决定相机/屏幕比率开始捕捉的最佳分辨率后,我得到相机捕捉高度和我想要显示的高度之间的比例。

mPreviewSize = chooseOptimalSize(...);

int viewHeight = getDP(this, R.dimen.videoCaptureHeight);
float scaleY = mPreviewSize.getHeight() / viewHeight;
setScaleY(scaleY);

答案 8 :(得分:0)

假设您的Rect或RecF是您的计算值

<%= f.select(:city_id, options_for_select(City.pluck(:name, :id), target_blank: true )) %>