簡體   English   中英

使用TextureView縮放Camera2預覽

[英]Zoom Camera2 Preview using TextureView

我對Camera2 API的預覽縮放有問題。 我正在使用TextureView。

我想只縮放TextureView中顯示的預覽流。

我想縮放我使用Zoom Gesture的區域。

我使用SimpleOnScaleGestureListener!

我添加了以下代碼。 zoomingFactor和x和y位置都是正確的。

 private void updateTextureViewSize(float xPosi,float yPosi, float scale){
        float scaleX = 1.0f;
        float scaleY = 1.0f;




        float mVideoWidth = mCamcontrol.getmPreviewSize().getWidth();
        float mVideoHeight = mCamcontrol.getmPreviewSize().getHeight();

        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        RectF viewRect = new RectF(0, 0, 1440, 2560);
        RectF bufferRect = new RectF(0, 0, mVideoHeight, mVideoWidth);

        bufferRect.offset(xPosi - bufferRect.centerX(), yPosi -    bufferRect.centerY());

         //16:9 faktor
        scaleX = ((mScale * scale) / 9f) * 16f;
        scaleY = ((mScale * scale) / 16f) * 9f;

        Matrix matrix = new Matrix();

        matrix.setRectToRect(bufferRect, viewRect, Matrix.ScaleToFit.FILL);
        scalefactorView.setText(String.valueOf(xPosi) + "  " + String.valueOf(yPosi));

        matrix.setScale(scaleY, scaleX, xPosi, yPosi);
        matrix.postRotate(90 * (rotation - 2), xPosi, yPosi);

        mTextureView.setTransform(matrix);


}

縮放是正確的,但不是我縮放的位置。 例如! 當我放大右/中間位置時,我只看到Stream的左/上矩形。

我添加了以下圖片來解決問題。

Android Camera2 api:捏放大/縮小

將此示例代碼用於Google開發人員的Camera2Basic。 https://github.com/googlesamples/android-Camera2Basic

現在聲明兩個類變量 -

public float finger_spacing = 0;
public int zoom_level = 1;

並更新給定的onTouch()方法。

public boolean onTouch(View v, MotionEvent event) {
    try {
        Activity activity = getActivity();
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId);
        float maxzoom = (characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM))*10;

        Rect m = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
        int action = event.getAction();
        float current_finger_spacing;

        if (event.getPointerCount() > 1) {
            // Multi touch logic
            current_finger_spacing = getFingerSpacing(event);
            if(finger_spacing != 0){
                if(current_finger_spacing > finger_spacing && maxzoom > zoom_level){
                    zoom_level++;
                } else if (current_finger_spacing < finger_spacing && zoom_level > 1){
                    zoom_level--;
                }
                int minW = (int) (m.width() / maxzoom);
                int minH = (int) (m.height() / maxzoom);
                int difW = m.width() - minW;
                int difH = m.height() - minH;
                int cropW = difW /100 *(int)zoom_level;
                int cropH = difH /100 *(int)zoom_level;
                cropW -= cropW & 3;
                cropH -= cropH & 3;
                Rect zoom = new Rect(cropW, cropH, m.width() - cropW, m.height() - cropH);
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
            }
            finger_spacing = current_finger_spacing;
        } else{
            if (action == MotionEvent.ACTION_UP) {
                //single touch logic
            }
        }

        try {
            mCaptureSession
                .setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (NullPointerException ex) {
            ex.printStackTrace();
        }
    } catch (CameraAccessException e) {
        throw new RuntimeException("can not access camera.", e);
    }
    return true;
}


//Determine the space between the first two fingers
@SuppressWarnings("deprecation")
private float getFingerSpacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
}

感謝@arin的回答,我做了一個改進的版本。

他的代碼基本上可以工作,但有兩個問題:
1)可讀性 - 實際上我不知道計算Rect zoom
2)在我的Android 7.1.1設備中,如果縮放在一定程度上很大,預覽將凍結。 由於我使用下面的代碼解決了這個問題,我很確定這是因為原始代碼允許過度縮放超出相機的最大縮放比率。
(事實上​​,我不知道為什么他需要在CameraCharacteristics返回的比率上應用* 10)

下面是我的代碼:(我在我的自定義TextureView執行此TextureView ,它還存儲我的Camera2對象和邏輯):

相關成員變量:

protected CameraCharacteristics cameraCharacteristics;
protected CameraCaptureSession captureSession;
protected CaptureRequest.Builder previewRequestBuilder;

//Zooming
protected float fingerSpacing = 0;
protected float zoomLevel = 1f;
protected float maximumZoomLevel;
protected Rect zoom;

