我对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);
}
缩放是正确的,但不是我缩放的位置。例如!当我放大右/中间位置时,我只看到流的左/上矩形。
我添加following pictures以解决问题。
答案 0 :(得分:24)
将此示例代码用于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);
}
答案 1 :(得分:10)
感谢@arin的回答,我做了一个改进的版本。
他的代码基本上有效,但有两个问题:
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);
}
答案 2 :(得分:6)
@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--;
}
答案 3 :(得分:0)
这是来自Camera2的Pan和Zoom对象,我使用OnScaleGestureListener和SimpleOnGestureListener-onScroll输出工作。只有拥有支持级别为&gt;的摄像头时,这才能正常工作。 LEGACY,因为LEGACY只支持作物到中心。
两个警告: 一个是当前没有设置为输出到JPEG输出,因为JPEG输出的矩形必须具有16的倍数(请参阅here的原因)。第二个是我将屏幕锁定为横向模式,我的相机也被锁定为横向,但是经过一些调整后应该可以处理屏幕旋转。
您需要传递屏幕尺寸
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();
}
}
}
}
这些函数的输入直接从手势监听器传递
我希望这有助于某人!
答案 4 :(得分:0)
除了arin回答,还需要添加captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
captureStillPicture()方法让zoom在捕获时生效