「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
一、对焦模式
1、对焦模式介绍 常用的四种
public static final String FOCUS_MODE_AUTO = "auto";
public static final String FOCUS_MODE_FIXED = "fixed";
public static final String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";
public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture";
FOCUS_MODE_AUTO:自动对焦,这个只会触发一次对焦,并且是需要在预览开启后,调用autoFocus接口后才会触发, 像触点对焦、和拍照对焦都可以用到该模式;
FOCUS_MODE_FIXED:定焦,有些摄像头本身不支持对焦;
FOCUS_MODE_CONTINUOUS_VIDEO:录像的时候,可以采用该模式,会持续对焦,设置parameter参数后就会生效;
FOCUS_MODE_CONTINUOUS_PICTURE :拍照的时候,可以采用该模式,会持续对焦,设置parameter参数后就会生效, 对焦速度相对 FOCUS_MODE_CONTINUOUS_VIDEO 会快点;
2、设置对焦模式
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, int focusMode);
二、实现
1. 操作摄像头进行对焦
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[] {new MeteringRectangle(rect, 1000)});
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[] {new MeteringRectangle(rect, 1000)});
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START); mPreviewRequest = mPreviewRequestBuilder.build();
try
{ mCaptureSession.setRepeatingRequest(mPreviewRequest, mAfCaptureCallback, mBackgroundHandler); }
catch (CameraAccessException e)
{ Log.e(TAG, "setRepeatingRequest failed, " + e.getMessage()); }
2. 对焦完成恢复正常的预览模式
if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState)
{ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); mPreviewRequest = mPreviewRequestBuilder.build();
try
{ mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mBackgroundHandler); }
catch (CameraAccessException e)
{ Log.e(TAG, "setRepeatingRequest failed, errMsg: " + e.getMessage()); } }
3. 计算自动对焦区域
CONTROL_AF_REGIONS 的参数是指定用来测量对焦的区域,摄像头设备使用这个区域来测量光度情况进行对焦,因此该区域坐标系基于摄像头成像区域的坐标系。那么app接收到的图像数据与成像区域的关系如何呢?
手机摄像头成像区域是固定的,因此系统对于app中需要采集不同分辨率的需求做法是:将成像区域按照需要的分辨率比例进行居中裁剪,然后缩放到对应的尺寸。
- 一般app拿到摄像头来的数据之后,会将摄像头进行合适的旋转,然后根据屏幕比例居中裁剪图像数据,再进行显示。所以我们先将屏幕上面点击区域的坐标转化为app收到的图像区域的坐标:
// 先取相对于view上面的坐标
double x = event.getX(), y = event.getY(), tmp;
// 取出来的图像如果有旋转角度的话,则需要将宽高交换下
int realPreviewWidth = mPreviewSize.width, realPreviewHeight = mPreviewSize.height;
if (90 == mDisplayRotate || 270 == mDisplayRotate)
{ realPreviewWidth = mPreviewSize.height; realPreviewHeight = mPreviewSize.width; }
// 计算摄像头取出的图像相对于view放大了多少,以及有多少偏移
double imgScale = 1.0, verticalOffset = 0, horizontalOffset = 0;
if (realPreviewHeight * viewWidth > realPreviewWidth * viewHeight)
{ imgScale = viewWidth * 1.0 / realPreviewWidth; verticalOffset = (realPreviewHeight - viewHeight / imgScale) / 2; }
else { imgScale = viewHeight * 1.0 / realPreviewHeight; horizontalOffset = (realPreviewWidth - viewWidth / imgScale) / 2; }
// 将点击的坐标转换为图像上的坐标
x = x / imgScale + horizontalOffset; y = y / imgScale + verticalOffset;
if (90 == mDisplayRotate) { tmp = x; x = y; y = mPreviewSize.height - tmp; }
else if (270 == mDisplayRotate) { tmp = x; x = mPreviewSize.width - y; y = tmp; }
- app取到的图像是按照裁剪区域(crop region)按照预览尺寸的比例进行居中裁剪的,所以需要计算app取到的图像相对于裁剪区域进行了多少缩放,以及有多少位移:
// 计算取到的图像相对于裁剪区域的缩放系数,以及位移
Rect cropRegion = mPreviewRequest.get(CaptureRequest.SCALER_CROP_REGION);
if (null == cropRegion)
{ Log.e(TAG, "can't get crop region"); cropRegion = mActiveArraySize; } int cropWidth = cropRegion.width(), cropHeight = cropRegion.height();
if (mPreviewSize.height * cropWidth > mPreviewSize.width * cropHeight)
{ imgScale = cropHeight * 1.0 / mPreviewSize.height; verticalOffset = 0; horizontalOffset = (cropWidth - imgScale * mPreviewSize.width) / 2; }
else { imgScale = cropWidth * 1.0 / mPreviewSize.width; horizontalOffset = 0; verticalOffset = (cropHeight - imgScale * mPreviewSize.height) / 2; }
- 将点击区域相对于app取到的图像坐标,转化为相对于成像区域的坐标:
// 将点击区域相对于图像的坐标,转化为相对于成像区域的坐标 x = x * imgScale + horizontalOffset + cropRegion.left; y = y * imgScale + verticalOffset + cropRegion.top;
- 按照对焦区域为成像区域的0.1倍计算对焦的矩形:
double tapAreaRatio = 0.1; Rect rect = new Rect();
rect.left = clamp((int) (x - tapAreaRatio / 2 * cropRegion.width()), 0, cropRegion.width());
rect.right = clamp((int) (x + tapAreaRatio / 2 * cropRegion.width()), 0, cropRegion.width());
rect.top = clamp((int) (y - tapAreaRatio / 2 * cropRegion.height()), 0, cropRegion.height());
rect.bottom = clamp((int) (y + tapAreaRatio / 2 * cropRegion.height()), 0, cropRegion.height());