CameraManager獲得CameraCharacteristics后,可能在一些初始設置中:

maximumZoomLevel = cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);

覆蓋onTouchEvent

@Override
public boolean onTouchEvent(MotionEvent event) {
    try {
        Rect rect = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
        if (rect == null) return false;
        float currentFingerSpacing;

        if (event.getPointerCount() == 2) { //Multi touch.
            currentFingerSpacing = getFingerSpacing(event);
            float delta = 0.05f; //Control this value to control the zooming sensibility
            if (fingerSpacing != 0) {
                if (currentFingerSpacing > fingerSpacing) { //Don't over zoom-in
                    if ((maximumZoomLevel - zoomLevel) <= delta) {
                        delta = maximumZoomLevel - zoomLevel;
                    }
                    zoomLevel = zoomLevel + delta;
                } else if (currentFingerSpacing < fingerSpacing){ //Don't over zoom-out
                    if ((zoomLevel - delta) < 1f) {
                        delta = zoomLevel - 1f;
                    }
                    zoomLevel = zoomLevel - delta;
                }
                float ratio = (float) 1 / zoomLevel; //This ratio is the ratio of cropped Rect to Camera's original(Maximum) Rect
                //croppedWidth and croppedHeight are the pixels cropped away, not pixels after cropped
                int croppedWidth = rect.width() - Math.round((float)rect.width() * ratio);
                int croppedHeight = rect.height() - Math.round((float)rect.height() * ratio);
                //Finally, zoom represents the zoomed visible area
                zoom = new Rect(croppedWidth/2, croppedHeight/2,
                        rect.width() - croppedWidth/2, rect.height() - croppedHeight/2);
                previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
            }
            fingerSpacing = currentFingerSpacing;
        } else { //Single touch point, needs to return true in order to detect one more touch point
            return true;
        }
        captureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallback, null);
        return true;
    } catch (final Exception e) {
        //Error handling up to you
        return true;
    }
}

getFingerSpacing方法:

private float getFingerSpacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
}

最后,不要忘記在實際拍攝照片時設置裁剪區域。 我的代碼基於這個Camera2Basic ,我在captureStillPicture()方法中執行此操作:

        //Zoom
        if (zoom != null) {
            captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
        }

@arin答案正在工作,感謝@arin只有一件事變焦靈敏度太高了。

為了控制這一點,我做了一些改變可能對你有用。

將zoom_level數據類型更改為double

public int zoom_level = 1; public double zoom_level = 1;

然后使用低值增加或減少zoom_level我使用0.4

if (current_finger_spacing > finger_spacing && maxzoom > zoom_level) {
        zoom_level = zoom_level + .4;
        //zoom_level++;
    } else if (current_finger_spacing < finger_spacing && zoom_level > 1) {
        zoom_level = zoom_level - .4;
        //zoom_level--;
      }

這是Camera2使用OnScaleGestureListener和SimpleOnGestureListener-onScroll輸出工作的Pan和Zoom對象。 如果您的相機支持級別> LEGACY,則只能按預期工作,因為LEGACY僅支持裁剪到中心。

兩個警告:一個是當前沒有設置為輸出到JPEG輸出,因為JPEG輸出的矩形必須具有16的倍數(請參見此處的原因)。 第二個是我已將屏幕鎖定為橫向模式,我的相機也被鎖定為橫向,但應該可以在經過一些調整后處理屏幕旋轉。

您需要傳遞屏幕尺寸

DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) mView.getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

最大相機數碼變焦

try {
    CameraManager manager = (CameraManager) mView.getContext().getSystemService(Context.CAMERA_SERVICE);
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
    float maxZoom = (characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM));
} catch (CameraAccessException e) {
   e.printStackTrace();
}

相機傳感器的有源陣列尺寸

try {
    CameraManager manager = (CameraManager) mView.getContext().getSystemService(Context.CAMERA_SERVICE);
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
    Rect rectInit = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
} catch (CameraAccessException e) {
    e.printStackTrace();
}

這是我的對象初始化

mScaler = new CamScaler(maxZoom, rectInit.width(), rectInit.height(), displayMetrics.heightPixels, displayMetrics.widthPixels);

CamScaler類

public class CamScaler {
    private final float ZOOM_MIN = 1.0f;
    private final int X_MIN = 0;
    private final int Y_MIN = 0;
    private int displayWidth;
    private int displayHeight;

