26.学习Camera之——PDAF(相位对焦)的基本原理

在自动对焦的时候总是有一个困惑,知道图像是不清楚的,但是lens应该向前还是向后移动呢?
总是要前后移动lens一下才知道,普通的反差法对焦就是这么做的,爬山嘛。PDAF的出现就是为了
解决这个lens移动的问题,可以根据图像,预判lens运动的方向。

PDAF最早运用在单反上,已经是非常成熟的技术了,原理如下图。

左图为CCD在焦后的情况,右图为焦前。当CCD在焦后时,在线阵CCD1和CCD2上会发现聚焦点CCD1的
在左,反之,则聚焦点CCD2的在左(图中红色标记的光线)。

这种方法需要加额外的一个半透半反镜,还需要加两个透镜,两个线阵CCD,结构复杂,不适用于
手机等便携式设备中。所以pdaf sensor应运而生。

PDAF sensor的原理和单反中的略有不同。

以轴上点为例,紫色光线代表经过上半部分透镜的光线,而蓝色代表经过下半部分透镜的光线,
可以看到,当CCD在焦前的时候,上半部分的CCD接收到的是上半部分的光线,而下半部分的CCD
则接收到的是下半部分的光线,反之,在焦后的时候恰好相反。那么如果我们可以区分光线是来
自透镜的上半部分还是下半部分,我们就可以知道是焦前还是焦后了。不难发现,CCD越接近理
想成像面,两色光线在CCD上所成像越接近,当恰好成像清晰时,两色光线是重合成一点的。

 PDAF sensor的一种实现如下图所示,在CMOS上面一半的位置加了金属遮盖,这样,被遮住左边
一半的像素点就只能接受左边来的光,同理,pair的被遮住右边一般的像素点就只能接受右边来的
光。一般在CMOS中,遮住左边和遮住右边的像素点是在相邻位置会成对出现。

按照这种原理,我们就可以知道,上图的轴上点发出的光在位于焦前的CCD上所成像应如下图所示。
横坐标为pixel的x轴坐标,以光轴中心为0,纵坐标是像素接收到的光强。可以看到,左边遮挡的
像素(L)和右边遮挡的像素(R)所得到的图像是关于光轴对称的。所以,我们可以通过求自相关
得到两个图像的shift,进而求得sensor需要移动的距离(当然要通过tunning获得这样的对应关系)。

讲到这里,应该是没毛病的,对于轴上一点,pdaf是可以有效的得到相位信息的。

那么对于轴外一点呢?如下图所示,当然也是可以的。

细心的可能会发现这里有个问题,轴上点和轴外点在CCD上面的成像会彼此交叠,这样,应该会
对pdaf的效果产生影响的。好在我们平常的场景中,一般都有一些中低频物体,那么对这些物体
的话应该是影响很小的。

 PDAF的最终效果还和什么有关系呢?

1. 相机的F数,F数越小,PDAF的效果越精确。F数越小,景深越小,那么在物体离焦时,得到的
弥散斑越大,L和R的shift越大

2. 相机的曝光时间理论上不影响PDAF的效果,但是如果曝光时间过短,图像噪点过大,是会造成
pdaf的L和R shift不准确的

3. 物体距离相机越近,pdaf的效果越精确。物体距离相机越近,物体移动相同距离的shift比在
物距远时要大,这个跟景深是吻合的

4. 物体的pattern,当物体的pattern呈某种特定形状,会使得pdaf的像素检测不到shift,比如
纯色或重复的pattern

5. 轴外点的精度低于轴上点。同样的离焦情况下,轴上点的shift比轴外点要大。

6.物体本身的颜色。当pdaf的像素都是G时,如果物体本身的颜色为R或者B,则会导致pdaf接收到
的光能过小,从而影响pdaf的准确性

  • 3
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Android Camera2 API中实现手动对焦需要以下步骤: 1. 获取CameraManager实例,使用该实例获取可用的相机设备列表。 2. 获取要使用的相机设备的CameraDevice实例。 3. 创建一个CaptureRequest.Builder对象,并将其与相机设备关联。 4. 设置手动对焦模式和对焦区域。 5. 构建CaptureRequest对象。 6. 启动相机预览。 7. 实现一个触摸事件监听器,在用户触摸屏幕时获取焦点坐标,并设置对焦区域。 8. 在触摸事件监听器中调用CaptureRequest.Builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START)触发对焦操作。 9. 实现一个CameraCaptureSession.CaptureCallback回调函数,在对焦完成后更新预览界面。 下面是一个简单的实现示例: ```java private void setupCamera() { // 获取相机管理器实例 CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { // 获取可用的相机设备列表 String[] cameraIds = cameraManager.getCameraIdList(); for (String cameraId : cameraIds) { // 获取相机设备实例 CameraDevice cameraDevice = cameraManager.openCamera(cameraId, mStateCallback, mBackgroundHandler); // 创建一个CaptureRequest.Builder对象,并将其与相机设备关联 mPreviewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 设置手动对焦模式和对焦区域 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{new MeteringRectangle(focusRect, 1000)}); // 构建CaptureRequest对象 mPreviewRequest = mPreviewRequestBuilder.build(); // 启动相机预览 cameraDevice.createCaptureSession(Arrays.asList(mSurface), mSessionCallback, mBackgroundHandler); } } catch (CameraAccessException e) { e.printStackTrace(); } } private void startPreview() { try { // 设置自动对焦模式 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); // 启动预览 mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private void setFocusArea(float x, float y) { // 计算对焦区域 Rect focusRect = calculateFocusRect(x, y); // 设置对焦区域 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{new MeteringRectangle(focusRect, 1000)}); // 触发对焦操作 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); try { // 更新预览界面 mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); // 对焦完成后更新预览界面 if (request.get(CaptureRequest.CONTROL_AF_TRIGGER) != null && request.get(CaptureRequest.CONTROL_AF_TRIGGER) == CameraMetadata.CONTROL_AF_TRIGGER_START) { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); try { mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } } }; private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { mCameraDevice = camera; startPreview(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { mCameraDevice.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice camera, int error) { mCameraDevice.close(); mCameraDevice = null; } }; private CameraCaptureSession.StateCallback mSessionCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { mCaptureSession = session; try { mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { session.close(); mCaptureSession = null; } }; ``` 在以上代码中,calculateFocusRect()方法用于计算对焦区域,可以根据需要自行实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值