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);
}
ズームは正しいですが、ズームする位置ではありません。例えば!右/中央の位置をズームすると、ストリームの左/上の長方形のみが表示されます。
問題を理解するために 次の写真 を追加しました。
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);
}
@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);
}
_
@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--;
}
Arinの回答に加えて、キャプチャでズームを有効にするには、captureStillPicture()メソッドにcaptureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
を追加する必要があります
これは、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();
}
}
}
}
これらの関数への入力は、ジェスチャリスナーから直接渡されます
これが誰かに役立つことを願っています!