Android 设置对焦区域和自动对焦

41

我已经和这个特性斗争了几天了...

似乎相机正在忽略我定义的焦点区域。以下是代码片段:

对焦:

protected void focusOnTouch(MotionEvent event) {
    if (camera != null) {
        Rect rect = calculateFocusArea(event.getX(), event.getY());

        Parameters parameters = camera.getParameters();
        parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
        parameters.setFocusAreas(Lists.newArrayList(new Camera.Area(rect, 500)));

        camera.setParameters(parameters);
        camera.autoFocus(this);
    }
}

焦点区域计算:

private Rect calculateFocusArea(float x, float y) {
    int left = clamp(Float.valueOf((x / getSurfaceView().getWidth()) * 2000 - 1000).intValue(), focusAreaSize);
    int top = clamp(Float.valueOf((y / getSurfaceView().getHeight()) * 2000 - 1000).intValue(), focusAreaSize);

    return new Rect(left, top, left + focusAreaSize, top + focusAreaSize);
}

Camera.AutoFocusCallback#onAutoFocus 的几个日志事件:

Log.d(TAG, String.format("自动对焦成功=%s。对焦模式:'%s'。对焦点位置:%s", focused, camera.getParameters().getFocusMode(), camera.getParameters().getFocusAreas().get(0).rect.toString()));

08-27 11:19:42.240: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(-109, 643 - -13, 739)
08-27 11:19:55.514: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(20, 457 - 116, 553)
08-27 11:19:58.037: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(-159, 536 - -63, 632)
08-27 11:20:00.129: DEBUG/MyCameraActivity(26268): Auto focus success=true. Focus mode: 'auto'. Focused on: Rect(-28, 577 - 68, 673)

从视觉上看,似乎焦点成功地放在了登录区域,但突然失去了焦点并集中于中心点 (0, 0) 或者是获得了 SurfaceView 的更大部分。

计算中使用的 focusAreaSize 大约为 210 像素(96dp)。 在 HTC One 上测试,其中 Camera.getParameters().getMaxNumFocusAreas()1

初始对焦模式(在第一次点击之前)设置为 FOCUS_MODE_CONTINUOUS_PICTURE

我这里有什么做错了吗? 尝试调整 Camera.Area 矩形的大小或权重没有任何显着的效果。

5个回答

54

我的问题要简单得多 :)

我所要做的就是取消之前调用的自动对焦。基本上正确的操作顺序如下:

protected void focusOnTouch(MotionEvent event) {
    if (camera != null) {

        camera.cancelAutoFocus();
        Rect focusRect = calculateTapArea(event.getX(), event.getY(), 1f);
        Rect meteringRect = calculateTapArea(event.getX(), event.getY(), 1.5f);

        Parameters parameters = camera.getParameters();
        parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
        parameters.setFocusAreas(Lists.newArrayList(new Camera.Area(focusRect, 1000)));

        if (meteringAreaSupported) {
            parameters.setMeteringAreas(Lists.newArrayList(new Camera.Area(meteringRect, 1000)));
        }

        camera.setParameters(parameters);
        camera.autoFocus(this);
    }
}

更新

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    ...
    Parameters p = camera.getParameters();
    if (p.getMaxNumMeteringAreas() > 0) {
       this.meteringAreaSupported = true;
    }
    ...
}

/**
 * Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.
 */
private Rect calculateTapArea(float x, float y, float coefficient) {
    int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();

    int left = clamp((int) x - areaSize / 2, 0, getSurfaceView().getWidth() - areaSize);
    int top = clamp((int) y - areaSize / 2, 0, getSurfaceView().getHeight() - areaSize);

    RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
    matrix.mapRect(rectF);

    return new Rect(Math.round(rectF.left), Math.round(rectF.top), Math.round(rectF.right), Math.round(rectF.bottom));
}

private int clamp(int x, int min, int max) {
    if (x > max) {
        return max;
    }
    if (x < min) {
        return min;
    }
    return x;
}

1
你是怎么获取meteringAreaSupported的?tx - MatheusJardimB
calculateTapArea也没有啊 :/ - MatheusJardimB
2
只是实验而已。我使用了 72dp 作为我的尺寸。 - Martynas Jurkus
1
@Hugo 这是完整的代码:https://gist.github.com/mjurkus/21f7b9aa9b27a7661184 - Martynas Jurkus
1
谢谢您的帖子!您如何确保点击区域将在-1000/1000之间?在创建矩形之前,这些值不应该被夹紧吗? - gbero
显示剩余18条评论

5

除了设置之外:

parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);

您需要设置:

parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

如果您想要真正的“实时”自动对焦。此外,检查可用的对焦方式也很重要:

List<String> focusModes = parameters.getSupportedFocusModes();
LLog.d("focusModes=" + focusModes);
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
    parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

在三星S6上,您必须在获取相机预览后稍微延迟一下(约500毫秒)来设置此项。

什么是在自动对焦和连续对焦之间切换的最佳方式?假设用户点击屏幕,我们使用自动对焦,当他移动画面时,我们将其设置为连续对焦。 - Cyph3rCod3r

3

今天我遇到了这个问题 :/

经过数小时的挣扎,我终于找到了解决方法!