    private Rect current_rect;
    private int xCenter;
    private int yCenter;
    private int xWidth;
    private int yHeight;
    private int xMax;
    private int yMax;
    private float zoomMax;
    private float zoomCurrent;

    public CamScaler(float zoomMax, int xMax, int yMax, int displayHeight, int displayWidth) {
        this.xMax = xMax;
        this.yMax = yMax;
        this.zoomMax = zoomMax;

        current_rect = new Rect(X_MIN,Y_MIN, xMax, yMax); //(0,0,xMax,yMax) as the starting rectangle
        zoomCurrent = ZOOM_MIN;
        xWidth = current_rect.width();
        yHeight = current_rect.height();
        xCenter = current_rect.centerX();
        yCenter = current_rect.centerY();

        this.displayHeight = displayHeight;
        this.displayWidth = displayWidth;
    }

    public void pan(float distanceX, float distanceY){
        //calculate the shift in the we want to take on the camera sensor with respect to the distance moved on the screen
        int xShift = Math.round((distanceX/displayWidth)*xWidth); //scales down to a percentage of the current view width->converts to a pixel shift
        int yShift = Math.round((distanceY/displayHeight)*yHeight); //scales down to a percentage of the current view height->converts to a pixel shift

        //check if the shift will push us pass our maximums, this should account for both negative and positive values of xShift and yShift correctly
        if ( !((xCenter + Math.round(xWidth/2.0) + xShift < xMax) && (xCenter - Math.round(xWidth/2.0) + xShift > 0))) { //if not within xBounds, set xShift to 0
            xShift = 0;
        }
        if ( !((yCenter + Math.round(yHeight/2) + yShift < yMax) && (yCenter - Math.round(yHeight/2.0) + yShift > 0))) { //if not within yBounds, set yShift to 0
            yShift = 0;
        }

        Log.d("Scaler", "pan: xShift" + xShift + " yShift " + yShift);
        current_rect.offset(xShift,yShift);
        Log.d("Scaler", "pan: current_rect" + current_rect.toString());
        xCenter = current_rect.centerX(); //update center
        yCenter = current_rect.centerY(); //update center
    }

    public void zoom(float scale_change){
        if ( (zoomCurrent*scale_change < zoomMax) && (zoomCurrent*scale_change > ZOOM_MIN) ){ //if we are within zoom bounds
            zoomCurrent *= scale_change; //update the zoom factor
            int newWidthHalf = (int)Math.floor(xMax/zoomCurrent/2.0);
            int newHeightHalf = (int)Math.floor(yMax/zoomCurrent/2.0);
            int xTempCenter = xCenter;
            int yTempCenter = yCenter;

            //if at edge we need to shift and scale
            if (xCenter + newWidthHalf > xMax) { //if at right edge
                xTempCenter = xMax - newWidthHalf; //shift center to the left
            } else if (xCenter - newWidthHalf < 0) { //if at left edge
                xTempCenter = newWidthHalf; //shift center to the right
            }
            if (yCenter + newHeightHalf > yMax) { //if at bottom
                yTempCenter = yMax - newHeightHalf; //shift center up
            } else if (yCenter - newHeightHalf < 0) { //if at top
                yTempCenter = newHeightHalf; //shift center down
            }
            Log.d("Scaler", "zoom: " + zoomCurrent);
            Log.d(TAG, "current center(x,y) " + xTempCenter + " " + yTempCenter + "current halfwidths(x,y) " + newWidthHalf + " " + newHeightHalf);
            current_rect.set(xTempCenter - newWidthHalf, yTempCenter - newHeightHalf,xTempCenter + newWidthHalf, yTempCenter + newHeightHalf);
            Log.d("Scaler", "zoom: current_rect" + current_rect.toString());
            xWidth = current_rect.width();
            yHeight = current_rect.height();
            xCenter = current_rect.centerX(); //update center
            yCenter = current_rect.centerY(); //update center
        } //if not in digital zoom bounds, do nothing
    }

    public Rect getCurrentView() {
        return current_rect;
    }
}

以及如何使用它

public void pan(float distanceX, float distanceY){
    if (mScaler != null) {
        synchronized (mScaler) {
            mScaler.pan(distanceX, distanceY);
            try {
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mScaler.getCurrentView());
                mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

public void zoom(float scale_factor) {
    if (mScaler!= null) {
        synchronized (mScaler) {
            mScaler.zoom(scale_factor);
            try {
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mScaler.getCurrentView());
                mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

這些函數的輸入直接從手勢監聽器傳遞

我希望這可以幫助別人!

除了arin的回答,需要添加captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom); captureStillPicture()方法讓zoom在捕獲時生效

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM