web-dev-qa-db-ja.com

TextureViewを使用してCamera2プレビューをズーム

camera2APIのプレビューズームに問題があります。 TextureViewを使用しています。

TextureViewに表示されたプレビューストリームのみをズームしたい。

ズームジェスチャを使用する領域をズームしたい。

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


}

ズームは正しいですが、ズームする位置ではありません。例えば!右/中央の位置をズームすると、ストリームの左/上の長方形のみが表示されます。

問題を理解するために 次の写真 を追加しました。

9
m.b.

Android Camera2 api:ピンチズームイン/アウト

Google開発者によるCamera2Basicのこのサンプルコードを使用してください。 https://github.com/googlesamples/Android-Camera2Basic

次に、2つのクラス変数を宣言します–

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

@arinの回答のおかげで、改良版を作成しました。

彼のコードは基本的に機能していますが、2つの問題があります。
1)読みやすさ-実際、_Rect zoom_の計算で何が起こっているのかわかりません
2)私のAndroid 7.1.1デバイスでは、ズームがある程度大きいとプレビューがフリーズします。以下のコードでこの問題を解決したので、私は元のコードでは、カメラの最大ズーム比を超えてオーバーズームが許可されていたためです。
(実際、彼がCameraCharacteristicsによって返される比率に* 10を適用する必要がある理由はわかりません)

以下は私のコードです:(これはすべてカスタム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;
_

CameraCharacteristicsからCameraManagerを取得した直後、おそらく初期設定で:

_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);
        }
_
11
Sira Lam

@arinAnswerは機能しています。@ 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--;
      }
6
Sohail Zahid

Arinの回答に加えて、キャプチャでズームを有効にするには、captureStillPicture()メソッドにcaptureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);を追加する必要があります

0
Omar Alnajjar

これは、OnScaleGestureListenerおよびSimpleOnGestureListener-onScroll出力を使用して動作するように作成したCamera2のパンおよびズームオブジェクトです。 LEGACYは中央へのトリミングのみをサポートするため、これは、サポートレベルがLEGACYを超えるカメラがある場合にのみ期待どおりに機能します。

2つの注意点:1つは、JPEG出力の長方形の寸法が16の倍数である必要があるため、現在JPEG出力に出力するように設定されていないことです(理由を参照 ここ )。 2つ目は、画面を横向きモードにロックし、カメラも横向きにロックしていることですが、いくつかの調整を行うと画面の回転に対応できるはずです。

画面サイズを渡す必要があります

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

これらの関数への入力は、ジェスチャリスナーから直接渡されます

これが誰かに役立つことを願っています!

0
Oren Hinkis