很奇怪,但似乎在设置对焦区域之前将对焦模式设置为"宏"可以解决问题 ;)

params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
params.setFocusAreas(focusAreas);
mCamera.setParameters(params);

我有一台带有Android 4.1.2的Galaxy S3。

我希望这对你也有用 :)


是的,在s3、s4上,使用4.3和5.0.1版本都可以正常工作,但在htc one上无法工作。 - Ahmad Arslan

2

使用 FOCUS_MODE_FIXED

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        mCamera = Camera.open(mCameraId);
    } else {
        mCamera = Camera.open();
    }
cameraParams = mCamera.getParameters();
// set the focus mode
cameraParams.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
// set Camera parameters
mCamera.setParameters(cameraParams);

0

你好,尝试复制下面的代码并根据自己的需求进行更改

public class CameraActivity extends AppCompatActivity implements Camera.AutoFocusCallback {

    private Camera camera;
    private FrameLayout fl_camera_preview;

    ...

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView( R.layout.camera_activity );
        //this View, is lens camera 
        fl_camera_preview = findViewById( R.id.fl_camera_preview );
        Button someButtonCapturePicture = findViewById(R.id.someButtonCapturePicture);

        pictureCall = getPictureCallback();

        //check camera access
        if ( getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) ) {

            if ( safeCameraOpen(0) ) {

                cameraPreview = new CameraPreview( this, camera );
                fl_camera_preview.addView( cameraPreview );

                someButtonCapturePicture.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        camera.takePicture(null, null, pictureCall);

                    }
                });

            } else {
                Log.w(TAG, "getCameraInstance:  Camera is not available (in use or does not exist)." );
            }        

        }

    }

    private boolean safeCameraOpen(int id) {

        boolean qOpened = false;

        try {

            camera = Camera.open( id );
            // set some parameters
            Camera.Parameters par = camera.getParameters();

        List<Camera.Size> supportedPreviewSizes = par.getSupportedPreviewSizes();

            for ( Camera.Size cs : supportedPreviewSizes ) {
              if ( cs.height == 720 ) {
                 par.setPictureSize(cs.width, cs.height);
                 par.setPreviewSize(cs.width, cs.height);
                 break;
              }
            }

            camera.setParameters(par);

            qOpened = ( camera != null );

        } catch (Exception e) {
            Log.e(TAG, "safeCameraOpen: failed to open Camera");
            e.printStackTrace();
        }

        return qOpened;

    }

    public void touchFocusCamera( final Rect touchFocusRect ) {

        //Convert touche coordinate, in width and height to -/+ 1000 range
        final Rect targetFocusRect = new Rect(
                touchFocusRect.left * 2000/fl_camera_preview.getWidth() - 1000,
                touchFocusRect.top * 2000/fl_camera_preview.getHeight() - 1000,
                touchFocusRect.right * 2000/fl_camera_preview.getWidth() - 1000,
                touchFocusRect.bottom * 2000/fl_camera_preview.getHeight() - 1000);

        final List<Camera.Area> focusList = new ArrayList<Camera.Area>();
        Camera.Area focusArea = new Camera.Area(targetFocusRect, 1000);
        focusList.add(focusArea);

        Camera.Parameters para = camera.getParameters();
        List<String> supportedFocusModes = para.getSupportedFocusModes();
        if ( supportedFocusModes != null &&
                supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO) ) {

            try {

                para.setFocusAreas(focusList);
                para.setMeteringAreas(focusList);
                camera.setParameters(para);

                camera.autoFocus( CameraActivity.this );

            } catch (Exception e) {
                Log.e(TAG, "handleFocus: " + e.getMessage() );
            }

        }

    }

    @Override
    public void onAutoFocus(boolean success, Camera camera) {

        if ( success ) {
            camera.cancelAutoFocus();
        }

        float focusDistances[] = new float[3];
        camera.getParameters().getFocusDistances(focusDistances);

    }

    /**
     * Get Bitmap from camera
     * @return picture
     */
    private Camera.PictureCallback getPictureCallback() {

        Camera.PictureCallback picture = new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {

                Log.i(TAG, "onPictureTaken: size bytes photo: " + data.length );

            }
        };

        return picture;

    }

    ...

}

//And SurfaceView with Callback
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";
    SurfaceHolder holder;
    Camera camera;

    public CameraPreview( Context context, Camera _camera ) {
        super(context);

        camera = _camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        holder = getHolder();
        holder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        // The Surface has been created, now tell the camera where to draw the preview.
        try {

            camera.setPreviewDisplay(holder);
            camera.startPreview();

        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if( event.getAction() == MotionEvent.ACTION_DOWN ) {

            // Get the pointer's current position
            float x = event.getX();
            float y = event.getY();
            float touchMajor = event.getTouchMajor();
            float touchMinor = event.getTouchMinor();

            Rect touchRect = new Rect(
                    (int)(x - touchMajor/2),
                    (int)(y - touchMinor/2),
                    (int)(x + touchMajor/2),
                    (int)(y + touchMinor/2));

            ((CameraActivity)getContext())
                        .touchFocusCamera( touchRect );

        }

        return true;

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (this.holder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            camera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            camera.setPreviewDisplay(this.holder);
            camera.startPreview();

        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }

    }

    ...

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接